Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * OpenTyrian: A modern cross-platform port of Tyrian
  3.  * Copyright (C) 2015  The OpenTyrian Development Team
  4.  *
  5.  * This program is free software; you can redistribute it and/or
  6.  * modify it under the terms of the GNU General Public License
  7.  * as published by the Free Software Foundation; either version 2
  8.  * of the License, or (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  18.  */
  19. /*!
  20.  * \file config_file.c
  21.  * \author Carl Reinke
  22.  * \date 2015
  23.  * \copyright GNU General Public License v2+ or Mozilla Public License 2.0
  24.  */
  25. #include "config_file.h"
  26.  
  27. #include <assert.h>
  28. #include <limits.h>
  29. #include <stdbool.h>
  30. #include <stddef.h>
  31. #include <string.h>
  32.  
  33. /* potential size of decimal representation of type */
  34. #define udecsizeof(t) ((CHAR_BIT * sizeof(t) / 3) + 1)
  35. #define sdecsizeof(t) (udecsizeof(t) + 1)
  36.  
  37. extern void config_oom( void );
  38.  
  39. void config_oom( void )
  40. {
  41.         fprintf(stderr, "out of memory\n");
  42.         exit(EXIT_FAILURE);
  43. }
  44.  
  45. /* string manipulators */
  46.  
  47. static ConfigString string_init_len( const char *s, size_t n )
  48. {
  49.         ConfigString string;
  50.        
  51.         if (s == NULL)
  52.         {
  53.                 CONFIG_STRING_LONG_TAG(string) = true;
  54.                
  55.                 string.long_buf = NULL;
  56.         }
  57.         else
  58.         {
  59.                 char is_long = n >= COUNTOF(string.short_buf);
  60.                
  61.                 CONFIG_STRING_LONG_TAG(string) = is_long;
  62.                
  63.                 char *buffer = is_long ?
  64.                         string.long_buf = malloc((n + 1) * sizeof(char)) :
  65.                         string.short_buf;
  66.                 if (buffer == NULL)
  67.                         config_oom();
  68.                
  69.                 memcpy(buffer, s, n * sizeof(char));
  70.                 buffer[n] = '\0';
  71.         }
  72.        
  73.         return string;
  74. }
  75.  
  76. static void string_deinit( ConfigString *string )
  77. {
  78.         char is_long = CONFIG_STRING_LONG_TAG(*string);
  79.        
  80.         if (is_long)
  81.         {
  82.                 free(string->long_buf);
  83.                 string->long_buf = NULL;
  84.         }
  85. }
  86.  
  87. static bool string_equal_len( ConfigString *string, const char *s, size_t n )
  88. {
  89.         const char *cstr = config_string_to_cstr(string);
  90.         return strncmp(cstr, s, n) == 0 && cstr[n] == '\0';
  91. }
  92.  
  93. /* config manipulators */
  94.  
  95. static void deinit_section( ConfigSection *section );
  96. static void deinit_option( ConfigOption *option );
  97.  
  98. void config_init( Config *config )
  99. {
  100.         assert(config != NULL);
  101.        
  102.         config->sections_count = 0;
  103.         config->sections = NULL;
  104. }
  105.  
  106. void config_deinit( Config *config )
  107. {
  108.         assert(config != NULL);
  109.        
  110.         for (unsigned int s = 0; s < config->sections_count; ++s)
  111.         {
  112.                 ConfigSection *section = &config->sections[s];
  113.                
  114.                 deinit_section(section);
  115.         }
  116.        
  117.         free(config->sections);
  118.         config->sections = NULL;
  119. }
  120.  
  121. /* config section manipulators -- internal */
  122.  
  123. static void init_section( ConfigSection *section, const char *type, size_t type_len, const char *name, size_t name_len )
  124. {
  125.         section->type = string_init_len(type, type_len);
  126.         section->name = string_init_len(name, name_len);
  127.         section->options_count = 0;
  128.         section->options = NULL;
  129. }
  130.  
  131. static void deinit_section( ConfigSection *section )
  132. {
  133.         for (unsigned int o = 0; o < section->options_count; ++o)
  134.         {
  135.                 ConfigOption *option = &section->options[o];
  136.                
  137.                 deinit_option(option);
  138.         }
  139.        
  140.         string_deinit(&section->type);
  141.         string_deinit(&section->name);
  142.        
  143.         free(section->options);
  144.         section->options = NULL;
  145. }
  146.  
  147. /* config section accessors/manipulators -- by type, name */
  148.  
  149. ConfigSection *config_add_section_len( Config *config, const char *type, size_t type_len, const char *name, size_t name_len )
  150. {
  151.         assert(config != NULL);
  152.         assert(type != NULL);
  153.        
  154.         ConfigSection *sections = realloc(config->sections, (config->sections_count + 1) * sizeof(ConfigSection));
  155.         if (sections == NULL)
  156.                 return NULL;
  157.        
  158.         ConfigSection *section = &sections[config->sections_count];
  159.        
  160.         config->sections_count += 1;
  161.         config->sections = sections;
  162.        
  163.         init_section(section, type, type_len, name, name_len);
  164.        
  165.         return section;
  166. }
  167.  
  168. ConfigSection *config_find_sections( Config *config, const char *type, ConfigSection **save )
  169. {
  170.         assert(config != NULL);
  171.         assert(type != NULL);
  172.        
  173.         ConfigSection *sections_end = &config->sections[config->sections_count];
  174.        
  175.         ConfigSection *section = save != NULL && *save != NULL ?
  176.                 *save :
  177.                 &config->sections[0];
  178.        
  179.         for (; section < sections_end; ++section)
  180.                 if (strcmp(config_string_to_cstr(&section->type), type) == 0)
  181.                         break;
  182.        
  183.         if (save != NULL)
  184.                 *save = section;
  185.        
  186.         return section < sections_end ? section : NULL;
  187. }
  188.  
  189. ConfigSection *config_find_section( Config *config, const char *type, const char *name )
  190. {
  191.         assert(config != NULL);
  192.         assert(type != NULL);
  193.        
  194.         ConfigSection *sections_end = &config->sections[config->sections_count];
  195.        
  196.         for (ConfigSection *section = &config->sections[0]; section < sections_end; ++section)
  197.         {
  198.                 if (strcmp(config_string_to_cstr(&section->type), type) == 0)
  199.                 {
  200.                         const char *section_name = config_string_to_cstr(&section->name);
  201.                         if ((section_name == NULL || name == NULL) ? section_name == name : strcmp(config_string_to_cstr(&section->name), name) == 0)
  202.                                 return section;
  203.                 }
  204.         }
  205.        
  206.         return NULL;
  207. }
  208.  
  209. ConfigSection *config_find_or_add_section( Config *config, const char *type, const char *name )
  210. {
  211.         assert(config != NULL);
  212.         assert(type != NULL);
  213.        
  214.         ConfigSection *section = config_find_section(config, type, name);
  215.        
  216.         if (section != NULL)
  217.                 return section;
  218.        
  219.         return config_add_section(config, type, name);
  220. }
  221.  
  222. /* config option manipulators -- internal */
  223.  
  224. static void init_option_value( ConfigOption *option, const char *value, size_t value_len )
  225. {
  226.         option->values_count = 0;
  227.         option->v.value = string_init_len(value, value_len);
  228. }
  229.  
  230. static void deinit_option_value( ConfigOption *option )
  231. {
  232.         if (option->values_count != 0)
  233.         {
  234.                 ConfigString *values_end = &option->v.values[option->values_count];
  235.                 for (ConfigString *value = &option->v.values[0]; value < values_end; ++value)
  236.                         string_deinit(value);
  237.                
  238.                 free(option->v.values);
  239.                 option->v.values = NULL;
  240.         }
  241.         else
  242.         {
  243.                 string_deinit(&option->v.value);
  244.         }
  245. }
  246.  
  247. static void init_option( ConfigOption *option, const char *key, size_t key_len, const char *value, size_t value_len )
  248. {
  249.         option->key = string_init_len(key, key_len);
  250.         init_option_value(option, value, value_len);
  251. }
  252.  
  253. static void deinit_option( ConfigOption *option )
  254. {
  255.         string_deinit(&option->key);
  256.         deinit_option_value(option);
  257. }
  258.  
  259. static ConfigOption *append_option( ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len )
  260. {
  261.         ConfigOption *options = realloc(section->options, (section->options_count + 1) * sizeof(ConfigSection));
  262.         if (options == NULL)
  263.                 return NULL;
  264.        
  265.         ConfigOption *option = &options[section->options_count];
  266.        
  267.         section->options_count += 1;
  268.         section->options = options;
  269.        
  270.         init_option(option, key, key_len, value, value_len);
  271.        
  272.         return option;
  273. }
  274.  
  275. static ConfigOption *get_option_len( ConfigSection *section, const char *key, size_t key_len )
  276. {
  277.         assert(section != NULL);
  278.         assert(key != NULL);
  279.        
  280.         ConfigOption *options_end = &section->options[section->options_count];
  281.         for (ConfigOption *option = &section->options[0]; option < options_end; ++option)
  282.                 if (string_equal_len(&option->key, key, key_len))
  283.                         return option;
  284.        
  285.         return NULL;
  286. }
  287.  
  288. /* config option accessors/manipulators -- by key */
  289.  
  290. ConfigOption *config_set_option_len( ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len )
  291. {
  292.         assert(section != NULL);
  293.         assert(key != NULL);
  294.        
  295.         ConfigOption *option = get_option_len(section, key, key_len);
  296.        
  297.         if (option != NULL)
  298.                 return config_set_value_len(option, value, value_len);
  299.        
  300.         return append_option(section, key, key_len, value, value_len);
  301. }
  302.  
  303. ConfigOption *config_get_option( const ConfigSection *section, const char *key )
  304. {
  305.         assert(section != NULL);
  306.         assert(key != NULL);
  307.        
  308.         ConfigOption *options_end = &section->options[section->options_count];
  309.         for (ConfigOption *option = &section->options[0]; option < options_end; ++option)
  310.                 if (strcmp(config_string_to_cstr(&option->key), key) == 0)
  311.                         return option;
  312.        
  313.         return NULL;
  314. }
  315.  
  316. ConfigOption *config_get_or_set_option_len( ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len )
  317. {
  318.         assert(section != NULL);
  319.         assert(key != NULL);
  320.        
  321.         ConfigOption *option = get_option_len(section, key, key_len);
  322.        
  323.         if (option != NULL)
  324.                 return option;
  325.        
  326.         return append_option(section, key, key_len, value, value_len);
  327. }
  328.  
  329. void config_set_string_option_len( ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len )
  330. {
  331.         if (config_set_option_len(section, key, key_len, value, value_len) == NULL)
  332.                 config_oom();
  333. }
  334.  
  335. bool config_get_string_option( const ConfigSection *section, const char *key, const char **out_value )
  336. {
  337.         assert(section != NULL);
  338.         assert(key != NULL);
  339.        
  340.         ConfigOption *option = config_get_option(section, key);
  341.         if (option != NULL)
  342.         {
  343.                 const char *value = config_get_value(option);
  344.                 if (value != NULL)
  345.                 {
  346.                         *out_value = value;
  347.                         return true;
  348.                 }
  349.         }
  350.        
  351.         return false;
  352. }
  353.  
  354. const char *config_get_or_set_string_option( ConfigSection *section, const char *key, const char *value )
  355. {
  356.         if (!config_get_string_option(section, key, &value))
  357.                 config_set_string_option_len(section, key, strlen(key), value, value == NULL ? 0 : strlen(value));
  358.         return value;
  359. }
  360.  
  361. static const char *bool_values[][2] =
  362. {
  363.         { "0", "1" },
  364.         { "no", "yes" },
  365.         { "off", "on" },
  366.         { "false", "true" },
  367. };
  368.  
  369. void config_set_bool_option( ConfigSection *section, const char *key, bool value, ConfigBoolStyle style )
  370. {
  371.         if (config_set_option(section, key, bool_values[style][value ? 1 : 0]) == NULL)
  372.                 config_oom();
  373. }
  374.  
  375. bool config_get_bool_option( const ConfigSection *section, const char *key, bool *out_value )
  376. {
  377.         assert(section != NULL);
  378.         assert(key != NULL);
  379.         assert(out_value != NULL);
  380.        
  381.         const char *value;
  382.         if (config_get_string_option(section, key, &value))
  383.         {
  384.                 for (size_t i = 0; i < COUNTOF(bool_values); ++i)
  385.                 {
  386.                         for (size_t j = 0; j < COUNTOF(bool_values[i]); ++j)
  387.                         {
  388.                                 if (strcmp(value, bool_values[i][j]) == 0)
  389.                                 {
  390.                                         *out_value = j == 0 ? false : true;
  391.                                         return true;
  392.                                 }
  393.                         }
  394.                 }
  395.         }
  396.        
  397.         return false;
  398. }
  399.  
  400. bool config_get_or_set_bool_option( ConfigSection *section, const char *key, bool value, ConfigBoolStyle style )
  401. {
  402.         if (!config_get_bool_option(section, key, &value))
  403.                 config_set_bool_option(section, key, value, style);
  404.         return value;
  405. }
  406.  
  407. void config_set_int_option( ConfigSection *section, const char *key, int value )
  408. {
  409.         assert(key != NULL);
  410.        
  411.         char buffer[sdecsizeof(int) + 1];
  412.         int buffer_len = snprintf(buffer, sizeof(buffer), "%i", value);
  413.        
  414.         if (config_set_option_len(section, key, strlen(key), buffer, buffer_len) == NULL)
  415.                 config_oom();
  416. }
  417.  
  418. bool config_get_int_option( const ConfigSection *section, const char *key, int *out_value )
  419. {
  420.         assert(section != NULL);
  421.         assert(key != NULL);
  422.         assert(out_value != NULL);
  423.        
  424.         const char *value;
  425.         if (config_get_string_option(section, key, &value))
  426.         {
  427.                 int i;
  428.                 int n;
  429.                 if (sscanf(value, "%i%n", &i, &n) > 0 && value[n] == '\0')  /* must be entire string */
  430.                 {
  431.                         *out_value = i;
  432.                         return true;
  433.                 }
  434.         }
  435.        
  436.         return false;
  437. }
  438.  
  439. int config_get_or_set_int_option( ConfigSection *section, const char *key, int value )
  440. {
  441.         if (!config_get_int_option(section, key, &value))
  442.                 config_set_int_option(section, key, value);
  443.         return value;
  444. }
  445.  
  446. void config_set_uint_option( ConfigSection *section, const char *key, unsigned int value )
  447. {
  448.         assert(key != NULL);
  449.        
  450.         char buffer[udecsizeof(unsigned int) + 1];
  451.         int buffer_len = snprintf(buffer, sizeof(buffer), "%u", value);
  452.        
  453.         if (config_set_option_len(section, key, strlen(key), buffer, buffer_len) == NULL)
  454.                 config_oom();
  455. }
  456.  
  457. bool config_get_uint_option( const ConfigSection *section, const char *key, unsigned int *out_value )
  458. {
  459.         assert(section != NULL);
  460.         assert(key != NULL);
  461.         assert(out_value != NULL);
  462.        
  463.         const char *value;
  464.         if (config_get_string_option(section, key, &value))
  465.         {
  466.                 unsigned int u;
  467.                 int n;
  468.                 if (sscanf(value, "%u%n", &u, &n) > 0 && value[n] == '\0')  /* must be entire string */
  469.                 {
  470.                         *out_value = u;
  471.                         return true;
  472.                 }
  473.         }
  474.        
  475.         return false;
  476. }
  477.  
  478. unsigned int config_get_or_set_uint_option( ConfigSection *section, const char *key, unsigned int value )
  479. {
  480.         if (!config_get_uint_option(section, key, &value))
  481.                 config_set_uint_option(section, key, value);
  482.         return value;
  483. }
  484.  
  485. /* config option accessors/manipulators -- by reference */
  486.  
  487. ConfigOption *config_set_value_len( ConfigOption *option, const char *value, size_t value_len )
  488. {
  489.         assert(option != NULL);
  490.        
  491.         deinit_option_value(option);
  492.        
  493.         init_option_value(option, value, value_len);
  494.        
  495.         return option;
  496. }
  497.  
  498. ConfigOption *config_add_value_len( ConfigOption *option, const char *value, size_t value_len )
  499. {
  500.         assert(option != NULL);
  501.         assert(value != NULL);
  502.        
  503.         /* convert 'item' to 'list' */
  504.         if (option->values_count == 0 && config_string_to_cstr(&option->v.value) != NULL)
  505.         {
  506.                 ConfigString option_value = option->v.value;
  507.                
  508.                 ConfigString *values = malloc(2 * sizeof(ConfigString));
  509.                 if (values == NULL)
  510.                         return NULL;
  511.                
  512.                 option->v.values = values;
  513.                 option->v.values[0] = option_value;
  514.                 option->v.values[1] = string_init_len(value, value_len);
  515.                 option->values_count = 2;
  516.         }
  517.         else
  518.         {
  519.                 ConfigString *values = realloc(option->v.values, (option->values_count + 1) * sizeof(ConfigString));
  520.                 if (values == NULL)
  521.                         return NULL;
  522.                
  523.                 option->v.values = values;
  524.                 option->v.values[option->values_count] = string_init_len(value, value_len);
  525.                 option->values_count += 1;
  526.         }
  527.        
  528.         return option;
  529. }
  530.  
  531. ConfigOption *config_remove_value( ConfigOption *option, unsigned int i )
  532. {
  533.         assert(option != NULL);
  534.        
  535.         if (!config_is_value_list(option))
  536.         {
  537.                 if (i > 0)
  538.                         return NULL;
  539.                
  540.                 config_set_value_len(option, NULL, 0);
  541.         }
  542.         else
  543.         {
  544.                 if (i >= option->values_count)
  545.                         return NULL;
  546.                
  547.                 string_deinit(&option->v.values[i]);
  548.                 memmove(&option->v.values[i], &option->v.values[i + 1], (option->values_count - i - 1) * sizeof(ConfigString));
  549.                
  550.                 if (option->values_count - 1 == 0)
  551.                 {
  552.                         option->v.value = string_init_len(NULL, 0);
  553.                         option->values_count = 0;
  554.                 }
  555.                 else
  556.                 {
  557.                         ConfigString *values = realloc(option->v.values, (option->values_count - 1) * sizeof(ConfigString));
  558.                         if (values == NULL)
  559.                                 return NULL;
  560.                        
  561.                         option->v.values = values;
  562.                         option->values_count -= 1;
  563.                 }
  564.         }
  565.        
  566.         return option;
  567. }
  568.  
  569. const char *config_get_value( const ConfigOption *option )
  570. {
  571.         if (option == NULL || option->values_count != 0)
  572.                 return NULL;
  573.        
  574.         return config_string_to_cstr(&option->v.value);
  575. }
  576.  
  577. /* config parser */
  578.  
  579. static bool is_whitespace( char c )
  580. {
  581.         return c == '\t' || c == ' ';
  582. }
  583.  
  584. static bool is_end( char c )
  585. {
  586.         return c == '\0' || c == '\n' || c == '\r';
  587. }
  588.  
  589. static bool is_whitespace_or_end( char c )
  590. {
  591.         return is_whitespace(c) || is_end(c);
  592. }
  593.  
  594. typedef enum
  595. {
  596.         INVALID_DIRECTIVE = 0,
  597.         SECTION_DIRECTIVE,
  598.         ITEM_DIRECTIVE,
  599.         LIST_DIRECTIVE,
  600. } Directive;
  601.  
  602. static Directive match_directive( const char *buffer, size_t *index )
  603. {
  604.         size_t i = *index;
  605.        
  606.         while (is_whitespace(buffer[i]))
  607.                 ++i;
  608.        
  609.         Directive directive;
  610.        
  611.         if (strncmp("section", &buffer[i], 7) == 0)
  612.         {
  613.                 directive = SECTION_DIRECTIVE;
  614.                 i += 7;
  615.         }
  616.         else if (strncmp("item", &buffer[i], 4) == 0)
  617.         {
  618.                 directive = ITEM_DIRECTIVE;
  619.                 i += 4;
  620.         }
  621.         else if (strncmp("list", &buffer[i], 4) == 0)
  622.         {
  623.                 directive = LIST_DIRECTIVE;
  624.                 i += 4;
  625.         }
  626.         else
  627.         {
  628.                 return INVALID_DIRECTIVE;
  629.         }
  630.        
  631.         if (!is_whitespace_or_end(buffer[i]))
  632.                 return INVALID_DIRECTIVE;
  633.        
  634.         *index = i;
  635.        
  636.         return directive;
  637. }
  638.  
  639. static bool match_nonquote_field( const char *buffer, size_t *index, size_t *length )
  640. {
  641.         size_t i = *index;
  642.        
  643.         for (; ; ++i)
  644.         {
  645.                 char c = buffer[i];
  646.                
  647.                 if (is_whitespace_or_end(c))
  648.                 {
  649.                         break;
  650.                 }
  651.                 else if (c <= ' ' || c > '~' || c == '#' || c == '\'' || c == '"')
  652.                 {
  653.                         return false;
  654.                 }
  655.         }
  656.        
  657.         *length = i - *index;
  658.         *index = i;
  659.        
  660.         return *length > 0;
  661. }
  662.  
  663. static bool parse_quote_field( char *buffer, size_t *index, size_t *length )
  664. {
  665.         size_t i = *index;
  666.         size_t o = *index;
  667.        
  668.         char quote = buffer[i];
  669.        
  670.         for (; ; )
  671.         {
  672.                 char c = buffer[++i];
  673.                
  674.                 if (c == quote)
  675.                 {
  676.                         ++i;
  677.                         break;
  678.                 }
  679.                 else if (c == '\\')
  680.                 {
  681.                         c = buffer[++i];
  682.                         if (c == quote)
  683.                         {
  684.                                 buffer[o++] = quote;
  685.                         }
  686.                         else
  687.                         {
  688.                                 switch (c)
  689.                                 {
  690.                                 case 't':
  691.                                         buffer[o++] = '\t';
  692.                                         break;
  693.                                 case 'n':
  694.                                         buffer[o++] = '\n';
  695.                                         break;
  696.                                 case 'r':
  697.                                         buffer[o++] = '\r';
  698.                                         break;
  699.                                 case '\\':
  700.                                         buffer[o++] = '\\';
  701.                                         break;
  702.                                 case 'x':
  703.                                         /* parse two hex digits */
  704.                                         c = buffer[++i];
  705.                                         char m = (c >= '0' && c <= '9') ? '0' :
  706.                                                  (c >= 'a' && c <= 'f') ? 'a' - 10 :
  707.                                                  (c >= 'A' && c <= 'F') ? 'A' - 10 : 0;
  708.                                         if (m == 0)
  709.                                                 return false;
  710.                                         char h = c - m;
  711.                                         c = buffer[++i];
  712.                                         m = (c >= '0' && c <= '9') ? '0' :
  713.                                             (c >= 'a' && c <= 'f') ? 'a' - 10 :
  714.                                             (c >= 'A' && c <= 'F') ? 'A' - 10 : 0;
  715.                                         if (m == 0)
  716.                                                 return false;
  717.                                         buffer[o++] = (h << 4) | (c - m);
  718.                                         break;
  719.                                 default:
  720.                                         return false;
  721.                                 }
  722.                         }
  723.                 }
  724.                 else if (c >= ' ' && c <= '~')
  725.                 {
  726.                         buffer[o++] = c;
  727.                 }
  728.                 else
  729.                 {
  730.                         return false;
  731.                 }
  732.         }
  733.        
  734.         *length = o - *index;
  735.         *index = i;
  736.        
  737.         return true;
  738. }
  739.  
  740. static bool parse_field( char *buffer, size_t *index, size_t *start, size_t *length )
  741. {
  742.         size_t i = *index;
  743.        
  744.         while (is_whitespace(buffer[i]))
  745.                 ++i;
  746.        
  747.         *start = i;
  748.        
  749.         if (buffer[i] == '"' || buffer[i] == '\'')
  750.         {
  751.                 if (!parse_quote_field(buffer, &i, length))
  752.                         return false;
  753.         }
  754.         else
  755.         {
  756.                 if (!match_nonquote_field(buffer, &i, length))
  757.                         return false;
  758.         }
  759.        
  760.         if (!is_whitespace_or_end(buffer[i]))
  761.                 return INVALID_DIRECTIVE;
  762.        
  763.         *index = i;
  764.        
  765.         return true;
  766. }
  767.  
  768. bool config_parse( Config *config, FILE *file )
  769. {
  770.         assert(config != NULL);
  771.         assert(file != NULL);
  772.        
  773.         config_init(config);
  774.        
  775.         ConfigSection *section = NULL;
  776.         ConfigOption *option = NULL;
  777.        
  778.         size_t buffer_cap = 128;
  779.         char *buffer = malloc(buffer_cap * sizeof(char));
  780.         if (buffer == NULL)
  781.                 config_oom();
  782.         size_t buffer_end = 1;
  783.         buffer[buffer_end - 1] = '\0';
  784.        
  785.         for (size_t line = 0, next_line = 0; ; line = next_line)
  786.         {
  787.                 /* find begining of next line */
  788.                 while (next_line < buffer_end)
  789.                 {
  790.                         char c = buffer[next_line];
  791.                        
  792.                         if (c == '\0' && next_line == buffer_end - 1)
  793.                         {
  794.                                 if (line > 0)
  795.                                 {
  796.                                         /* shift to front */
  797.                                         memmove(&buffer[0], &buffer[line], buffer_end - line);
  798.                                         buffer_end -= line;
  799.                                         next_line -= line;
  800.                                         line = 0;
  801.                                 }
  802.                                 else if (buffer_end > 1)
  803.                                 {
  804.                                         /* need larger capacity */
  805.                                         buffer_cap *= 2;
  806.                                         char *new_buffer = realloc(buffer, buffer_cap * sizeof(char));
  807.                                         if (new_buffer == NULL)
  808.                                                 config_oom();
  809.                                         buffer = new_buffer;
  810.                                 }
  811.                                
  812.                                 size_t read = fread(&buffer[buffer_end - 1], sizeof(char), buffer_cap - buffer_end, file);
  813.                                 if (read == 0)
  814.                                         break;
  815.                                
  816.                                 buffer_end += read;
  817.                                 buffer[buffer_end - 1] = '\0';
  818.                         }
  819.                         else
  820.                         {
  821.                                 ++next_line;
  822.                                
  823.                                 if (c == '\n' || c == '\r')
  824.                                         break;
  825.                         }
  826.                 }
  827.                
  828.                 /* if at end of file */
  829.                 if (next_line == line)
  830.                         break;
  831.                
  832.                 size_t i = line;
  833.                
  834.                 Directive directive = match_directive(buffer, &i);
  835.                
  836.                 switch (directive)
  837.                 {
  838.                 case INVALID_DIRECTIVE:
  839.                         continue;
  840.                 case SECTION_DIRECTIVE:
  841.                         {
  842.                                 size_t type_start;
  843.                                 size_t type_length;
  844.                                
  845.                                 if (!parse_field(buffer, &i, &type_start, &type_length))
  846.                                         continue;
  847.                                
  848.                                 size_t name_start;
  849.                                 size_t name_length;
  850.                                
  851.                                 bool has_name = parse_field(buffer, &i, &name_start, &name_length);
  852.                                
  853.                                 section = config_add_section_len(config,
  854.                                                 &buffer[type_start], type_length,
  855.                                                 has_name ? &buffer[name_start] : NULL, has_name ? name_length : 0);
  856.                                 if (section == NULL)
  857.                                         config_oom();
  858.                                 option = NULL;
  859.                         }
  860.                         break;
  861.                 case ITEM_DIRECTIVE:
  862.                 case LIST_DIRECTIVE:
  863.                         {
  864.                                 if (section == NULL)
  865.                                         continue;
  866.                                
  867.                                 size_t key_start;
  868.                                 size_t key_length;
  869.                                
  870.                                 if (!parse_field(buffer, &i, &key_start, &key_length))
  871.                                         continue;
  872.                                
  873.                                 size_t value_start;
  874.                                 size_t value_length;
  875.                                
  876.                                 if (!parse_field(buffer, &i, &value_start, &value_length))
  877.                                         continue;
  878.                                
  879.                                 if (directive == ITEM_DIRECTIVE)
  880.                                 {
  881.                                         option = config_set_option_len(section,
  882.                                                         &buffer[key_start], key_length,
  883.                                                         &buffer[value_start], value_length);
  884.                                 }
  885.                                 else
  886.                                 {
  887.                                         if (option == NULL || !string_equal_len(&option->key, &buffer[key_start], key_length))
  888.                                                 option = config_get_or_set_option_len(section,
  889.                                                                 &buffer[key_start], key_length,
  890.                                                                 NULL, 0);
  891.                                         if (option != NULL)
  892.                                                 option = config_add_value_len(option,
  893.                                                                 &buffer[value_start], value_length);
  894.                                 }
  895.                                 if (option == NULL)
  896.                                         config_oom();
  897.                         }
  898.                         break;
  899.                 }
  900.                
  901.                 assert(i <= next_line);
  902.         }
  903.        
  904.         free(buffer);
  905.        
  906.         return config;
  907. }
  908.  
  909. /* config writer */
  910.  
  911. static void write_field( const ConfigString *field, FILE *file )
  912. {
  913.         fputc('\'', file);
  914.        
  915.         char buffer[128];
  916.         size_t o = 0;
  917.        
  918.         for (const char *ci = config_string_to_cstr(field); *ci != '\0'; ++ci)
  919.         {
  920.                 char c = *ci;
  921.                
  922.                 size_t l;
  923.                 switch (c)
  924.                 {
  925.                         case '\t':
  926.                         case '\n':
  927.                         case '\r':
  928.                         case '\'':
  929.                         case '\\':
  930.                                 l = 2;
  931.                                 break;
  932.                         default:
  933.                                 l = (c >= ' ' && c <= '~') ? 1 : 4;
  934.                                 break;
  935.                 }
  936.                
  937.                 if (o + l > COUNTOF(buffer))
  938.                 {
  939.                         fwrite(buffer, sizeof(*buffer), o, file);
  940.                         o = 0;
  941.                 }
  942.                
  943.                 switch (l)
  944.                 {
  945.                         case 1:
  946.                                 buffer[o++] = c;
  947.                                 break;
  948.                         case 2:
  949.                                 switch (c)
  950.                                 {
  951.                                         case '\t':
  952.                                                 buffer[o++] = '\\';
  953.                                                 buffer[o++] = 't';
  954.                                                 break;
  955.                                         case '\n':
  956.                                                 buffer[o++] = '\\';
  957.                                                 buffer[o++] = 'n';
  958.                                                 break;
  959.                                         case '\r':
  960.                                                 buffer[o++] = '\\';
  961.                                                 buffer[o++] = 'r';
  962.                                                 break;
  963.                                         case '\'':
  964.                                         case '\\':
  965.                                                 buffer[o++] = '\\';
  966.                                                 buffer[o++] = c;
  967.                                                 break;
  968.                                 }
  969.                                 break;
  970.                         case 4:
  971.                                 buffer[o++] = '\\';
  972.                                 buffer[o++] = 'x';
  973.                                 char n = (c >> 4) & 0x0f;
  974.                                 buffer[o++] = (n < 10 ? '0' : ('a' - 10)) + n;
  975.                                 n = c & 0x0f;
  976.                                 buffer[o++] = (n < 10 ? '0' : ('a' - 10)) + n;
  977.                                 break;
  978.                 }
  979.         }
  980.        
  981.         if (o > 0)
  982.                 fwrite(buffer, sizeof(*buffer), o, file);
  983.        
  984.         fputc('\'', file);
  985. }
  986.  
  987. void config_write( const Config *config, FILE *file )
  988. {
  989.         assert(config != NULL);
  990.         assert(file != NULL);
  991.        
  992.         for (unsigned int s = 0; s < config->sections_count; ++s)
  993.         {
  994.                 ConfigSection *section = &config->sections[s];
  995.                
  996.                 fputs("section ", file);
  997.                 write_field(&section->type, file);
  998.                 if (config_string_to_cstr(&section->name) != NULL)
  999.                 {
  1000.                         fputc(' ', file);
  1001.                         write_field(&section->name, file);
  1002.                 }
  1003.                 fputc('\n', file);
  1004.                
  1005.                 for (unsigned int o = 0; o < section->options_count; ++o)
  1006.                 {
  1007.                         ConfigOption *option = &section->options[o];
  1008.                        
  1009.                         if (option->values_count == 0 && config_string_to_cstr(&option->v.value) != NULL)
  1010.                         {
  1011.                                 fputs("\titem ", file);
  1012.                                 write_field(&option->key, file);
  1013.                                 fputc(' ', file);
  1014.                                 write_field(&option->v.value, file);
  1015.                                 fputc('\n', file);
  1016.                         }
  1017.                         else
  1018.                         {
  1019.                                 ConfigString *values_end = &option->v.values[option->values_count];
  1020.                                 for (ConfigString *value = &option->v.values[0]; value < values_end; ++value)
  1021.                                 {
  1022.                                         fputs("\tlist ", file);
  1023.                                         write_field(&option->key, file);
  1024.                                         fputc(' ', file);
  1025.                                         write_field(value, file);
  1026.                                         fputc('\n', file);
  1027.                                 }
  1028.                         }
  1029.                 }
  1030.                
  1031.                 fputc('\n', file);
  1032.         }
  1033. }
  1034.