Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | |
michael@0 | 2 | /*-------------------------------------------------------------------------*/ |
michael@0 | 3 | /** |
michael@0 | 4 | @file iniparser.c |
michael@0 | 5 | @author N. Devillard |
michael@0 | 6 | @date Sep 2007 |
michael@0 | 7 | @version 3.0 |
michael@0 | 8 | @brief Parser for ini files. |
michael@0 | 9 | */ |
michael@0 | 10 | /*--------------------------------------------------------------------------*/ |
michael@0 | 11 | /* |
michael@0 | 12 | $Id: iniparser.c,v 2.19 2011-03-02 20:15:13 ndevilla Exp $ |
michael@0 | 13 | $Revision: 2.19 $ |
michael@0 | 14 | $Date: 2011-03-02 20:15:13 $ |
michael@0 | 15 | */ |
michael@0 | 16 | /*---------------------------- Includes ------------------------------------*/ |
michael@0 | 17 | #include <ctype.h> |
michael@0 | 18 | #include "iniparser.h" |
michael@0 | 19 | |
michael@0 | 20 | /*---------------------------- Defines -------------------------------------*/ |
michael@0 | 21 | #define ASCIILINESZ (1024) |
michael@0 | 22 | #define INI_INVALID_KEY ((char*)-1) |
michael@0 | 23 | |
michael@0 | 24 | /*--------------------------------------------------------------------------- |
michael@0 | 25 | Private to this module |
michael@0 | 26 | ---------------------------------------------------------------------------*/ |
michael@0 | 27 | /** |
michael@0 | 28 | * This enum stores the status for each parsed line (internal use only). |
michael@0 | 29 | */ |
michael@0 | 30 | typedef enum _line_status_ { |
michael@0 | 31 | LINE_UNPROCESSED, |
michael@0 | 32 | LINE_ERROR, |
michael@0 | 33 | LINE_EMPTY, |
michael@0 | 34 | LINE_COMMENT, |
michael@0 | 35 | LINE_SECTION, |
michael@0 | 36 | LINE_VALUE |
michael@0 | 37 | } line_status ; |
michael@0 | 38 | |
michael@0 | 39 | /*-------------------------------------------------------------------------*/ |
michael@0 | 40 | /** |
michael@0 | 41 | @brief Convert a string to lowercase. |
michael@0 | 42 | @param s String to convert. |
michael@0 | 43 | @return ptr to statically allocated string. |
michael@0 | 44 | |
michael@0 | 45 | This function returns a pointer to a statically allocated string |
michael@0 | 46 | containing a lowercased version of the input string. Do not free |
michael@0 | 47 | or modify the returned string! Since the returned string is statically |
michael@0 | 48 | allocated, it will be modified at each function call (not re-entrant). |
michael@0 | 49 | */ |
michael@0 | 50 | /*--------------------------------------------------------------------------*/ |
michael@0 | 51 | static char * strlwc(char * s) |
michael@0 | 52 | { |
michael@0 | 53 | static char l[ASCIILINESZ+1]; |
michael@0 | 54 | int i ; |
michael@0 | 55 | |
michael@0 | 56 | if (s==NULL) return NULL ; |
michael@0 | 57 | memset(l, 0, ASCIILINESZ+1); |
michael@0 | 58 | i=0 ; |
michael@0 | 59 | while (s[i] && i<ASCIILINESZ) { |
michael@0 | 60 | l[i] = (char)tolower((int)s[i]); |
michael@0 | 61 | i++ ; |
michael@0 | 62 | } |
michael@0 | 63 | l[ASCIILINESZ]=(char)0; |
michael@0 | 64 | return l ; |
michael@0 | 65 | } |
michael@0 | 66 | |
michael@0 | 67 | /*-------------------------------------------------------------------------*/ |
michael@0 | 68 | /** |
michael@0 | 69 | @brief Remove blanks at the beginning and the end of a string. |
michael@0 | 70 | @param s String to parse. |
michael@0 | 71 | @return ptr to statically allocated string. |
michael@0 | 72 | |
michael@0 | 73 | This function returns a pointer to a statically allocated string, |
michael@0 | 74 | which is identical to the input string, except that all blank |
michael@0 | 75 | characters at the end and the beg. of the string have been removed. |
michael@0 | 76 | Do not free or modify the returned string! Since the returned string |
michael@0 | 77 | is statically allocated, it will be modified at each function call |
michael@0 | 78 | (not re-entrant). |
michael@0 | 79 | */ |
michael@0 | 80 | /*--------------------------------------------------------------------------*/ |
michael@0 | 81 | static char * strstrip(char * s) |
michael@0 | 82 | { |
michael@0 | 83 | static char l[ASCIILINESZ+1]; |
michael@0 | 84 | char * last ; |
michael@0 | 85 | |
michael@0 | 86 | if (s==NULL) return NULL ; |
michael@0 | 87 | |
michael@0 | 88 | while (isspace((int)*s) && *s) s++; |
michael@0 | 89 | memset(l, 0, ASCIILINESZ+1); |
michael@0 | 90 | strcpy(l, s); |
michael@0 | 91 | last = l + strlen(l); |
michael@0 | 92 | while (last > l) { |
michael@0 | 93 | if (!isspace((int)*(last-1))) |
michael@0 | 94 | break ; |
michael@0 | 95 | last -- ; |
michael@0 | 96 | } |
michael@0 | 97 | *last = (char)0; |
michael@0 | 98 | return (char*)l ; |
michael@0 | 99 | } |
michael@0 | 100 | |
michael@0 | 101 | /*-------------------------------------------------------------------------*/ |
michael@0 | 102 | /** |
michael@0 | 103 | @brief Get number of sections in a dictionary |
michael@0 | 104 | @param d Dictionary to examine |
michael@0 | 105 | @return int Number of sections found in dictionary |
michael@0 | 106 | |
michael@0 | 107 | This function returns the number of sections found in a dictionary. |
michael@0 | 108 | The test to recognize sections is done on the string stored in the |
michael@0 | 109 | dictionary: a section name is given as "section" whereas a key is |
michael@0 | 110 | stored as "section:key", thus the test looks for entries that do not |
michael@0 | 111 | contain a colon. |
michael@0 | 112 | |
michael@0 | 113 | This clearly fails in the case a section name contains a colon, but |
michael@0 | 114 | this should simply be avoided. |
michael@0 | 115 | |
michael@0 | 116 | This function returns -1 in case of error. |
michael@0 | 117 | */ |
michael@0 | 118 | /*--------------------------------------------------------------------------*/ |
michael@0 | 119 | int iniparser_getnsec(dictionary * d) |
michael@0 | 120 | { |
michael@0 | 121 | int i ; |
michael@0 | 122 | int nsec ; |
michael@0 | 123 | |
michael@0 | 124 | if (d==NULL) return -1 ; |
michael@0 | 125 | nsec=0 ; |
michael@0 | 126 | for (i=0 ; i<d->size ; i++) { |
michael@0 | 127 | if (d->key[i]==NULL) |
michael@0 | 128 | continue ; |
michael@0 | 129 | if (strchr(d->key[i], ':')==NULL) { |
michael@0 | 130 | nsec ++ ; |
michael@0 | 131 | } |
michael@0 | 132 | } |
michael@0 | 133 | return nsec ; |
michael@0 | 134 | } |
michael@0 | 135 | |
michael@0 | 136 | /*-------------------------------------------------------------------------*/ |
michael@0 | 137 | /** |
michael@0 | 138 | @brief Get name for section n in a dictionary. |
michael@0 | 139 | @param d Dictionary to examine |
michael@0 | 140 | @param n Section number (from 0 to nsec-1). |
michael@0 | 141 | @return Pointer to char string |
michael@0 | 142 | |
michael@0 | 143 | This function locates the n-th section in a dictionary and returns |
michael@0 | 144 | its name as a pointer to a string statically allocated inside the |
michael@0 | 145 | dictionary. Do not free or modify the returned string! |
michael@0 | 146 | |
michael@0 | 147 | This function returns NULL in case of error. |
michael@0 | 148 | */ |
michael@0 | 149 | /*--------------------------------------------------------------------------*/ |
michael@0 | 150 | char * iniparser_getsecname(dictionary * d, int n) |
michael@0 | 151 | { |
michael@0 | 152 | int i ; |
michael@0 | 153 | int foundsec ; |
michael@0 | 154 | |
michael@0 | 155 | if (d==NULL || n<0) return NULL ; |
michael@0 | 156 | foundsec=0 ; |
michael@0 | 157 | for (i=0 ; i<d->size ; i++) { |
michael@0 | 158 | if (d->key[i]==NULL) |
michael@0 | 159 | continue ; |
michael@0 | 160 | if (strchr(d->key[i], ':')==NULL) { |
michael@0 | 161 | foundsec++ ; |
michael@0 | 162 | if (foundsec>n) |
michael@0 | 163 | break ; |
michael@0 | 164 | } |
michael@0 | 165 | } |
michael@0 | 166 | if (foundsec<=n) { |
michael@0 | 167 | return NULL ; |
michael@0 | 168 | } |
michael@0 | 169 | return d->key[i] ; |
michael@0 | 170 | } |
michael@0 | 171 | |
michael@0 | 172 | /*-------------------------------------------------------------------------*/ |
michael@0 | 173 | /** |
michael@0 | 174 | @brief Dump a dictionary to an opened file pointer. |
michael@0 | 175 | @param d Dictionary to dump. |
michael@0 | 176 | @param f Opened file pointer to dump to. |
michael@0 | 177 | @return void |
michael@0 | 178 | |
michael@0 | 179 | This function prints out the contents of a dictionary, one element by |
michael@0 | 180 | line, onto the provided file pointer. It is OK to specify @c stderr |
michael@0 | 181 | or @c stdout as output files. This function is meant for debugging |
michael@0 | 182 | purposes mostly. |
michael@0 | 183 | */ |
michael@0 | 184 | /*--------------------------------------------------------------------------*/ |
michael@0 | 185 | void iniparser_dump(dictionary * d, FILE * f) |
michael@0 | 186 | { |
michael@0 | 187 | int i ; |
michael@0 | 188 | |
michael@0 | 189 | if (d==NULL || f==NULL) return ; |
michael@0 | 190 | for (i=0 ; i<d->size ; i++) { |
michael@0 | 191 | if (d->key[i]==NULL) |
michael@0 | 192 | continue ; |
michael@0 | 193 | if (d->val[i]!=NULL) { |
michael@0 | 194 | fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]); |
michael@0 | 195 | } else { |
michael@0 | 196 | fprintf(f, "[%s]=UNDEF\n", d->key[i]); |
michael@0 | 197 | } |
michael@0 | 198 | } |
michael@0 | 199 | return ; |
michael@0 | 200 | } |
michael@0 | 201 | |
michael@0 | 202 | /*-------------------------------------------------------------------------*/ |
michael@0 | 203 | /** |
michael@0 | 204 | @brief Save a dictionary to a loadable ini file |
michael@0 | 205 | @param d Dictionary to dump |
michael@0 | 206 | @param f Opened file pointer to dump to |
michael@0 | 207 | @return void |
michael@0 | 208 | |
michael@0 | 209 | This function dumps a given dictionary into a loadable ini file. |
michael@0 | 210 | It is Ok to specify @c stderr or @c stdout as output files. |
michael@0 | 211 | */ |
michael@0 | 212 | /*--------------------------------------------------------------------------*/ |
michael@0 | 213 | void iniparser_dump_ini(dictionary * d, FILE * f) |
michael@0 | 214 | { |
michael@0 | 215 | int i, j ; |
michael@0 | 216 | char keym[ASCIILINESZ+1]; |
michael@0 | 217 | int nsec ; |
michael@0 | 218 | char * secname ; |
michael@0 | 219 | int seclen ; |
michael@0 | 220 | |
michael@0 | 221 | if (d==NULL || f==NULL) return ; |
michael@0 | 222 | |
michael@0 | 223 | nsec = iniparser_getnsec(d); |
michael@0 | 224 | if (nsec<1) { |
michael@0 | 225 | /* No section in file: dump all keys as they are */ |
michael@0 | 226 | for (i=0 ; i<d->size ; i++) { |
michael@0 | 227 | if (d->key[i]==NULL) |
michael@0 | 228 | continue ; |
michael@0 | 229 | fprintf(f, "%s = %s\n", d->key[i], d->val[i]); |
michael@0 | 230 | } |
michael@0 | 231 | return ; |
michael@0 | 232 | } |
michael@0 | 233 | for (i=0 ; i<nsec ; i++) { |
michael@0 | 234 | secname = iniparser_getsecname(d, i) ; |
michael@0 | 235 | seclen = (int)strlen(secname); |
michael@0 | 236 | fprintf(f, "\n[%s]\n", secname); |
michael@0 | 237 | sprintf(keym, "%s:", secname); |
michael@0 | 238 | for (j=0 ; j<d->size ; j++) { |
michael@0 | 239 | if (d->key[j]==NULL) |
michael@0 | 240 | continue ; |
michael@0 | 241 | if (!strncmp(d->key[j], keym, seclen+1)) { |
michael@0 | 242 | fprintf(f, |
michael@0 | 243 | "%-30s = %s\n", |
michael@0 | 244 | d->key[j]+seclen+1, |
michael@0 | 245 | d->val[j] ? d->val[j] : ""); |
michael@0 | 246 | } |
michael@0 | 247 | } |
michael@0 | 248 | } |
michael@0 | 249 | fprintf(f, "\n"); |
michael@0 | 250 | return ; |
michael@0 | 251 | } |
michael@0 | 252 | |
michael@0 | 253 | /*-------------------------------------------------------------------------*/ |
michael@0 | 254 | /** |
michael@0 | 255 | @brief Get the string associated to a key |
michael@0 | 256 | @param d Dictionary to search |
michael@0 | 257 | @param key Key string to look for |
michael@0 | 258 | @param def Default value to return if key not found. |
michael@0 | 259 | @return pointer to statically allocated character string |
michael@0 | 260 | |
michael@0 | 261 | This function queries a dictionary for a key. A key as read from an |
michael@0 | 262 | ini file is given as "section:key". If the key cannot be found, |
michael@0 | 263 | the pointer passed as 'def' is returned. |
michael@0 | 264 | The returned char pointer is pointing to a string allocated in |
michael@0 | 265 | the dictionary, do not free or modify it. |
michael@0 | 266 | */ |
michael@0 | 267 | /*--------------------------------------------------------------------------*/ |
michael@0 | 268 | char * iniparser_getstring(dictionary * d, char * key, char * def) |
michael@0 | 269 | { |
michael@0 | 270 | char * lc_key ; |
michael@0 | 271 | char * sval ; |
michael@0 | 272 | |
michael@0 | 273 | if (d==NULL || key==NULL) |
michael@0 | 274 | return def ; |
michael@0 | 275 | |
michael@0 | 276 | lc_key = strlwc(key); |
michael@0 | 277 | sval = dictionary_get(d, lc_key, def); |
michael@0 | 278 | return sval ; |
michael@0 | 279 | } |
michael@0 | 280 | |
michael@0 | 281 | /*-------------------------------------------------------------------------*/ |
michael@0 | 282 | /** |
michael@0 | 283 | @brief Get the string associated to a key, convert to an int |
michael@0 | 284 | @param d Dictionary to search |
michael@0 | 285 | @param key Key string to look for |
michael@0 | 286 | @param notfound Value to return in case of error |
michael@0 | 287 | @return integer |
michael@0 | 288 | |
michael@0 | 289 | This function queries a dictionary for a key. A key as read from an |
michael@0 | 290 | ini file is given as "section:key". If the key cannot be found, |
michael@0 | 291 | the notfound value is returned. |
michael@0 | 292 | |
michael@0 | 293 | Supported values for integers include the usual C notation |
michael@0 | 294 | so decimal, octal (starting with 0) and hexadecimal (starting with 0x) |
michael@0 | 295 | are supported. Examples: |
michael@0 | 296 | |
michael@0 | 297 | "42" -> 42 |
michael@0 | 298 | "042" -> 34 (octal -> decimal) |
michael@0 | 299 | "0x42" -> 66 (hexa -> decimal) |
michael@0 | 300 | |
michael@0 | 301 | Warning: the conversion may overflow in various ways. Conversion is |
michael@0 | 302 | totally outsourced to strtol(), see the associated man page for overflow |
michael@0 | 303 | handling. |
michael@0 | 304 | |
michael@0 | 305 | Credits: Thanks to A. Becker for suggesting strtol() |
michael@0 | 306 | */ |
michael@0 | 307 | /*--------------------------------------------------------------------------*/ |
michael@0 | 308 | int iniparser_getint(dictionary * d, char * key, int notfound) |
michael@0 | 309 | { |
michael@0 | 310 | char * str ; |
michael@0 | 311 | |
michael@0 | 312 | str = iniparser_getstring(d, key, INI_INVALID_KEY); |
michael@0 | 313 | if (str==INI_INVALID_KEY) return notfound ; |
michael@0 | 314 | return (int)strtol(str, NULL, 0); |
michael@0 | 315 | } |
michael@0 | 316 | |
michael@0 | 317 | /*-------------------------------------------------------------------------*/ |
michael@0 | 318 | /** |
michael@0 | 319 | @brief Get the string associated to a key, convert to a double |
michael@0 | 320 | @param d Dictionary to search |
michael@0 | 321 | @param key Key string to look for |
michael@0 | 322 | @param notfound Value to return in case of error |
michael@0 | 323 | @return double |
michael@0 | 324 | |
michael@0 | 325 | This function queries a dictionary for a key. A key as read from an |
michael@0 | 326 | ini file is given as "section:key". If the key cannot be found, |
michael@0 | 327 | the notfound value is returned. |
michael@0 | 328 | */ |
michael@0 | 329 | /*--------------------------------------------------------------------------*/ |
michael@0 | 330 | double iniparser_getdouble(dictionary * d, char * key, double notfound) |
michael@0 | 331 | { |
michael@0 | 332 | char * str ; |
michael@0 | 333 | |
michael@0 | 334 | str = iniparser_getstring(d, key, INI_INVALID_KEY); |
michael@0 | 335 | if (str==INI_INVALID_KEY) return notfound ; |
michael@0 | 336 | return atof(str); |
michael@0 | 337 | } |
michael@0 | 338 | |
michael@0 | 339 | /*-------------------------------------------------------------------------*/ |
michael@0 | 340 | /** |
michael@0 | 341 | @brief Get the string associated to a key, convert to a boolean |
michael@0 | 342 | @param d Dictionary to search |
michael@0 | 343 | @param key Key string to look for |
michael@0 | 344 | @param notfound Value to return in case of error |
michael@0 | 345 | @return integer |
michael@0 | 346 | |
michael@0 | 347 | This function queries a dictionary for a key. A key as read from an |
michael@0 | 348 | ini file is given as "section:key". If the key cannot be found, |
michael@0 | 349 | the notfound value is returned. |
michael@0 | 350 | |
michael@0 | 351 | A true boolean is found if one of the following is matched: |
michael@0 | 352 | |
michael@0 | 353 | - A string starting with 'y' |
michael@0 | 354 | - A string starting with 'Y' |
michael@0 | 355 | - A string starting with 't' |
michael@0 | 356 | - A string starting with 'T' |
michael@0 | 357 | - A string starting with '1' |
michael@0 | 358 | |
michael@0 | 359 | A false boolean is found if one of the following is matched: |
michael@0 | 360 | |
michael@0 | 361 | - A string starting with 'n' |
michael@0 | 362 | - A string starting with 'N' |
michael@0 | 363 | - A string starting with 'f' |
michael@0 | 364 | - A string starting with 'F' |
michael@0 | 365 | - A string starting with '0' |
michael@0 | 366 | |
michael@0 | 367 | The notfound value returned if no boolean is identified, does not |
michael@0 | 368 | necessarily have to be 0 or 1. |
michael@0 | 369 | */ |
michael@0 | 370 | /*--------------------------------------------------------------------------*/ |
michael@0 | 371 | int iniparser_getboolean(dictionary * d, char * key, int notfound) |
michael@0 | 372 | { |
michael@0 | 373 | char * c ; |
michael@0 | 374 | int ret ; |
michael@0 | 375 | |
michael@0 | 376 | c = iniparser_getstring(d, key, INI_INVALID_KEY); |
michael@0 | 377 | if (c==INI_INVALID_KEY) return notfound ; |
michael@0 | 378 | if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') { |
michael@0 | 379 | ret = 1 ; |
michael@0 | 380 | } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') { |
michael@0 | 381 | ret = 0 ; |
michael@0 | 382 | } else { |
michael@0 | 383 | ret = notfound ; |
michael@0 | 384 | } |
michael@0 | 385 | return ret; |
michael@0 | 386 | } |
michael@0 | 387 | |
michael@0 | 388 | /*-------------------------------------------------------------------------*/ |
michael@0 | 389 | /** |
michael@0 | 390 | @brief Finds out if a given entry exists in a dictionary |
michael@0 | 391 | @param ini Dictionary to search |
michael@0 | 392 | @param entry Name of the entry to look for |
michael@0 | 393 | @return integer 1 if entry exists, 0 otherwise |
michael@0 | 394 | |
michael@0 | 395 | Finds out if a given entry exists in the dictionary. Since sections |
michael@0 | 396 | are stored as keys with NULL associated values, this is the only way |
michael@0 | 397 | of querying for the presence of sections in a dictionary. |
michael@0 | 398 | */ |
michael@0 | 399 | /*--------------------------------------------------------------------------*/ |
michael@0 | 400 | int iniparser_find_entry( |
michael@0 | 401 | dictionary * ini, |
michael@0 | 402 | char * entry |
michael@0 | 403 | ) |
michael@0 | 404 | { |
michael@0 | 405 | int found=0 ; |
michael@0 | 406 | if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) { |
michael@0 | 407 | found = 1 ; |
michael@0 | 408 | } |
michael@0 | 409 | return found ; |
michael@0 | 410 | } |
michael@0 | 411 | |
michael@0 | 412 | /*-------------------------------------------------------------------------*/ |
michael@0 | 413 | /** |
michael@0 | 414 | @brief Set an entry in a dictionary. |
michael@0 | 415 | @param ini Dictionary to modify. |
michael@0 | 416 | @param entry Entry to modify (entry name) |
michael@0 | 417 | @param val New value to associate to the entry. |
michael@0 | 418 | @return int 0 if Ok, -1 otherwise. |
michael@0 | 419 | |
michael@0 | 420 | If the given entry can be found in the dictionary, it is modified to |
michael@0 | 421 | contain the provided value. If it cannot be found, -1 is returned. |
michael@0 | 422 | It is Ok to set val to NULL. |
michael@0 | 423 | */ |
michael@0 | 424 | /*--------------------------------------------------------------------------*/ |
michael@0 | 425 | int iniparser_set(dictionary * ini, char * entry, char * val) |
michael@0 | 426 | { |
michael@0 | 427 | return dictionary_set(ini, strlwc(entry), val) ; |
michael@0 | 428 | } |
michael@0 | 429 | |
michael@0 | 430 | /*-------------------------------------------------------------------------*/ |
michael@0 | 431 | /** |
michael@0 | 432 | @brief Delete an entry in a dictionary |
michael@0 | 433 | @param ini Dictionary to modify |
michael@0 | 434 | @param entry Entry to delete (entry name) |
michael@0 | 435 | @return void |
michael@0 | 436 | |
michael@0 | 437 | If the given entry can be found, it is deleted from the dictionary. |
michael@0 | 438 | */ |
michael@0 | 439 | /*--------------------------------------------------------------------------*/ |
michael@0 | 440 | void iniparser_unset(dictionary * ini, char * entry) |
michael@0 | 441 | { |
michael@0 | 442 | dictionary_unset(ini, strlwc(entry)); |
michael@0 | 443 | } |
michael@0 | 444 | |
michael@0 | 445 | /*-------------------------------------------------------------------------*/ |
michael@0 | 446 | /** |
michael@0 | 447 | @brief Load a single line from an INI file |
michael@0 | 448 | @param input_line Input line, may be concatenated multi-line input |
michael@0 | 449 | @param section Output space to store section |
michael@0 | 450 | @param key Output space to store key |
michael@0 | 451 | @param value Output space to store value |
michael@0 | 452 | @return line_status value |
michael@0 | 453 | */ |
michael@0 | 454 | /*--------------------------------------------------------------------------*/ |
michael@0 | 455 | static line_status iniparser_line( |
michael@0 | 456 | char * input_line, |
michael@0 | 457 | char * section, |
michael@0 | 458 | char * key, |
michael@0 | 459 | char * value) |
michael@0 | 460 | { |
michael@0 | 461 | line_status sta ; |
michael@0 | 462 | char line[ASCIILINESZ+1]; |
michael@0 | 463 | int len ; |
michael@0 | 464 | |
michael@0 | 465 | strcpy(line, strstrip(input_line)); |
michael@0 | 466 | len = (int)strlen(line); |
michael@0 | 467 | |
michael@0 | 468 | sta = LINE_UNPROCESSED ; |
michael@0 | 469 | if (len<1) { |
michael@0 | 470 | /* Empty line */ |
michael@0 | 471 | sta = LINE_EMPTY ; |
michael@0 | 472 | } else if (line[0]=='#' || line[0]==';') { |
michael@0 | 473 | /* Comment line */ |
michael@0 | 474 | sta = LINE_COMMENT ; |
michael@0 | 475 | } else if (line[0]=='[' && line[len-1]==']') { |
michael@0 | 476 | /* Section name */ |
michael@0 | 477 | sscanf(line, "[%[^]]", section); |
michael@0 | 478 | strcpy(section, strstrip(section)); |
michael@0 | 479 | strcpy(section, strlwc(section)); |
michael@0 | 480 | sta = LINE_SECTION ; |
michael@0 | 481 | } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2 |
michael@0 | 482 | || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2 |
michael@0 | 483 | || sscanf (line, "%[^=] = %[^;#]", key, value) == 2) { |
michael@0 | 484 | /* Usual key=value, with or without comments */ |
michael@0 | 485 | strcpy(key, strstrip(key)); |
michael@0 | 486 | strcpy(key, strlwc(key)); |
michael@0 | 487 | strcpy(value, strstrip(value)); |
michael@0 | 488 | /* |
michael@0 | 489 | * sscanf cannot handle '' or "" as empty values |
michael@0 | 490 | * this is done here |
michael@0 | 491 | */ |
michael@0 | 492 | if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) { |
michael@0 | 493 | value[0]=0 ; |
michael@0 | 494 | } |
michael@0 | 495 | sta = LINE_VALUE ; |
michael@0 | 496 | } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2 |
michael@0 | 497 | || sscanf(line, "%[^=] %[=]", key, value) == 2) { |
michael@0 | 498 | /* |
michael@0 | 499 | * Special cases: |
michael@0 | 500 | * key= |
michael@0 | 501 | * key=; |
michael@0 | 502 | * key=# |
michael@0 | 503 | */ |
michael@0 | 504 | strcpy(key, strstrip(key)); |
michael@0 | 505 | strcpy(key, strlwc(key)); |
michael@0 | 506 | value[0]=0 ; |
michael@0 | 507 | sta = LINE_VALUE ; |
michael@0 | 508 | } else { |
michael@0 | 509 | /* Generate syntax error */ |
michael@0 | 510 | sta = LINE_ERROR ; |
michael@0 | 511 | } |
michael@0 | 512 | return sta ; |
michael@0 | 513 | } |
michael@0 | 514 | |
michael@0 | 515 | /*-------------------------------------------------------------------------*/ |
michael@0 | 516 | /** |
michael@0 | 517 | @brief Parse an ini file and return an allocated dictionary object |
michael@0 | 518 | @param ininame Name of the ini file to read. |
michael@0 | 519 | @return Pointer to newly allocated dictionary |
michael@0 | 520 | |
michael@0 | 521 | This is the parser for ini files. This function is called, providing |
michael@0 | 522 | the name of the file to be read. It returns a dictionary object that |
michael@0 | 523 | should not be accessed directly, but through accessor functions |
michael@0 | 524 | instead. |
michael@0 | 525 | |
michael@0 | 526 | The returned dictionary must be freed using iniparser_freedict(). |
michael@0 | 527 | */ |
michael@0 | 528 | /*--------------------------------------------------------------------------*/ |
michael@0 | 529 | dictionary * iniparser_load(char * ininame) |
michael@0 | 530 | { |
michael@0 | 531 | FILE * in ; |
michael@0 | 532 | |
michael@0 | 533 | char line [ASCIILINESZ+1] ; |
michael@0 | 534 | char section [ASCIILINESZ+1] ; |
michael@0 | 535 | char key [ASCIILINESZ+1] ; |
michael@0 | 536 | char tmp [ASCIILINESZ+1] ; |
michael@0 | 537 | char val [ASCIILINESZ+1] ; |
michael@0 | 538 | |
michael@0 | 539 | int last=0 ; |
michael@0 | 540 | int len ; |
michael@0 | 541 | int lineno=0 ; |
michael@0 | 542 | int errs=0; |
michael@0 | 543 | |
michael@0 | 544 | dictionary * dict ; |
michael@0 | 545 | |
michael@0 | 546 | if ((in=fopen(ininame, "r"))==NULL) { |
michael@0 | 547 | fprintf(stderr, "iniparser: cannot open %s\n", ininame); |
michael@0 | 548 | return NULL ; |
michael@0 | 549 | } |
michael@0 | 550 | |
michael@0 | 551 | dict = dictionary_new(0) ; |
michael@0 | 552 | if (!dict) { |
michael@0 | 553 | fclose(in); |
michael@0 | 554 | return NULL ; |
michael@0 | 555 | } |
michael@0 | 556 | |
michael@0 | 557 | memset(line, 0, ASCIILINESZ); |
michael@0 | 558 | memset(section, 0, ASCIILINESZ); |
michael@0 | 559 | memset(key, 0, ASCIILINESZ); |
michael@0 | 560 | memset(val, 0, ASCIILINESZ); |
michael@0 | 561 | last=0 ; |
michael@0 | 562 | |
michael@0 | 563 | while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) { |
michael@0 | 564 | lineno++ ; |
michael@0 | 565 | len = (int)strlen(line)-1; |
michael@0 | 566 | if (len==0) |
michael@0 | 567 | continue; |
michael@0 | 568 | /* Safety check against buffer overflows */ |
michael@0 | 569 | if (line[len]!='\n') { |
michael@0 | 570 | fprintf(stderr, |
michael@0 | 571 | "iniparser: input line too long in %s (%d)\n", |
michael@0 | 572 | ininame, |
michael@0 | 573 | lineno); |
michael@0 | 574 | dictionary_del(dict); |
michael@0 | 575 | fclose(in); |
michael@0 | 576 | return NULL ; |
michael@0 | 577 | } |
michael@0 | 578 | /* Get rid of \n and spaces at end of line */ |
michael@0 | 579 | while ((len>=0) && |
michael@0 | 580 | ((line[len]=='\n') || (isspace(line[len])))) { |
michael@0 | 581 | line[len]=0 ; |
michael@0 | 582 | len-- ; |
michael@0 | 583 | } |
michael@0 | 584 | /* Detect multi-line */ |
michael@0 | 585 | if (line[len]=='\\') { |
michael@0 | 586 | /* Multi-line value */ |
michael@0 | 587 | last=len ; |
michael@0 | 588 | continue ; |
michael@0 | 589 | } else { |
michael@0 | 590 | last=0 ; |
michael@0 | 591 | } |
michael@0 | 592 | switch (iniparser_line(line, section, key, val)) { |
michael@0 | 593 | case LINE_EMPTY: |
michael@0 | 594 | case LINE_COMMENT: |
michael@0 | 595 | break ; |
michael@0 | 596 | |
michael@0 | 597 | case LINE_SECTION: |
michael@0 | 598 | errs = dictionary_set(dict, section, NULL); |
michael@0 | 599 | break ; |
michael@0 | 600 | |
michael@0 | 601 | case LINE_VALUE: |
michael@0 | 602 | sprintf(tmp, "%s:%s", section, key); |
michael@0 | 603 | errs = dictionary_set(dict, tmp, val) ; |
michael@0 | 604 | break ; |
michael@0 | 605 | |
michael@0 | 606 | case LINE_ERROR: |
michael@0 | 607 | fprintf(stderr, "iniparser: syntax error in %s (%d):\n", |
michael@0 | 608 | ininame, |
michael@0 | 609 | lineno); |
michael@0 | 610 | fprintf(stderr, "-> %s\n", line); |
michael@0 | 611 | errs++ ; |
michael@0 | 612 | break; |
michael@0 | 613 | |
michael@0 | 614 | default: |
michael@0 | 615 | break ; |
michael@0 | 616 | } |
michael@0 | 617 | memset(line, 0, ASCIILINESZ); |
michael@0 | 618 | last=0; |
michael@0 | 619 | if (errs<0) { |
michael@0 | 620 | fprintf(stderr, "iniparser: memory allocation failure\n"); |
michael@0 | 621 | break ; |
michael@0 | 622 | } |
michael@0 | 623 | } |
michael@0 | 624 | if (errs) { |
michael@0 | 625 | dictionary_del(dict); |
michael@0 | 626 | dict = NULL ; |
michael@0 | 627 | } |
michael@0 | 628 | fclose(in); |
michael@0 | 629 | return dict ; |
michael@0 | 630 | } |
michael@0 | 631 | |
michael@0 | 632 | /*-------------------------------------------------------------------------*/ |
michael@0 | 633 | /** |
michael@0 | 634 | @brief Free all memory associated to an ini dictionary |
michael@0 | 635 | @param d Dictionary to free |
michael@0 | 636 | @return void |
michael@0 | 637 | |
michael@0 | 638 | Free all memory associated to an ini dictionary. |
michael@0 | 639 | It is mandatory to call this function before the dictionary object |
michael@0 | 640 | gets out of the current context. |
michael@0 | 641 | */ |
michael@0 | 642 | /*--------------------------------------------------------------------------*/ |
michael@0 | 643 | void iniparser_freedict(dictionary * d) |
michael@0 | 644 | { |
michael@0 | 645 | dictionary_del(d); |
michael@0 | 646 | } |
michael@0 | 647 | |
michael@0 | 648 | /* vim: set ts=4 et sw=4 tw=75 */ |