ltk

GUI toolkit for X11 (WIP)
git clone git://lumidify.org/ltk.git (fast, but not encrypted)
git clone https://lumidify.org/ltk.git (encrypted, but very slow)
git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltk.git (over tor)
Log | Files | Refs | README | LICENSE

ini.c (5350B)


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