/src/ntp-dev/ntpd/ntp_scanner.c
Line | Count | Source (jump to first uncovered line) |
1 | | |
2 | | /* ntp_scanner.c |
3 | | * |
4 | | * The source code for a simple lexical analyzer. |
5 | | * |
6 | | * Written By: Sachin Kamboj |
7 | | * University of Delaware |
8 | | * Newark, DE 19711 |
9 | | * Copyright (c) 2006 |
10 | | */ |
11 | | |
12 | | #ifdef HAVE_CONFIG_H |
13 | | # include <config.h> |
14 | | #endif |
15 | | |
16 | | #include <stdio.h> |
17 | | #include <ctype.h> |
18 | | #include <stdlib.h> |
19 | | #include <errno.h> |
20 | | #include <string.h> |
21 | | |
22 | | #include "ntpd.h" |
23 | | #include "ntp_config.h" |
24 | | #include "ntpsim.h" |
25 | | #include "ntp_scanner.h" |
26 | | #include "ntp_parser.h" |
27 | | |
28 | | /* ntp_keyword.h declares finite state machine and token text */ |
29 | | #include "ntp_keyword.h" |
30 | | |
31 | | |
32 | | |
33 | | /* SCANNER GLOBAL VARIABLES |
34 | | * ------------------------ |
35 | | */ |
36 | | |
37 | | #define MAX_LEXEME (1024 + 1) /* The maximum size of a lexeme */ |
38 | | char yytext[MAX_LEXEME]; /* Buffer for storing the input text/lexeme */ |
39 | | u_int32 conf_file_sum; /* Simple sum of characters read */ |
40 | | |
41 | | static struct FILE_INFO * lex_stack = NULL; |
42 | | |
43 | | |
44 | | |
45 | | /* CONSTANTS |
46 | | * --------- |
47 | | */ |
48 | | |
49 | | |
50 | | /* SCANNER GLOBAL VARIABLES |
51 | | * ------------------------ |
52 | | */ |
53 | | const char special_chars[] = "{}(),;|="; |
54 | | |
55 | | |
56 | | /* FUNCTIONS |
57 | | * --------- |
58 | | */ |
59 | | |
60 | | static int is_keyword(char *lexeme, follby *pfollowedby); |
61 | | |
62 | | |
63 | | /* |
64 | | * keyword() - Return the keyword associated with token T_ identifier. |
65 | | * See also token_name() for the string-ized T_ identifier. |
66 | | * Example: keyword(T_Server) returns "server" |
67 | | * token_name(T_Server) returns "T_Server" |
68 | | */ |
69 | | const char * |
70 | | keyword( |
71 | | int token |
72 | | ) |
73 | 0 | { |
74 | 0 | size_t i; |
75 | 0 | const char *text; |
76 | |
|
77 | 0 | i = token - LOWEST_KEYWORD_ID; |
78 | |
|
79 | 0 | if (i < COUNTOF(keyword_text)) |
80 | 0 | text = keyword_text[i]; |
81 | 0 | else |
82 | 0 | text = NULL; |
83 | |
|
84 | 0 | return (text != NULL) |
85 | 0 | ? text |
86 | 0 | : "(keyword not found)"; |
87 | 0 | } |
88 | | |
89 | | |
90 | | /* FILE & STRING BUFFER INTERFACE |
91 | | * ------------------------------ |
92 | | * |
93 | | * This set out as a couple of wrapper functions around the standard C |
94 | | * fgetc and ungetc functions in order to include positional |
95 | | * bookkeeping. Alas, this is no longer a good solution with nested |
96 | | * input files and the possibility to send configuration commands via |
97 | | * 'ntpdc' and 'ntpq'. |
98 | | * |
99 | | * Now there are a few functions to maintain a stack of nested input |
100 | | * sources (though nesting is only allowd for disk files) and from the |
101 | | * scanner / parser point of view there's no difference between both |
102 | | * types of sources. |
103 | | * |
104 | | * The 'fgetc()' / 'ungetc()' replacements now operate on a FILE_INFO |
105 | | * structure. Instead of trying different 'ungetc()' strategies for file |
106 | | * and buffer based parsing, we keep the backup char in our own |
107 | | * FILE_INFO structure. This is sufficient, as the parser does *not* |
108 | | * jump around via 'seek' or the like, and there's no need to |
109 | | * check/clear the backup store in other places than 'lex_getch()'. |
110 | | */ |
111 | | |
112 | | /* |
113 | | * Allocate an info structure and attach it to a file. |
114 | | * |
115 | | * Note: When 'mode' is NULL, then the INFO block will be set up to |
116 | | * contain a NULL file pointer, as suited for remote config command |
117 | | * parsing. Otherwise having a NULL file pointer is considered an error, |
118 | | * and a NULL info block pointer is returned to indicate failure! |
119 | | * |
120 | | * Note: We use a variable-sized structure to hold a copy of the file |
121 | | * name (or, more proper, the input source description). This is more |
122 | | * secure than keeping a reference to some other storage that might go |
123 | | * out of scope. |
124 | | */ |
125 | | static struct FILE_INFO * |
126 | | lex_open( |
127 | | const char *path, |
128 | | const char *mode |
129 | | ) |
130 | 0 | { |
131 | 0 | struct FILE_INFO *stream; |
132 | 0 | size_t nnambuf; |
133 | |
|
134 | 0 | nnambuf = strlen(path); |
135 | 0 | stream = emalloc_zero(sizeof(*stream) + nnambuf); |
136 | 0 | stream->curpos.nline = 1; |
137 | 0 | stream->backch = EOF; |
138 | | /* copy name with memcpy -- trailing NUL already there! */ |
139 | 0 | memcpy(stream->fname, path, nnambuf); |
140 | |
|
141 | 0 | if (NULL != mode) { |
142 | 0 | stream->fpi = fopen(path, mode); |
143 | 0 | if (NULL == stream->fpi) { |
144 | 0 | free(stream); |
145 | 0 | stream = NULL; |
146 | 0 | } |
147 | 0 | } |
148 | 0 | return stream; |
149 | 0 | } |
150 | | |
151 | | /* get next character from buffer or file. This will return any putback |
152 | | * character first; it will also make sure the last line is at least |
153 | | * virtually terminated with a '\n'. |
154 | | */ |
155 | | static int |
156 | | lex_getch( |
157 | | struct FILE_INFO *stream |
158 | | ) |
159 | 0 | { |
160 | 0 | int ch; |
161 | |
|
162 | 0 | if (NULL == stream || stream->force_eof) |
163 | 0 | return EOF; |
164 | | |
165 | 0 | if (EOF != stream->backch) { |
166 | 0 | ch = stream->backch; |
167 | 0 | stream->backch = EOF; |
168 | 0 | if (stream->fpi) |
169 | 0 | conf_file_sum += ch; |
170 | 0 | stream->curpos.ncol++; |
171 | 0 | } else if (stream->fpi) { |
172 | | /* fetch next 7-bit ASCII char (or EOF) from file */ |
173 | 0 | while ((ch = fgetc(stream->fpi)) != EOF && ch > SCHAR_MAX) |
174 | 0 | stream->curpos.ncol++; |
175 | 0 | if (EOF != ch) { |
176 | 0 | conf_file_sum += ch; |
177 | 0 | stream->curpos.ncol++; |
178 | 0 | } |
179 | 0 | } else { |
180 | | /* fetch next 7-bit ASCII char from buffer */ |
181 | 0 | const char * scan; |
182 | 0 | scan = &remote_config.buffer[remote_config.pos]; |
183 | 0 | while ((ch = (u_char)*scan) > SCHAR_MAX) { |
184 | 0 | scan++; |
185 | 0 | stream->curpos.ncol++; |
186 | 0 | } |
187 | 0 | if ('\0' != ch) { |
188 | 0 | scan++; |
189 | 0 | stream->curpos.ncol++; |
190 | 0 | } else { |
191 | 0 | ch = EOF; |
192 | 0 | } |
193 | 0 | remote_config.pos = (int)(scan - remote_config.buffer); |
194 | 0 | } |
195 | | |
196 | | /* If the last line ends without '\n', generate one. This |
197 | | * happens most likely on Windows, where editors often have a |
198 | | * sloppy concept of a line. |
199 | | */ |
200 | 0 | if (EOF == ch && stream->curpos.ncol != 0) |
201 | 0 | ch = '\n'; |
202 | | |
203 | | /* update scan position tallies */ |
204 | 0 | if (ch == '\n') { |
205 | 0 | stream->bakpos = stream->curpos; |
206 | 0 | stream->curpos.nline++; |
207 | 0 | stream->curpos.ncol = 0; |
208 | 0 | } |
209 | |
|
210 | 0 | return ch; |
211 | 0 | } |
212 | | |
213 | | /* Note: lex_ungetch will fail to track more than one line of push |
214 | | * back. But since it guarantees only one char of back storage anyway, |
215 | | * this should not be a problem. |
216 | | */ |
217 | | static int |
218 | | lex_ungetch( |
219 | | int ch, |
220 | | struct FILE_INFO *stream |
221 | | ) |
222 | 0 | { |
223 | | /* check preconditions */ |
224 | 0 | if (NULL == stream || stream->force_eof) |
225 | 0 | return EOF; |
226 | 0 | if (EOF != stream->backch || EOF == ch) |
227 | 0 | return EOF; |
228 | | |
229 | | /* keep for later reference and update checksum */ |
230 | 0 | stream->backch = (u_char)ch; |
231 | 0 | if (stream->fpi) |
232 | 0 | conf_file_sum -= stream->backch; |
233 | | |
234 | | /* update position */ |
235 | 0 | if (stream->backch == '\n') { |
236 | 0 | stream->curpos = stream->bakpos; |
237 | 0 | stream->bakpos.ncol = -1; |
238 | 0 | } |
239 | 0 | stream->curpos.ncol--; |
240 | 0 | return stream->backch; |
241 | 0 | } |
242 | | |
243 | | /* dispose of an input structure. If the file pointer is not NULL, close |
244 | | * the file. This function does not check the result of 'fclose()'. |
245 | | */ |
246 | | static void |
247 | | lex_close( |
248 | | struct FILE_INFO *stream |
249 | | ) |
250 | 0 | { |
251 | 0 | if (NULL != stream) { |
252 | 0 | if (NULL != stream->fpi) |
253 | 0 | fclose(stream->fpi); |
254 | 0 | free(stream); |
255 | 0 | } |
256 | 0 | } |
257 | | |
258 | | /* INPUT STACK |
259 | | * ----------- |
260 | | * |
261 | | * Nested input sources are a bit tricky at first glance. We deal with |
262 | | * this problem using a stack of input sources, that is, a forward |
263 | | * linked list of FILE_INFO structs. |
264 | | * |
265 | | * This stack is never empty during parsing; while an encounter with EOF |
266 | | * can and will remove nested input sources, removing the last element |
267 | | * in the stack will not work during parsing, and the EOF condition of |
268 | | * the outermost input file remains until the parser folds up. |
269 | | */ |
270 | | |
271 | | static struct FILE_INFO * |
272 | | _drop_stack_do( |
273 | | struct FILE_INFO * head |
274 | | ) |
275 | 0 | { |
276 | 0 | struct FILE_INFO * tail; |
277 | 0 | while (NULL != head) { |
278 | 0 | tail = head->st_next; |
279 | 0 | lex_close(head); |
280 | 0 | head = tail; |
281 | 0 | } |
282 | 0 | return head; |
283 | 0 | } |
284 | | |
285 | | |
286 | | |
287 | | /* Create a singleton input source on an empty lexer stack. This will |
288 | | * fail if there is already an input source, or if the underlying disk |
289 | | * file cannot be opened. |
290 | | * |
291 | | * Returns TRUE if a new input object was successfully created. |
292 | | */ |
293 | | int/*BOOL*/ |
294 | | lex_init_stack( |
295 | | const char * path, |
296 | | const char * mode |
297 | | ) |
298 | 0 | { |
299 | 0 | if (NULL != lex_stack || NULL == path) |
300 | 0 | return FALSE; |
301 | | |
302 | 0 | lex_stack = lex_open(path, mode); |
303 | 0 | return (NULL != lex_stack); |
304 | 0 | } |
305 | | |
306 | | /* This removes *all* input sources from the stack, leaving the head |
307 | | * pointer as NULL. Any attempt to parse in that state is likely to bomb |
308 | | * with segmentation faults or the like. |
309 | | * |
310 | | * In other words: Use this to clean up after parsing, and do not parse |
311 | | * anything until the next 'lex_init_stack()' succeeded. |
312 | | */ |
313 | | void |
314 | | lex_drop_stack() |
315 | 0 | { |
316 | 0 | lex_stack = _drop_stack_do(lex_stack); |
317 | 0 | } |
318 | | |
319 | | /* Flush the lexer input stack: This will nip all input objects on the |
320 | | * stack (but keeps the current top-of-stack) and marks the top-of-stack |
321 | | * as inactive. Any further calls to lex_getch yield only EOF, and it's |
322 | | * no longer possible to push something back. |
323 | | * |
324 | | * Returns TRUE if there is a head element (top-of-stack) that was not |
325 | | * in the force-eof mode before this call. |
326 | | */ |
327 | | int/*BOOL*/ |
328 | | lex_flush_stack() |
329 | 0 | { |
330 | 0 | int retv = FALSE; |
331 | |
|
332 | 0 | if (NULL != lex_stack) { |
333 | 0 | retv = !lex_stack->force_eof; |
334 | 0 | lex_stack->force_eof = TRUE; |
335 | 0 | lex_stack->st_next = _drop_stack_do( |
336 | 0 | lex_stack->st_next); |
337 | 0 | } |
338 | 0 | return retv; |
339 | 0 | } |
340 | | |
341 | | /* Push another file on the parsing stack. If the mode is NULL, create a |
342 | | * FILE_INFO suitable for in-memory parsing; otherwise, create a |
343 | | * FILE_INFO that is bound to a local/disc file. Note that 'path' must |
344 | | * not be NULL, or the function will fail. |
345 | | * |
346 | | * Returns TRUE if a new info record was pushed onto the stack. |
347 | | */ |
348 | | int/*BOOL*/ lex_push_file( |
349 | | const char * path, |
350 | | const char * mode |
351 | | ) |
352 | 0 | { |
353 | 0 | struct FILE_INFO * next = NULL; |
354 | |
|
355 | 0 | if (NULL != path) { |
356 | 0 | next = lex_open(path, mode); |
357 | 0 | if (NULL != next) { |
358 | 0 | next->st_next = lex_stack; |
359 | 0 | lex_stack = next; |
360 | 0 | } |
361 | 0 | } |
362 | 0 | return (NULL != next); |
363 | 0 | } |
364 | | |
365 | | /* Pop, close & free the top of the include stack, unless the stack |
366 | | * contains only a singleton input object. In that case the function |
367 | | * fails, because the parser does not expect the input stack to be |
368 | | * empty. |
369 | | * |
370 | | * Returns TRUE if an object was successfuly popped from the stack. |
371 | | */ |
372 | | int/*BOOL*/ |
373 | | lex_pop_file(void) |
374 | 0 | { |
375 | 0 | struct FILE_INFO * head = lex_stack; |
376 | 0 | struct FILE_INFO * tail = NULL; |
377 | | |
378 | 0 | if (NULL != head) { |
379 | 0 | tail = head->st_next; |
380 | 0 | if (NULL != tail) { |
381 | 0 | lex_stack = tail; |
382 | 0 | lex_close(head); |
383 | 0 | } |
384 | 0 | } |
385 | 0 | return (NULL != tail); |
386 | 0 | } |
387 | | |
388 | | /* Get include nesting level. This currently loops over the stack and |
389 | | * counts elements; but since this is of concern only with an include |
390 | | * statement and the nesting depth has a small limit, there's no |
391 | | * bottleneck expected here. |
392 | | * |
393 | | * Returns the nesting level of includes, that is, the current depth of |
394 | | * the lexer input stack. |
395 | | * |
396 | | * Note: |
397 | | */ |
398 | | size_t |
399 | | lex_level(void) |
400 | 0 | { |
401 | 0 | size_t cnt = 0; |
402 | 0 | struct FILE_INFO *ipf = lex_stack; |
403 | |
|
404 | 0 | while (NULL != ipf) { |
405 | 0 | cnt++; |
406 | 0 | ipf = ipf->st_next; |
407 | 0 | } |
408 | 0 | return cnt; |
409 | 0 | } |
410 | | |
411 | | /* check if the current input is from a file */ |
412 | | int/*BOOL*/ |
413 | | lex_from_file(void) |
414 | 0 | { |
415 | 0 | return (NULL != lex_stack) && (NULL != lex_stack->fpi); |
416 | 0 | } |
417 | | |
418 | | struct FILE_INFO * |
419 | | lex_current() |
420 | 0 | { |
421 | | /* this became so simple, it could be a macro. But then, |
422 | | * lex_stack needed to be global... |
423 | | */ |
424 | 0 | return lex_stack; |
425 | 0 | } |
426 | | |
427 | | |
428 | | /* STATE MACHINES |
429 | | * -------------- |
430 | | */ |
431 | | |
432 | | /* Keywords */ |
433 | | static int |
434 | | is_keyword( |
435 | | char *lexeme, |
436 | | follby *pfollowedby |
437 | | ) |
438 | 0 | { |
439 | 0 | follby fb; |
440 | 0 | int curr_s; /* current state index */ |
441 | 0 | int token; |
442 | 0 | int i; |
443 | |
|
444 | 0 | curr_s = SCANNER_INIT_S; |
445 | 0 | token = 0; |
446 | |
|
447 | 0 | for (i = 0; lexeme[i]; i++) { |
448 | 0 | while (curr_s && (lexeme[i] != SS_CH(sst[curr_s]))) |
449 | 0 | curr_s = SS_OTHER_N(sst[curr_s]); |
450 | |
|
451 | 0 | if (curr_s && (lexeme[i] == SS_CH(sst[curr_s]))) { |
452 | 0 | if ('\0' == lexeme[i + 1] |
453 | 0 | && FOLLBY_NON_ACCEPTING |
454 | 0 | != SS_FB(sst[curr_s])) { |
455 | 0 | fb = SS_FB(sst[curr_s]); |
456 | 0 | *pfollowedby = fb; |
457 | 0 | token = curr_s; |
458 | 0 | break; |
459 | 0 | } |
460 | 0 | curr_s = SS_MATCH_N(sst[curr_s]); |
461 | 0 | } else |
462 | 0 | break; |
463 | 0 | } |
464 | |
|
465 | 0 | return token; |
466 | 0 | } |
467 | | |
468 | | |
469 | | /* Integer */ |
470 | | static int |
471 | | is_integer( |
472 | | char *lexeme |
473 | | ) |
474 | 0 | { |
475 | 0 | int i; |
476 | 0 | int is_neg; |
477 | 0 | u_int u_val; |
478 | | |
479 | 0 | i = 0; |
480 | | |
481 | | /* Allow a leading minus sign */ |
482 | 0 | if (lexeme[i] == '-') { |
483 | 0 | i++; |
484 | 0 | is_neg = TRUE; |
485 | 0 | } else { |
486 | 0 | is_neg = FALSE; |
487 | 0 | } |
488 | | |
489 | | /* Check that all the remaining characters are digits */ |
490 | 0 | for (; lexeme[i] != '\0'; i++) { |
491 | 0 | if (!isdigit((u_char)lexeme[i])) |
492 | 0 | return FALSE; |
493 | 0 | } |
494 | | |
495 | 0 | if (is_neg) |
496 | 0 | return TRUE; |
497 | | |
498 | | /* Reject numbers that fit in unsigned but not in signed int */ |
499 | 0 | if (1 == sscanf(lexeme, "%u", &u_val)) |
500 | 0 | return (u_val <= INT_MAX); |
501 | 0 | else |
502 | 0 | return FALSE; |
503 | 0 | } |
504 | | |
505 | | |
506 | | /* U_int -- assumes is_integer() has returned FALSE */ |
507 | | static int |
508 | | is_u_int( |
509 | | char *lexeme |
510 | | ) |
511 | 0 | { |
512 | 0 | int i; |
513 | 0 | int is_hex; |
514 | | |
515 | 0 | i = 0; |
516 | 0 | if ('0' == lexeme[i] && 'x' == tolower((u_char)lexeme[i + 1])) { |
517 | 0 | i += 2; |
518 | 0 | is_hex = TRUE; |
519 | 0 | } else { |
520 | 0 | is_hex = FALSE; |
521 | 0 | } |
522 | | |
523 | | /* Check that all the remaining characters are digits */ |
524 | 0 | for (; lexeme[i] != '\0'; i++) { |
525 | 0 | if (is_hex && !isxdigit((u_char)lexeme[i])) |
526 | 0 | return FALSE; |
527 | 0 | if (!is_hex && !isdigit((u_char)lexeme[i])) |
528 | 0 | return FALSE; |
529 | 0 | } |
530 | | |
531 | 0 | return TRUE; |
532 | 0 | } |
533 | | |
534 | | |
535 | | /* Double */ |
536 | | static int |
537 | | is_double( |
538 | | char *lexeme |
539 | | ) |
540 | 0 | { |
541 | 0 | u_int num_digits = 0; /* Number of digits read */ |
542 | 0 | u_int i; |
543 | |
|
544 | 0 | i = 0; |
545 | | |
546 | | /* Check for an optional '+' or '-' */ |
547 | 0 | if ('+' == lexeme[i] || '-' == lexeme[i]) |
548 | 0 | i++; |
549 | | |
550 | | /* Read the integer part */ |
551 | 0 | for (; lexeme[i] && isdigit((u_char)lexeme[i]); i++) |
552 | 0 | num_digits++; |
553 | | |
554 | | /* Check for the optional decimal point */ |
555 | 0 | if ('.' == lexeme[i]) { |
556 | 0 | i++; |
557 | | /* Check for any digits after the decimal point */ |
558 | 0 | for (; lexeme[i] && isdigit((u_char)lexeme[i]); i++) |
559 | 0 | num_digits++; |
560 | 0 | } |
561 | | |
562 | | /* |
563 | | * The number of digits in both the decimal part and the |
564 | | * fraction part must not be zero at this point |
565 | | */ |
566 | 0 | if (!num_digits) |
567 | 0 | return 0; |
568 | | |
569 | | /* Check if we are done */ |
570 | 0 | if (!lexeme[i]) |
571 | 0 | return 1; |
572 | | |
573 | | /* There is still more input, read the exponent */ |
574 | 0 | if ('e' == tolower((u_char)lexeme[i])) |
575 | 0 | i++; |
576 | 0 | else |
577 | 0 | return 0; |
578 | | |
579 | | /* Read an optional Sign */ |
580 | 0 | if ('+' == lexeme[i] || '-' == lexeme[i]) |
581 | 0 | i++; |
582 | | |
583 | | /* Now read the exponent part */ |
584 | 0 | while (lexeme[i] && isdigit((u_char)lexeme[i])) |
585 | 0 | i++; |
586 | | |
587 | | /* Check if we are done */ |
588 | 0 | if (!lexeme[i]) |
589 | 0 | return 1; |
590 | 0 | else |
591 | 0 | return 0; |
592 | 0 | } |
593 | | |
594 | | |
595 | | /* is_special() - Test whether a character is a token */ |
596 | | static inline int |
597 | | is_special( |
598 | | int ch |
599 | | ) |
600 | 0 | { |
601 | 0 | return strchr(special_chars, ch) != NULL; |
602 | 0 | } |
603 | | |
604 | | |
605 | | static int |
606 | | is_EOC( |
607 | | int ch |
608 | | ) |
609 | 0 | { |
610 | 0 | if ((old_config_style && (ch == '\n')) || |
611 | 0 | (!old_config_style && (ch == ';'))) |
612 | 0 | return 1; |
613 | 0 | return 0; |
614 | 0 | } |
615 | | |
616 | | |
617 | | char * |
618 | | quote_if_needed(char *str) |
619 | 0 | { |
620 | 0 | char *ret; |
621 | 0 | size_t len; |
622 | 0 | size_t octets; |
623 | |
|
624 | 0 | len = strlen(str); |
625 | 0 | octets = len + 2 + 1; |
626 | 0 | ret = emalloc(octets); |
627 | 0 | if ('"' != str[0] |
628 | 0 | && (strcspn(str, special_chars) < len |
629 | 0 | || strchr(str, ' ') != NULL)) { |
630 | 0 | snprintf(ret, octets, "\"%s\"", str); |
631 | 0 | } else |
632 | 0 | strlcpy(ret, str, octets); |
633 | |
|
634 | 0 | return ret; |
635 | 0 | } |
636 | | |
637 | | |
638 | | static int |
639 | | create_string_token( |
640 | | char *lexeme |
641 | | ) |
642 | 0 | { |
643 | 0 | char *pch; |
644 | | |
645 | | /* |
646 | | * ignore end of line whitespace |
647 | | */ |
648 | 0 | pch = lexeme; |
649 | 0 | while (*pch && isspace((u_char)*pch)) |
650 | 0 | pch++; |
651 | |
|
652 | 0 | if (!*pch) { |
653 | 0 | yylval.Integer = T_EOC; |
654 | 0 | return yylval.Integer; |
655 | 0 | } |
656 | | |
657 | 0 | yylval.String = estrdup(lexeme); |
658 | 0 | return T_String; |
659 | 0 | } |
660 | | |
661 | | |
662 | | /* |
663 | | * yylex() - function that does the actual scanning. |
664 | | * Bison expects this function to be called yylex and for it to take no |
665 | | * input and return an int. |
666 | | * Conceptually yylex "returns" yylval as well as the actual return |
667 | | * value representing the token or type. |
668 | | */ |
669 | | int |
670 | | yylex(void) |
671 | 0 | { |
672 | 0 | static follby followedby = FOLLBY_TOKEN; |
673 | 0 | size_t i; |
674 | 0 | int instring; |
675 | 0 | int yylval_was_set; |
676 | 0 | int converted; |
677 | 0 | int token; /* The return value */ |
678 | 0 | int ch; |
679 | |
|
680 | 0 | instring = FALSE; |
681 | 0 | yylval_was_set = FALSE; |
682 | |
|
683 | 0 | do { |
684 | | /* Ignore whitespace at the beginning */ |
685 | 0 | while (EOF != (ch = lex_getch(lex_stack)) && |
686 | 0 | isspace(ch) && |
687 | 0 | !is_EOC(ch)) |
688 | | |
689 | 0 | ; /* Null Statement */ |
690 | |
|
691 | 0 | if (EOF == ch) { |
692 | |
|
693 | 0 | if ( ! lex_pop_file()) |
694 | 0 | return 0; |
695 | 0 | token = T_EOC; |
696 | 0 | goto normal_return; |
697 | |
|
698 | 0 | } else if (is_EOC(ch)) { |
699 | | |
700 | | /* end FOLLBY_STRINGS_TO_EOC effect */ |
701 | 0 | followedby = FOLLBY_TOKEN; |
702 | 0 | token = T_EOC; |
703 | 0 | goto normal_return; |
704 | |
|
705 | 0 | } else if (is_special(ch) && FOLLBY_TOKEN == followedby) { |
706 | | /* special chars are their own token values */ |
707 | 0 | token = ch; |
708 | | /* |
709 | | * '=' outside simulator configuration implies |
710 | | * a single string following as in: |
711 | | * setvar Owner = "The Boss" default |
712 | | */ |
713 | 0 | if ('=' == ch && old_config_style) |
714 | 0 | followedby = FOLLBY_STRING; |
715 | 0 | yytext[0] = (char)ch; |
716 | 0 | yytext[1] = '\0'; |
717 | 0 | goto normal_return; |
718 | 0 | } else |
719 | 0 | lex_ungetch(ch, lex_stack); |
720 | | |
721 | | /* save the position of start of the token */ |
722 | 0 | lex_stack->tokpos = lex_stack->curpos; |
723 | | |
724 | | /* Read in the lexeme */ |
725 | 0 | i = 0; |
726 | 0 | while (EOF != (ch = lex_getch(lex_stack))) { |
727 | |
|
728 | 0 | yytext[i] = (char)ch; |
729 | | |
730 | | /* Break on whitespace or a special character */ |
731 | 0 | if (isspace(ch) || is_EOC(ch) |
732 | 0 | || '"' == ch |
733 | 0 | || (FOLLBY_TOKEN == followedby |
734 | 0 | && is_special(ch))) |
735 | 0 | break; |
736 | | |
737 | | /* Read the rest of the line on reading a start |
738 | | of comment character */ |
739 | 0 | if ('#' == ch) { |
740 | 0 | while (EOF != (ch = lex_getch(lex_stack)) |
741 | 0 | && '\n' != ch) |
742 | 0 | ; /* Null Statement */ |
743 | 0 | break; |
744 | 0 | } |
745 | | |
746 | 0 | i++; |
747 | 0 | if (i >= COUNTOF(yytext)) |
748 | 0 | goto lex_too_long; |
749 | 0 | } |
750 | | /* Pick up all of the string inside between " marks, to |
751 | | * end of line. If we make it to EOL without a |
752 | | * terminating " assume it for them. |
753 | | * |
754 | | * XXX - HMS: I'm not sure we want to assume the closing " |
755 | | */ |
756 | 0 | if ('"' == ch) { |
757 | 0 | instring = TRUE; |
758 | 0 | while (EOF != (ch = lex_getch(lex_stack)) && |
759 | 0 | ch != '"' && ch != '\n') { |
760 | 0 | yytext[i++] = (char)ch; |
761 | 0 | if (i >= COUNTOF(yytext)) |
762 | 0 | goto lex_too_long; |
763 | 0 | } |
764 | | /* |
765 | | * yytext[i] will be pushed back as not part of |
766 | | * this lexeme, but any closing quote should |
767 | | * not be pushed back, so we read another char. |
768 | | */ |
769 | 0 | if ('"' == ch) |
770 | 0 | ch = lex_getch(lex_stack); |
771 | 0 | } |
772 | | /* Pushback the last character read that is not a part |
773 | | * of this lexeme. This fails silently if ch is EOF, |
774 | | * but then the EOF condition persists and is handled on |
775 | | * the next turn by the include stack mechanism. |
776 | | */ |
777 | 0 | lex_ungetch(ch, lex_stack); |
778 | |
|
779 | 0 | yytext[i] = '\0'; |
780 | 0 | } while (i == 0); |
781 | | |
782 | | /* Now return the desired token */ |
783 | | |
784 | | /* First make sure that the parser is *not* expecting a string |
785 | | * as the next token (based on the previous token that was |
786 | | * returned) and that we haven't read a string. |
787 | | */ |
788 | | |
789 | 0 | if (followedby == FOLLBY_TOKEN && !instring) { |
790 | 0 | token = is_keyword(yytext, &followedby); |
791 | 0 | if (token) { |
792 | | /* |
793 | | * T_Server is exceptional as it forces the |
794 | | * following token to be a string in the |
795 | | * non-simulator parts of the configuration, |
796 | | * but in the simulator configuration section, |
797 | | * "server" is followed by "=" which must be |
798 | | * recognized as a token not a string. |
799 | | */ |
800 | 0 | if (T_Server == token && !old_config_style) |
801 | 0 | followedby = FOLLBY_TOKEN; |
802 | 0 | goto normal_return; |
803 | 0 | } else if (is_integer(yytext)) { |
804 | 0 | yylval_was_set = TRUE; |
805 | 0 | errno = 0; |
806 | 0 | if ((yylval.Integer = strtol(yytext, NULL, 10)) == 0 |
807 | 0 | && ((errno == EINVAL) || (errno == ERANGE))) { |
808 | 0 | msyslog(LOG_ERR, |
809 | 0 | "Integer cannot be represented: %s", |
810 | 0 | yytext); |
811 | 0 | if (lex_from_file()) { |
812 | 0 | exit(1); |
813 | 0 | } else { |
814 | | /* force end of parsing */ |
815 | 0 | yylval.Integer = 0; |
816 | 0 | return 0; |
817 | 0 | } |
818 | 0 | } |
819 | 0 | token = T_Integer; |
820 | 0 | goto normal_return; |
821 | 0 | } else if (is_u_int(yytext)) { |
822 | 0 | yylval_was_set = TRUE; |
823 | 0 | if ('0' == yytext[0] && |
824 | 0 | 'x' == tolower((unsigned long)yytext[1])) |
825 | 0 | converted = sscanf(&yytext[2], "%x", |
826 | 0 | &yylval.U_int); |
827 | 0 | else |
828 | 0 | converted = sscanf(yytext, "%u", |
829 | 0 | &yylval.U_int); |
830 | 0 | if (1 != converted) { |
831 | 0 | msyslog(LOG_ERR, |
832 | 0 | "U_int cannot be represented: %s", |
833 | 0 | yytext); |
834 | 0 | if (lex_from_file()) { |
835 | 0 | exit(1); |
836 | 0 | } else { |
837 | | /* force end of parsing */ |
838 | 0 | yylval.Integer = 0; |
839 | 0 | return 0; |
840 | 0 | } |
841 | 0 | } |
842 | 0 | token = T_U_int; |
843 | 0 | goto normal_return; |
844 | 0 | } else if (is_double(yytext)) { |
845 | 0 | yylval_was_set = TRUE; |
846 | 0 | errno = 0; |
847 | 0 | if ((yylval.Double = atof(yytext)) == 0 && errno == ERANGE) { |
848 | 0 | msyslog(LOG_ERR, |
849 | 0 | "Double too large to represent: %s", |
850 | 0 | yytext); |
851 | 0 | exit(1); |
852 | 0 | } else { |
853 | 0 | token = T_Double; |
854 | 0 | goto normal_return; |
855 | 0 | } |
856 | 0 | } else { |
857 | | /* Default: Everything is a string */ |
858 | 0 | yylval_was_set = TRUE; |
859 | 0 | token = create_string_token(yytext); |
860 | 0 | goto normal_return; |
861 | 0 | } |
862 | 0 | } |
863 | | |
864 | | /* |
865 | | * Either followedby is not FOLLBY_TOKEN or this lexeme is part |
866 | | * of a string. Hence, we need to return T_String. |
867 | | * |
868 | | * _Except_ we might have a -4 or -6 flag on a an association |
869 | | * configuration line (server, peer, pool, etc.). |
870 | | * |
871 | | * This is a terrible hack, but the grammar is ambiguous so we |
872 | | * don't have a choice. [SK] |
873 | | * |
874 | | * The ambiguity is in the keyword scanner, not ntp_parser.y. |
875 | | * We do not require server addresses be quoted in ntp.conf, |
876 | | * complicating the scanner's job. To avoid trying (and |
877 | | * failing) to match an IP address or DNS name to a keyword, |
878 | | * the association keywords use FOLLBY_STRING in the keyword |
879 | | * table, which tells the scanner to force the next token to be |
880 | | * a T_String, so it does not try to match a keyword but rather |
881 | | * expects a string when -4/-6 modifiers to server, peer, etc. |
882 | | * are encountered. |
883 | | * restrict -4 and restrict -6 parsing works correctly without |
884 | | * this hack, as restrict uses FOLLBY_TOKEN. [DH] |
885 | | */ |
886 | 0 | if ('-' == yytext[0]) { |
887 | 0 | if ('4' == yytext[1]) { |
888 | 0 | token = T_Ipv4_flag; |
889 | 0 | goto normal_return; |
890 | 0 | } else if ('6' == yytext[1]) { |
891 | 0 | token = T_Ipv6_flag; |
892 | 0 | goto normal_return; |
893 | 0 | } |
894 | 0 | } |
895 | | |
896 | 0 | if (FOLLBY_STRING == followedby) |
897 | 0 | followedby = FOLLBY_TOKEN; |
898 | |
|
899 | 0 | yylval_was_set = TRUE; |
900 | 0 | token = create_string_token(yytext); |
901 | |
|
902 | 0 | normal_return: |
903 | 0 | if (T_EOC == token) |
904 | 0 | DPRINTF(4,("\t<end of command>\n")); |
905 | 0 | else |
906 | 0 | DPRINTF(4, ("yylex: lexeme '%s' -> %s\n", yytext, |
907 | 0 | token_name(token))); |
908 | |
|
909 | 0 | if (!yylval_was_set) |
910 | 0 | yylval.Integer = token; |
911 | |
|
912 | 0 | return token; |
913 | | |
914 | 0 | lex_too_long: |
915 | 0 | yytext[min(sizeof(yytext) - 1, 50)] = 0; |
916 | 0 | msyslog(LOG_ERR, |
917 | 0 | "configuration item on line %d longer than limit of %lu, began with '%s'", |
918 | 0 | lex_stack->curpos.nline, (u_long)min(sizeof(yytext) - 1, 50), |
919 | 0 | yytext); |
920 | | |
921 | | /* |
922 | | * If we hit the length limit reading the startup configuration |
923 | | * file, abort. |
924 | | */ |
925 | 0 | if (lex_from_file()) |
926 | 0 | exit(sizeof(yytext) - 1); |
927 | | |
928 | | /* |
929 | | * If it's runtime configuration via ntpq :config treat it as |
930 | | * if the configuration text ended before the too-long lexeme, |
931 | | * hostname, or string. |
932 | | */ |
933 | 0 | yylval.Integer = 0; |
934 | 0 | return 0; |
935 | 0 | } |