/src/gnutls/lib/inih/ini.c
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | /* inih -- simple .INI file parser  | 
2  |  |  | 
3  |  | inih is released under the New BSD license (see LICENSE.txt). Go to the project  | 
4  |  | home page for more info:  | 
5  |  |  | 
6  |  | https://github.com/benhoyt/inih  | 
7  |  |  | 
8  |  | */  | 
9  |  |  | 
10  |  | #include "config.h"  | 
11  |  |  | 
12  |  | #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)  | 
13  |  | #define _CRT_SECURE_NO_WARNINGS  | 
14  |  | #endif  | 
15  |  |  | 
16  |  | #include <stdio.h>  | 
17  |  | #include <ctype.h>  | 
18  |  | #include <string.h>  | 
19  |  |  | 
20  |  | #include "ini.h"  | 
21  |  |  | 
22  |  | #if !INI_USE_STACK  | 
23  |  | #include <stdlib.h>  | 
24  |  | #endif  | 
25  |  |  | 
26  |  | #define MAX_SECTION 50  | 
27  |  | #define MAX_NAME 50  | 
28  |  |  | 
29  |  | /* Strip whitespace chars off end of given string, in place. Return s. */  | 
30  |  | static char *rstrip(char *s)  | 
31  | 0  | { | 
32  | 0  |   char *p = s + strlen(s);  | 
33  | 0  |   while (p > s && isspace((unsigned char)(*--p)))  | 
34  | 0  |     *p = '\0';  | 
35  | 0  |   return s;  | 
36  | 0  | }  | 
37  |  |  | 
38  |  | /* Return pointer to first non-whitespace char in given string. */  | 
39  |  | static char *lskip(const char *s)  | 
40  | 0  | { | 
41  | 0  |   while (*s && isspace((unsigned char)(*s)))  | 
42  | 0  |     s++;  | 
43  | 0  |   return (char *)s;  | 
44  | 0  | }  | 
45  |  |  | 
46  |  | /* Return pointer to first char (of chars) or inline comment in given string,  | 
47  |  |    or pointer to null at end of string if neither found. Inline comment must  | 
48  |  |    be prefixed by a whitespace character to register as a comment. */  | 
49  |  | static char *find_chars_or_comment(const char *s, const char *chars)  | 
50  | 0  | { | 
51  | 0  | #if INI_ALLOW_INLINE_COMMENTS  | 
52  | 0  |   int was_space = 0;  | 
53  | 0  |   while (*s && (!chars || !strchr(chars, *s)) &&  | 
54  | 0  |          !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { | 
55  | 0  |     was_space = isspace((unsigned char)(*s));  | 
56  | 0  |     s++;  | 
57  | 0  |   }  | 
58  |  | #else  | 
59  |  |   while (*s && (!chars || !strchr(chars, *s))) { | 
60  |  |     s++;  | 
61  |  |   }  | 
62  |  | #endif  | 
63  | 0  |   return (char *)s;  | 
64  | 0  | }  | 
65  |  |  | 
66  |  | /* Version of strncpy that ensures dest (size bytes) is null-terminated. */  | 
67  |  | static char *strncpy0(char *dest, const char *src, size_t size)  | 
68  | 0  | { | 
69  | 0  |   strncpy(dest, src, size - 1);  | 
70  | 0  |   dest[size - 1] = '\0';  | 
71  | 0  |   return dest;  | 
72  | 0  | }  | 
73  |  |  | 
74  |  | /* See documentation in header file. */  | 
75  |  | int ini_parse_file(FILE *file, ini_handler handler, void *user)  | 
76  | 0  | { | 
77  |  |   /* Uses a fair bit of stack (use heap instead if you need to) */  | 
78  | 0  | #if INI_USE_STACK  | 
79  | 0  |   char line[INI_MAX_LINE];  | 
80  | 0  |   int max_line = INI_MAX_LINE;  | 
81  |  | #else  | 
82  |  |   char *line;  | 
83  |  |   int max_line = INI_INITIAL_ALLOC;  | 
84  |  | #endif  | 
85  |  | #if INI_ALLOW_REALLOC && !INI_USE_STACK  | 
86  |  |   char *new_line;  | 
87  |  |   int offset;  | 
88  |  | #endif  | 
89  | 0  |   char section[MAX_SECTION] = "";  | 
90  | 0  |   char prev_name[MAX_NAME] = "";  | 
91  |  | 
  | 
92  | 0  |   char *end;  | 
93  | 0  |   char *name;  | 
94  | 0  |   char *value;  | 
95  | 0  |   int lineno = 0;  | 
96  | 0  |   int error = 0;  | 
97  |  | 
  | 
98  |  | #if !INI_USE_STACK  | 
99  |  |   line = (char *)malloc(INI_INITIAL_ALLOC);  | 
100  |  |   if (!line) { | 
101  |  |     return -2;  | 
102  |  |   }  | 
103  |  | #endif  | 
104  |  | 
  | 
105  |  | #if INI_HANDLER_LINENO  | 
106  |  | #define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)  | 
107  |  | #else  | 
108  | 0  | #define HANDLER(u, s, n, v) handler(u, s, n, v)  | 
109  | 0  | #endif  | 
110  |  |  | 
111  |  |   /* Scan through stream line by line */  | 
112  | 0  |   while (fgets(line, max_line, file) != NULL) { | 
113  | 0  |     char *start;  | 
114  |  | #if INI_ALLOW_REALLOC && !INI_USE_STACK  | 
115  |  |     offset = strlen(line);  | 
116  |  |     while (offset == max_line - 1 && line[offset - 1] != '\n') { | 
117  |  |       max_line *= 2;  | 
118  |  |       if (max_line > INI_MAX_LINE)  | 
119  |  |         max_line = INI_MAX_LINE;  | 
120  |  |       new_line = realloc(line, max_line);  | 
121  |  |       if (!new_line) { | 
122  |  |         free(line);  | 
123  |  |         return -2;  | 
124  |  |       }  | 
125  |  |       line = new_line;  | 
126  |  |       if (fgets(line + offset, max_line - offset, file) ==  | 
127  |  |           NULL)  | 
128  |  |         break;  | 
129  |  |       if (max_line >= INI_MAX_LINE)  | 
130  |  |         break;  | 
131  |  |       offset += strlen(line + offset);  | 
132  |  |     }  | 
133  |  | #endif  | 
134  |  | 
  | 
135  | 0  |     lineno++;  | 
136  |  | 
  | 
137  | 0  |     start = line;  | 
138  | 0  | #if INI_ALLOW_BOM  | 
139  | 0  |     if (lineno == 1 && (unsigned char)start[0] == 0xEF &&  | 
140  | 0  |         (unsigned char)start[1] == 0xBB &&  | 
141  | 0  |         (unsigned char)start[2] == 0xBF) { | 
142  | 0  |       start += 3;  | 
143  | 0  |     }  | 
144  | 0  | #endif  | 
145  | 0  |     start = lskip(rstrip(start));  | 
146  |  | 
  | 
147  | 0  |     if (strchr(INI_START_COMMENT_PREFIXES, *start)) { | 
148  |  |       /* Start-of-line comment */  | 
149  | 0  |     }  | 
150  | 0  | #if INI_ALLOW_MULTILINE  | 
151  | 0  |     else if (*prev_name && *start && start > line) { | 
152  |  |       /* Non-blank line with leading whitespace, treat as continuation  | 
153  |  |          of previous name's value (as per Python configparser). */  | 
154  | 0  |       if (!HANDLER(user, section, prev_name, start) && !error)  | 
155  | 0  |         error = lineno;  | 
156  | 0  |     }  | 
157  | 0  | #endif  | 
158  | 0  |     else if (*start == '[') { | 
159  |  |       /* A "[section]" line */  | 
160  | 0  |       end = find_chars_or_comment(start + 1, "]");  | 
161  | 0  |       if (*end == ']') { | 
162  | 0  |         *end = '\0';  | 
163  | 0  |         strncpy0(section, start + 1, sizeof(section));  | 
164  | 0  |         *prev_name = '\0';  | 
165  | 0  |       } else if (!error) { | 
166  |  |         /* No ']' found on section line */  | 
167  | 0  |         error = lineno;  | 
168  | 0  |       }  | 
169  | 0  |     } else if (*start) { | 
170  |  |       /* Not a comment, must be a name[=:]value pair */  | 
171  | 0  |       end = find_chars_or_comment(start, "=:");  | 
172  | 0  |       if (*end == '=' || *end == ':') { | 
173  | 0  |         *end = '\0';  | 
174  | 0  |         name = rstrip(start);  | 
175  | 0  |         value = end + 1;  | 
176  | 0  | #if INI_ALLOW_INLINE_COMMENTS  | 
177  | 0  |         end = find_chars_or_comment(value, NULL);  | 
178  | 0  |         if (*end)  | 
179  | 0  |           *end = '\0';  | 
180  | 0  | #endif  | 
181  | 0  |         value = lskip(value);  | 
182  | 0  |         rstrip(value);  | 
183  |  |  | 
184  |  |         /* Valid name[=:]value pair found, call handler */  | 
185  | 0  |         strncpy0(prev_name, name, sizeof(prev_name));  | 
186  | 0  |         if (!HANDLER(user, section, name, value) &&  | 
187  | 0  |             !error)  | 
188  | 0  |           error = lineno;  | 
189  | 0  |       } else if (!error) { | 
190  |  |         /* No '=' or ':' found on name[=:]value line */  | 
191  | 0  |         error = lineno;  | 
192  | 0  |       }  | 
193  | 0  |     }  | 
194  | 0  | #if INI_STOP_ON_FIRST_ERROR  | 
195  | 0  |     if (error)  | 
196  | 0  |       break;  | 
197  | 0  | #endif  | 
198  | 0  |   }  | 
199  |  | 
  | 
200  |  | #if !INI_USE_STACK  | 
201  |  |   free(line);  | 
202  |  | #endif  | 
203  |  | 
  | 
204  | 0  |   return error;  | 
205  | 0  | }  |