1 /*-
2 * Copyright (c) 2003-2004 Andrey Simonenko
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include "config.h"
28
29 #ifndef lint
30 static const char rcsid[] ATTR_UNUSED =
31 "@(#)$Id: parser.c,v 1.4 2011/01/23 18:42:35 simon Exp $";
32 #endif /* !lint */
33
34 /*
35 * I considered using of yacc/lex for implementing the parser for
36 * ipa.conf(5), but have found that there will not be much advantages
37 * in using such tools. At least: ipa.conf(5) accepts macro variables
38 * and a module is allowed to add macro variables at any time, when it
39 * parses a configuration stream, so we have to get one-by-one logical
40 * line from the file and then parse it or give it to a module for parsing.
41 * If a module considers that yacc/lex or similar tools are better, then
42 * it can mark all its arguments values as IPA_CONF_TYPE_MISC and take
43 * full control on parsing any argument.
44 */
45
46 #include <sys/types.h>
47
48 #include <ctype.h>
49 #include <errno.h>
50 #include <limits.h>
51 #include <stdarg.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <strings.h>
56
57 #include "ipa_mod.h"
58
59 #include "queue.h"
60
61 #include "memfunc.h"
62 #include "parser.h"
63
64 /*
65 * Set DEBUG_PARSER to non-zero value to output debug
66 * information about parsing to stderr.
67 */
68 #ifndef DEBUG_PARSER
69 # define DEBUG_PARSER 0
70 #endif
71
72 ipa_mem_type *m_parser; /* Memory allocated by parser. */
73 char *parser_str_buf = NULL; /* -> buffer in parser_stringify(). */
74
75 void (*parser_vlogmsgx)(const char *, va_list) = NULL;
76
77 unsigned int parser_token_id; /* Token ID. */
78 char *parser_token; /* Token name or NULL (section end). */
79 char *parser_args; /* Arguments or NULL (no arguments). */
80 size_t parser_args_len; /* strlen() of arguments. */
81 int parser_nargs; /* Number of arguments. */
82
83 #define PB_SIZE 4096 /* Initial size of buffer. */
84 #define PB_SIZE_MAX (4096 * 1024) /* Maximum size of buffer. */
85
86 /* Stack of pbs. */
87 static SLIST_HEAD(EMPTY, parser_pb) pb_stack;
88
89 /* Pointer to current char read by parser_get_curhcar(). */
90 static unsigned char *curchar;
91
92 /*
93 * One symbol (macro variable).
94 */
95 struct psym {
96 TAILQ_ENTRY(psym) link; /* List of symbols. */
97 char *sym; /* Symbol's name. */
98 char *val; /* Symbol's value. */
99 size_t val_len; /* Length of val. */
100 char copy_flag; /* If set then copy sym and val.*/
101 };
102
103 TAILQ_HEAD(psym_list, psym); /* Head of list of symbols. */
104
105 /* Local and global symbols lists. */
106 static struct psym_list local_sym_list;
107 static struct psym_list global_sym_list;
108
109 static int section_cnt; /* +1 when '{' and -1 when '}'. */
110 static unsigned int quotes_cnt; /* Number of '\"' in the argument. */
111
112 /* Set if we are in a string. */
113 static char in_string_flag;
114
115 /* Set if there was a space character in arguments. */
116 static char was_space_flag;
117
118 /* Set if we are in symbol definition. */
119 static char sym_def_flag;
120
121 static void logmsgx(const char *, ...) ATTR_FORMAT(printf, 1, 2);
122 static void syntax_logmsgx(const char *, ...) ATTR_FORMAT(printf, 1, 2);
123 static void error_logmsgx(const char *, ...) ATTR_FORMAT(printf, 1, 2);
124
125 #if DEBUG_PARSER
126 static void debuglog(const char *, ...) ATTR_FORMAT(printf, 1, 2);
127 #endif
128
129 /*
130 * Some constants for characters.
131 */
132 enum {
133 CH_NEWLINE = 0, /* 0 | '\n' */
134 CH_SEMICOLON, /* 1 | ';' */
135 CH_SPACE, /* 2 | ' ' or '\t' */
136 CH_QUOTE, /* 3 | '\"' */
137 CH_OPEN_BRACE, /* 4 | '{' */
138 CH_CLOSE_BRACE, /* 5 | '}' */
139 CH_EQUAL, /* 6 | '=' */
140 CH_POUND, /* 7 | '#' */
141 CH_DOLLAR, /* 8 | '$' */
142 CH_COLON, /* 9 | ':' */
143 CH_UNDERSCORE, /* 10 | '_' */
144 CH_BACKSLASH, /* 11 | '\\' */
145 CH_SLASH, /* 12 | '/' */
146 CH_ALPHA, /* 13 | letter */
147 CH_DIGIT, /* 14 | digit */
148 CH_PUNCT, /* 15 | punctuation */
149 CH_OTHER /* 16 | other */
150 };
151
152 #if DEBUG_PARSER
153 static const char *const ch_msg[] = { "NEWLINE", "SEMICOLON", "SPACE", "QUOTE",
154 "OPEN_BRACE", "CLOSE_BRACE", "EQUAL", "POUND", "DOLLAR", "COLON",
155 "UNDERSCORE", "BACKSLASH", "SLASH", "ALPHA", "DIGIT", "PUNCT",
156 "OTHER" };
157 #endif
158
159 #define PARSER_AC_T unsigned char /* Type for AC_xxx values. */
160 #define PARSER_ST_T unsigned char /* Type for ST_xxx and ER_xxx values. */
161
162 /*
163 * Parser's states.
164 */
165 enum {
166 ST_INI = 0, /* 0 | Initial state. */
167 ST_TOK, /* 1 | Characters go to the token. */
168 ST_ARG, /* 2 | Characters go to the argument. */
169 ST_STR, /* 3 | Characters go to a string in the argument. */
170 ST_AEX /* 4 | An argument is expected, wait for non space. */
171 };
172
173 #define ST_NO 5 /* Number of states. */
174
175 static PARSER_ST_T curstate = ST_INI; /* Current state. */
176
177 #if DEBUG_PARSER
178 static const char *const st_msg[] = { "INI", "TOK", "ARG", "STR", "AEX" };
179 #endif
180
181 /*
182 * Parser's actions.
183 */
184 enum {
185 AC_SKP = 0, /* 0 | Skip a character, continue parsing. */
186 AC_AAR, /* 1 | Add a character to the argument. */
187 AC_ATK, /* 2 | Add a character to the token. */
188 AC_AQU, /* 3 | Add '\"' to the argument. */
189 AC_TOK, /* 4 | Definition of the token is complete. */
190 AC_PAR, /* 5 | Definition of the parameter is complete. */
191 AC_BOS, /* 6 | Begin of the section. */
192 AC_EOS, /* 7 | End of the section. */
193 AC_PND, /* 8 | '#' appeared. */
194 AC_FMT, /* 9 | '\\' appeared in a string. */
195 AC_ASP, /* 10 | Space in the argument. */
196 AC_SLH, /* 11 | '/' appeared. */
197 AC_DLR, /* 12 | '$' appeared. */
198 AC_ERR /* 13 | A syntax error occurred. */
199 };
200
201 #define AC_NO 14 /* Number of actions. */
202
203 #if DEBUG_PARSER
204 static const char *const ac_msg[] = { "SKP", "AAR", "ATK", "AQU", "TOK",
205 "PAR", "BOS", "EOS", "PND", "FMT", "ASP", "SLH", "DLR", "ERR" };
206 #endif
207
208 /*
209 * Parser's errors.
210 */
211 enum {
212 ER_UXP, /* 0 | Unexpected character. */
213 ER_NSN, /* 1 | No section name. */
214 ER_NLV, /* 2 | No lvalue: section/parameter name is absent). */
215 ER_FMT /* 3 | Wrong character after '\\' in a string. */
216 };
217
218 #if DEBUG_PARSER
219 static const char *const er_msg[] = { "UXP", "NSN", "NLV", "FMT" };
220 #endif
221
222 static const char *const error_msg[] = {
223 /* 0 */ "unexpected character",
224 /* 1 */ "no section name",
225 /* 2 */ "no lvalue (section or parameter name is absent)",
226 /* 3 */ "wrong format character after '\\' in a string"
227 };
228
229 struct stac {
230 PARSER_AC_T action; /* AC_xxx */
231 PARSER_ST_T state; /* ST_xxx or
232 ER_xxx if action == AC_ERR. */
233 } ATTR_PACKED;
234
235 static const struct stac *curstac; /* Current entry in stac_tbl. */
236
237 static const struct stac stac_tbl[][ST_NO] = {
238 /* ST_INI ST_TOK ST_ARG ST_STR ST_AEX */
239 /* \n */ { {AC_SKP,ST_INI}, {AC_TOK,ST_AEX}, {AC_ASP,ST_ARG}, {AC_AAR,ST_STR}, {AC_SKP,ST_AEX} },
240 /* ; */ { {AC_ERR,ER_UXP}, {AC_PAR,ST_INI}, {AC_PAR,ST_INI}, {AC_AAR,ST_STR}, {AC_PAR,ST_INI} },
241 /* sp */ { {AC_SKP,ST_INI}, {AC_TOK,ST_AEX}, {AC_ASP,ST_ARG}, {AC_AAR,ST_STR}, {AC_SKP,ST_AEX} },
242 /* \" */ { {AC_ERR,ER_UXP}, {AC_ERR,ER_UXP}, {AC_AQU,ST_STR}, {AC_AQU,ST_ARG}, {AC_AQU,ST_STR} },
243 /* { */ { {AC_ERR,ER_NSN}, {AC_BOS,ST_INI}, {AC_BOS,ST_INI}, {AC_AAR,ST_STR}, {AC_BOS,ST_INI} },
244 /* } */ { {AC_EOS,ST_INI}, {AC_ERR,ER_UXP}, {AC_ERR,ER_UXP}, {AC_AAR,ST_STR}, {AC_ERR,ER_UXP} },
245 /* = */ { {AC_ERR,ER_NLV}, {AC_TOK,ST_ARG}, {AC_AAR,ST_ARG}, {AC_AAR,ST_STR}, {AC_SKP,ST_ARG} },
246 /* # */ { {AC_PND,ST_INI}, {AC_PND,ST_ARG}, {AC_PND,ST_ARG}, {AC_AAR,ST_STR}, {AC_PND,ST_AEX} },
247 /* $ */ { {AC_DLR,ST_AEX}, {AC_DLR,ST_TOK}, {AC_DLR,ST_ARG}, {AC_DLR,ST_STR}, {AC_DLR,ST_AEX} },
248 /* : */ { {AC_ATK,ST_TOK}, {AC_ATK,ST_TOK}, {AC_AAR,ST_ARG}, {AC_AAR,ST_STR}, {AC_AAR,ST_ARG} },
249 /* _ */ { {AC_ATK,ST_TOK}, {AC_ATK,ST_TOK}, {AC_AAR,ST_ARG}, {AC_AAR,ST_STR}, {AC_AAR,ST_ARG} },
250 /* \\ */ { {AC_ERR,ER_UXP}, {AC_ERR,ER_UXP}, {AC_AAR,ST_ARG}, {AC_FMT,ST_STR}, {AC_AAR,ST_ARG} },
251 /* / */ { {AC_SLH,ST_INI}, {AC_SLH,ST_TOK}, {AC_SLH,ST_ARG}, {AC_AAR,ST_STR}, {AC_SLH,ST_AEX} },
252 /*alpha*/ { {AC_ATK,ST_TOK}, {AC_ATK,ST_TOK}, {AC_AAR,ST_ARG}, {AC_AAR,ST_STR}, {AC_AAR,ST_ARG} },
253 /*digit*/ { {AC_ERR,ER_UXP}, {AC_ATK,ST_TOK}, {AC_AAR,ST_ARG}, {AC_AAR,ST_STR}, {AC_AAR,ST_ARG} },
254 /*punct*/ { {AC_ERR,ER_UXP}, {AC_ERR,ER_UXP}, {AC_AAR,ST_ARG}, {AC_AAR,ST_STR}, {AC_AAR,ST_ARG} },
255 /*other*/ { {AC_ERR,ER_UXP}, {AC_ERR,ER_UXP}, {AC_ERR,ER_UXP}, {AC_AAR,ST_STR}, {AC_ERR,ER_UXP} }
256 };
257
258 static unsigned char *whatis_char; /* Characters --> classes table. */
259
260 /*
261 * This generic configuration buffer structure is used for adding
262 * characters to token, argument and symbol name.
263 */
264 struct conf_buf {
265 char *buf; /* Buffer. */
266 size_t size; /* Its size. */
267 size_t off; /* Current offset. */
268 };
269
270 static struct conf_buf token_buf; /* Buffer for a token. */
271 static struct conf_buf args_buf; /* Buffer for arguments. */
272 static struct conf_buf sym_buf; /* Buffer for a symbol name. */
273 static struct conf_buf *cur_buf; /* Pointer to the current buffer. */
274
275 /*
276 * Return codes from parser_get_curchar() and ac_xxx().
277 */
278 enum {
279 AC_RET_CONT = 0, /* 0 | Update state, continue parsing. */
280 AC_RET_LINE, /* 1 | Update state, return logical line. */
281 AC_RET_CONTX, /* 2 | Continue parsing. */
282 AC_RET_ERR, /* 3 | An error occurred. */
283 AC_RET_EOF /* 4 | EOF. */
284 };
285
286 static int ac_skp(void), ac_aar(void), ac_atk(void), ac_aqu(void);
287 static int ac_tok(void), ac_par(void), ac_bos(void), ac_eos(void);
288 static int ac_pnd(void), ac_fmt(void), ac_asp(void), ac_slh(void);
289 static int ac_dlr(void), ac_err(void);
290
291 static int (* const ac_func_tbl[AC_NO])(void) = {
292 ac_skp, /* AC_SKP */
293 ac_aar, /* AC_AAR */
294 ac_atk, /* AC_ATK */
295 ac_aqu, /* AC_AQU */
296 ac_tok, /* AC_TOK */
297 ac_par, /* AC_PAR */
298 ac_bos, /* AC_BOS */
299 ac_eos, /* AC_EOS */
300 ac_pnd, /* AC_PND */
301 ac_fmt, /* AC_FMT */
302 ac_asp, /* AC_ASP */
303 ac_slh, /* AC_SLH */
304 ac_dlr, /* AC_DLR */
305 ac_err /* AC_ERR */
306 };
307
308 /*
309 * The wrapper for parser_vlog, its declaration above will check
310 * format string and arguments.
311 */
312 static void
logmsgx(const char * format,...)313 logmsgx(const char *format, ...)
314 {
315 va_list ap;
316
317 va_start(ap, format);
318 parser_vlogmsgx(format, ap);
319 va_end(ap);
320 }
321
322 #if DEBUG_PARSER
323 /*
324 * printf-like function, which outputs debug messages to stderr.
325 */
326 static void
debuglog(const char * format,...)327 debuglog(const char *format, ...)
328 {
329 va_list ap;
330
331 va_start(ap, format);
332 vfprintf(stderr, format, ap);
333 va_end(ap);
334 }
335 #endif
336
337 /*
338 * Output error message header.
339 */
340 static void
error_header_log(const char * what)341 error_header_log(const char *what)
342 {
343 const struct parser_pb *pb;
344
345 pb = SLIST_FIRST(&pb_stack);
346 if (pb != NULL) {
347 char buf[5];
348
349 if (isprint(*curchar)) {
350 buf[0] = *curchar;
351 buf[1] = '\0';
352 } else
353 switch (*curchar) {
354 case '\n':
355 strncpy(buf, "\\n", sizeof(buf));
356 break;
357 case '\t':
358 strncpy(buf, "\\t", sizeof(buf));
359 break;
360 default:
361 snprintf(buf, sizeof(buf), "0x%02x", *curchar);
362 }
363 if (pb->fp != NULL)
364 logmsgx("%serror occurred near line %u at character "
365 "'%s' in %s", what, pb->lineno, buf, pb->fname);
366 else
367 logmsgx("%serror occurred near line %u at character "
368 "'%s' in %s (in macro variable ${%s} value)",
369 what, pb->lineno, buf, pb->fname, pb->sname);
370 } else
371 logmsgx("%serror occurred near last line in main "
372 "configuration file", what);
373 }
374
375 /*
376 * Report about syntax error.
377 */
378 static void
syntax_logmsgx(const char * format,...)379 syntax_logmsgx(const char *format, ...)
380 {
381 va_list ap;
382
383 error_header_log("syntax ");
384 va_start(ap, format);
385 parser_vlogmsgx(format, ap);
386 va_end(ap);
387 }
388
389 /*
390 * Report about syntax error given by the code.
391 */
392 static void
syntax_error(PARSER_ST_T error)393 syntax_error(PARSER_ST_T error)
394 {
395 syntax_logmsgx("%s", error_msg[error]);
396 }
397
398 /*
399 * Report about non-syntax error.
400 */
401 static void
error_logmsgx(const char * format,...)402 error_logmsgx(const char *format, ...)
403 {
404 va_list ap;
405
406 error_header_log("");
407 va_start(ap, format);
408 parser_vlogmsgx(format, ap);
409 va_end(ap);
410 }
411
412 /*
413 * Return a pointer to psym for the symbol with name sym
414 * in the given psym_list.
415 */
416 static struct psym *
find_sym(const struct psym_list * list,const char * sym)417 find_sym(const struct psym_list *list, const char *sym)
418 {
419 struct psym *psym;
420
421 TAILQ_FOREACH(psym, list, link)
422 if (strcmp(psym->sym, sym) == 0)
423 return (psym);
424 return (NULL);
425 }
426
427 /*
428 * Release memory used by the given psym_list.
429 */
430 static void
free_sym_list(struct psym_list * list)431 free_sym_list(struct psym_list *list)
432 {
433 struct psym *psym, *psym_next;
434
435 TAILQ_FOREACH_SAFE(psym, list, link, psym_next) {
436 if (psym->copy_flag) {
437 mem_free(psym->sym, m_parser);
438 mem_free(psym->val, m_parser);
439 }
440 mem_free(psym, m_parser);
441 }
442 TAILQ_INIT(list);
443 }
444
445 /*
446 * Add a new symbol with name sym and with value val to list, if
447 * the list already has this symbol, then replace it with a new value,
448 * honoring the copy_flag value.
449 */
450 static int
sym_add(struct psym_list * list,char * sym,char * val,int copy_flag)451 sym_add(struct psym_list *list, char *sym, char *val, int copy_flag)
452 {
453 struct psym *psym;
454 char old_copy_flag;
455
456 psym = find_sym(list, sym);
457 if (psym != NULL) {
458 /* Replace symbol. */
459 old_copy_flag = psym->copy_flag;
460 if (old_copy_flag) {
461 if (!copy_flag)
462 mem_free(psym->sym, m_parser);
463 mem_free(psym->val, m_parser);
464 }
465 } else {
466 /* New symbol. */
467 old_copy_flag = 0;
468 psym = mem_malloc(sizeof(*psym), m_parser);
469 if (psym == NULL) {
470 logmsgx("sym_add: mem_malloc failed");
471 return (-1);
472 }
473 TAILQ_INSERT_TAIL(list, psym, link);
474 }
475
476 if (copy_flag) {
477 if (!old_copy_flag) {
478 psym->sym = mem_strdup(sym, m_parser);
479 if (psym->sym == NULL) {
480 logmsgx("sym_add: mem_strdup failed");
481 mem_free(psym, m_parser);
482 return (-1);
483 }
484 }
485 psym->val = mem_strdup(val, m_parser);
486 if (psym->val == NULL) {
487 logmsgx("sym_add: mem_strdup failed");
488 mem_free(psym->sym, m_parser);
489 mem_free(psym, m_parser);
490 return (-1);
491 }
492 } else {
493 psym->sym = sym;
494 psym->val = val;
495 }
496
497 psym->val_len = strlen(val);
498 psym->copy_flag = copy_flag;
499
500 return (0);
501 }
502
503 /*
504 * Remove a symbol sym from the list, return -1 if the list
505 * does not have a symbol with the given name.
506 */
507 static int
sym_del(struct psym_list * list,const char * sym)508 sym_del(struct psym_list *list, const char *sym)
509 {
510 struct psym *psym;
511
512 psym = find_sym(list, sym);
513 if (psym != NULL) {
514 TAILQ_REMOVE(list, psym, link);
515 if (psym->copy_flag) {
516 mem_free(psym->sym, m_parser);
517 mem_free(psym->val, m_parser);
518 }
519 mem_free(psym, m_parser);
520 } else
521 return (-1);
522 return (0);
523 }
524
525 /*
526 * Delete a local symbol.
527 */
528 int
parser_local_sym_del(const char * sym)529 parser_local_sym_del(const char *sym)
530 {
531 return (sym_del(&local_sym_list, sym));
532 }
533
534 /*
535 * Add a local symbol.
536 */
537 int
parser_local_sym_add(char * sym,char * val,int copy_flag)538 parser_local_sym_add(char *sym, char *val, int copy_flag)
539 {
540 return (sym_add(&local_sym_list, sym, val, copy_flag));
541 }
542
543 /*
544 * Delete a global symbol.
545 */
546 int
parser_global_sym_del(const char * sym)547 parser_global_sym_del(const char *sym)
548 {
549 return (sym_del(&global_sym_list, sym));
550 }
551
552 /*
553 * Add a global symbol.
554 */
555 int
parser_global_sym_add(char * sym,char * val,int copy_flag)556 parser_global_sym_add(char *sym, char *val, int copy_flag)
557 {
558 return (sym_add(&global_sym_list, sym, val, copy_flag));
559 }
560
561 /*
562 * Create a new pb. If size is zero, then allocate pb->buf,
563 * else do not allocate pb->buf, it should be allocated somewhere
564 * outside of this function.
565 */
566 struct parser_pb *
parser_new_pb(size_t size)567 parser_new_pb(size_t size)
568 {
569 struct parser_pb *pb;
570
571 pb = mem_malloc(sizeof(*pb), m_parser);
572 if (pb == NULL) {
573 logmsgx("parser_new_pb: mem_malloc failed");
574 return (NULL);
575 }
576 if (size == 0) {
577 pb->buf_size = PB_SIZE;
578 pb->buf = mem_malloc(PB_SIZE, m_parser);
579 if (pb->buf == NULL) {
580 logmsgx("parser_new_pb: mem_malloc failed");
581 mem_free(pb, m_parser);
582 return (NULL);
583 }
584 } else {
585 pb->buf_size = size;
586 pb->buf = NULL;
587 }
588 pb->buf_nread = pb->buf_off = 0;
589 pb->fname = NULL;
590 pb->sname = NULL;
591 pb->fp = NULL;
592 pb->lineno = 1;
593 return (pb);
594 }
595
596 /*
597 * Push pb to the top of pb_stack and set curchar pointer. If the
598 * given pb is a macro variable's value, then check for loops.
599 */
600 int
parser_push_pb(struct parser_pb * pb1)601 parser_push_pb(struct parser_pb *pb1)
602 {
603 const struct parser_pb *pb;
604 struct parser_pb *pb2;
605
606 pb = SLIST_FIRST(&pb_stack);
607
608 #if DEBUG_PARSER
609 if (pb != NULL) {
610 debuglog("CURPBUF: ");
611 debuglog("file %s", pb->fname);
612 if (pb->sname != NULL)
613 debuglog(" (sym ${%s} mem <%s>)", pb->sname, pb->buf);
614 debuglog(" size %lu, off %lu, nread %lu\n",
615 (unsigned long)pb->buf_size, (unsigned long)pb->buf_off,
616 (unsigned long)pb->buf_nread);
617 }
618 debuglog("PBUF PUSH: ");
619 if (pb1->fname != NULL)
620 debuglog("file %s", pb1->fname);
621 else
622 debuglog("sym ${%s} mem <%s>", pb1->sname, pb1->buf);
623 debuglog(" size %lu, off %lu, nread %lu\n",
624 (unsigned long)pb1->buf_size, (unsigned long)pb1->buf_off,
625 (unsigned long)pb1->buf_nread);
626 #endif /* DEBUG_PARSER */
627
628 /* Check for loopbacks in macro variable expanding. */
629 if (pb1->sname != NULL)
630 SLIST_FOREACH(pb2, &pb_stack, link)
631 if (pb2->sname != NULL)
632 if (strcmp(pb2->sname, pb1->sname) == 0) {
633 error_logmsgx("parser_push_pb: loop"
634 "back is detected while expanding "
635 "${%s} macro variable", pb1->sname);
636 return (-1);
637 }
638
639 /* Link just created new pb. */
640 SLIST_INSERT_HEAD(&pb_stack, pb1, link);
641
642 if (pb1->fname == NULL) {
643 /* This is needed only for log functions. */
644 pb1->fname = pb->fname;
645 pb1->lineno = pb->lineno;
646 }
647
648 /* ...-1 for parser_get_curchar(). */
649 curchar = pb1->buf - 1;
650
651 return (0);
652 }
653
654 /*
655 * Pop pb from the top of pb_stack and restore curchar pointer.
656 */
657 struct parser_pb *
parser_pop_pb(void)658 parser_pop_pb(void)
659 {
660 struct parser_pb *pb;
661
662 /* Unlink pb. */
663 pb = SLIST_FIRST(&pb_stack);
664 SLIST_REMOVE_HEAD(&pb_stack, link);
665
666 /* For macro variables buffer is shared with macro variable's value. */
667 if (pb->fp != NULL)
668 mem_free(pb->buf, m_parser);
669
670 /* Release memory used by pb structure. */
671 mem_free(pb, m_parser);
672
673 /* Get new pb and set curchar, ...-1 for parser_get_curchar(). */
674 pb = SLIST_FIRST(&pb_stack);
675 if (pb != NULL)
676 curchar = pb->buf + pb->buf_off - 1;
677
678 #if DEBUG_PARSER
679 if (pb == NULL)
680 debuglog("PBUF POP: NULL\n");
681 else {
682 debuglog("PBUF POP: ");
683 debuglog("file %s", pb->fname);
684 if (pb->sname != NULL)
685 debuglog(" (sym ${%s} mem <%s>)", pb->sname, pb->buf);
686 debuglog(" %lu bytes, off %lu, nread %lu\n",
687 (unsigned long)pb->buf_size, (unsigned long)pb->buf_off,
688 (unsigned long)pb->buf_nread);
689 }
690 #endif
691
692 return (pb);
693 }
694
695 /*
696 * Return pointer to top pb.
697 */
698 struct parser_pb *
parser_top_pb(void)699 parser_top_pb(void)
700 {
701 return (SLIST_FIRST(&pb_stack));
702 }
703
704 /*
705 * Initialize whatis_char table (only once), {token,args,sym}_buf,
706 * symbol tables and pb_stack. This function can be called,
707 * even if parser_read_string() returned an error.
708 */
709 int
parser_init(void)710 parser_init(void)
711 {
712 static char called_before = 0;
713
714 if (parser_vlogmsgx == NULL) {
715 /* Cannot even log about problem. */
716 return (-1);
717 }
718
719 /*
720 * Initialize whatis_char only once. Cannot use static table,
721 * because we should honor locale (isxxxx() functions).
722 */
723 if (!called_before) {
724 unsigned int ch;
725
726 called_before = 1;
727 whatis_char = malloc(2 << (CHAR_BIT - 1));
728 if (whatis_char == NULL) {
729 logmsgx("parser_init: malloc: %s", strerror(errno));
730 return (-1);
731 }
732 for (ch = 0; ch < (2 << (CHAR_BIT - 1)); ++ch)
733 if (isalpha(ch))
734 whatis_char[ch] = CH_ALPHA;
735 else if (ispunct(ch))
736 whatis_char[ch] = CH_PUNCT;
737 else if (isdigit(ch))
738 whatis_char[ch] = CH_DIGIT;
739 else
740 whatis_char[ch] = CH_OTHER;
741 whatis_char['\t'] = CH_SPACE;
742 whatis_char['\n'] = CH_NEWLINE;
743 whatis_char[' '] = CH_SPACE;
744 whatis_char['\"'] = CH_QUOTE;
745 whatis_char['#'] = CH_POUND;
746 whatis_char['$'] = CH_DOLLAR;
747 whatis_char['/'] = CH_SLASH;
748 whatis_char[':'] = CH_COLON;
749 whatis_char[';'] = CH_SEMICOLON;
750 whatis_char['='] = CH_EQUAL;
751 whatis_char['\\'] = CH_BACKSLASH;
752 whatis_char['_'] = CH_UNDERSCORE;
753 whatis_char['{'] = CH_OPEN_BRACE;
754 whatis_char['}'] = CH_CLOSE_BRACE;
755 }
756
757 /* Initialize token_buf, args_buf and sym_buf. */
758 token_buf.buf = args_buf.buf = sym_buf.buf = NULL;
759 token_buf.size = args_buf.size = sym_buf.size = 0;
760
761 /* Initialize symbol tables. */
762 TAILQ_INIT(&global_sym_list);
763 TAILQ_INIT(&local_sym_list);
764
765 /* Initialize pb_stack. */
766 SLIST_INIT(&pb_stack);
767
768 section_cnt = 0;
769
770 in_string_flag = was_space_flag = sym_def_flag = 0;
771
772 return (0);
773 }
774
775 /*
776 * Do last check of syntax and release all memory except whatis_char
777 * table allocated in parser_init().
778 */
779 int
parser_deinit(void)780 parser_deinit(void)
781 {
782 /* Last check of syntax. */
783 if (section_cnt) {
784 syntax_logmsgx("section is not closed");
785 return (-1);
786 }
787
788 /* Release internal memory. */
789 mem_free(token_buf.buf, m_parser);
790 mem_free(args_buf.buf, m_parser);
791 mem_free(sym_buf.buf, m_parser);
792
793 /*
794 * Deinitialize global symbol table, local one automatically
795 * was deinitialized.
796 */
797 free_sym_list(&global_sym_list);
798
799 return (0);
800 }
801
802 /*
803 * Add the given character to the current buffer, if there is not
804 * enough space in the buffer, then realloc() it.
805 */
806 static int
parser_add_char(unsigned char ch)807 parser_add_char(unsigned char ch)
808 {
809 char *buf;
810
811 if (cur_buf->off == cur_buf->size) {
812 if (cur_buf->size == 0)
813 cur_buf->size = PB_SIZE;
814 else {
815 cur_buf->size *= 2;
816 if (cur_buf->size > PB_SIZE_MAX) {
817 error_logmsgx("parser_add_char: "
818 "too big buffer size %llu",
819 (unsigned long long)cur_buf->size);
820 return (-1);
821 }
822 }
823 buf = mem_realloc(cur_buf->buf, cur_buf->size, m_parser);
824 if (buf == NULL) {
825 error_logmsgx("parser_add_char: mem_realloc failed");
826 return (-1);
827 }
828 cur_buf->buf = buf;
829 }
830 cur_buf->buf[cur_buf->off++] = ch;
831 return (0);
832 }
833
834 /*
835 * Get next curchar from the configuration stream.
836 * Return:
837 * AC_RET_CONT, if curchar was successfully read;
838 * AC_RET_ERR, if an error occurred;
839 * AC_RET_EOF, if EOF occurred.
840 */
841 static int
parser_get_curchar(void)842 parser_get_curchar(void)
843 {
844 struct parser_pb *pb;
845
846 pb = SLIST_FIRST(&pb_stack);
847 for (;;) {
848 if (pb->buf_off < pb->buf_nread) {
849 /* Get curchar from the buffer. */
850 curchar++;
851 if (*curchar == '\n' && pb->fp != NULL)
852 pb->lineno++;
853 pb->buf_off++;
854 return (AC_RET_CONT);
855 }
856 if (pb->fp != NULL) {
857 /* Pb content from the file. */
858 pb->buf_nread = fread(pb->buf, sizeof(char),
859 pb->buf_size, pb->fp);
860 if (pb->buf_nread == 0) {
861 if (feof(pb->fp) != 0)
862 return (AC_RET_EOF);
863 error_logmsgx("parser_get_curchar: fread(%s): "
864 "%s", pb->fname, strerror(errno));
865 return (AC_RET_ERR);
866 }
867 pb->buf_off = 0;
868 curchar = pb->buf - 1;
869 } else {
870 /* This pb ended, use previous one. */
871 pb = parser_pop_pb();
872 }
873 }
874 /* NOTREACHED */
875 }
876
877 /*
878 * Put curchar back to the buffer, this function must be called
879 * no more than one time after parser_get_curchar(), because it
880 * cannot restore pb_stack.
881 */
882 static int
parser_put_curchar(void)883 parser_put_curchar(void)
884 {
885 struct parser_pb *pb;
886
887 pb = SLIST_FIRST(&pb_stack);
888 if (pb->buf_off == 0) {
889 error_logmsgx("internal error: parser_put_curchar: "
890 "buf_off == 0");
891 return (-1);
892 }
893
894 /* Put curchar to the buffer. */
895 if (*curchar == '\n' && pb->fp != NULL)
896 pb->lineno--;
897 curchar--;
898 pb->buf_off--;
899
900 return (0);
901 }
902
903 #if DEBUG_PARSER
904 /*
905 * Output current character, current state and new state and action.
906 */
907 static void
print_curchar_and_stac(void)908 print_curchar_and_stac(void)
909 {
910 switch (*curchar) {
911 case '\n':
912 debuglog("\\n");
913 break;
914 case '\t':
915 debuglog("\\t");
916 break;
917 case ' ':
918 debuglog("sp");
919 break;
920 default:
921 if (isprint(*curchar))
922 debuglog(" %c", *curchar);
923 else
924 debuglog("%02x", *curchar);
925 }
926 debuglog(" : %s -> %s : %s (%s)\n", st_msg[curstate],
927 curstac->action != AC_ERR ?
928 st_msg[curstac->state] : er_msg[curstac->state],
929 ac_msg[curstac->action], ch_msg[whatis_char[*curchar]]);
930 }
931
932 /*
933 * Output current token and argument.
934 */
935 static void
print_token_and_args(void)936 print_token_and_args(void)
937 {
938 switch (parser_token_id) {
939 case TOKEN_ID_SECTION_BEGIN:
940 debuglog("SECTION_BEGIN: TOKEN <%s>", parser_token);
941 break;
942 case TOKEN_ID_SECTION_END:
943 debuglog("SECTION_END");
944 break;
945 case TOKEN_ID_PARAMETER:
946 debuglog("PARAMETER: TOKEN <%s>", parser_token);
947 break;
948 default:
949 debuglog("Unknown token ID %u", parser_token_id);
950 }
951 if (parser_args != NULL)
952 debuglog(", ARGS <%s>", parser_args);
953 debuglog("\n");
954 }
955 #endif /* DEBUG_PARSER */
956
957 static void
init_read_string(void)958 init_read_string(void)
959 {
960 cur_buf = &token_buf;
961 parser_args = NULL;
962 parser_nargs = 0;
963 token_buf.off = args_buf.off = 0;
964 was_space_flag = sym_def_flag = 0;
965 quotes_cnt = 0;
966 }
967
968 /*
969 * Read one logical string from the configuration stream.
970 * Macro variables definitions are not returned, instead they
971 * are saved in local symbols tables.
972 * Return:
973 * 1, if one string was successfully read;
974 * -1, if syntax or another error occurred;
975 * 0, if EOF occurred.
976 */
977 int
parser_read_string(void)978 parser_read_string(void)
979 {
980 init_read_string();
981
982 for (;;) {
983 switch (parser_get_curchar()) {
984 case AC_RET_CONT:
985 curstac = &stac_tbl[whatis_char[*curchar]][curstate];
986 #if DEBUG_PARSER
987 print_curchar_and_stac();
988 #endif
989 switch (ac_func_tbl[curstac->action]()) {
990 case AC_RET_CONT:
991 curstate = curstac->state;
992 break;
993 case AC_RET_LINE:
994 #if DEBUG_PARSER
995 print_token_and_args();
996 #endif
997 curstate = curstac->state;
998 return (1);
999 case AC_RET_CONTX:
1000 break;
1001 case AC_RET_ERR:
1002 return (-1);
1003 case AC_RET_EOF:
1004 goto end_of_file;
1005 }
1006 break;
1007 case AC_RET_EOF:
1008 goto end_of_file;
1009 case AC_RET_ERR:
1010 return (-1);
1011 }
1012 }
1013
1014 end_of_file:
1015 if (curstate != ST_INI) {
1016 if (in_string_flag)
1017 syntax_logmsgx("string is not closed with '\\\"' "
1018 "and end of file occurred");
1019 else
1020 syntax_logmsgx("unexpected end of file");
1021 return (-1);
1022 }
1023 return (0);
1024 }
1025
1026 /*
1027 * Skip current character, continue.
1028 */
1029 static int
ac_skp(void)1030 ac_skp(void)
1031 {
1032 return (AC_RET_CONT);
1033 }
1034
1035 /*
1036 * Add curchar to the arguments buffer.
1037 */
1038 static int
ac_aar(void)1039 ac_aar(void)
1040 {
1041 if (parser_add_char(*curchar) < 0)
1042 return (AC_RET_ERR);
1043
1044 if (was_space_flag) {
1045 was_space_flag = 0;
1046 parser_nargs++;
1047 }
1048
1049 return (AC_RET_CONT);
1050 }
1051
1052 /*
1053 * Add curchar to the token buffer.
1054 */
1055 static int
ac_atk(void)1056 ac_atk(void)
1057 {
1058 if (parser_add_char(*curchar) < 0)
1059 return (AC_RET_ERR);
1060
1061 return (AC_RET_CONT);
1062 }
1063
1064 /*
1065 * Add curchar '\"' to the arguments buffer, increase number
1066 * of double quotes and change in_string_flag.
1067 */
1068 static int
ac_aqu(void)1069 ac_aqu(void)
1070 {
1071 int rv;
1072
1073 rv = ac_aar();
1074 if (rv != AC_RET_CONT)
1075 return (rv);
1076
1077 ++quotes_cnt;
1078 in_string_flag = !in_string_flag;
1079
1080 return (AC_RET_CONT);
1081 }
1082
1083 /*
1084 * Definition of the token is complete. Switch current conf_buf
1085 * to the arguments buffer.
1086 */
1087 static int
ac_tok(void)1088 ac_tok(void)
1089 {
1090 if (parser_add_char('\0') < 0)
1091 return (AC_RET_ERR);
1092
1093 cur_buf = &args_buf;
1094
1095 return (AC_RET_CONT);
1096 }
1097
1098 /*
1099 * If configuration buffers are not complete (there is not '\0'
1100 * character at the end for example), then this function must be called.
1101 * Set parser_token and parser_args pointers.
1102 */
1103 static int
complete_conf_bufs(void)1104 complete_conf_bufs(void)
1105 {
1106 if (cur_buf == &token_buf) {
1107 if (parser_add_char('\0') < 0)
1108 return (-1);
1109 } else if (args_buf.off != 0) {
1110 char *ptr;
1111
1112 ptr = args_buf.buf + args_buf.off - 1;
1113 switch (*ptr) {
1114 case ' ':
1115 case '\t':
1116 *ptr = '\0';
1117 break;
1118 default:
1119 if (parser_add_char('\0') < 0)
1120 return (-1);
1121 }
1122 parser_args = args_buf.buf;
1123 parser_args_len = args_buf.off - 1;
1124 parser_nargs++;
1125 }
1126 parser_token = token_buf.buf;
1127 return (0);
1128 }
1129
1130 /*
1131 * Definition of the parameter or macro variable is complete.
1132 */
1133 static int
ac_par(void)1134 ac_par(void)
1135 {
1136 if (complete_conf_bufs() < 0)
1137 return (AC_RET_ERR);
1138
1139 if (sym_def_flag) {
1140 /* Definition of a macro variable. */
1141 char *val;
1142
1143 if (!parser_arg_is_str()) {
1144 syntax_logmsgx("macro variable's value should be a "
1145 "string");
1146 return (AC_RET_ERR);
1147 }
1148 val = parser_strdup(args_buf.buf, m_parser);
1149 if (val == NULL) {
1150 error_logmsgx("cannot allocate memory for value of "
1151 "${%s} macro variable", sym_buf.buf);
1152 return (AC_RET_ERR);
1153 }
1154 if (section_cnt == 0) {
1155 if (parser_global_sym_add(sym_buf.buf, val, 1) < 0) {
1156 error_logmsgx("cannot register "
1157 "a macro variable");
1158 return (-1);
1159 }
1160 } else {
1161 if (parser_local_sym_add(sym_buf.buf, val, 1) < 0) {
1162 error_logmsgx("cannot register "
1163 "a macro variable");
1164 return (-1);
1165 }
1166 }
1167 mem_free(val, m_parser);
1168 init_read_string();
1169 curstate = ST_INI;
1170 return (AC_RET_CONTX);
1171 }
1172
1173 parser_token_id = TOKEN_ID_PARAMETER;
1174 return (AC_RET_LINE);
1175 }
1176
1177 /*
1178 * Begin of the section.
1179 */
1180 static int
ac_bos(void)1181 ac_bos(void)
1182 {
1183 if (sym_def_flag) {
1184 /* Looks like incorrect definition of a macro variable. */
1185 syntax_logmsgx("macro variable's value should be a "
1186 "string or wrong usage or macro variable definition");
1187 return (AC_RET_ERR);
1188 }
1189
1190 if (complete_conf_bufs() < 0)
1191 return (AC_RET_ERR);
1192
1193 ++section_cnt;
1194
1195 parser_token_id = TOKEN_ID_SECTION_BEGIN;
1196 return (AC_RET_LINE);
1197 }
1198
1199 /*
1200 * End of the section.
1201 */
1202 static int
ac_eos(void)1203 ac_eos(void)
1204 {
1205 if (section_cnt == 0) {
1206 syntax_logmsgx("unmatched closed curly brace");
1207 return (AC_RET_ERR);
1208 }
1209
1210 if (--section_cnt == 0)
1211 free_sym_list(&local_sym_list);
1212
1213 parser_token_id = TOKEN_ID_SECTION_END;
1214 return (AC_RET_LINE);
1215 }
1216
1217 /*
1218 * '#' appeared, read current line up to the '\n' or EOF.
1219 */
1220 static int
ac_pnd(void)1221 ac_pnd(void)
1222 {
1223 int rv;
1224
1225 for (;;) {
1226 rv = parser_get_curchar();
1227 if (rv != AC_RET_CONT)
1228 return (rv);
1229 if (*curchar == '\n')
1230 break;
1231 }
1232
1233 return (AC_RET_CONT);
1234 }
1235
1236 /*
1237 * '\\' appeared in a string, check if the next character is either
1238 * '\"', '\\', 't', 'n' or '\n'. If the next character is '\n', then
1239 * ignore '\\' and '\n'; if next character is 't' or 'n', then convert
1240 * this sequence to real character, else add "\\x" character sequence
1241 * to a string.
1242 */
1243 static int
ac_fmt(void)1244 ac_fmt(void)
1245 {
1246 int rv;
1247
1248 rv = parser_get_curchar();
1249 if (rv != AC_RET_CONT) {
1250 if (rv == AC_RET_EOF)
1251 syntax_error(ER_FMT);
1252 return (rv);
1253 }
1254 switch (*curchar) {
1255 case '\"':
1256 case '\\':
1257 if (parser_add_char('\\') < 0 ||
1258 parser_add_char(*curchar) < 0)
1259 return (AC_RET_ERR);
1260 break;
1261 case 'n':
1262 if (parser_add_char('\n') < 0)
1263 return (AC_RET_ERR);
1264 break;
1265 case 't':
1266 if (parser_add_char('\t') < 0)
1267 return (AC_RET_ERR);
1268 break;
1269 case '\n':
1270 break;
1271 default:
1272 syntax_error(ER_FMT);
1273 return (AC_RET_ERR);
1274 }
1275
1276 return (AC_RET_CONT);
1277 }
1278
1279 /*
1280 * Space in the argument.
1281 */
1282 static int
ac_asp(void)1283 ac_asp(void)
1284 {
1285 if (!was_space_flag && args_buf.off != 0) {
1286 if (parser_add_char(' ') < 0)
1287 return (AC_RET_ERR);
1288 was_space_flag = 1;
1289 }
1290
1291 return (AC_RET_CONT);
1292 }
1293
1294 /*
1295 * '/' appeared. To not make stac_tbl too complex here, there are
1296 * some tricks in checking if '/' can appear in current state.
1297 */
1298 static int
ac_slh(void)1299 ac_slh(void)
1300 {
1301 int rv;
1302
1303 rv = parser_get_curchar();
1304 if (rv != AC_RET_CONT) {
1305 if (rv == AC_RET_EOF)
1306 syntax_error(ER_UXP);
1307 return (rv);
1308 }
1309
1310 if (*curchar != '*') {
1311 if (curstate == ST_INI || curstate == ST_TOK) {
1312 syntax_error(ER_UXP);
1313 return (AC_RET_ERR);
1314 }
1315 if (parser_add_char('/') < 0)
1316 return (AC_RET_ERR);
1317 if (parser_put_curchar() < 0)
1318 return (AC_RET_ERR);
1319 curstate = ST_ARG;
1320 return (AC_RET_CONTX);
1321 } else {
1322 int was_star;
1323
1324 was_star = 0;
1325 for (;;) {
1326 rv = parser_get_curchar();
1327 if (rv != AC_RET_CONT) {
1328 if (rv == AC_RET_EOF) {
1329 syntax_logmsgx("C-like comment is not "
1330 "closed");
1331 return (AC_RET_ERR);
1332 }
1333 return (rv);
1334 }
1335 if (*curchar == '/') {
1336 if (was_star)
1337 break;
1338 } else
1339 was_star = *curchar == '*';
1340 }
1341 return (AC_RET_CONT);
1342 }
1343 }
1344
1345 /*
1346 * '$' appeared. To not make stac_tbl too complex here, we will decide
1347 * if it is the begin of a macro variable definition or we need to
1348 * expand a macro variable.
1349 */
1350 static int
ac_dlr(void)1351 ac_dlr(void)
1352 {
1353 struct conf_buf *next_buf;
1354 int rv;
1355
1356 if (sym_def_flag) {
1357 if (parser_add_char('$') < 0)
1358 return (AC_RET_ERR);
1359 return (AC_RET_CONT);
1360 }
1361 rv = parser_get_curchar();
1362 if (rv != AC_RET_CONT) {
1363 if (rv == AC_RET_EOF)
1364 syntax_error(ER_UXP);
1365 return (rv);
1366 }
1367 if (*curchar != '{') {
1368 syntax_logmsgx("wrong usage of character '$': "
1369 "character '{' was missed");
1370 return (AC_RET_ERR);
1371 }
1372
1373 next_buf = cur_buf;
1374
1375 /* Get name of a macro variable. */
1376 cur_buf = &sym_buf;
1377 sym_buf.off = 0;
1378 for (;;) {
1379 rv = parser_get_curchar();
1380 if (rv != AC_RET_CONT)
1381 return (rv);
1382 switch (whatis_char[*curchar]) {
1383 case CH_UNDERSCORE:
1384 case CH_ALPHA:
1385 case CH_DIGIT:
1386 case CH_DOLLAR:
1387 if (parser_add_char(*curchar) < 0)
1388 return (AC_RET_ERR);
1389 break;
1390 case CH_CLOSE_BRACE:
1391 if (parser_add_char('\0') < 0)
1392 return (AC_RET_ERR);
1393 goto got_sym_name;
1394 default:
1395 syntax_logmsgx("this character is not allowed in "
1396 "macro variable name");
1397 return (AC_RET_ERR);
1398 }
1399 }
1400
1401 got_sym_name:
1402 switch (sym_buf.off) {
1403 case 1:
1404 syntax_logmsgx("empty macro variable's name is not allowed");
1405 return (AC_RET_ERR);
1406 case 2:
1407 if (sym_buf.buf[0] == '$') {
1408 /* ${$} */
1409 if (curstate == ST_INI) {
1410 syntax_logmsgx("macro variable ${$} is "
1411 "reserved, cannot redefine it");
1412 return (AC_RET_ERR);
1413 }
1414 /*
1415 * Expand ${$} right now to allow to insert
1416 * '$' character.
1417 */
1418 cur_buf = &args_buf;
1419 if (parser_add_char('$') < 0)
1420 return (AC_RET_ERR);
1421 break;
1422 }
1423 /* FALLTHROUGH */
1424 default:
1425 if (curstate == ST_INI) {
1426 /* Symbol definition. */
1427 next_buf = &args_buf;
1428 sym_def_flag = 1;
1429 } else {
1430 /* Expand symbol. */
1431 struct parser_pb *pb;
1432 struct psym *psym;
1433
1434 psym = find_sym(&local_sym_list, sym_buf.buf);
1435 if (psym == NULL)
1436 psym = find_sym(&global_sym_list, sym_buf.buf);
1437 if (psym == NULL) {
1438 syntax_logmsgx("unknown macro variable ${%s}",
1439 sym_buf.buf);
1440 return (AC_RET_ERR);
1441 }
1442 pb = parser_new_pb(psym->val_len);
1443 if (pb == NULL) {
1444 error_logmsgx("cannot expand macro variable "
1445 "${%s}", sym_buf.buf);
1446 return (AC_RET_ERR);
1447 }
1448 pb->buf = (unsigned char *)psym->val;
1449 pb->buf_nread = psym->val_len;
1450 pb->sname = psym->sym;
1451 if (parser_push_pb(pb) < 0)
1452 return (AC_RET_ERR);
1453 }
1454 }
1455
1456 cur_buf = next_buf;
1457
1458 return (AC_RET_CONT);
1459 }
1460
1461 /*
1462 * A syntax error occurred.
1463 */
1464 static int
ac_err(void)1465 ac_err(void)
1466 {
1467 syntax_error(curstac->state);
1468 return (AC_RET_ERR);
1469 }
1470
1471 /*
1472 * Return non-zero if the given buffer is a string.
1473 */
1474 int
parser_buf_is_str(const char * s)1475 parser_buf_is_str(const char *s)
1476 {
1477 if (*s != '\"')
1478 return (0);
1479
1480 for (++s; *s != '\0'; ++s)
1481 switch (*s) {
1482 case '\\':
1483 /* "\\x" */
1484 ++s;
1485 break;
1486 case '\"':
1487 /* Last '\"'. */
1488 return (*(s + 1) == '\0');
1489 }
1490
1491 return (0);
1492 }
1493
1494 /*
1495 * Return non-zero if whole parser_args is a string.
1496 * This is the optimized version, no linear search.
1497 */
1498 int
parser_arg_is_str(void)1499 parser_arg_is_str(void)
1500 {
1501 return (parser_nargs == 1 && quotes_cnt == 2 &&
1502 parser_args[0] == '\"' && parser_args[parser_args_len - 1] == '\"');
1503 }
1504
1505 /*
1506 * Allocate buffer and copy string from str converting '\\'-like
1507 * sequences to real characters (thanks to ac_fmt() there are only
1508 * two such sequences \" and \\).
1509 */
1510 char *
parser_strdup(char * str,void * mem_type)1511 parser_strdup(char *str, void *mem_type)
1512 {
1513 const char *ptr;
1514 char *res, *mod;
1515 size_t len;
1516 int has_special;
1517
1518 len = strlen(str) - 1;
1519 res = mem_malloc(len, mem_type);
1520 if (res == NULL) {
1521 error_logmsgx("parser_strdup: mem_malloc failed");
1522 return (NULL);
1523 }
1524 has_special = 0;
1525 for (mod = res, ptr = str + 1, len = 0; *ptr != '\"';
1526 ++len, ++mod, ++ptr)
1527 if (*ptr == '\\') {
1528 has_special = 1;
1529 /* \" or \\. */
1530 *mod = *++ptr;
1531 } else
1532 *mod = *ptr;
1533
1534 *mod = '\0';
1535 if (has_special) {
1536 mod = mem_realloc(res, ++len, mem_type);
1537 if (mod == NULL)
1538 error_logmsgx("parser_strdup: mem_realloc failed");
1539 res = mod;
1540 }
1541 return (res);
1542 }
1543
1544 /*
1545 * Allocate buffer and convert a string pointed by ptr to
1546 * this buffer expanding all escape sequences.
1547 */
1548 char *
parser_stringify(const char * ptr)1549 parser_stringify(const char *ptr)
1550 {
1551 char *mod;
1552
1553 /* Can free, because mem_malloc can return NULL. */
1554 mem_free(parser_str_buf, m_parser);
1555
1556 /* Allocated buffer will be bigger than really needed. */
1557 parser_str_buf = mem_malloc(2 + 1 + strlen(ptr) * 2, m_parser);
1558 if (parser_str_buf == NULL)
1559 return ("(parser_stringify: mem_malloc failed)");
1560
1561 *parser_str_buf = '\"';
1562
1563 for (mod = parser_str_buf + 1; *ptr != '\0'; ++mod, ++ptr)
1564 switch (*ptr) {
1565 case '\\':
1566 case '\"':
1567 case '\t':
1568 case '\n':
1569 *mod = '\\';
1570 ++mod;
1571 /* FALLTHROUGH */
1572 default:
1573 *mod = *ptr;
1574 }
1575
1576 *mod = '\"';
1577 *(mod + 1) = '\0';
1578
1579 return (parser_str_buf);
1580 }
1581