cnfparse.c

Small library to make config parsing easier
git clone git://git.finwo.net/lib/cnfparse.c
Log | Files | Refs | README | LICENSE

cnfparse.c (4108B)


      1 #ifdef __cplusplus
      2 extern "C" {
      3 #endif
      4 
      5 // includes/system {{{
      6 #include <stdbool.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 // }}}
     10 // includes/deps {{{
     11 #include "finwo/buf.h"
     12 // }}}
     13 // includes/local {{{
     14 #include "cnfparse.h"
     15 // }}}
     16 
     17 #define STATE_BLANK   0
     18 #define STATE_COMMENT 1
     19 #define STATE_QUOTE   2
     20 #define STATE_WORD    3
     21 
     22 // cnf_directive_append(subject,value,len) {{{
     23 // caller frees
     24 void cnf_directive_append(struct cnf_directive *subject, char *value, size_t len) {
     25   if (!value) return;
     26   char *val = calloc(1, len+1);
     27   memcpy(val, value, len);
     28   if (!subject->name) {
     29     subject->name = val;
     30     return;
     31   }
     32   subject->argv = realloc(subject->argv, (subject->argc + 1) * sizeof(char*));
     33   subject->argv[subject->argc++] = val;
     34 }
     35 // }}}
     36 // cnf_directive_free(subject) {{{
     37 void cnf_directive_free(struct cnf_directive *subject) {
     38   if (!subject) return;
     39   if (subject->name) {
     40     free(subject->name);
     41   }
     42   for(int i = 0 ; i < subject->argc ; i++) {
     43     free(subject->argv[i]);
     44   }
     45   if (subject->argv) {
     46     free(subject->argv);
     47   }
     48   free(subject);
     49 }
     50 // }}}
     51 // cnf_directive_read(fd) {{{
     52 struct cnf_directive * cnf_directive_read(FILE *fd) {
     53   if (feof(fd)) return NULL;
     54 
     55   struct buf *acc = calloc(1, sizeof(struct buf));
     56   struct cnf_directive *output = calloc(1, sizeof(struct cnf_directive));
     57 
     58   int state = STATE_BLANK;
     59   char c;
     60   char quote;
     61   bool escape;
     62 
     63   while(!feof(fd)) {
     64     if (!fread(&c, sizeof(char), 1, fd)) break;
     65 
     66     switch(state) {
     67       case STATE_BLANK:
     68         switch(c) {
     69           case ' ':
     70           case '\t':
     71           case '\r':
     72             // Skip leading whitespace
     73             // Consume without further action
     74             continue;
     75           case '\n':
     76             // Output directive if we have something
     77             if (output->name) goto cnf_directive_read_finalize;
     78             continue;
     79           case '#':
     80             state = STATE_COMMENT;
     81             continue;
     82           case '"':
     83           case '\'':
     84             state = STATE_QUOTE;
     85             quote = c;
     86             escape = false;
     87             continue;
     88           default:
     89             buf_append_byte(acc, c);
     90             state = STATE_WORD;
     91             continue;
     92         }
     93         break;
     94       case STATE_COMMENT:
     95         switch(c) {
     96           case '\n': // newline breaks comment
     97             if (output->name) goto cnf_directive_read_finalize; // Operator = directive complete
     98             state = STATE_BLANK;                                // No operator = read one more line
     99             // acc->len = 0;
    100             continue;
    101           default:
    102             // Consume without further action
    103             continue;
    104         }
    105         break;
    106       case STATE_QUOTE:
    107         if (escape) {
    108           buf_append_byte(acc, c);
    109           escape = false;
    110           continue;
    111         }
    112         if (c == '\\') {
    113           escape = true;
    114           continue;
    115         }
    116         if (c == quote) {
    117           cnf_directive_append(output, acc->dat, acc->len);
    118           state = STATE_BLANK;
    119           memset(acc->dat, 0, acc->cap);
    120           acc->len = 0;
    121           continue;
    122         }
    123         buf_append_byte(acc, c);
    124         continue;;
    125       case STATE_WORD:
    126         switch(c) {
    127           case ' ':
    128           case '\t':
    129           case '\r':
    130           case '\n':
    131             // whitespace breaks word
    132             cnf_directive_append(output, acc->dat, acc->len);
    133             state = STATE_BLANK;
    134             memset(acc->dat, 0, acc->cap);
    135             acc->len = 0;
    136             if ('\n' == c) {
    137               goto cnf_directive_read_finalize;
    138             }
    139             continue;
    140           default:
    141             // append char to word
    142             buf_append_byte(acc, c);
    143             continue;
    144         }
    145         break;
    146       default:
    147         fprintf(stderr, "Invalid state: %d\n", state);
    148         exit(1);
    149     }
    150 
    151   }
    152 
    153 cnf_directive_read_finalize:
    154   if (acc->len) {
    155     cnf_directive_append(output, acc->dat, acc->len);
    156   }
    157   buf_clear(acc);
    158   free(acc);
    159   if (!output->name) {
    160     free(output);
    161     return NULL;
    162   }
    163   return output;
    164 }
    165 // }}}
    166 
    167 #ifdef __cplusplus
    168 } // extern "C"
    169 #endif
    170 
    171 // vim:fdm=marker:fdl=0