1*eabc0478Schristos /* $NetBSD: ntp_scanner.c,v 1.15 2024/08/18 20:47:18 christos Exp $ */ 2abb0f93cSkardel 3abb0f93cSkardel 4abb0f93cSkardel /* ntp_scanner.c 5abb0f93cSkardel * 6abb0f93cSkardel * The source code for a simple lexical analyzer. 7abb0f93cSkardel * 8abb0f93cSkardel * Written By: Sachin Kamboj 9abb0f93cSkardel * University of Delaware 10abb0f93cSkardel * Newark, DE 19711 11abb0f93cSkardel * Copyright (c) 2006 12abb0f93cSkardel */ 13abb0f93cSkardel 14abb0f93cSkardel #ifdef HAVE_CONFIG_H 15abb0f93cSkardel # include <config.h> 16abb0f93cSkardel #endif 17abb0f93cSkardel 18abb0f93cSkardel #include <stdio.h> 19abb0f93cSkardel #include <ctype.h> 20abb0f93cSkardel #include <stdlib.h> 21abb0f93cSkardel #include <errno.h> 22abb0f93cSkardel #include <string.h> 23abb0f93cSkardel 242950cc38Schristos #include "ntpd.h" 25abb0f93cSkardel #include "ntp_config.h" 26abb0f93cSkardel #include "ntpsim.h" 27abb0f93cSkardel #include "ntp_scanner.h" 28abb0f93cSkardel #include "ntp_parser.h" 29abb0f93cSkardel 30abb0f93cSkardel /* ntp_keyword.h declares finite state machine and token text */ 31abb0f93cSkardel #include "ntp_keyword.h" 32abb0f93cSkardel 33abb0f93cSkardel 34abb0f93cSkardel 35abb0f93cSkardel /* SCANNER GLOBAL VARIABLES 36abb0f93cSkardel * ------------------------ 37abb0f93cSkardel */ 38abb0f93cSkardel 39*eabc0478Schristos #define MAX_LEXEME 128 /* The maximum size of a lexeme */ 40abb0f93cSkardel char yytext[MAX_LEXEME]; /* Buffer for storing the input text/lexeme */ 412950cc38Schristos u_int32 conf_file_sum; /* Simple sum of characters read */ 42abb0f93cSkardel 435d681e99Schristos static struct FILE_INFO * lex_stack = NULL; 44abb0f93cSkardel 45abb0f93cSkardel 46abb0f93cSkardel 47abb0f93cSkardel /* CONSTANTS 48abb0f93cSkardel * --------- 49abb0f93cSkardel */ 50abb0f93cSkardel 51abb0f93cSkardel 52abb0f93cSkardel /* SCANNER GLOBAL VARIABLES 53abb0f93cSkardel * ------------------------ 54abb0f93cSkardel */ 55abb0f93cSkardel const char special_chars[] = "{}(),;|="; 56abb0f93cSkardel 57abb0f93cSkardel 58abb0f93cSkardel /* FUNCTIONS 59abb0f93cSkardel * --------- 60abb0f93cSkardel */ 61abb0f93cSkardel 62abb0f93cSkardel static int is_keyword(char *lexeme, follby *pfollowedby); 63abb0f93cSkardel 64abb0f93cSkardel 65abb0f93cSkardel /* 663123f114Skardel * keyword() - Return the keyword associated with token T_ identifier. 673123f114Skardel * See also token_name() for the string-ized T_ identifier. 683123f114Skardel * Example: keyword(T_Server) returns "server" 693123f114Skardel * token_name(T_Server) returns "T_Server" 70abb0f93cSkardel */ 71abb0f93cSkardel const char * 72abb0f93cSkardel keyword( 73abb0f93cSkardel int token 74abb0f93cSkardel ) 75abb0f93cSkardel { 76e19314b7Schristos size_t i; 77abb0f93cSkardel const char *text; 78cdfa2a7eSchristos static char sbuf[64]; 79abb0f93cSkardel 80abb0f93cSkardel i = token - LOWEST_KEYWORD_ID; 81abb0f93cSkardel 82cdfa2a7eSchristos switch (token) { 83cdfa2a7eSchristos case T_ServerresponseFuzz: 84cdfa2a7eSchristos text = "serverresponse fuzz"; 85cdfa2a7eSchristos break; 86abb0f93cSkardel 87cdfa2a7eSchristos default: 88cdfa2a7eSchristos if (i < COUNTOF(keyword_text)) { 89cdfa2a7eSchristos text = keyword_text[i]; 90cdfa2a7eSchristos } else { 91cdfa2a7eSchristos snprintf(sbuf, sizeof sbuf, 92cdfa2a7eSchristos "(keyword #%u not found)", token); 93cdfa2a7eSchristos text = sbuf; 94cdfa2a7eSchristos } 95cdfa2a7eSchristos } 96cdfa2a7eSchristos 97cdfa2a7eSchristos return text; 98abb0f93cSkardel } 99abb0f93cSkardel 100abb0f93cSkardel 1015d681e99Schristos /* FILE & STRING BUFFER INTERFACE 1025d681e99Schristos * ------------------------------ 1035d681e99Schristos * 1045d681e99Schristos * This set out as a couple of wrapper functions around the standard C 1055d681e99Schristos * fgetc and ungetc functions in order to include positional 1065d681e99Schristos * bookkeeping. Alas, this is no longer a good solution with nested 1075d681e99Schristos * input files and the possibility to send configuration commands via 1085d681e99Schristos * 'ntpdc' and 'ntpq'. 1095d681e99Schristos * 1105d681e99Schristos * Now there are a few functions to maintain a stack of nested input 1115d681e99Schristos * sources (though nesting is only allowd for disk files) and from the 1125d681e99Schristos * scanner / parser point of view there's no difference between both 1135d681e99Schristos * types of sources. 1145d681e99Schristos * 1155d681e99Schristos * The 'fgetc()' / 'ungetc()' replacements now operate on a FILE_INFO 1165d681e99Schristos * structure. Instead of trying different 'ungetc()' strategies for file 1175d681e99Schristos * and buffer based parsing, we keep the backup char in our own 1185d681e99Schristos * FILE_INFO structure. This is sufficient, as the parser does *not* 1195d681e99Schristos * jump around via 'seek' or the like, and there's no need to 1205d681e99Schristos * check/clear the backup store in other places than 'lex_getch()'. 121abb0f93cSkardel */ 122abb0f93cSkardel 1235d681e99Schristos /* 1245d681e99Schristos * Allocate an info structure and attach it to a file. 1255d681e99Schristos * 1265d681e99Schristos * Note: When 'mode' is NULL, then the INFO block will be set up to 1275d681e99Schristos * contain a NULL file pointer, as suited for remote config command 1285d681e99Schristos * parsing. Otherwise having a NULL file pointer is considered an error, 1295d681e99Schristos * and a NULL info block pointer is returned to indicate failure! 1305d681e99Schristos * 1315d681e99Schristos * Note: We use a variable-sized structure to hold a copy of the file 1325d681e99Schristos * name (or, more proper, the input source description). This is more 1335d681e99Schristos * secure than keeping a reference to some other storage that might go 1345d681e99Schristos * out of scope. 1355d681e99Schristos */ 1365d681e99Schristos static struct FILE_INFO * 1375d681e99Schristos lex_open( 138abb0f93cSkardel const char *path, 139abb0f93cSkardel const char *mode 140abb0f93cSkardel ) 141abb0f93cSkardel { 1425d681e99Schristos struct FILE_INFO *stream; 1435d681e99Schristos size_t nnambuf; 144abb0f93cSkardel 1455d681e99Schristos nnambuf = strlen(path); 1465d681e99Schristos stream = emalloc_zero(sizeof(*stream) + nnambuf); 1475d681e99Schristos stream->curpos.nline = 1; 1485d681e99Schristos stream->backch = EOF; 1495d681e99Schristos /* copy name with memcpy -- trailing NUL already there! */ 1505d681e99Schristos memcpy(stream->fname, path, nnambuf); 151abb0f93cSkardel 1525d681e99Schristos if (NULL != mode) { 1535d681e99Schristos stream->fpi = fopen(path, mode); 1545d681e99Schristos if (NULL == stream->fpi) { 1555d681e99Schristos free(stream); 1565d681e99Schristos stream = NULL; 157abb0f93cSkardel } 1585d681e99Schristos } 1595d681e99Schristos return stream; 160abb0f93cSkardel } 161abb0f93cSkardel 1625d681e99Schristos /* get next character from buffer or file. This will return any putback 1635d681e99Schristos * character first; it will also make sure the last line is at least 1645d681e99Schristos * virtually terminated with a '\n'. 1655d681e99Schristos */ 1665d681e99Schristos static int 1675d681e99Schristos lex_getch( 168abb0f93cSkardel struct FILE_INFO *stream 169abb0f93cSkardel ) 170abb0f93cSkardel { 1712950cc38Schristos int ch; 172abb0f93cSkardel 1735d681e99Schristos if (NULL == stream || stream->force_eof) 1745d681e99Schristos return EOF; 1752950cc38Schristos 1765d681e99Schristos if (EOF != stream->backch) { 1775d681e99Schristos ch = stream->backch; 1785d681e99Schristos stream->backch = EOF; 1795d681e99Schristos if (stream->fpi) 1805d681e99Schristos conf_file_sum += ch; 1814eea345dSchristos stream->curpos.ncol++; 1825d681e99Schristos } else if (stream->fpi) { 1835d681e99Schristos /* fetch next 7-bit ASCII char (or EOF) from file */ 1845d681e99Schristos while ((ch = fgetc(stream->fpi)) != EOF && ch > SCHAR_MAX) 1855d681e99Schristos stream->curpos.ncol++; 1862950cc38Schristos if (EOF != ch) { 1875d681e99Schristos conf_file_sum += ch; 1885d681e99Schristos stream->curpos.ncol++; 189abb0f93cSkardel } 1905d681e99Schristos } else { 1915d681e99Schristos /* fetch next 7-bit ASCII char from buffer */ 1925d681e99Schristos const char * scan; 1935d681e99Schristos scan = &remote_config.buffer[remote_config.pos]; 1945d681e99Schristos while ((ch = (u_char)*scan) > SCHAR_MAX) { 1955d681e99Schristos scan++; 1965d681e99Schristos stream->curpos.ncol++; 1975d681e99Schristos } 1985d681e99Schristos if ('\0' != ch) { 1995d681e99Schristos scan++; 2005d681e99Schristos stream->curpos.ncol++; 2015d681e99Schristos } else { 2025d681e99Schristos ch = EOF; 2035d681e99Schristos } 2045d681e99Schristos remote_config.pos = (int)(scan - remote_config.buffer); 2055d681e99Schristos } 2065d681e99Schristos 2075d681e99Schristos /* If the last line ends without '\n', generate one. This 2085d681e99Schristos * happens most likely on Windows, where editors often have a 2095d681e99Schristos * sloppy concept of a line. 2105d681e99Schristos */ 2115d681e99Schristos if (EOF == ch && stream->curpos.ncol != 0) 2125d681e99Schristos ch = '\n'; 2135d681e99Schristos 2145d681e99Schristos /* update scan position tallies */ 2155d681e99Schristos if (ch == '\n') { 2165d681e99Schristos stream->bakpos = stream->curpos; 2175d681e99Schristos stream->curpos.nline++; 2185d681e99Schristos stream->curpos.ncol = 0; 2192950cc38Schristos } 2202950cc38Schristos 221abb0f93cSkardel return ch; 222abb0f93cSkardel } 223abb0f93cSkardel 2245d681e99Schristos /* Note: lex_ungetch will fail to track more than one line of push 2255d681e99Schristos * back. But since it guarantees only one char of back storage anyway, 2265d681e99Schristos * this should not be a problem. 227abb0f93cSkardel */ 2285d681e99Schristos static int 2295d681e99Schristos lex_ungetch( 230abb0f93cSkardel int ch, 231abb0f93cSkardel struct FILE_INFO *stream 232abb0f93cSkardel ) 233abb0f93cSkardel { 2345d681e99Schristos /* check preconditions */ 2355d681e99Schristos if (NULL == stream || stream->force_eof) 2365d681e99Schristos return EOF; 2375d681e99Schristos if (EOF != stream->backch || EOF == ch) 2385d681e99Schristos return EOF; 2395d681e99Schristos 2405d681e99Schristos /* keep for later reference and update checksum */ 2415d681e99Schristos stream->backch = (u_char)ch; 2425d681e99Schristos if (stream->fpi) 2435d681e99Schristos conf_file_sum -= stream->backch; 2445d681e99Schristos 2455d681e99Schristos /* update position */ 2465d681e99Schristos if (stream->backch == '\n') { 2475d681e99Schristos stream->curpos = stream->bakpos; 2485d681e99Schristos stream->bakpos.ncol = -1; 249abb0f93cSkardel } 2505d681e99Schristos stream->curpos.ncol--; 2515d681e99Schristos return stream->backch; 252abb0f93cSkardel } 253abb0f93cSkardel 2545d681e99Schristos /* dispose of an input structure. If the file pointer is not NULL, close 2555d681e99Schristos * the file. This function does not check the result of 'fclose()'. 2565d681e99Schristos */ 2575d681e99Schristos static void 2585d681e99Schristos lex_close( 259abb0f93cSkardel struct FILE_INFO *stream 260abb0f93cSkardel ) 261abb0f93cSkardel { 2625d681e99Schristos if (NULL != stream) { 2635d681e99Schristos if (NULL != stream->fpi) 2645d681e99Schristos fclose(stream->fpi); 265abb0f93cSkardel free(stream); 2665d681e99Schristos } 267abb0f93cSkardel } 268abb0f93cSkardel 2695d681e99Schristos /* INPUT STACK 2705d681e99Schristos * ----------- 271abb0f93cSkardel * 2725d681e99Schristos * Nested input sources are a bit tricky at first glance. We deal with 2735d681e99Schristos * this problem using a stack of input sources, that is, a forward 2745d681e99Schristos * linked list of FILE_INFO structs. 2755d681e99Schristos * 2765d681e99Schristos * This stack is never empty during parsing; while an encounter with EOF 2775d681e99Schristos * can and will remove nested input sources, removing the last element 2785d681e99Schristos * in the stack will not work during parsing, and the EOF condition of 2795d681e99Schristos * the outermost input file remains until the parser folds up. 280abb0f93cSkardel */ 281abb0f93cSkardel 2825d681e99Schristos static struct FILE_INFO * 283*eabc0478Schristos drop_stack_do( 2845d681e99Schristos struct FILE_INFO * head 285abb0f93cSkardel ) 286abb0f93cSkardel { 2875d681e99Schristos struct FILE_INFO * tail; 2885d681e99Schristos while (NULL != head) { 2895d681e99Schristos tail = head->st_next; 2905d681e99Schristos lex_close(head); 2915d681e99Schristos head = tail; 292abb0f93cSkardel } 2935d681e99Schristos return head; 294abb0f93cSkardel } 295abb0f93cSkardel 2965d681e99Schristos 2975d681e99Schristos 2985d681e99Schristos /* Create a singleton input source on an empty lexer stack. This will 2995d681e99Schristos * fail if there is already an input source, or if the underlying disk 3005d681e99Schristos * file cannot be opened. 3015d681e99Schristos * 3025d681e99Schristos * Returns TRUE if a new input object was successfully created. 3035d681e99Schristos */ 3045d681e99Schristos int/*BOOL*/ 3055d681e99Schristos lex_init_stack( 3065d681e99Schristos const char * path, 3075d681e99Schristos const char * mode 308abb0f93cSkardel ) 309abb0f93cSkardel { 3105d681e99Schristos if (NULL != lex_stack || NULL == path) 3115d681e99Schristos return FALSE; 312abb0f93cSkardel 3135d681e99Schristos lex_stack = lex_open(path, mode); 3145d681e99Schristos return (NULL != lex_stack); 315abb0f93cSkardel } 316abb0f93cSkardel 3175d681e99Schristos /* This removes *all* input sources from the stack, leaving the head 3185d681e99Schristos * pointer as NULL. Any attempt to parse in that state is likely to bomb 3195d681e99Schristos * with segmentation faults or the like. 3205d681e99Schristos * 3215d681e99Schristos * In other words: Use this to clean up after parsing, and do not parse 3225d681e99Schristos * anything until the next 'lex_init_stack()' succeeded. 3235d681e99Schristos */ 3245d681e99Schristos void 325*eabc0478Schristos lex_drop_stack(void) 3265d681e99Schristos { 327*eabc0478Schristos lex_stack = drop_stack_do(lex_stack); 3285d681e99Schristos } 3295d681e99Schristos 3305d681e99Schristos /* Flush the lexer input stack: This will nip all input objects on the 3315d681e99Schristos * stack (but keeps the current top-of-stack) and marks the top-of-stack 3325d681e99Schristos * as inactive. Any further calls to lex_getch yield only EOF, and it's 3335d681e99Schristos * no longer possible to push something back. 3345d681e99Schristos * 3355d681e99Schristos * Returns TRUE if there is a head element (top-of-stack) that was not 3365d681e99Schristos * in the force-eof mode before this call. 3375d681e99Schristos */ 3385d681e99Schristos int/*BOOL*/ 339*eabc0478Schristos lex_flush_stack(void) 3405d681e99Schristos { 3415d681e99Schristos int retv = FALSE; 3425d681e99Schristos 3435d681e99Schristos if (NULL != lex_stack) { 3445d681e99Schristos retv = !lex_stack->force_eof; 3455d681e99Schristos lex_stack->force_eof = TRUE; 346*eabc0478Schristos lex_stack->st_next = drop_stack_do( 3475d681e99Schristos lex_stack->st_next); 3485d681e99Schristos } 3495d681e99Schristos return retv; 3505d681e99Schristos } 3515d681e99Schristos 3525d681e99Schristos /* Push another file on the parsing stack. If the mode is NULL, create a 3535d681e99Schristos * FILE_INFO suitable for in-memory parsing; otherwise, create a 3545d681e99Schristos * FILE_INFO that is bound to a local/disc file. Note that 'path' must 3555d681e99Schristos * not be NULL, or the function will fail. 3565d681e99Schristos * 3575d681e99Schristos * Returns TRUE if a new info record was pushed onto the stack. 3585d681e99Schristos */ 3595d681e99Schristos int/*BOOL*/ lex_push_file( 3605d681e99Schristos const char * path, 3615d681e99Schristos const char * mode 3625d681e99Schristos ) 3635d681e99Schristos { 3645d681e99Schristos struct FILE_INFO * next = NULL; 3655d681e99Schristos 3665d681e99Schristos if (NULL != path) { 3675d681e99Schristos next = lex_open(path, mode); 3685d681e99Schristos if (NULL != next) { 3695d681e99Schristos next->st_next = lex_stack; 3705d681e99Schristos lex_stack = next; 3715d681e99Schristos } 3725d681e99Schristos } 3735d681e99Schristos return (NULL != next); 3745d681e99Schristos } 3755d681e99Schristos 3765d681e99Schristos /* Pop, close & free the top of the include stack, unless the stack 3775d681e99Schristos * contains only a singleton input object. In that case the function 3785d681e99Schristos * fails, because the parser does not expect the input stack to be 3795d681e99Schristos * empty. 3805d681e99Schristos * 3815d681e99Schristos * Returns TRUE if an object was successfuly popped from the stack. 3825d681e99Schristos */ 3835d681e99Schristos int/*BOOL*/ 3845d681e99Schristos lex_pop_file(void) 3855d681e99Schristos { 3865d681e99Schristos struct FILE_INFO * head = lex_stack; 3875d681e99Schristos struct FILE_INFO * tail = NULL; 3885d681e99Schristos 3895d681e99Schristos if (NULL != head) { 3905d681e99Schristos tail = head->st_next; 3915d681e99Schristos if (NULL != tail) { 3925d681e99Schristos lex_stack = tail; 3935d681e99Schristos lex_close(head); 3945d681e99Schristos } 3955d681e99Schristos } 3965d681e99Schristos return (NULL != tail); 3975d681e99Schristos } 3985d681e99Schristos 3995d681e99Schristos /* Get include nesting level. This currently loops over the stack and 4005d681e99Schristos * counts elements; but since this is of concern only with an include 4015d681e99Schristos * statement and the nesting depth has a small limit, there's no 4025d681e99Schristos * bottleneck expected here. 4035d681e99Schristos * 4045d681e99Schristos * Returns the nesting level of includes, that is, the current depth of 4055d681e99Schristos * the lexer input stack. 4065d681e99Schristos * 4075d681e99Schristos * Note: 4085d681e99Schristos */ 4095d681e99Schristos size_t 4105d681e99Schristos lex_level(void) 4115d681e99Schristos { 4125d681e99Schristos size_t cnt = 0; 4135d681e99Schristos struct FILE_INFO *ipf = lex_stack; 4145d681e99Schristos 4155d681e99Schristos while (NULL != ipf) { 4165d681e99Schristos cnt++; 4175d681e99Schristos ipf = ipf->st_next; 4185d681e99Schristos } 4195d681e99Schristos return cnt; 4205d681e99Schristos } 4215d681e99Schristos 4225d681e99Schristos /* check if the current input is from a file */ 4235d681e99Schristos int/*BOOL*/ 4245d681e99Schristos lex_from_file(void) 4255d681e99Schristos { 4265d681e99Schristos return (NULL != lex_stack) && (NULL != lex_stack->fpi); 4275d681e99Schristos } 4285d681e99Schristos 4295d681e99Schristos struct FILE_INFO * 430*eabc0478Schristos lex_current(void) 4315d681e99Schristos { 4325d681e99Schristos /* this became so simple, it could be a macro. But then, 4335d681e99Schristos * lex_stack needed to be global... 4345d681e99Schristos */ 4355d681e99Schristos return lex_stack; 4365d681e99Schristos } 437abb0f93cSkardel 438abb0f93cSkardel 439abb0f93cSkardel /* STATE MACHINES 440abb0f93cSkardel * -------------- 441abb0f93cSkardel */ 442abb0f93cSkardel 443abb0f93cSkardel /* Keywords */ 444abb0f93cSkardel static int 445abb0f93cSkardel is_keyword( 446abb0f93cSkardel char *lexeme, 447abb0f93cSkardel follby *pfollowedby 448abb0f93cSkardel ) 449abb0f93cSkardel { 450abb0f93cSkardel follby fb; 451abb0f93cSkardel int curr_s; /* current state index */ 452abb0f93cSkardel int token; 453abb0f93cSkardel int i; 454abb0f93cSkardel 455abb0f93cSkardel curr_s = SCANNER_INIT_S; 456abb0f93cSkardel token = 0; 457abb0f93cSkardel 458abb0f93cSkardel for (i = 0; lexeme[i]; i++) { 459abb0f93cSkardel while (curr_s && (lexeme[i] != SS_CH(sst[curr_s]))) 460abb0f93cSkardel curr_s = SS_OTHER_N(sst[curr_s]); 461abb0f93cSkardel 462abb0f93cSkardel if (curr_s && (lexeme[i] == SS_CH(sst[curr_s]))) { 463abb0f93cSkardel if ('\0' == lexeme[i + 1] 464abb0f93cSkardel && FOLLBY_NON_ACCEPTING 465abb0f93cSkardel != SS_FB(sst[curr_s])) { 466abb0f93cSkardel fb = SS_FB(sst[curr_s]); 467abb0f93cSkardel *pfollowedby = fb; 468abb0f93cSkardel token = curr_s; 469abb0f93cSkardel break; 470abb0f93cSkardel } 471abb0f93cSkardel curr_s = SS_MATCH_N(sst[curr_s]); 472abb0f93cSkardel } else 473abb0f93cSkardel break; 474abb0f93cSkardel } 475abb0f93cSkardel 476abb0f93cSkardel return token; 477abb0f93cSkardel } 478abb0f93cSkardel 479abb0f93cSkardel 480abb0f93cSkardel /* Integer */ 481abb0f93cSkardel static int 482abb0f93cSkardel is_integer( 483abb0f93cSkardel char *lexeme 484abb0f93cSkardel ) 485abb0f93cSkardel { 4862950cc38Schristos int i; 4872950cc38Schristos int is_neg; 4882950cc38Schristos u_int u_val; 4892950cc38Schristos 4902950cc38Schristos i = 0; 491abb0f93cSkardel 492abb0f93cSkardel /* Allow a leading minus sign */ 4932950cc38Schristos if (lexeme[i] == '-') { 4942950cc38Schristos i++; 4952950cc38Schristos is_neg = TRUE; 4962950cc38Schristos } else { 4972950cc38Schristos is_neg = FALSE; 4982950cc38Schristos } 499abb0f93cSkardel 500abb0f93cSkardel /* Check that all the remaining characters are digits */ 5012950cc38Schristos for (; lexeme[i] != '\0'; i++) { 5025d681e99Schristos if (!isdigit((u_char)lexeme[i])) 5032950cc38Schristos return FALSE; 504abb0f93cSkardel } 5052950cc38Schristos 5062950cc38Schristos if (is_neg) 5072950cc38Schristos return TRUE; 5082950cc38Schristos 5092950cc38Schristos /* Reject numbers that fit in unsigned but not in signed int */ 5102950cc38Schristos if (1 == sscanf(lexeme, "%u", &u_val)) 5112950cc38Schristos return (u_val <= INT_MAX); 5122950cc38Schristos else 5132950cc38Schristos return FALSE; 5142950cc38Schristos } 5152950cc38Schristos 5162950cc38Schristos 5172950cc38Schristos /* U_int -- assumes is_integer() has returned FALSE */ 5182950cc38Schristos static int 5192950cc38Schristos is_u_int( 5202950cc38Schristos char *lexeme 5212950cc38Schristos ) 5222950cc38Schristos { 5232950cc38Schristos int i; 5242950cc38Schristos int is_hex; 5252950cc38Schristos 5262950cc38Schristos i = 0; 5275d681e99Schristos if ('0' == lexeme[i] && 'x' == tolower((u_char)lexeme[i + 1])) { 5282950cc38Schristos i += 2; 5292950cc38Schristos is_hex = TRUE; 5302950cc38Schristos } else { 5312950cc38Schristos is_hex = FALSE; 5322950cc38Schristos } 5332950cc38Schristos 5342950cc38Schristos /* Check that all the remaining characters are digits */ 5352950cc38Schristos for (; lexeme[i] != '\0'; i++) { 5365d681e99Schristos if (is_hex && !isxdigit((u_char)lexeme[i])) 5372950cc38Schristos return FALSE; 5385d681e99Schristos if (!is_hex && !isdigit((u_char)lexeme[i])) 5392950cc38Schristos return FALSE; 5402950cc38Schristos } 5412950cc38Schristos 5422950cc38Schristos return TRUE; 543abb0f93cSkardel } 544abb0f93cSkardel 545abb0f93cSkardel 546abb0f93cSkardel /* Double */ 547abb0f93cSkardel static int 548abb0f93cSkardel is_double( 549abb0f93cSkardel char *lexeme 550abb0f93cSkardel ) 551abb0f93cSkardel { 5523123f114Skardel u_int num_digits = 0; /* Number of digits read */ 5533123f114Skardel u_int i; 554abb0f93cSkardel 555abb0f93cSkardel i = 0; 556abb0f93cSkardel 557abb0f93cSkardel /* Check for an optional '+' or '-' */ 558abb0f93cSkardel if ('+' == lexeme[i] || '-' == lexeme[i]) 559abb0f93cSkardel i++; 560abb0f93cSkardel 561abb0f93cSkardel /* Read the integer part */ 5625d681e99Schristos for (; lexeme[i] && isdigit((u_char)lexeme[i]); i++) 563abb0f93cSkardel num_digits++; 564abb0f93cSkardel 5652950cc38Schristos /* Check for the optional decimal point */ 5662950cc38Schristos if ('.' == lexeme[i]) { 567abb0f93cSkardel i++; 568abb0f93cSkardel /* Check for any digits after the decimal point */ 5695d681e99Schristos for (; lexeme[i] && isdigit((u_char)lexeme[i]); i++) 570abb0f93cSkardel num_digits++; 5712950cc38Schristos } 572abb0f93cSkardel 573abb0f93cSkardel /* 574abb0f93cSkardel * The number of digits in both the decimal part and the 575abb0f93cSkardel * fraction part must not be zero at this point 576abb0f93cSkardel */ 577abb0f93cSkardel if (!num_digits) 578abb0f93cSkardel return 0; 579abb0f93cSkardel 580abb0f93cSkardel /* Check if we are done */ 581abb0f93cSkardel if (!lexeme[i]) 582abb0f93cSkardel return 1; 583abb0f93cSkardel 584abb0f93cSkardel /* There is still more input, read the exponent */ 5855d681e99Schristos if ('e' == tolower((u_char)lexeme[i])) 586abb0f93cSkardel i++; 587abb0f93cSkardel else 588abb0f93cSkardel return 0; 589abb0f93cSkardel 590abb0f93cSkardel /* Read an optional Sign */ 591abb0f93cSkardel if ('+' == lexeme[i] || '-' == lexeme[i]) 592abb0f93cSkardel i++; 593abb0f93cSkardel 594abb0f93cSkardel /* Now read the exponent part */ 5955d681e99Schristos while (lexeme[i] && isdigit((u_char)lexeme[i])) 596abb0f93cSkardel i++; 597abb0f93cSkardel 598abb0f93cSkardel /* Check if we are done */ 599abb0f93cSkardel if (!lexeme[i]) 600abb0f93cSkardel return 1; 601abb0f93cSkardel else 602abb0f93cSkardel return 0; 603abb0f93cSkardel } 604abb0f93cSkardel 605abb0f93cSkardel 606abb0f93cSkardel /* is_special() - Test whether a character is a token */ 607abb0f93cSkardel static inline int 608abb0f93cSkardel is_special( 609abb0f93cSkardel int ch 610abb0f93cSkardel ) 611abb0f93cSkardel { 6129893a031Skardel return strchr(special_chars, ch) != NULL; 613abb0f93cSkardel } 614abb0f93cSkardel 615abb0f93cSkardel 616abb0f93cSkardel static int 617abb0f93cSkardel is_EOC( 618abb0f93cSkardel int ch 619abb0f93cSkardel ) 620abb0f93cSkardel { 621abb0f93cSkardel if ((old_config_style && (ch == '\n')) || 622abb0f93cSkardel (!old_config_style && (ch == ';'))) 623abb0f93cSkardel return 1; 624abb0f93cSkardel return 0; 625abb0f93cSkardel } 626abb0f93cSkardel 627abb0f93cSkardel 628abb0f93cSkardel char * 629abb0f93cSkardel quote_if_needed(char *str) 630abb0f93cSkardel { 631abb0f93cSkardel char *ret; 632abb0f93cSkardel size_t len; 633abb0f93cSkardel size_t octets; 634abb0f93cSkardel 635abb0f93cSkardel len = strlen(str); 636abb0f93cSkardel octets = len + 2 + 1; 637abb0f93cSkardel ret = emalloc(octets); 638abb0f93cSkardel if ('"' != str[0] 639abb0f93cSkardel && (strcspn(str, special_chars) < len 640abb0f93cSkardel || strchr(str, ' ') != NULL)) { 641abb0f93cSkardel snprintf(ret, octets, "\"%s\"", str); 642abb0f93cSkardel } else 6432950cc38Schristos strlcpy(ret, str, octets); 644abb0f93cSkardel 645abb0f93cSkardel return ret; 646abb0f93cSkardel } 647abb0f93cSkardel 648abb0f93cSkardel 649abb0f93cSkardel static int 650abb0f93cSkardel create_string_token( 651abb0f93cSkardel char *lexeme 652abb0f93cSkardel ) 653abb0f93cSkardel { 654abb0f93cSkardel char *pch; 655abb0f93cSkardel 656abb0f93cSkardel /* 657abb0f93cSkardel * ignore end of line whitespace 658abb0f93cSkardel */ 659abb0f93cSkardel pch = lexeme; 6605d681e99Schristos while (*pch && isspace((u_char)*pch)) 661abb0f93cSkardel pch++; 662abb0f93cSkardel 663abb0f93cSkardel if (!*pch) { 664abb0f93cSkardel yylval.Integer = T_EOC; 665abb0f93cSkardel return yylval.Integer; 666abb0f93cSkardel } 667abb0f93cSkardel 668abb0f93cSkardel yylval.String = estrdup(lexeme); 669abb0f93cSkardel return T_String; 670abb0f93cSkardel } 671abb0f93cSkardel 672abb0f93cSkardel 673abb0f93cSkardel /* 674abb0f93cSkardel * yylex() - function that does the actual scanning. 675abb0f93cSkardel * Bison expects this function to be called yylex and for it to take no 676abb0f93cSkardel * input and return an int. 677abb0f93cSkardel * Conceptually yylex "returns" yylval as well as the actual return 678abb0f93cSkardel * value representing the token or type. 679abb0f93cSkardel */ 680abb0f93cSkardel int 6815d681e99Schristos yylex(void) 682abb0f93cSkardel { 683abb0f93cSkardel static follby followedby = FOLLBY_TOKEN; 6842950cc38Schristos size_t i; 6852950cc38Schristos int instring; 6862950cc38Schristos int yylval_was_set; 6872950cc38Schristos int converted; 6882950cc38Schristos int token; /* The return value */ 6892950cc38Schristos int ch; 6902950cc38Schristos 6912950cc38Schristos instring = FALSE; 6922950cc38Schristos yylval_was_set = FALSE; 693abb0f93cSkardel 694abb0f93cSkardel do { 695abb0f93cSkardel /* Ignore whitespace at the beginning */ 6965d681e99Schristos while (EOF != (ch = lex_getch(lex_stack)) && 697abb0f93cSkardel isspace(ch) && 698abb0f93cSkardel !is_EOC(ch)) 6995d681e99Schristos 700abb0f93cSkardel ; /* Null Statement */ 701abb0f93cSkardel 702abb0f93cSkardel if (EOF == ch) { 703abb0f93cSkardel 7045d681e99Schristos if ( ! lex_pop_file()) 705abb0f93cSkardel return 0; 706abb0f93cSkardel token = T_EOC; 707abb0f93cSkardel goto normal_return; 708abb0f93cSkardel 709abb0f93cSkardel } else if (is_EOC(ch)) { 710abb0f93cSkardel 711abb0f93cSkardel /* end FOLLBY_STRINGS_TO_EOC effect */ 712abb0f93cSkardel followedby = FOLLBY_TOKEN; 713abb0f93cSkardel token = T_EOC; 714abb0f93cSkardel goto normal_return; 715abb0f93cSkardel 716abb0f93cSkardel } else if (is_special(ch) && FOLLBY_TOKEN == followedby) { 717abb0f93cSkardel /* special chars are their own token values */ 718abb0f93cSkardel token = ch; 719abb0f93cSkardel /* 7202950cc38Schristos * '=' outside simulator configuration implies 7212950cc38Schristos * a single string following as in: 722abb0f93cSkardel * setvar Owner = "The Boss" default 723abb0f93cSkardel */ 7242950cc38Schristos if ('=' == ch && old_config_style) 725abb0f93cSkardel followedby = FOLLBY_STRING; 726abb0f93cSkardel yytext[0] = (char)ch; 727abb0f93cSkardel yytext[1] = '\0'; 728abb0f93cSkardel goto normal_return; 729abb0f93cSkardel } else 7305d681e99Schristos lex_ungetch(ch, lex_stack); 731abb0f93cSkardel 732abb0f93cSkardel /* save the position of start of the token */ 7335d681e99Schristos lex_stack->tokpos = lex_stack->curpos; 734abb0f93cSkardel 735abb0f93cSkardel /* Read in the lexeme */ 736abb0f93cSkardel i = 0; 7375d681e99Schristos while (EOF != (ch = lex_getch(lex_stack))) { 738abb0f93cSkardel 739abb0f93cSkardel yytext[i] = (char)ch; 740abb0f93cSkardel 741abb0f93cSkardel /* Break on whitespace or a special character */ 742abb0f93cSkardel if (isspace(ch) || is_EOC(ch) 743abb0f93cSkardel || '"' == ch 744abb0f93cSkardel || (FOLLBY_TOKEN == followedby 745abb0f93cSkardel && is_special(ch))) 746abb0f93cSkardel break; 747abb0f93cSkardel 748abb0f93cSkardel /* Read the rest of the line on reading a start 749abb0f93cSkardel of comment character */ 750abb0f93cSkardel if ('#' == ch) { 7515d681e99Schristos while (EOF != (ch = lex_getch(lex_stack)) 752abb0f93cSkardel && '\n' != ch) 753abb0f93cSkardel ; /* Null Statement */ 754abb0f93cSkardel break; 755abb0f93cSkardel } 756abb0f93cSkardel 757abb0f93cSkardel i++; 758abb0f93cSkardel if (i >= COUNTOF(yytext)) 759abb0f93cSkardel goto lex_too_long; 760abb0f93cSkardel } 761abb0f93cSkardel /* Pick up all of the string inside between " marks, to 762abb0f93cSkardel * end of line. If we make it to EOL without a 763abb0f93cSkardel * terminating " assume it for them. 764abb0f93cSkardel * 765abb0f93cSkardel * XXX - HMS: I'm not sure we want to assume the closing " 766abb0f93cSkardel */ 767abb0f93cSkardel if ('"' == ch) { 7682950cc38Schristos instring = TRUE; 7695d681e99Schristos while (EOF != (ch = lex_getch(lex_stack)) && 770abb0f93cSkardel ch != '"' && ch != '\n') { 771abb0f93cSkardel yytext[i++] = (char)ch; 772abb0f93cSkardel if (i >= COUNTOF(yytext)) 773abb0f93cSkardel goto lex_too_long; 774abb0f93cSkardel } 775abb0f93cSkardel /* 776abb0f93cSkardel * yytext[i] will be pushed back as not part of 777abb0f93cSkardel * this lexeme, but any closing quote should 778abb0f93cSkardel * not be pushed back, so we read another char. 779abb0f93cSkardel */ 780abb0f93cSkardel if ('"' == ch) 7815d681e99Schristos ch = lex_getch(lex_stack); 782abb0f93cSkardel } 783abb0f93cSkardel /* Pushback the last character read that is not a part 7845d681e99Schristos * of this lexeme. This fails silently if ch is EOF, 7855d681e99Schristos * but then the EOF condition persists and is handled on 7865d681e99Schristos * the next turn by the include stack mechanism. 787abb0f93cSkardel */ 7885d681e99Schristos lex_ungetch(ch, lex_stack); 7895d681e99Schristos 790abb0f93cSkardel yytext[i] = '\0'; 791abb0f93cSkardel } while (i == 0); 792abb0f93cSkardel 793abb0f93cSkardel /* Now return the desired token */ 794abb0f93cSkardel 795abb0f93cSkardel /* First make sure that the parser is *not* expecting a string 796abb0f93cSkardel * as the next token (based on the previous token that was 797abb0f93cSkardel * returned) and that we haven't read a string. 798abb0f93cSkardel */ 799abb0f93cSkardel 800abb0f93cSkardel if (followedby == FOLLBY_TOKEN && !instring) { 801abb0f93cSkardel token = is_keyword(yytext, &followedby); 8022950cc38Schristos if (token) { 8032950cc38Schristos /* 8042950cc38Schristos * T_Server is exceptional as it forces the 8052950cc38Schristos * following token to be a string in the 8062950cc38Schristos * non-simulator parts of the configuration, 8072950cc38Schristos * but in the simulator configuration section, 8082950cc38Schristos * "server" is followed by "=" which must be 8092950cc38Schristos * recognized as a token not a string. 8102950cc38Schristos */ 8112950cc38Schristos if (T_Server == token && !old_config_style) 8122950cc38Schristos followedby = FOLLBY_TOKEN; 813abb0f93cSkardel goto normal_return; 8142950cc38Schristos } else if (is_integer(yytext)) { 8152950cc38Schristos yylval_was_set = TRUE; 816abb0f93cSkardel errno = 0; 817abb0f93cSkardel if ((yylval.Integer = strtol(yytext, NULL, 10)) == 0 818abb0f93cSkardel && ((errno == EINVAL) || (errno == ERANGE))) { 819abb0f93cSkardel msyslog(LOG_ERR, 820abb0f93cSkardel "Integer cannot be represented: %s", 821abb0f93cSkardel yytext); 8225d681e99Schristos if (lex_from_file()) { 823abb0f93cSkardel exit(1); 824abb0f93cSkardel } else { 8252950cc38Schristos /* force end of parsing */ 8262950cc38Schristos yylval.Integer = 0; 8272950cc38Schristos return 0; 8282950cc38Schristos } 8292950cc38Schristos } 830abb0f93cSkardel token = T_Integer; 831abb0f93cSkardel goto normal_return; 8322950cc38Schristos } else if (is_u_int(yytext)) { 8332950cc38Schristos yylval_was_set = TRUE; 8342950cc38Schristos if ('0' == yytext[0] && 8355d681e99Schristos 'x' == tolower((unsigned long)yytext[1])) 8362950cc38Schristos converted = sscanf(&yytext[2], "%x", 8372950cc38Schristos &yylval.U_int); 8382950cc38Schristos else 8392950cc38Schristos converted = sscanf(yytext, "%u", 8402950cc38Schristos &yylval.U_int); 8412950cc38Schristos if (1 != converted) { 8422950cc38Schristos msyslog(LOG_ERR, 8432950cc38Schristos "U_int cannot be represented: %s", 8442950cc38Schristos yytext); 8455d681e99Schristos if (lex_from_file()) { 8462950cc38Schristos exit(1); 8472950cc38Schristos } else { 8482950cc38Schristos /* force end of parsing */ 8492950cc38Schristos yylval.Integer = 0; 8502950cc38Schristos return 0; 851abb0f93cSkardel } 852abb0f93cSkardel } 8532950cc38Schristos token = T_U_int; 8542950cc38Schristos goto normal_return; 8552950cc38Schristos } else if (is_double(yytext)) { 8562950cc38Schristos yylval_was_set = TRUE; 857abb0f93cSkardel errno = 0; 858abb0f93cSkardel if ((yylval.Double = atof(yytext)) == 0 && errno == ERANGE) { 859abb0f93cSkardel msyslog(LOG_ERR, 860abb0f93cSkardel "Double too large to represent: %s", 861abb0f93cSkardel yytext); 862abb0f93cSkardel exit(1); 863abb0f93cSkardel } else { 864abb0f93cSkardel token = T_Double; 865abb0f93cSkardel goto normal_return; 866abb0f93cSkardel } 867abb0f93cSkardel } else { 868abb0f93cSkardel /* Default: Everything is a string */ 8692950cc38Schristos yylval_was_set = TRUE; 870abb0f93cSkardel token = create_string_token(yytext); 871abb0f93cSkardel goto normal_return; 872abb0f93cSkardel } 873abb0f93cSkardel } 874abb0f93cSkardel 875abb0f93cSkardel /* 876abb0f93cSkardel * Either followedby is not FOLLBY_TOKEN or this lexeme is part 877abb0f93cSkardel * of a string. Hence, we need to return T_String. 878abb0f93cSkardel * 879abb0f93cSkardel * _Except_ we might have a -4 or -6 flag on a an association 880abb0f93cSkardel * configuration line (server, peer, pool, etc.). 881abb0f93cSkardel * 882abb0f93cSkardel * This is a terrible hack, but the grammar is ambiguous so we 883abb0f93cSkardel * don't have a choice. [SK] 884abb0f93cSkardel * 885abb0f93cSkardel * The ambiguity is in the keyword scanner, not ntp_parser.y. 886abb0f93cSkardel * We do not require server addresses be quoted in ntp.conf, 887abb0f93cSkardel * complicating the scanner's job. To avoid trying (and 888abb0f93cSkardel * failing) to match an IP address or DNS name to a keyword, 889abb0f93cSkardel * the association keywords use FOLLBY_STRING in the keyword 890abb0f93cSkardel * table, which tells the scanner to force the next token to be 891abb0f93cSkardel * a T_String, so it does not try to match a keyword but rather 892abb0f93cSkardel * expects a string when -4/-6 modifiers to server, peer, etc. 893abb0f93cSkardel * are encountered. 894abb0f93cSkardel * restrict -4 and restrict -6 parsing works correctly without 895abb0f93cSkardel * this hack, as restrict uses FOLLBY_TOKEN. [DH] 896abb0f93cSkardel */ 897abb0f93cSkardel if ('-' == yytext[0]) { 898abb0f93cSkardel if ('4' == yytext[1]) { 899abb0f93cSkardel token = T_Ipv4_flag; 900abb0f93cSkardel goto normal_return; 901abb0f93cSkardel } else if ('6' == yytext[1]) { 902abb0f93cSkardel token = T_Ipv6_flag; 903abb0f93cSkardel goto normal_return; 904abb0f93cSkardel } 905abb0f93cSkardel } 906abb0f93cSkardel 907abb0f93cSkardel if (FOLLBY_STRING == followedby) 908abb0f93cSkardel followedby = FOLLBY_TOKEN; 909abb0f93cSkardel 9102950cc38Schristos yylval_was_set = TRUE; 911abb0f93cSkardel token = create_string_token(yytext); 912abb0f93cSkardel 913abb0f93cSkardel normal_return: 914abb0f93cSkardel if (T_EOC == token) 915*eabc0478Schristos DPRINTF(10, ("\t<end of command>\n")); 916abb0f93cSkardel else 917*eabc0478Schristos DPRINTF(10, ("yylex: lexeme '%s' -> %s\n", yytext, 918abb0f93cSkardel token_name(token))); 919abb0f93cSkardel 920abb0f93cSkardel if (!yylval_was_set) 921abb0f93cSkardel yylval.Integer = token; 922abb0f93cSkardel 923abb0f93cSkardel return token; 924abb0f93cSkardel 925abb0f93cSkardel lex_too_long: 926*eabc0478Schristos /* 927*eabc0478Schristos * DLH: What is the purpose of the limit of 50? 928*eabc0478Schristos * Is there any reason for yytext[] to be bigger? 929*eabc0478Schristos */ 930abb0f93cSkardel yytext[min(sizeof(yytext) - 1, 50)] = 0; 931abb0f93cSkardel msyslog(LOG_ERR, 9322950cc38Schristos "configuration item on line %d longer than limit of %lu, began with '%s'", 9335d681e99Schristos lex_stack->curpos.nline, (u_long)min(sizeof(yytext) - 1, 50), 9342950cc38Schristos yytext); 935abb0f93cSkardel 936abb0f93cSkardel /* 937abb0f93cSkardel * If we hit the length limit reading the startup configuration 938abb0f93cSkardel * file, abort. 939abb0f93cSkardel */ 9405d681e99Schristos if (lex_from_file()) 941abb0f93cSkardel exit(sizeof(yytext) - 1); 942abb0f93cSkardel 943abb0f93cSkardel /* 944abb0f93cSkardel * If it's runtime configuration via ntpq :config treat it as 945abb0f93cSkardel * if the configuration text ended before the too-long lexeme, 946abb0f93cSkardel * hostname, or string. 947abb0f93cSkardel */ 948abb0f93cSkardel yylval.Integer = 0; 949abb0f93cSkardel return 0; 950abb0f93cSkardel } 951