1 /*
2  *  Copyright (C) 2005  James Antill
3  *
4  *  This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Lesser General Public
6  *  License as published by the Free Software Foundation; either
7  *  version 2 of the License, or (at your option) any later version.
8  *
9  *  This library is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public
15  *  License along with this library; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  *  email: james@and.org
19  */
20 /* configuration file functions */
21 
22 #define EX_UTILS_NO_FUNCS 1
23 #include "ex_utils.h"
24 
25 #include "conf.h"
26 
27 #include <limits.h>
28 
29 #include "mk.h"
30 
31 #define CLEN VSTR__AT_COMPILE_STRLEN
32 
33 #define VPREFIX(vstr, p, l, cstr)                                       \
34     (((l) >= CLEN(cstr)) &&                                             \
35      vstr_cmp_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
36 
conf_parse_make(Vstr_conf * conf)37 Conf_parse *conf_parse_make(Vstr_conf *conf)
38 {
39   Conf_parse *ret   = MK(sizeof(Conf_parse));
40   Vstr_sects *sects = vstr_sects_make(4);
41   Vstr_base *s1     = vstr_make_base(conf);
42   Vstr_base *s2     = vstr_make_base(conf);
43   unsigned int *ptr = MK(sizeof(unsigned int) * 4);
44 
45   if (!ret || !s1 || !sects || !ptr)
46   {
47     F(ret);
48     vstr_sects_free(sects);
49     vstr_free_base(s1);
50     vstr_free_base(s2);
51     F(ptr);
52     return (NULL);
53   }
54 
55   ret->sects     = sects;
56   ret->data      = s1;
57   ret->tmp       = s2;
58   ret->types_sz  = 4;
59   ret->types_ptr = ptr;
60   ret->uvals_sz  = 0;
61   ret->uvals_num = 0;
62   ret->uvals_ptr = NULL;
63   ret->state     = CONF_PARSE_STATE_BEG;
64   ret->depth     = 0;
65 
66   return (ret);
67 }
68 
conf_parse_free(Conf_parse * conf)69 void conf_parse_free(Conf_parse *conf)
70 {
71   if (!conf)
72     return;
73 
74   F(conf->types_ptr);
75   vstr_sects_free(conf->sects);
76   vstr_free_base(conf->data);
77   vstr_free_base(conf->tmp);
78 
79   while (conf->uvals_num)
80     vstr_ref_del(conf->uvals_ptr[--conf->uvals_num].ref);
81   F(conf->uvals_ptr);
82 
83   F(conf);
84 }
85 
conf__parse_type_add(Conf_parse * conf,unsigned int type)86 static int conf__parse_type_add(Conf_parse *conf, unsigned int type)
87 {
88   unsigned int *ptr = NULL;
89 
90   ASSERT(conf->types_sz <= conf->sects->sz);
91 
92   if (conf->types_sz == conf->sects->sz)
93     goto fin_ok;
94 
95   if (!MV(conf->types_ptr, ptr, sizeof(unsigned int) * conf->sects->sz))
96     return (FALSE);
97   conf->types_sz  = conf->sects->sz;
98 
99  fin_ok:
100   conf->types_ptr[conf->sects->num - 1] = type;
101 
102   return (TRUE);
103 }
104 
105 
conf__parse_ws(Conf_parse * conf,size_t pos,size_t len)106 static size_t conf__parse_ws(Conf_parse *conf, size_t pos, size_t len)
107 {
108   conf->state = CONF_PARSE_STATE_CHOOSE;
109   return (vstr_spn_cstr_chrs_fwd(conf->data, pos, len, " \t\v\r\n"));
110 }
111 
112 /* if the last token is a line comment token ... comment out that line */
conf__parse_comment(Conf_parse * conf,size_t pos,size_t len)113 static size_t conf__parse_comment(Conf_parse *conf, size_t pos, size_t len)
114 {
115   Vstr_sect_node *node = NULL;
116 
117   ASSERT(conf->sects);
118 
119   if (conf->sects->malloc_bad)
120     return (0);
121 
122   ASSERT(conf->sects->num);
123 
124   node = VSTR_SECTS_NUM(conf->sects, conf->sects->num);
125 
126   if (node->len >= 1)
127   {
128     Vstr_base *data = conf->data;
129     int byte = vstr_export_chr(data, node->pos);
130     size_t p = node->pos;
131     size_t l = node->len;
132 
133     if (((byte == ';') && (vstr_spn_cstr_chrs_fwd(data, p, l, ";") == l)) ||
134         ((byte == '#') && (vstr_spn_cstr_chrs_fwd(data, p, l, "#") == l)))
135     {
136       vstr_sects_del(conf->sects, conf->sects->num);
137       conf->state = CONF_PARSE_STATE_CHOOSE;
138       return (vstr_cspn_cstr_chrs_fwd(conf->data, pos, len, "\n"));
139     }
140   }
141 
142   return (0);
143 }
144 
conf__parse_beg_list(Conf_parse * conf,size_t pos,unsigned int * list_nums)145 static int conf__parse_beg_list(Conf_parse *conf, size_t pos,
146                                 unsigned int *list_nums)
147 {
148   if (conf->depth >= CONF_PARSE_LIST_DEPTH_SZ)
149     return (FALSE);
150 
151   vstr_sects_add(conf->sects, pos, 0);
152   conf__parse_type_add(conf, CONF_TOKEN_TYPE_ERR);
153   list_nums[conf->depth++] = conf->sects->num;
154 
155   conf->state = CONF_PARSE_STATE_CHOOSE;
156   return (TRUE);
157 }
158 
conf__parse_end_list(Conf_parse * conf,unsigned int * list_nums,int byte)159 static int conf__parse_end_list(Conf_parse *conf, unsigned int *list_nums,
160                                 int byte)
161 {
162   Vstr_sects *sects = conf->sects;
163   unsigned int depth_beg_num = 0;
164   Vstr_sect_node *node = NULL;
165 
166   if (!conf->depth)
167     return (FALSE);
168 
169   depth_beg_num = list_nums[--conf->depth];
170   node = VSTR_SECTS_NUM(sects, depth_beg_num);
171   if (byte == ']')
172   {
173     conf->types_ptr[depth_beg_num - 1] = CONF_TOKEN_TYPE_SLIST;
174     if (vstr_export_chr(conf->data, node->pos) != '[')
175       return (FALSE);
176   }
177 
178   if (byte == ')')
179   {
180     conf->types_ptr[depth_beg_num - 1] = CONF_TOKEN_TYPE_CLIST;
181     if (vstr_export_chr(conf->data, node->pos) != '(')
182       return (FALSE);
183   }
184 
185   ASSERT(!node->len);
186   node->len = sects->num - depth_beg_num;
187 
188   conf->state = CONF_PARSE_STATE_CHOOSE;
189   return (TRUE);
190 }
191 
conf__parse_beg_quote_d(Conf_parse * conf,size_t pos,unsigned int * list_nums)192 static void conf__parse_beg_quote_d(Conf_parse *conf, size_t pos,
193                                     unsigned int *list_nums)
194 {
195   ASSERT(conf->depth <= CONF_PARSE_LIST_DEPTH_SZ);
196 
197   vstr_sects_add(conf->sects, pos, 0);
198   conf__parse_type_add(conf, CONF_TOKEN_TYPE_QUOTE_D);
199   list_nums[conf->depth++] = conf->sects->num;
200 
201   conf->state = CONF_PARSE_STATE_QUOTE_D_BEG;
202 }
203 
conf__parse_beg_quote_s(Conf_parse * conf,size_t pos,unsigned int * list_nums)204 static void conf__parse_beg_quote_s(Conf_parse *conf, size_t pos,
205                                     unsigned int *list_nums)
206 {
207   ASSERT(conf->depth <= CONF_PARSE_LIST_DEPTH_SZ);
208 
209   vstr_sects_add(conf->sects, pos, 0);
210   conf__parse_type_add(conf, CONF_TOKEN_TYPE_QUOTE_S);
211   list_nums[conf->depth++] = conf->sects->num;
212 
213   conf->state = CONF_PARSE_STATE_QUOTE_S_BEG;
214 }
215 
conf__parse_beg_quote_xxx(Conf_parse * conf,unsigned int * list_nums,int three)216 static void conf__parse_beg_quote_xxx(Conf_parse *conf, unsigned int *list_nums,
217                                       int three)
218 {
219   Vstr_sects *sects = conf->sects;
220   unsigned int depth_beg_num = 0;
221   size_t mv_pos = 1;
222   unsigned int state = 0;
223 
224   ASSERT(conf->depth);
225 
226   depth_beg_num = list_nums[conf->depth - 1];
227 
228   if (conf->state == CONF_PARSE_STATE_QUOTE_S_BEG)
229     state = CONF_PARSE_STATE_QUOTE_S_END;
230   if (conf->state == CONF_PARSE_STATE_QUOTE_D_BEG)
231     state = CONF_PARSE_STATE_QUOTE_D_END;
232 
233   if (three)
234   {
235     mv_pos = 3;
236     state += CONF_PARSE_STATE_QUOTE_X2XXX;
237     conf->types_ptr[depth_beg_num - 1] += 2;
238   }
239 
240   VSTR_SECTS_NUM(sects, depth_beg_num)->pos += mv_pos;
241 
242   conf->state = state;
243 }
244 
conf__parse_beg_unquote(Conf_parse * conf,size_t pos,size_t len,unsigned int * list_nums)245 static size_t conf__parse_beg_unquote(Conf_parse *conf, size_t pos, size_t len,
246                                       unsigned int *list_nums)
247 {
248   size_t plen = 1;
249   int d   = FALSE;
250   int xxx = FALSE;
251 
252   if (FALSE) { }
253   else if (VPREFIX(conf->data, pos, len, "\"\"\""))
254   {
255     d   = TRUE;
256     xxx = TRUE;
257   }
258   else if (VPREFIX(conf->data, pos, len, "\""))
259   {
260     d   = TRUE;
261     xxx = FALSE;
262   }
263   else if (VPREFIX(conf->data, pos, len, "'''"))
264   {
265     d   = FALSE;
266     xxx = TRUE;
267   }
268   else if (VPREFIX(conf->data, pos, len, "'"))
269   {
270     d   = FALSE;
271     xxx = FALSE;
272   }
273   else
274     return (0);
275 
276   if (d)
277     conf__parse_beg_quote_d(conf, pos, list_nums);
278   else
279     conf__parse_beg_quote_s(conf, pos, list_nums);
280 
281   if (!xxx)
282     conf__parse_beg_quote_xxx(conf, list_nums, FALSE);
283   else
284   {
285     plen = 3;
286     conf__parse_beg_quote_xxx(conf, list_nums, TRUE);
287   }
288 
289   conf->state += CONF_PARSE_STATE_QUOTE2UNQUOTE_END;
290 
291   len -= plen; pos += plen;
292   if (VPREFIX(conf->data, pos, len, "\\\n")) /* allow first line */
293   {
294     unsigned int depth_beg_num = list_nums[conf->depth - 1];
295 
296     VSTR_SECTS_NUM(conf->sects, depth_beg_num)->pos += CLEN("\\\n");
297     plen                                            += CLEN("\\\n");
298   }
299 
300   return (plen);
301 }
302 
conf__parse_end_quote_xxx(Conf_parse * conf,size_t pos,unsigned int * list_nums)303 static void conf__parse_end_quote_xxx(Conf_parse *conf, size_t pos,
304                                       unsigned int *list_nums)
305 {
306   Vstr_sects *sects = conf->sects;
307   unsigned int depth_beg_num = 0;
308   size_t beg_pos = 0;
309 
310   ASSERT(conf->depth);
311 
312   depth_beg_num = list_nums[--conf->depth];
313   beg_pos = VSTR_SECTS_NUM(sects, depth_beg_num)->pos;
314   VSTR_SECTS_NUM(sects, depth_beg_num)->pos = beg_pos;
315   VSTR_SECTS_NUM(sects, depth_beg_num)->len = pos - beg_pos;
316 
317   conf->state = CONF_PARSE_STATE_LIST_END_OR_WS;
318 }
319 
conf__parse_esc_quote(Conf_parse * conf,unsigned int * list_nums,size_t pos,size_t len,unsigned int type)320 static int conf__parse_esc_quote(Conf_parse *conf, unsigned int *list_nums,
321                                  size_t pos, size_t len, unsigned int type)
322 {
323   Vstr_sects *sects = conf->sects;
324   unsigned int depth_beg_num = 0;
325 
326   ASSERT(conf->depth);
327 
328   if (len < 2)
329     return (FALSE);
330 
331   depth_beg_num = list_nums[conf->depth - 1];
332   if ((pos == VSTR_SECTS_NUM(sects, depth_beg_num)->pos) &&
333       vstr_cmp_cstr_eq(conf->data, pos, CLEN("\\\n"), "\\\n"))
334   {
335     VSTR_SECTS_NUM(sects, depth_beg_num)->pos += CLEN("\\\n");
336     return (TRUE);
337   }
338 
339   if (conf->types_ptr[depth_beg_num - 1] == type)
340     conf->types_ptr[depth_beg_num - 1] += CONF_TOKEN_TYPE_2ESC;
341 
342   return (TRUE);
343 }
344 #define CONF__SC_PARSE_ESC_QUOTE(x) do {                                \
345       ASSERT(vstr_export_chr(data, pos) == '\\');                       \
346                                                                         \
347       if (!conf__parse_esc_quote(conf, list_nums, pos, len, x))         \
348         return (CONF_PARSE_ERR);                                        \
349       plen = 2;                                                         \
350     } while (FALSE)
351 
352 #define CONF__SC_PARSE_QUOTE_X_BEG(x) do {                           \
353       if (!VPREFIX(data, pos, len, x))                               \
354         conf__parse_beg_quote_xxx(conf, list_nums, FALSE);           \
355       else                                                           \
356       {                                                              \
357         plen = 2;                                                    \
358         conf__parse_beg_quote_xxx(conf, list_nums, TRUE);            \
359       }                                                              \
360     } while (FALSE)
361 
362 #define CONF__SC_PARSE_QUOTE_XXX_END(x, y, z) do {                      \
363       if (!(plen = vstr_cspn_cstr_chrs_fwd(data, pos, len, x)))         \
364       {                                                                 \
365         plen = 3;                                                       \
366         if (VPREFIX(data, pos, len, y))                                 \
367           conf__parse_end_quote_xxx(conf, pos, list_nums);              \
368         else if (vstr_export_chr(data, pos) != '\\')                    \
369           plen = 1;                                                     \
370         else /* \x */                                                   \
371           CONF__SC_PARSE_ESC_QUOTE(CONF_TOKEN_TYPE_QUOTE_ ## z);        \
372       }                                                                 \
373     } while (FALSE)
374 
375 #define CONF__SC_PARSE_QUOTE_X_END(x, y, z) do {                        \
376       if (!(plen = vstr_cspn_cstr_chrs_fwd(data, pos, len, x)))         \
377       {                                                                 \
378         plen = 1;                                                       \
379         if (vstr_export_chr(data, pos) == y)                            \
380           conf__parse_end_quote_xxx(conf, pos, list_nums);              \
381         else /* \x */                                                   \
382           CONF__SC_PARSE_ESC_QUOTE(CONF_TOKEN_TYPE_QUOTE_ ## z);        \
383       }                                                                 \
384     } while (FALSE)
385 
386 #define CONF__SC_PARSE_UNQUOTE_END(x) do {                           \
387       if (!(plen = vstr_srch_cstr_buf_fwd(data, pos, len, x)))       \
388         plen = len;                                                  \
389       else                                                           \
390       {                                                              \
391         conf__parse_end_quote_xxx(conf, plen, list_nums);            \
392         plen -= pos;                                                 \
393         plen += CLEN(x);                                             \
394       }                                                              \
395     } while (FALSE)
396 
conf_parse_lex(Conf_parse * conf,size_t pos,size_t len)397 int conf_parse_lex(Conf_parse *conf, size_t pos, size_t len)
398 {
399   unsigned int list_nums[CONF_PARSE_LIST_DEPTH_SZ + 1];
400   Vstr_base *data = NULL;
401 
402   ASSERT(conf && conf->data && conf->sects &&
403          conf->types_ptr && conf->types_sz);
404 
405   data = conf->data;
406 
407   if (conf->state != CONF_PARSE_STATE_BEG)
408     return (CONF_PARSE_ERR);
409 
410   while (len)
411   {
412     size_t plen = 0; /* amount parsed this loop */
413 
414     switch (conf->state)
415     {
416       case CONF_PARSE_STATE_BEG: /* unix she-bang */
417         conf->state = CONF_PARSE_STATE_CHOOSE;
418         if (VPREFIX(data, pos, len, "#!"))
419         {
420           plen = vstr_cspn_cstr_chrs_fwd(data, pos, len, "\n");
421           conf->state = CONF_PARSE_STATE_WS;
422         }
423         break;
424 
425       case CONF_PARSE_STATE_WS:
426         if (!(plen = conf__parse_ws(conf, pos, len)))
427           return (CONF_PARSE_ERR);
428         break;
429 
430       case CONF_PARSE_STATE_LIST_END_OR_WS:
431       {
432         int byte = vstr_export_chr(data, pos);
433 
434         plen = 1;
435         switch (byte)
436         {
437           case ' ':  /* whitespace */
438           case '\t': /* whitespace */
439           case '\v': /* whitespace */
440           case '\r': /* whitespace */
441           case '\n': /* whitespace */
442             plen = conf__parse_ws(conf, pos, len);
443             break;
444 
445           case ')':
446           case ']':
447             if (!conf__parse_end_list(conf, list_nums, byte))
448               return (CONF_PARSE_ERR);
449             break;
450           default:
451             return (CONF_PARSE_ERR);
452         }
453       }
454       break;
455 
456       case CONF_PARSE_STATE_CHOOSE:
457       {
458         int byte = vstr_export_chr(data, pos);
459 
460         plen = 1;
461         switch (byte)
462         {
463           case ' ':  /* whitespace */
464           case '\t': /* whitespace */
465           case '\v': /* whitespace */
466           case '\r': /* whitespace */
467           case '\n': /* whitespace */
468             plen = conf__parse_ws(conf, pos, len);
469             break;
470 
471           case ')':
472           case ']':
473             if (!conf__parse_end_list(conf, list_nums, byte))
474               return (CONF_PARSE_ERR);
475             break;
476 
477           case '(':
478           case '[':
479             if (!conf__parse_beg_list(conf, pos, list_nums))
480               return (CONF_PARSE_ERR);
481             break;
482 
483           case '"':
484             conf__parse_beg_quote_d(conf, pos, list_nums);
485             break;
486           case '\'':
487             conf__parse_beg_quote_s(conf, pos, list_nums);
488             break;
489 
490           case '@': /* allow "raw" strings, for backslashes, @ == C# */
491           case 'r': /* allow "raw" strings, for backslashes, r == python */
492             if (len > 1)
493             {
494               char tmp = vstr_export_chr(data, pos + 1);
495               if ((tmp == '"') || (tmp == '\''))
496               {
497                 conf->state = CONF_PARSE_STATE_UNQUOTE_BEG;
498                 break;
499               }
500             }
501 
502           default:
503             plen = vstr_cspn_cstr_chrs_fwd(data, pos, len, " \t\v\r\n\"'()[]");
504             conf->state = CONF_PARSE_STATE_SYMBOL_END;
505             vstr_sects_add(conf->sects, pos, plen);
506             conf__parse_type_add(conf, CONF_TOKEN_TYPE_SYMBOL);
507             break;
508         }
509       }
510       break;
511 
512       case CONF_PARSE_STATE_QUOTE_D_BEG:
513         CONF__SC_PARSE_QUOTE_X_BEG("\"\"");
514         break;
515 
516       case CONF_PARSE_STATE_QUOTE_S_BEG:
517         CONF__SC_PARSE_QUOTE_X_BEG("''");
518       break;
519 
520       case CONF_PARSE_STATE_UNQUOTE_BEG:
521         if (!(plen = conf__parse_beg_unquote(conf, pos, len, list_nums)))
522           return (CONF_PARSE_ERR);
523         break;
524 
525       case CONF_PARSE_STATE_QUOTE_D_END:
526         CONF__SC_PARSE_QUOTE_X_END("\"\\", '"', D);
527         break;
528 
529       case CONF_PARSE_STATE_QUOTE_DDD_END:
530         CONF__SC_PARSE_QUOTE_XXX_END("\"\\", "\"\"\"", DDD);
531         break;
532 
533       case CONF_PARSE_STATE_QUOTE_S_END:
534         CONF__SC_PARSE_QUOTE_X_END("'\\", '\'', S);
535         break;
536 
537       case CONF_PARSE_STATE_QUOTE_SSS_END:
538         CONF__SC_PARSE_QUOTE_XXX_END("'\\", "'''", SSS);
539         break;
540 
541       case CONF_PARSE_STATE_UNQUOTE_DDD_END:
542         CONF__SC_PARSE_UNQUOTE_END("\"\"\"");
543         break;
544 
545       case CONF_PARSE_STATE_UNQUOTE_D_END:
546         CONF__SC_PARSE_UNQUOTE_END("\"");
547         break;
548 
549       case CONF_PARSE_STATE_UNQUOTE_SSS_END:
550         CONF__SC_PARSE_UNQUOTE_END("'''");
551         break;
552 
553       case CONF_PARSE_STATE_UNQUOTE_S_END:
554         CONF__SC_PARSE_UNQUOTE_END("'");
555         break;
556 
557       case CONF_PARSE_STATE_SYMBOL_END:
558       {
559         int byte = vstr_export_chr(data, pos);
560 
561         switch (byte)
562         {
563           case ' ':  /* whitespace */
564           case '\t': /* whitespace */
565           case '\v': /* whitespace */
566           case '\r': /* whitespace */
567           case '\n': /* whitespace */
568             if (!(plen = conf__parse_comment(conf, pos, len)))
569               plen = conf__parse_ws(conf, pos, len);
570             break;
571 
572           case ')':
573           case ']':
574             if (!(plen = conf__parse_comment(conf, pos, len)))
575             {
576               plen = 1;
577               if (!conf__parse_end_list(conf, list_nums, byte))
578                 return (CONF_PARSE_ERR);
579             }
580             break;
581 
582           case '(':
583           case '[':
584             if (!(plen = conf__parse_comment(conf, pos, len)))
585             {
586               plen = 1;
587               if (!conf__parse_beg_list(conf, pos, list_nums))
588                 return (CONF_PARSE_ERR);
589             }
590             break;
591 
592           case '"':
593           case '\'':
594             return (CONF_PARSE_ERR);
595           default:
596             assert(FALSE);
597             return (CONF_PARSE_ERR);
598         }
599       }
600       break;
601 
602       default:
603         assert(FALSE);
604         return (CONF_PARSE_ERR);
605     }
606 
607     len -= plen; pos += plen;
608   }
609 
610   if (conf->sects->malloc_bad || conf->depth)
611     return (CONF_PARSE_ERR);
612 
613   conf->state = CONF_PARSE_STATE_END;
614   return (CONF_PARSE_FIN);
615 }
616 #undef CONF__SC_PARSE_ESC_QUOTE
617 #undef CONF__SC_PARSE_QUOTE_X_BEG
618 #undef CONF__SC_PARSE_QUOTE_XXX_END
619 #undef CONF__SC_PARSE_QUOTE_X_END
620 #undef CONF__SC_PARSE_UNQUOTE_END
621 
conf_token_make(void)622 Conf_token *conf_token_make(void)
623 {
624   Conf_token dummy = CONF_TOKEN_INIT;
625   Conf_token *ret = MK(sizeof(Conf_token));
626 
627   if (!ret)
628     return (NULL);
629 
630   *ret = dummy;
631 
632   return (ret);
633 }
634 
conf_token_free(Conf_token * token)635 void conf_token_free(Conf_token *token)
636 {
637   F(token);
638 }
639 
640 static const char *conf__token_name_map[] = {
641  "<** Error **>",
642  "Circular bracket list",
643  "Square bracket list",
644  "Quoted string (double, RAW)",
645  "Quoted string (double, with Escaping)",
646  "Quoted string (3x double, RAW)",
647  "Quoted string (3x double, with Escaping)",
648  "Quoted string (single, RAW)",
649  "Quoted string (single, with Escaping)",
650  "Quoted string (3x single, RAW)",
651  "Quoted string (3x single, with Escaping)",
652  "Symbol"
653 };
654 
conf_token_name(const Conf_token * token)655 const char *conf_token_name(const Conf_token *token)
656 {
657   ASSERT(token);
658 
659   if (token->type > CONF_TOKEN_TYPE_SYMBOL)
660     return ("User type");
661 
662   return (conf__token_name_map[token->type]);
663 }
664 
conf_sc_token_parse_uint(const Conf_parse * conf,Conf_token * token,unsigned int * val)665 int conf_sc_token_parse_uint(const Conf_parse *conf, Conf_token *token,
666                              unsigned int *val)
667 {
668   unsigned int num = conf_token_list_num(token, token->depth_num);
669   int ern = CONF_SC_TYPE_RET_OK;
670   const Vstr_sect_node *pv = NULL;
671   unsigned int nflags = VSTR_FLAG02(PARSE_NUM, OVERFLOW, SEP);
672   size_t len = 0;
673 
674   ASSERT(val);
675 
676   if (!num)
677     return (CONF_SC_TYPE_RET_ERR_NOT_EXIST);
678   conf_parse_token(conf, token);
679   if (!(pv = conf_token_value(token)))
680     return (CONF_SC_TYPE_RET_ERR_PARSE);
681 
682   *val = vstr_parse_uint(conf->data, pv->pos, pv->len, nflags, &len, NULL);
683   if (len != pv->len)
684     ern = CONF_SC_TYPE_RET_ERR_PARSE;
685 
686   return (ern);
687 }
688 
conf_sc_token_app_vstr(const Conf_parse * conf,Conf_token * token,Vstr_base * s1,const Vstr_base ** a_s1,size_t * a_pos,size_t * a_len)689 int conf_sc_token_app_vstr(const Conf_parse *conf, Conf_token *token,
690                            Vstr_base *s1,
691                            const Vstr_base **a_s1, size_t *a_pos, size_t *a_len)
692 {
693   unsigned int num = conf_token_list_num(token, token->depth_num);
694   int ern = CONF_SC_TYPE_RET_OK;
695   const Vstr_sect_node *pv = NULL;
696 
697   ASSERT(s1);
698 
699   if (!num)
700     return (CONF_SC_TYPE_RET_ERR_NOT_EXIST);
701   conf_parse_token(conf, token);
702   if (!(pv = conf_token_value(token)))
703     return (CONF_SC_TYPE_RET_ERR_PARSE);
704 
705   if (vstr_add_vstr(s1, s1->len, conf->data, pv->pos, pv->len,
706                     VSTR_TYPE_SUB_BUF_REF))
707   {
708     *a_s1  = s1;
709     *a_pos = (s1->len - pv->len) + 1;
710     *a_len = pv->len;
711   }
712 
713   return (ern);
714 }
715 
conf_sc_token_sub_vstr(const Conf_parse * conf,Conf_token * token,Vstr_base * s1,size_t pos,size_t len)716 int conf_sc_token_sub_vstr(const Conf_parse *conf, Conf_token *token,
717                            Vstr_base *s1, size_t pos, size_t len)
718 {
719   unsigned int num = conf_token_list_num(token, token->depth_num);
720   int ern = CONF_SC_TYPE_RET_OK;
721   const Vstr_sect_node *pv = NULL;
722 
723   ASSERT(s1);
724 
725   if (!num)
726     return (CONF_SC_TYPE_RET_ERR_NOT_EXIST);
727   conf_parse_token(conf, token);
728   if (!(pv = conf_token_value(token)))
729     return (CONF_SC_TYPE_RET_ERR_PARSE);
730 
731   vstr_sub_vstr(s1, pos, len, conf->data, pv->pos, pv->len,
732                 VSTR_TYPE_SUB_BUF_REF);
733 
734   return (ern);
735 }
736 
737 #define SUB2(x, y, z) vstr_sub_cstr_buf(x, y, 2, z)
conf_sc_conv_unesc(Vstr_base * s1,size_t pos,size_t len,size_t * ret_len)738 int conf_sc_conv_unesc(Vstr_base *s1, size_t pos, size_t len,
739                        size_t *ret_len)
740 {
741   size_t dummy_len;
742 
743   if (!ret_len) ret_len = &dummy_len;
744 
745   *ret_len = len;
746 
747   while (!s1->conf->malloc_bad && (len > 1))
748   {
749     size_t plen = vstr_cspn_cstr_chrs_fwd(s1, pos, len, "\\");
750 
751     if (!plen)
752     {
753       char chr = vstr_export_chr(s1, pos + 1);
754 
755       if (chr == '\n')
756       {
757         vstr_del(s1, pos, 2);
758         len -= 2; *ret_len -= 2;
759         continue;
760       }
761       else if (chr == 't') { SUB2(s1, pos, "\t"); --len; --*ret_len; }
762       else if (chr == 'v') { SUB2(s1, pos, "\v"); --len; --*ret_len; }
763       else if (chr == 'r') { SUB2(s1, pos, "\r"); --len; --*ret_len; }
764       else if (chr == 'n') { SUB2(s1, pos, "\n"); --len; --*ret_len; }
765       else if (chr == 'b') { SUB2(s1, pos, "\b"); --len; --*ret_len; }
766       else if (chr == '0')
767       { /* \0 == NIL \0x0 == NIL */
768         unsigned char val = 0;
769         unsigned int nflags = VSTR_FLAG02(PARSE_NUM, OVERFLOW, SEP);
770 
771         if (chr != '0') nflags |= 8;
772 
773         /* FIXME: ... parse uchar */
774         val = vstr_parse_ushort(s1, pos + 1, len - 1, nflags, &plen, NULL);
775         vstr_sub_rep_chr(s1, pos, plen + 1, val, 1);
776         len      -= plen; /* byte == \ ... so not plen + 1 */
777         *ret_len -= plen;
778       }
779       else
780       { /* rm \ ... Eg. \" == " and \\ == \ */
781         vstr_del(s1, pos, 1);
782         --len; --*ret_len;
783       }
784       plen = 1;
785     }
786 
787     len -= plen;
788     pos += plen;
789   }
790 
791   return (!s1->conf->malloc_bad);
792 }
793 #undef SUB
794 
conf_token_set_user_value(Conf_parse * conf,Conf_token * token,unsigned int type,Vstr_ref * uval,unsigned int nxt)795 int conf_token_set_user_value(Conf_parse *conf, Conf_token *token,
796                               unsigned int type,
797                               Vstr_ref *uval, unsigned int nxt)
798 {
799   Vstr_ref *oref = NULL;
800 
801   ASSERT(conf && token);
802   ASSERT(type <= (UINT_MAX - CONF_TOKEN_TYPE_USER_BEG));
803 
804   /* must not be a CLIST or SLIST */
805   if ((token->type == CONF_TOKEN_TYPE_CLIST) ||
806       (token->type == CONF_TOKEN_TYPE_SLIST))
807     return (FALSE);
808 
809   if (token->type >= CONF_TOKEN_TYPE_USER_BEG)
810     oref = conf->uvals_ptr[token->u.uval_num].ref;
811   else
812   {
813     if (conf->uvals_sz <= conf->uvals_num)
814     {
815       unsigned int num = (conf->uvals_sz << 1) + 1;
816       Conf__uval_store *uvals  = NULL;
817 
818       if (num <= conf->uvals_sz)
819         return (FALSE);
820 
821       if (!conf->uvals_sz &&
822           !(conf->uvals_ptr = MK(sizeof(Conf__uval_store) * num)))
823         return (FALSE);
824       else if (!MV(conf->uvals_ptr, uvals, sizeof(Conf__uval_store) * num))
825         return (FALSE);
826 
827       conf->uvals_sz  = num;
828     }
829     VSTR_SECTS_NUM(conf->sects, token->num)->pos = conf->uvals_num;
830     token->u.uval_num                            = conf->uvals_num;
831 
832     ++conf->uvals_num;
833   }
834 
835   token->type = CONF_TOKEN_TYPE_USER_BEG + type;
836 
837   conf->types_ptr[token->num - 1]        = token->type;
838   conf->uvals_ptr[token->u.uval_num].ref = NULL;
839   conf->uvals_ptr[token->u.uval_num].nxt = nxt;
840   if (uval)
841     conf->uvals_ptr[token->u.uval_num].ref = vstr_ref_add(uval);
842 
843   vstr_ref_del(oref);
844 
845   return (TRUE);
846 }
847 
conf_parse_compress(Conf_parse * conf)848 void conf_parse_compress(Conf_parse *conf)
849 { /* FIXME: remove all Vstr data that isn't referenced, fixup sections */
850   (void)conf;
851 }
852 
conf_parse_backtrace(Vstr_base * out,const char * filename,const Conf_parse * conf,const Conf_token * token)853 void conf_parse_backtrace(Vstr_base *out, const char *filename,
854                           const Conf_parse *conf, const Conf_token *token)
855 {
856   const Vstr_sect_node *val = NULL;
857 
858   if (!out)
859     return;
860 
861   if (conf->state != CONF_PARSE_STATE_END)
862   {
863     vstr_add_fmt(out, out->len, "Syntax error in %s,  ", filename);
864     vstr_add_fmt(out, out->len, "  State was: %d", conf->state);
865     return;
866   }
867 
868   /* walk backwards */
869   if (token->type > CONF_TOKEN_TYPE_SYMBOL)
870     vstr_add_fmt(out, out->len, "Failed parse on node %u (%s %d)",
871                  token->num, conf_token_name(token),
872                  token->type - CONF_TOKEN_TYPE_SYMBOL);
873   else if (!(val = conf_token_value(token)))
874     vstr_add_fmt(out, out->len, "Failed parse on node %u [%s]",
875                  token->num, conf_token_name(token));
876   else
877   {
878     Vstr_base *s1 = conf->data;
879 
880     vstr_add_fmt(out, out->len, "Failed parse on node %u <%s> = ",
881                  token->num, conf_token_name(token));
882     vstr_add_vstr(out, out->len, s1, val->pos, val->len, VSTR_TYPE_ADD_BUF_REF);
883   }
884 }
885