cnfparse.c

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

0000-basics.c (12495B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <string.h>
      4 
      5 #include "assert.h"
      6 #include "cnfparse.h"
      7 
      8 static FILE *open_string(const char *text) {
      9   return fmemopen((void *)text, strlen(text), "r");
     10 }
     11 
     12 void test_empty_file() {
     13   FILE *fd = open_string("");
     14   ASSERT("fmemopen succeeds", fd != NULL);
     15   struct cnf_directive *dir = cnf_directive_read(fd);
     16   ASSERT("empty file returns NULL", dir == NULL);
     17   fclose(fd);
     18 }
     19 
     20 void test_blank_lines_only() {
     21   FILE *fd = open_string("\n\n   \t\r\n");
     22   ASSERT("fmemopen succeeds", fd != NULL);
     23   struct cnf_directive *dir = cnf_directive_read(fd);
     24   ASSERT("blank lines return NULL", dir == NULL);
     25   fclose(fd);
     26 }
     27 
     28 void test_comment_only_line() {
     29   FILE *fd = open_string("# this is a comment line\n");
     30   ASSERT("fmemopen succeeds", fd != NULL);
     31   struct cnf_directive *dir = cnf_directive_read(fd);
     32   ASSERT("comment-only line returns NULL", dir == NULL);
     33   fclose(fd);
     34 }
     35 
     36 void test_single_word_no_args() {
     37   FILE *fd = open_string("directive\n");
     38   ASSERT("fmemopen succeeds", fd != NULL);
     39   struct cnf_directive *dir = cnf_directive_read(fd);
     40   ASSERT("non-empty directive returned", dir != NULL);
     41   ASSERT_STRING_EQUALS("directive", dir->name);
     42   ASSERT_EQUALS(0, dir->argc);
     43   cnf_directive_free(dir);
     44   fclose(fd);
     45 }
     46 
     47 void test_leading_whitespace() {
     48   FILE *fd = open_string("   \t leading_spaces\n");
     49   ASSERT("fmemopen succeeds", fd != NULL);
     50   struct cnf_directive *dir = cnf_directive_read(fd);
     51   ASSERT("directive with leading whitespace returned", dir != NULL);
     52   ASSERT_STRING_EQUALS("leading_spaces", dir->name);
     53   ASSERT_EQUALS(0, dir->argc);
     54   cnf_directive_free(dir);
     55   fclose(fd);
     56 }
     57 
     58 void test_trailing_whitespace() {
     59   FILE *fd = open_string("trailing   \t\n");
     60   ASSERT("fmemopen succeeds", fd != NULL);
     61   struct cnf_directive *dir = cnf_directive_read(fd);
     62   ASSERT("directive with trailing whitespace returned", dir != NULL);
     63   ASSERT_STRING_EQUALS("trailing", dir->name);
     64   ASSERT_EQUALS(0, dir->argc);
     65   cnf_directive_free(dir);
     66   fclose(fd);
     67 }
     68 
     69 void test_multiple_space_separated_args() {
     70   FILE *fd = open_string("add 10.0.0.0/8 via 192.168.1.1\n");
     71   ASSERT("fmemopen succeeds", fd != NULL);
     72   struct cnf_directive *dir = cnf_directive_read(fd);
     73   ASSERT("directive with args returned", dir != NULL);
     74   ASSERT_STRING_EQUALS("add", dir->name);
     75   ASSERT_EQUALS(3, dir->argc);
     76   ASSERT_STRING_EQUALS("10.0.0.0/8", dir->argv[0]);
     77   ASSERT_STRING_EQUALS("via", dir->argv[1]);
     78   ASSERT_STRING_EQUALS("192.168.1.1", dir->argv[2]);
     79   cnf_directive_free(dir);
     80   fclose(fd);
     81 }
     82 
     83 void test_multiple_spaces_between_args() {
     84   FILE *fd = open_string("   del    10.1.0.0/16\n");
     85   ASSERT("fmemopen succeeds", fd != NULL);
     86   struct cnf_directive *dir = cnf_directive_read(fd);
     87   ASSERT("directive with extra spaces returned", dir != NULL);
     88   ASSERT_STRING_EQUALS("del", dir->name);
     89   ASSERT_EQUALS(1, dir->argc);
     90   ASSERT_STRING_EQUALS("10.1.0.0/16", dir->argv[0]);
     91   cnf_directive_free(dir);
     92   fclose(fd);
     93 }
     94 
     95 void test_quoted_double_args() {
     96   FILE *fd = open_string("set note \"hello world\" and \"foo bar\"\n");
     97   ASSERT("fmemopen succeeds", fd != NULL);
     98   struct cnf_directive *dir = cnf_directive_read(fd);
     99   ASSERT("directive with quoted args returned", dir != NULL);
    100   ASSERT_STRING_EQUALS("set", dir->name);
    101   ASSERT_EQUALS(4, dir->argc);
    102   ASSERT_STRING_EQUALS("note", dir->argv[0]);
    103   ASSERT_STRING_EQUALS("hello world", dir->argv[1]);
    104   ASSERT_STRING_EQUALS("and", dir->argv[2]);
    105   ASSERT_STRING_EQUALS("foo bar", dir->argv[3]);
    106   fclose(fd);
    107 }
    108 
    109 void test_quoted_single_args() {
    110   FILE *fd = open_string("op 'single quoted'\n");
    111   ASSERT("fmemopen succeeds", fd != NULL);
    112   struct cnf_directive *dir = cnf_directive_read(fd);
    113   ASSERT("directive with single-quoted arg returned", dir != NULL);
    114   ASSERT_STRING_EQUALS("op", dir->name);
    115   ASSERT_EQUALS(1, dir->argc);
    116   ASSERT_STRING_EQUALS("single quoted", dir->argv[0]);
    117   cnf_directive_free(dir);
    118   fclose(fd);
    119 }
    120 
    121 void test_escaped_quote_in_double() {
    122   FILE *fd = open_string("op arg \"a \\\" b\"\n");
    123   ASSERT("fmemopen succeeds", fd != NULL);
    124   struct cnf_directive *dir = cnf_directive_read(fd);
    125   ASSERT("directive with escaped quote returned", dir != NULL);
    126   ASSERT_STRING_EQUALS("op", dir->name);
    127   ASSERT_EQUALS(2, dir->argc);
    128   ASSERT_STRING_EQUALS("arg", dir->argv[0]);
    129   ASSERT_STRING_EQUALS("a \" b", dir->argv[1]);
    130   cnf_directive_free(dir);
    131   fclose(fd);
    132 }
    133 
    134 void test_escaped_newline_in_double_quotes() {
    135   FILE *fd = open_string("op \"a\\nb\"\n"); // config text: op "a\nb"
    136   ASSERT("fmemopen succeeds", fd != NULL);
    137   struct cnf_directive *dir = cnf_directive_read(fd);
    138   ASSERT("directive with escaped newline returned", dir != NULL);
    139   ASSERT_STRING_EQUALS("op", dir->name);
    140   ASSERT_EQUALS(1, dir->argc);
    141   ASSERT_STRING_EQUALS("a\nb", dir->argv[0]);
    142   cnf_directive_free(dir);
    143   fclose(fd);
    144 }
    145 
    146 void test_escaped_tab_cr_in_double_quotes() {
    147   FILE *fd = open_string("op \"a\\tb\\rc\"\n"); // config text: op "a\tb\rc"
    148   ASSERT("fmemopen succeeds", fd != NULL);
    149   struct cnf_directive *dir = cnf_directive_read(fd);
    150   ASSERT("directive with escaped tab/cr returned", dir != NULL);
    151   ASSERT_EQUALS(1, dir->argc);
    152   ASSERT_STRING_EQUALS("a\tb\rc", dir->argv[0]);
    153   cnf_directive_free(dir);
    154   fclose(fd);
    155 }
    156 
    157 void test_escaped_backslash_in_double_quotes() {
    158   FILE *fd = open_string("op \"a\\\\b\"\n"); // config text: op "a\\b"
    159   ASSERT("fmemopen succeeds", fd != NULL);
    160   struct cnf_directive *dir = cnf_directive_read(fd);
    161   ASSERT("directive with escaped backslash returned", dir != NULL);
    162   ASSERT_EQUALS(1, dir->argc);
    163   ASSERT_STRING_EQUALS("a\\b", dir->argv[0]);
    164   cnf_directive_free(dir);
    165   fclose(fd);
    166 }
    167 
    168 void test_unknown_escape_kept_verbatim() {
    169   FILE *fd = open_string("op \"a\\zb\"\n"); // config text: op "a\zb"
    170   ASSERT("fmemopen succeeds", fd != NULL);
    171   struct cnf_directive *dir = cnf_directive_read(fd);
    172   ASSERT("directive with unknown escape returned", dir != NULL);
    173   ASSERT_EQUALS(1, dir->argc);
    174   ASSERT_STRING_EQUALS("azb", dir->argv[0]);
    175   cnf_directive_free(dir);
    176   fclose(fd);
    177 }
    178 
    179 void test_null_escape_becomes_zero() {
    180   FILE *fd = open_string("op \"a\\0b\"\n"); // config text: op "a\0b"
    181   ASSERT("fmemopen succeeds", fd != NULL);
    182   struct cnf_directive *dir = cnf_directive_read(fd);
    183   ASSERT("directive with null escape returned", dir != NULL);
    184   ASSERT_EQUALS(1, dir->argc);
    185   ASSERT_STRING_EQUALS("a0b", dir->argv[0]);
    186   cnf_directive_free(dir);
    187   fclose(fd);
    188 }
    189 
    190 void test_single_quotes_keep_backslash_literal() {
    191   FILE *fd = open_string("op 'a\\nb'\n"); // config text: op 'a\nb'
    192   ASSERT("fmemopen succeeds", fd != NULL);
    193   struct cnf_directive *dir = cnf_directive_read(fd);
    194   ASSERT("directive with literal backslash in single quotes returned", dir != NULL);
    195   ASSERT_EQUALS(1, dir->argc);
    196   ASSERT_STRING_EQUALS("a\\nb", dir->argv[0]);
    197   cnf_directive_free(dir);
    198   fclose(fd);
    199 }
    200 
    201 void test_escaped_single_quote_in_single_quotes() {
    202   FILE *fd = open_string("op 'it\\'s'\n"); // config text: op 'it\'s'
    203   ASSERT("fmemopen succeeds", fd != NULL);
    204   struct cnf_directive *dir = cnf_directive_read(fd);
    205   ASSERT("directive with escaped single quote returned", dir != NULL);
    206   ASSERT_EQUALS(1, dir->argc);
    207   ASSERT_STRING_EQUALS("it's", dir->argv[0]);
    208   cnf_directive_free(dir);
    209   fclose(fd);
    210 }
    211 
    212 void test_empty_double_quote_first_token() {
    213   FILE *fd = open_string("\"\" foo\n"); // config text: "" foo
    214   ASSERT("fmemopen succeeds", fd != NULL);
    215   struct cnf_directive *dir = cnf_directive_read(fd);
    216   ASSERT("directive with empty first token returned", dir != NULL);
    217   ASSERT_STRING_EQUALS("", dir->name);
    218   ASSERT_EQUALS(1, dir->argc);
    219   ASSERT_STRING_EQUALS("foo", dir->argv[0]);
    220   cnf_directive_free(dir);
    221   fclose(fd);
    222 }
    223 
    224 void test_empty_double_quote_alone() {
    225   FILE *fd = open_string("\"\"\n"); // config text: ""
    226   ASSERT("fmemopen succeeds", fd != NULL);
    227   struct cnf_directive *dir = cnf_directive_read(fd);
    228   ASSERT("directive with only an empty token returned", dir != NULL);
    229   ASSERT_STRING_EQUALS("", dir->name);
    230   ASSERT_EQUALS(0, dir->argc);
    231   cnf_directive_free(dir);
    232   fclose(fd);
    233 }
    234 
    235 void test_hash_in_quoted_string() {
    236   FILE *fd = open_string("set note \"a # b\"\n");
    237   ASSERT("fmemopen succeeds", fd != NULL);
    238   struct cnf_directive *dir = cnf_directive_read(fd);
    239   ASSERT("directive with hash in quotes returned", dir != NULL);
    240   ASSERT_STRING_EQUALS("set", dir->name);
    241   ASSERT_EQUALS(2, dir->argc);
    242   ASSERT_STRING_EQUALS("note", dir->argv[0]);
    243   ASSERT_STRING_EQUALS("a # b", dir->argv[1]);
    244   cnf_directive_free(dir);
    245   fclose(fd);
    246 }
    247 
    248 void test_trailing_comment() {
    249   FILE *fd = open_string("op arg # trailing comment\n");
    250   ASSERT("fmemopen succeeds", fd != NULL);
    251   struct cnf_directive *dir = cnf_directive_read(fd);
    252   ASSERT("directive before trailing comment returned", dir != NULL);
    253   ASSERT_STRING_EQUALS("op", dir->name);
    254   ASSERT_EQUALS(1, dir->argc);
    255   ASSERT_STRING_EQUALS("arg", dir->argv[0]);
    256   cnf_directive_free(dir);
    257   fclose(fd);
    258 }
    259 
    260 void test_comment_no_space() {
    261   FILE *fd = open_string("route 172.16.0.0/12 #comment with no space\n");
    262   ASSERT("fmemopen succeeds", fd != NULL);
    263   struct cnf_directive *dir = cnf_directive_read(fd);
    264   ASSERT("directive before tight comment returned", dir != NULL);
    265   ASSERT_STRING_EQUALS("route", dir->name);
    266   ASSERT_EQUALS(1, dir->argc);
    267   ASSERT_STRING_EQUALS("172.16.0.0/12", dir->argv[0]);
    268   cnf_directive_free(dir);
    269   fclose(fd);
    270 }
    271 
    272 void test_multiple_directives_sequence() {
    273   FILE *fd = open_string("first one\nsecond two three\n");
    274   ASSERT("fmemopen succeeds", fd != NULL);
    275 
    276   struct cnf_directive *d1 = cnf_directive_read(fd);
    277   ASSERT("first directive returned", d1 != NULL);
    278   ASSERT_STRING_EQUALS("first", d1->name);
    279   ASSERT_EQUALS(1, d1->argc);
    280   ASSERT_STRING_EQUALS("one", d1->argv[0]);
    281   cnf_directive_free(d1);
    282 
    283   struct cnf_directive *d2 = cnf_directive_read(fd);
    284   ASSERT("second directive returned", d2 != NULL);
    285   ASSERT_STRING_EQUALS("second", d2->name);
    286   ASSERT_EQUALS(2, d2->argc);
    287   ASSERT_STRING_EQUALS("two", d2->argv[0]);
    288   ASSERT_STRING_EQUALS("three", d2->argv[1]);
    289   cnf_directive_free(d2);
    290 
    291   struct cnf_directive *d3 = cnf_directive_read(fd);
    292   ASSERT("exhausted file returns NULL", d3 == NULL);
    293   fclose(fd);
    294 }
    295 
    296 void test_blank_lines_between_directives() {
    297   FILE *fd = open_string("\n\nfirst arg\n\n\nsecond\n");
    298   ASSERT("fmemopen succeeds", fd != NULL);
    299 
    300   struct cnf_directive *d1 = cnf_directive_read(fd);
    301   ASSERT("directive after blank lines returned", d1 != NULL);
    302   ASSERT_STRING_EQUALS("first", d1->name);
    303   ASSERT_EQUALS(1, d1->argc);
    304   cnf_directive_free(d1);
    305 
    306   struct cnf_directive *d2 = cnf_directive_read(fd);
    307   ASSERT("directive after more blank lines returned", d2 != NULL);
    308   ASSERT_STRING_EQUALS("second", d2->name);
    309   ASSERT_EQUALS(0, d2->argc);
    310   cnf_directive_free(d2);
    311 
    312   fclose(fd);
    313 }
    314 
    315 void test_no_trailing_newline() {
    316   FILE *fd = open_string("final one two three");
    317   ASSERT("fmemopen succeeds", fd != NULL);
    318   struct cnf_directive *dir = cnf_directive_read(fd);
    319   ASSERT("directive without trailing newline returned", dir != NULL);
    320   ASSERT_STRING_EQUALS("final", dir->name);
    321   ASSERT_EQUALS(3, dir->argc);
    322   ASSERT_STRING_EQUALS("one", dir->argv[0]);
    323   ASSERT_STRING_EQUALS("two", dir->argv[1]);
    324   ASSERT_STRING_EQUALS("three", dir->argv[2]);
    325   cnf_directive_free(dir);
    326   fclose(fd);
    327 }
    328 
    329 void test_directive_free_null() {
    330   cnf_directive_free(NULL);
    331   ASSERT("free NULL does not crash", 1);
    332 }
    333 
    334 int main(int argc, const char *argv[]) {
    335   RUN(test_empty_file);
    336   RUN(test_blank_lines_only);
    337   RUN(test_comment_only_line);
    338   RUN(test_single_word_no_args);
    339   RUN(test_leading_whitespace);
    340   RUN(test_trailing_whitespace);
    341   RUN(test_multiple_space_separated_args);
    342   RUN(test_multiple_spaces_between_args);
    343   RUN(test_quoted_double_args);
    344   RUN(test_quoted_single_args);
    345   RUN(test_escaped_quote_in_double);
    346   RUN(test_escaped_newline_in_double_quotes);
    347   RUN(test_escaped_tab_cr_in_double_quotes);
    348   RUN(test_escaped_backslash_in_double_quotes);
    349   RUN(test_unknown_escape_kept_verbatim);
    350   RUN(test_null_escape_becomes_zero);
    351   RUN(test_single_quotes_keep_backslash_literal);
    352   RUN(test_escaped_single_quote_in_single_quotes);
    353   RUN(test_empty_double_quote_first_token);
    354   RUN(test_empty_double_quote_alone);
    355   RUN(test_hash_in_quoted_string);
    356   RUN(test_trailing_comment);
    357   RUN(test_comment_no_space);
    358   RUN(test_multiple_directives_sequence);
    359   RUN(test_blank_lines_between_directives);
    360   RUN(test_no_trailing_newline);
    361   RUN(test_directive_free_null);
    362   return TEST_REPORT();
    363 }