1258945Sroberto 2258945Sroberto/* ntp_scanner.c 3258945Sroberto * 4258945Sroberto * The source code for a simple lexical analyzer. 5258945Sroberto * 6258945Sroberto * Written By: Sachin Kamboj 7258945Sroberto * University of Delaware 8258945Sroberto * Newark, DE 19711 9258945Sroberto * Copyright (c) 2006 10258945Sroberto */ 11258945Sroberto 12258945Sroberto#ifdef HAVE_CONFIG_H 13258945Sroberto# include <config.h> 14258945Sroberto#endif 15258945Sroberto 16258945Sroberto#include <stdio.h> 17258945Sroberto#include <ctype.h> 18258945Sroberto#include <stdlib.h> 19258945Sroberto#include <errno.h> 20258945Sroberto#include <string.h> 21258945Sroberto 22280849Scy#include "ntpd.h" 23258945Sroberto#include "ntp_config.h" 24258945Sroberto#include "ntpsim.h" 25258945Sroberto#include "ntp_scanner.h" 26258945Sroberto#include "ntp_parser.h" 27258945Sroberto 28258945Sroberto/* ntp_keyword.h declares finite state machine and token text */ 29258945Sroberto#include "ntp_keyword.h" 30258945Sroberto 31258945Sroberto 32258945Sroberto 33258945Sroberto/* SCANNER GLOBAL VARIABLES 34258945Sroberto * ------------------------ 35258945Sroberto */ 36258945Sroberto 37258945Sroberto#define MAX_LEXEME (1024 + 1) /* The maximum size of a lexeme */ 38258945Srobertochar yytext[MAX_LEXEME]; /* Buffer for storing the input text/lexeme */ 39280849Scyu_int32 conf_file_sum; /* Simple sum of characters read */ 40258945Sroberto 41290000Sglebiusstatic struct FILE_INFO * lex_stack = NULL; 42258945Sroberto 43258945Sroberto 44258945Sroberto 45258945Sroberto/* CONSTANTS 46258945Sroberto * --------- 47258945Sroberto */ 48258945Sroberto 49258945Sroberto 50258945Sroberto/* SCANNER GLOBAL VARIABLES 51258945Sroberto * ------------------------ 52258945Sroberto */ 53258945Srobertoconst char special_chars[] = "{}(),;|="; 54258945Sroberto 55258945Sroberto 56258945Sroberto/* FUNCTIONS 57258945Sroberto * --------- 58258945Sroberto */ 59258945Sroberto 60258945Srobertostatic int is_keyword(char *lexeme, follby *pfollowedby); 61258945Sroberto 62258945Sroberto 63258945Sroberto/* 64258945Sroberto * keyword() - Return the keyword associated with token T_ identifier. 65258945Sroberto * See also token_name() for the string-ized T_ identifier. 66258945Sroberto * Example: keyword(T_Server) returns "server" 67258945Sroberto * token_name(T_Server) returns "T_Server" 68258945Sroberto */ 69258945Srobertoconst char * 70258945Srobertokeyword( 71258945Sroberto int token 72258945Sroberto ) 73258945Sroberto{ 74280849Scy size_t i; 75258945Sroberto const char *text; 76258945Sroberto 77258945Sroberto i = token - LOWEST_KEYWORD_ID; 78258945Sroberto 79280849Scy if (i < COUNTOF(keyword_text)) 80258945Sroberto text = keyword_text[i]; 81258945Sroberto else 82258945Sroberto text = NULL; 83258945Sroberto 84258945Sroberto return (text != NULL) 85258945Sroberto ? text 86258945Sroberto : "(keyword not found)"; 87258945Sroberto} 88258945Sroberto 89258945Sroberto 90290000Sglebius/* FILE & STRING BUFFER INTERFACE 91290000Sglebius * ------------------------------ 92290000Sglebius * 93290000Sglebius * This set out as a couple of wrapper functions around the standard C 94290000Sglebius * fgetc and ungetc functions in order to include positional 95290000Sglebius * bookkeeping. Alas, this is no longer a good solution with nested 96290000Sglebius * input files and the possibility to send configuration commands via 97290000Sglebius * 'ntpdc' and 'ntpq'. 98290000Sglebius * 99290000Sglebius * Now there are a few functions to maintain a stack of nested input 100290000Sglebius * sources (though nesting is only allowd for disk files) and from the 101290000Sglebius * scanner / parser point of view there's no difference between both 102290000Sglebius * types of sources. 103290000Sglebius * 104290000Sglebius * The 'fgetc()' / 'ungetc()' replacements now operate on a FILE_INFO 105290000Sglebius * structure. Instead of trying different 'ungetc()' strategies for file 106290000Sglebius * and buffer based parsing, we keep the backup char in our own 107290000Sglebius * FILE_INFO structure. This is sufficient, as the parser does *not* 108290000Sglebius * jump around via 'seek' or the like, and there's no need to 109290000Sglebius * check/clear the backup store in other places than 'lex_getch()'. 110258945Sroberto */ 111258945Sroberto 112290000Sglebius/* 113290000Sglebius * Allocate an info structure and attach it to a file. 114290000Sglebius * 115290000Sglebius * Note: When 'mode' is NULL, then the INFO block will be set up to 116290000Sglebius * contain a NULL file pointer, as suited for remote config command 117290000Sglebius * parsing. Otherwise having a NULL file pointer is considered an error, 118290000Sglebius * and a NULL info block pointer is returned to indicate failure! 119290000Sglebius * 120290000Sglebius * Note: We use a variable-sized structure to hold a copy of the file 121290000Sglebius * name (or, more proper, the input source description). This is more 122290000Sglebius * secure than keeping a reference to some other storage that might go 123290000Sglebius * out of scope. 124290000Sglebius */ 125290000Sglebiusstatic struct FILE_INFO * 126290000Sglebiuslex_open( 127258945Sroberto const char *path, 128258945Sroberto const char *mode 129258945Sroberto ) 130258945Sroberto{ 131290000Sglebius struct FILE_INFO *stream; 132290000Sglebius size_t nnambuf; 133258945Sroberto 134290000Sglebius nnambuf = strlen(path); 135290000Sglebius stream = emalloc_zero(sizeof(*stream) + nnambuf); 136290000Sglebius stream->curpos.nline = 1; 137290000Sglebius stream->backch = EOF; 138290000Sglebius /* copy name with memcpy -- trailing NUL already there! */ 139290000Sglebius memcpy(stream->fname, path, nnambuf); 140258945Sroberto 141290000Sglebius if (NULL != mode) { 142290000Sglebius stream->fpi = fopen(path, mode); 143290000Sglebius if (NULL == stream->fpi) { 144290000Sglebius free(stream); 145290000Sglebius stream = NULL; 146290000Sglebius } 147258945Sroberto } 148290000Sglebius return stream; 149258945Sroberto} 150258945Sroberto 151290000Sglebius/* get next character from buffer or file. This will return any putback 152290000Sglebius * character first; it will also make sure the last line is at least 153290000Sglebius * virtually terminated with a '\n'. 154290000Sglebius */ 155290000Sglebiusstatic int 156290000Sglebiuslex_getch( 157258945Sroberto struct FILE_INFO *stream 158258945Sroberto ) 159258945Sroberto{ 160280849Scy int ch; 161258945Sroberto 162290000Sglebius if (NULL == stream || stream->force_eof) 163290000Sglebius return EOF; 164290000Sglebius 165290000Sglebius if (EOF != stream->backch) { 166290000Sglebius ch = stream->backch; 167290000Sglebius stream->backch = EOF; 168290000Sglebius if (stream->fpi) 169290000Sglebius conf_file_sum += ch; 170290000Sglebius } else if (stream->fpi) { 171290000Sglebius /* fetch next 7-bit ASCII char (or EOF) from file */ 172290000Sglebius while ((ch = fgetc(stream->fpi)) != EOF && ch > SCHAR_MAX) 173290000Sglebius stream->curpos.ncol++; 174290000Sglebius if (EOF != ch) { 175290000Sglebius conf_file_sum += ch; 176290000Sglebius stream->curpos.ncol++; 177280849Scy } 178290000Sglebius } else { 179290000Sglebius /* fetch next 7-bit ASCII char from buffer */ 180290000Sglebius const char * scan; 181290000Sglebius scan = &remote_config.buffer[remote_config.pos]; 182290000Sglebius while ((ch = (u_char)*scan) > SCHAR_MAX) { 183290000Sglebius scan++; 184290000Sglebius stream->curpos.ncol++; 185290000Sglebius } 186290000Sglebius if ('\0' != ch) { 187290000Sglebius scan++; 188290000Sglebius stream->curpos.ncol++; 189290000Sglebius } else { 190290000Sglebius ch = EOF; 191290000Sglebius } 192290000Sglebius remote_config.pos = (int)(scan - remote_config.buffer); 193258945Sroberto } 194280849Scy 195290000Sglebius /* If the last line ends without '\n', generate one. This 196290000Sglebius * happens most likely on Windows, where editors often have a 197290000Sglebius * sloppy concept of a line. 198290000Sglebius */ 199290000Sglebius if (EOF == ch && stream->curpos.ncol != 0) 200290000Sglebius ch = '\n'; 201290000Sglebius 202290000Sglebius /* update scan position tallies */ 203290000Sglebius if (ch == '\n') { 204290000Sglebius stream->bakpos = stream->curpos; 205290000Sglebius stream->curpos.nline++; 206290000Sglebius stream->curpos.ncol = 0; 207290000Sglebius } 208290000Sglebius 209258945Sroberto return ch; 210258945Sroberto} 211258945Sroberto 212290000Sglebius/* Note: lex_ungetch will fail to track more than one line of push 213290000Sglebius * back. But since it guarantees only one char of back storage anyway, 214290000Sglebius * this should not be a problem. 215258945Sroberto */ 216290000Sglebiusstatic int 217290000Sglebiuslex_ungetch( 218258945Sroberto int ch, 219258945Sroberto struct FILE_INFO *stream 220258945Sroberto ) 221258945Sroberto{ 222290000Sglebius /* check preconditions */ 223290000Sglebius if (NULL == stream || stream->force_eof) 224290000Sglebius return EOF; 225290000Sglebius if (EOF != stream->backch || EOF == ch) 226290000Sglebius return EOF; 227290000Sglebius 228290000Sglebius /* keep for later reference and update checksum */ 229290000Sglebius stream->backch = (u_char)ch; 230290000Sglebius if (stream->fpi) 231290000Sglebius conf_file_sum -= stream->backch; 232290000Sglebius 233290000Sglebius /* update position */ 234290000Sglebius if (stream->backch == '\n') { 235290000Sglebius stream->curpos = stream->bakpos; 236290000Sglebius stream->bakpos.ncol = -1; 237258945Sroberto } 238290000Sglebius stream->curpos.ncol--; 239290000Sglebius return stream->backch; 240258945Sroberto} 241258945Sroberto 242290000Sglebius/* dispose of an input structure. If the file pointer is not NULL, close 243290000Sglebius * the file. This function does not check the result of 'fclose()'. 244290000Sglebius */ 245290000Sglebiusstatic void 246290000Sglebiuslex_close( 247258945Sroberto struct FILE_INFO *stream 248258945Sroberto ) 249258945Sroberto{ 250290000Sglebius if (NULL != stream) { 251290000Sglebius if (NULL != stream->fpi) 252290000Sglebius fclose(stream->fpi); 253258945Sroberto free(stream); 254290000Sglebius } 255258945Sroberto} 256258945Sroberto 257290000Sglebius/* INPUT STACK 258290000Sglebius * ----------- 259290000Sglebius * 260290000Sglebius * Nested input sources are a bit tricky at first glance. We deal with 261290000Sglebius * this problem using a stack of input sources, that is, a forward 262290000Sglebius * linked list of FILE_INFO structs. 263290000Sglebius * 264290000Sglebius * This stack is never empty during parsing; while an encounter with EOF 265290000Sglebius * can and will remove nested input sources, removing the last element 266290000Sglebius * in the stack will not work during parsing, and the EOF condition of 267290000Sglebius * the outermost input file remains until the parser folds up. 268258945Sroberto */ 269258945Sroberto 270290000Sglebiusstatic struct FILE_INFO * 271290000Sglebius_drop_stack_do( 272290000Sglebius struct FILE_INFO * head 273258945Sroberto ) 274258945Sroberto{ 275290000Sglebius struct FILE_INFO * tail; 276290000Sglebius while (NULL != head) { 277290000Sglebius tail = head->st_next; 278290000Sglebius lex_close(head); 279290000Sglebius head = tail; 280290000Sglebius } 281290000Sglebius return head; 282290000Sglebius} 283258945Sroberto 284290000Sglebius 285290000Sglebius 286290000Sglebius/* Create a singleton input source on an empty lexer stack. This will 287290000Sglebius * fail if there is already an input source, or if the underlying disk 288290000Sglebius * file cannot be opened. 289290000Sglebius * 290290000Sglebius * Returns TRUE if a new input object was successfully created. 291290000Sglebius */ 292290000Sglebiusint/*BOOL*/ 293290000Sglebiuslex_init_stack( 294290000Sglebius const char * path, 295290000Sglebius const char * mode 296290000Sglebius ) 297290000Sglebius{ 298290000Sglebius if (NULL != lex_stack || NULL == path) 299290000Sglebius return FALSE; 300290000Sglebius 301290000Sglebius lex_stack = lex_open(path, mode); 302290000Sglebius return (NULL != lex_stack); 303290000Sglebius} 304290000Sglebius 305290000Sglebius/* This removes *all* input sources from the stack, leaving the head 306290000Sglebius * pointer as NULL. Any attempt to parse in that state is likely to bomb 307290000Sglebius * with segmentation faults or the like. 308290000Sglebius * 309290000Sglebius * In other words: Use this to clean up after parsing, and do not parse 310290000Sglebius * anything until the next 'lex_init_stack()' succeeded. 311290000Sglebius */ 312290000Sglebiusvoid 313290000Sglebiuslex_drop_stack() 314290000Sglebius{ 315290000Sglebius lex_stack = _drop_stack_do(lex_stack); 316290000Sglebius} 317290000Sglebius 318290000Sglebius/* Flush the lexer input stack: This will nip all input objects on the 319290000Sglebius * stack (but keeps the current top-of-stack) and marks the top-of-stack 320290000Sglebius * as inactive. Any further calls to lex_getch yield only EOF, and it's 321290000Sglebius * no longer possible to push something back. 322290000Sglebius * 323290000Sglebius * Returns TRUE if there is a head element (top-of-stack) that was not 324290000Sglebius * in the force-eof mode before this call. 325290000Sglebius */ 326290000Sglebiusint/*BOOL*/ 327290000Sglebiuslex_flush_stack() 328290000Sglebius{ 329290000Sglebius int retv = FALSE; 330290000Sglebius 331290000Sglebius if (NULL != lex_stack) { 332290000Sglebius retv = !lex_stack->force_eof; 333290000Sglebius lex_stack->force_eof = TRUE; 334290000Sglebius lex_stack->st_next = _drop_stack_do( 335290000Sglebius lex_stack->st_next); 336258945Sroberto } 337290000Sglebius return retv; 338258945Sroberto} 339258945Sroberto 340290000Sglebius/* Push another file on the parsing stack. If the mode is NULL, create a 341290000Sglebius * FILE_INFO suitable for in-memory parsing; otherwise, create a 342290000Sglebius * FILE_INFO that is bound to a local/disc file. Note that 'path' must 343290000Sglebius * not be NULL, or the function will fail. 344290000Sglebius * 345290000Sglebius * Returns TRUE if a new info record was pushed onto the stack. 346290000Sglebius */ 347290000Sglebiusint/*BOOL*/ lex_push_file( 348290000Sglebius const char * path, 349290000Sglebius const char * mode 350258945Sroberto ) 351258945Sroberto{ 352290000Sglebius struct FILE_INFO * next = NULL; 353290000Sglebius 354290000Sglebius if (NULL != path) { 355290000Sglebius next = lex_open(path, mode); 356290000Sglebius if (NULL != next) { 357290000Sglebius next->st_next = lex_stack; 358290000Sglebius lex_stack = next; 359258945Sroberto } 360290000Sglebius } 361290000Sglebius return (NULL != next); 362290000Sglebius} 363258945Sroberto 364290000Sglebius/* Pop, close & free the top of the include stack, unless the stack 365290000Sglebius * contains only a singleton input object. In that case the function 366290000Sglebius * fails, because the parser does not expect the input stack to be 367290000Sglebius * empty. 368290000Sglebius * 369290000Sglebius * Returns TRUE if an object was successfuly popped from the stack. 370290000Sglebius */ 371290000Sglebiusint/*BOOL*/ 372290000Sglebiuslex_pop_file(void) 373290000Sglebius{ 374290000Sglebius struct FILE_INFO * head = lex_stack; 375290000Sglebius struct FILE_INFO * tail = NULL; 376290000Sglebius 377290000Sglebius if (NULL != head) { 378290000Sglebius tail = head->st_next; 379290000Sglebius if (NULL != tail) { 380290000Sglebius lex_stack = tail; 381290000Sglebius lex_close(head); 382290000Sglebius } 383258945Sroberto } 384290000Sglebius return (NULL != tail); 385258945Sroberto} 386258945Sroberto 387290000Sglebius/* Get include nesting level. This currently loops over the stack and 388290000Sglebius * counts elements; but since this is of concern only with an include 389290000Sglebius * statement and the nesting depth has a small limit, there's no 390290000Sglebius * bottleneck expected here. 391290000Sglebius * 392290000Sglebius * Returns the nesting level of includes, that is, the current depth of 393290000Sglebius * the lexer input stack. 394290000Sglebius * 395290000Sglebius * Note: 396290000Sglebius */ 397290000Sglebiussize_t 398290000Sglebiuslex_level(void) 399290000Sglebius{ 400290000Sglebius size_t cnt = 0; 401290000Sglebius struct FILE_INFO *ipf = lex_stack; 402258945Sroberto 403290000Sglebius while (NULL != ipf) { 404290000Sglebius cnt++; 405290000Sglebius ipf = ipf->st_next; 406290000Sglebius } 407290000Sglebius return cnt; 408290000Sglebius} 409290000Sglebius 410290000Sglebius/* check if the current input is from a file */ 411290000Sglebiusint/*BOOL*/ 412290000Sglebiuslex_from_file(void) 413290000Sglebius{ 414290000Sglebius return (NULL != lex_stack) && (NULL != lex_stack->fpi); 415290000Sglebius} 416290000Sglebius 417290000Sglebiusstruct FILE_INFO * 418290000Sglebiuslex_current() 419290000Sglebius{ 420290000Sglebius /* this became so simple, it could be a macro. But then, 421290000Sglebius * lex_stack needed to be global... 422290000Sglebius */ 423290000Sglebius return lex_stack; 424290000Sglebius} 425290000Sglebius 426290000Sglebius 427258945Sroberto/* STATE MACHINES 428258945Sroberto * -------------- 429258945Sroberto */ 430258945Sroberto 431258945Sroberto/* Keywords */ 432258945Srobertostatic int 433258945Srobertois_keyword( 434258945Sroberto char *lexeme, 435258945Sroberto follby *pfollowedby 436258945Sroberto ) 437258945Sroberto{ 438258945Sroberto follby fb; 439258945Sroberto int curr_s; /* current state index */ 440258945Sroberto int token; 441258945Sroberto int i; 442258945Sroberto 443258945Sroberto curr_s = SCANNER_INIT_S; 444258945Sroberto token = 0; 445258945Sroberto 446258945Sroberto for (i = 0; lexeme[i]; i++) { 447258945Sroberto while (curr_s && (lexeme[i] != SS_CH(sst[curr_s]))) 448258945Sroberto curr_s = SS_OTHER_N(sst[curr_s]); 449258945Sroberto 450258945Sroberto if (curr_s && (lexeme[i] == SS_CH(sst[curr_s]))) { 451258945Sroberto if ('\0' == lexeme[i + 1] 452258945Sroberto && FOLLBY_NON_ACCEPTING 453258945Sroberto != SS_FB(sst[curr_s])) { 454258945Sroberto fb = SS_FB(sst[curr_s]); 455258945Sroberto *pfollowedby = fb; 456258945Sroberto token = curr_s; 457258945Sroberto break; 458258945Sroberto } 459258945Sroberto curr_s = SS_MATCH_N(sst[curr_s]); 460258945Sroberto } else 461258945Sroberto break; 462258945Sroberto } 463258945Sroberto 464258945Sroberto return token; 465258945Sroberto} 466258945Sroberto 467258945Sroberto 468258945Sroberto/* Integer */ 469258945Srobertostatic int 470258945Srobertois_integer( 471258945Sroberto char *lexeme 472258945Sroberto ) 473258945Sroberto{ 474280849Scy int i; 475280849Scy int is_neg; 476280849Scy u_int u_val; 477280849Scy 478280849Scy i = 0; 479258945Sroberto 480258945Sroberto /* Allow a leading minus sign */ 481280849Scy if (lexeme[i] == '-') { 482280849Scy i++; 483280849Scy is_neg = TRUE; 484280849Scy } else { 485280849Scy is_neg = FALSE; 486280849Scy } 487258945Sroberto 488258945Sroberto /* Check that all the remaining characters are digits */ 489280849Scy for (; lexeme[i] != '\0'; i++) { 490290000Sglebius if (!isdigit((u_char)lexeme[i])) 491280849Scy return FALSE; 492258945Sroberto } 493280849Scy 494280849Scy if (is_neg) 495280849Scy return TRUE; 496280849Scy 497280849Scy /* Reject numbers that fit in unsigned but not in signed int */ 498280849Scy if (1 == sscanf(lexeme, "%u", &u_val)) 499280849Scy return (u_val <= INT_MAX); 500280849Scy else 501280849Scy return FALSE; 502258945Sroberto} 503258945Sroberto 504258945Sroberto 505280849Scy/* U_int -- assumes is_integer() has returned FALSE */ 506280849Scystatic int 507280849Scyis_u_int( 508280849Scy char *lexeme 509280849Scy ) 510280849Scy{ 511280849Scy int i; 512280849Scy int is_hex; 513280849Scy 514280849Scy i = 0; 515290000Sglebius if ('0' == lexeme[i] && 'x' == tolower((u_char)lexeme[i + 1])) { 516280849Scy i += 2; 517280849Scy is_hex = TRUE; 518280849Scy } else { 519280849Scy is_hex = FALSE; 520280849Scy } 521280849Scy 522280849Scy /* Check that all the remaining characters are digits */ 523280849Scy for (; lexeme[i] != '\0'; i++) { 524290000Sglebius if (is_hex && !isxdigit((u_char)lexeme[i])) 525280849Scy return FALSE; 526290000Sglebius if (!is_hex && !isdigit((u_char)lexeme[i])) 527280849Scy return FALSE; 528280849Scy } 529280849Scy 530280849Scy return TRUE; 531280849Scy} 532280849Scy 533280849Scy 534258945Sroberto/* Double */ 535258945Srobertostatic int 536258945Srobertois_double( 537258945Sroberto char *lexeme 538258945Sroberto ) 539258945Sroberto{ 540258945Sroberto u_int num_digits = 0; /* Number of digits read */ 541258945Sroberto u_int i; 542258945Sroberto 543258945Sroberto i = 0; 544258945Sroberto 545258945Sroberto /* Check for an optional '+' or '-' */ 546258945Sroberto if ('+' == lexeme[i] || '-' == lexeme[i]) 547258945Sroberto i++; 548258945Sroberto 549258945Sroberto /* Read the integer part */ 550290000Sglebius for (; lexeme[i] && isdigit((u_char)lexeme[i]); i++) 551258945Sroberto num_digits++; 552258945Sroberto 553280849Scy /* Check for the optional decimal point */ 554280849Scy if ('.' == lexeme[i]) { 555258945Sroberto i++; 556280849Scy /* Check for any digits after the decimal point */ 557290000Sglebius for (; lexeme[i] && isdigit((u_char)lexeme[i]); i++) 558280849Scy num_digits++; 559280849Scy } 560258945Sroberto 561258945Sroberto /* 562258945Sroberto * The number of digits in both the decimal part and the 563258945Sroberto * fraction part must not be zero at this point 564258945Sroberto */ 565258945Sroberto if (!num_digits) 566258945Sroberto return 0; 567258945Sroberto 568258945Sroberto /* Check if we are done */ 569258945Sroberto if (!lexeme[i]) 570258945Sroberto return 1; 571258945Sroberto 572258945Sroberto /* There is still more input, read the exponent */ 573290000Sglebius if ('e' == tolower((u_char)lexeme[i])) 574258945Sroberto i++; 575258945Sroberto else 576258945Sroberto return 0; 577258945Sroberto 578258945Sroberto /* Read an optional Sign */ 579258945Sroberto if ('+' == lexeme[i] || '-' == lexeme[i]) 580258945Sroberto i++; 581258945Sroberto 582258945Sroberto /* Now read the exponent part */ 583290000Sglebius while (lexeme[i] && isdigit((u_char)lexeme[i])) 584258945Sroberto i++; 585258945Sroberto 586258945Sroberto /* Check if we are done */ 587258945Sroberto if (!lexeme[i]) 588258945Sroberto return 1; 589258945Sroberto else 590258945Sroberto return 0; 591258945Sroberto} 592258945Sroberto 593258945Sroberto 594258945Sroberto/* is_special() - Test whether a character is a token */ 595258945Srobertostatic inline int 596258945Srobertois_special( 597258945Sroberto int ch 598258945Sroberto ) 599258945Sroberto{ 600280849Scy return strchr(special_chars, ch) != NULL; 601258945Sroberto} 602258945Sroberto 603258945Sroberto 604258945Srobertostatic int 605258945Srobertois_EOC( 606258945Sroberto int ch 607258945Sroberto ) 608258945Sroberto{ 609258945Sroberto if ((old_config_style && (ch == '\n')) || 610258945Sroberto (!old_config_style && (ch == ';'))) 611258945Sroberto return 1; 612258945Sroberto return 0; 613258945Sroberto} 614258945Sroberto 615258945Sroberto 616258945Srobertochar * 617258945Srobertoquote_if_needed(char *str) 618258945Sroberto{ 619258945Sroberto char *ret; 620258945Sroberto size_t len; 621258945Sroberto size_t octets; 622258945Sroberto 623258945Sroberto len = strlen(str); 624258945Sroberto octets = len + 2 + 1; 625258945Sroberto ret = emalloc(octets); 626258945Sroberto if ('"' != str[0] 627258945Sroberto && (strcspn(str, special_chars) < len 628258945Sroberto || strchr(str, ' ') != NULL)) { 629258945Sroberto snprintf(ret, octets, "\"%s\"", str); 630258945Sroberto } else 631280849Scy strlcpy(ret, str, octets); 632258945Sroberto 633258945Sroberto return ret; 634258945Sroberto} 635258945Sroberto 636258945Sroberto 637258945Srobertostatic int 638258945Srobertocreate_string_token( 639258945Sroberto char *lexeme 640258945Sroberto ) 641258945Sroberto{ 642258945Sroberto char *pch; 643258945Sroberto 644258945Sroberto /* 645258945Sroberto * ignore end of line whitespace 646258945Sroberto */ 647258945Sroberto pch = lexeme; 648290000Sglebius while (*pch && isspace((u_char)*pch)) 649258945Sroberto pch++; 650258945Sroberto 651258945Sroberto if (!*pch) { 652258945Sroberto yylval.Integer = T_EOC; 653258945Sroberto return yylval.Integer; 654258945Sroberto } 655258945Sroberto 656258945Sroberto yylval.String = estrdup(lexeme); 657258945Sroberto return T_String; 658258945Sroberto} 659258945Sroberto 660258945Sroberto 661258945Sroberto/* 662258945Sroberto * yylex() - function that does the actual scanning. 663258945Sroberto * Bison expects this function to be called yylex and for it to take no 664258945Sroberto * input and return an int. 665258945Sroberto * Conceptually yylex "returns" yylval as well as the actual return 666258945Sroberto * value representing the token or type. 667258945Sroberto */ 668258945Srobertoint 669290000Sglebiusyylex(void) 670258945Sroberto{ 671280849Scy static follby followedby = FOLLBY_TOKEN; 672294904Sdelphij size_t i; 673280849Scy int instring; 674280849Scy int yylval_was_set; 675280849Scy int converted; 676280849Scy int token; /* The return value */ 677280849Scy int ch; 678258945Sroberto 679280849Scy instring = FALSE; 680280849Scy yylval_was_set = FALSE; 681280849Scy 682258945Sroberto do { 683258945Sroberto /* Ignore whitespace at the beginning */ 684290000Sglebius while (EOF != (ch = lex_getch(lex_stack)) && 685258945Sroberto isspace(ch) && 686258945Sroberto !is_EOC(ch)) 687290000Sglebius 688258945Sroberto ; /* Null Statement */ 689258945Sroberto 690258945Sroberto if (EOF == ch) { 691258945Sroberto 692290000Sglebius if ( ! lex_pop_file()) 693258945Sroberto return 0; 694258945Sroberto token = T_EOC; 695258945Sroberto goto normal_return; 696258945Sroberto 697258945Sroberto } else if (is_EOC(ch)) { 698258945Sroberto 699258945Sroberto /* end FOLLBY_STRINGS_TO_EOC effect */ 700258945Sroberto followedby = FOLLBY_TOKEN; 701258945Sroberto token = T_EOC; 702258945Sroberto goto normal_return; 703258945Sroberto 704258945Sroberto } else if (is_special(ch) && FOLLBY_TOKEN == followedby) { 705258945Sroberto /* special chars are their own token values */ 706258945Sroberto token = ch; 707258945Sroberto /* 708280849Scy * '=' outside simulator configuration implies 709280849Scy * a single string following as in: 710258945Sroberto * setvar Owner = "The Boss" default 711258945Sroberto */ 712280849Scy if ('=' == ch && old_config_style) 713258945Sroberto followedby = FOLLBY_STRING; 714258945Sroberto yytext[0] = (char)ch; 715258945Sroberto yytext[1] = '\0'; 716258945Sroberto goto normal_return; 717258945Sroberto } else 718290000Sglebius lex_ungetch(ch, lex_stack); 719258945Sroberto 720258945Sroberto /* save the position of start of the token */ 721290000Sglebius lex_stack->tokpos = lex_stack->curpos; 722258945Sroberto 723258945Sroberto /* Read in the lexeme */ 724258945Sroberto i = 0; 725290000Sglebius while (EOF != (ch = lex_getch(lex_stack))) { 726258945Sroberto 727258945Sroberto yytext[i] = (char)ch; 728258945Sroberto 729258945Sroberto /* Break on whitespace or a special character */ 730258945Sroberto if (isspace(ch) || is_EOC(ch) 731258945Sroberto || '"' == ch 732258945Sroberto || (FOLLBY_TOKEN == followedby 733258945Sroberto && is_special(ch))) 734258945Sroberto break; 735258945Sroberto 736258945Sroberto /* Read the rest of the line on reading a start 737258945Sroberto of comment character */ 738258945Sroberto if ('#' == ch) { 739290000Sglebius while (EOF != (ch = lex_getch(lex_stack)) 740258945Sroberto && '\n' != ch) 741258945Sroberto ; /* Null Statement */ 742258945Sroberto break; 743258945Sroberto } 744258945Sroberto 745258945Sroberto i++; 746258945Sroberto if (i >= COUNTOF(yytext)) 747258945Sroberto goto lex_too_long; 748258945Sroberto } 749258945Sroberto /* Pick up all of the string inside between " marks, to 750258945Sroberto * end of line. If we make it to EOL without a 751258945Sroberto * terminating " assume it for them. 752258945Sroberto * 753258945Sroberto * XXX - HMS: I'm not sure we want to assume the closing " 754258945Sroberto */ 755258945Sroberto if ('"' == ch) { 756280849Scy instring = TRUE; 757290000Sglebius while (EOF != (ch = lex_getch(lex_stack)) && 758258945Sroberto ch != '"' && ch != '\n') { 759258945Sroberto yytext[i++] = (char)ch; 760258945Sroberto if (i >= COUNTOF(yytext)) 761258945Sroberto goto lex_too_long; 762258945Sroberto } 763258945Sroberto /* 764258945Sroberto * yytext[i] will be pushed back as not part of 765258945Sroberto * this lexeme, but any closing quote should 766258945Sroberto * not be pushed back, so we read another char. 767258945Sroberto */ 768258945Sroberto if ('"' == ch) 769290000Sglebius ch = lex_getch(lex_stack); 770258945Sroberto } 771258945Sroberto /* Pushback the last character read that is not a part 772290000Sglebius * of this lexeme. This fails silently if ch is EOF, 773290000Sglebius * but then the EOF condition persists and is handled on 774290000Sglebius * the next turn by the include stack mechanism. 775258945Sroberto */ 776290000Sglebius lex_ungetch(ch, lex_stack); 777290000Sglebius 778258945Sroberto yytext[i] = '\0'; 779258945Sroberto } while (i == 0); 780258945Sroberto 781258945Sroberto /* Now return the desired token */ 782258945Sroberto 783258945Sroberto /* First make sure that the parser is *not* expecting a string 784258945Sroberto * as the next token (based on the previous token that was 785258945Sroberto * returned) and that we haven't read a string. 786258945Sroberto */ 787258945Sroberto 788258945Sroberto if (followedby == FOLLBY_TOKEN && !instring) { 789258945Sroberto token = is_keyword(yytext, &followedby); 790280849Scy if (token) { 791280849Scy /* 792280849Scy * T_Server is exceptional as it forces the 793280849Scy * following token to be a string in the 794280849Scy * non-simulator parts of the configuration, 795280849Scy * but in the simulator configuration section, 796280849Scy * "server" is followed by "=" which must be 797280849Scy * recognized as a token not a string. 798280849Scy */ 799280849Scy if (T_Server == token && !old_config_style) 800280849Scy followedby = FOLLBY_TOKEN; 801258945Sroberto goto normal_return; 802280849Scy } else if (is_integer(yytext)) { 803280849Scy yylval_was_set = TRUE; 804258945Sroberto errno = 0; 805258945Sroberto if ((yylval.Integer = strtol(yytext, NULL, 10)) == 0 806258945Sroberto && ((errno == EINVAL) || (errno == ERANGE))) { 807258945Sroberto msyslog(LOG_ERR, 808258945Sroberto "Integer cannot be represented: %s", 809258945Sroberto yytext); 810290000Sglebius if (lex_from_file()) { 811280849Scy exit(1); 812280849Scy } else { 813280849Scy /* force end of parsing */ 814280849Scy yylval.Integer = 0; 815280849Scy return 0; 816280849Scy } 817258945Sroberto } 818280849Scy token = T_Integer; 819280849Scy goto normal_return; 820280849Scy } else if (is_u_int(yytext)) { 821280849Scy yylval_was_set = TRUE; 822280849Scy if ('0' == yytext[0] && 823290000Sglebius 'x' == tolower((unsigned long)yytext[1])) 824280849Scy converted = sscanf(&yytext[2], "%x", 825280849Scy &yylval.U_int); 826280849Scy else 827280849Scy converted = sscanf(yytext, "%u", 828280849Scy &yylval.U_int); 829280849Scy if (1 != converted) { 830280849Scy msyslog(LOG_ERR, 831280849Scy "U_int cannot be represented: %s", 832280849Scy yytext); 833290000Sglebius if (lex_from_file()) { 834280849Scy exit(1); 835280849Scy } else { 836280849Scy /* force end of parsing */ 837280849Scy yylval.Integer = 0; 838280849Scy return 0; 839280849Scy } 840280849Scy } 841280849Scy token = T_U_int; 842280849Scy goto normal_return; 843280849Scy } else if (is_double(yytext)) { 844280849Scy yylval_was_set = TRUE; 845258945Sroberto errno = 0; 846258945Sroberto if ((yylval.Double = atof(yytext)) == 0 && errno == ERANGE) { 847258945Sroberto msyslog(LOG_ERR, 848258945Sroberto "Double too large to represent: %s", 849258945Sroberto yytext); 850258945Sroberto exit(1); 851258945Sroberto } else { 852258945Sroberto token = T_Double; 853258945Sroberto goto normal_return; 854258945Sroberto } 855258945Sroberto } else { 856258945Sroberto /* Default: Everything is a string */ 857280849Scy yylval_was_set = TRUE; 858258945Sroberto token = create_string_token(yytext); 859258945Sroberto goto normal_return; 860258945Sroberto } 861258945Sroberto } 862258945Sroberto 863258945Sroberto /* 864258945Sroberto * Either followedby is not FOLLBY_TOKEN or this lexeme is part 865258945Sroberto * of a string. Hence, we need to return T_String. 866258945Sroberto * 867258945Sroberto * _Except_ we might have a -4 or -6 flag on a an association 868258945Sroberto * configuration line (server, peer, pool, etc.). 869258945Sroberto * 870258945Sroberto * This is a terrible hack, but the grammar is ambiguous so we 871258945Sroberto * don't have a choice. [SK] 872258945Sroberto * 873258945Sroberto * The ambiguity is in the keyword scanner, not ntp_parser.y. 874258945Sroberto * We do not require server addresses be quoted in ntp.conf, 875258945Sroberto * complicating the scanner's job. To avoid trying (and 876258945Sroberto * failing) to match an IP address or DNS name to a keyword, 877258945Sroberto * the association keywords use FOLLBY_STRING in the keyword 878258945Sroberto * table, which tells the scanner to force the next token to be 879258945Sroberto * a T_String, so it does not try to match a keyword but rather 880258945Sroberto * expects a string when -4/-6 modifiers to server, peer, etc. 881258945Sroberto * are encountered. 882258945Sroberto * restrict -4 and restrict -6 parsing works correctly without 883258945Sroberto * this hack, as restrict uses FOLLBY_TOKEN. [DH] 884258945Sroberto */ 885258945Sroberto if ('-' == yytext[0]) { 886258945Sroberto if ('4' == yytext[1]) { 887258945Sroberto token = T_Ipv4_flag; 888258945Sroberto goto normal_return; 889258945Sroberto } else if ('6' == yytext[1]) { 890258945Sroberto token = T_Ipv6_flag; 891258945Sroberto goto normal_return; 892258945Sroberto } 893258945Sroberto } 894258945Sroberto 895280849Scy instring = FALSE; 896258945Sroberto if (FOLLBY_STRING == followedby) 897258945Sroberto followedby = FOLLBY_TOKEN; 898258945Sroberto 899280849Scy yylval_was_set = TRUE; 900258945Sroberto token = create_string_token(yytext); 901258945Sroberto 902258945Srobertonormal_return: 903258945Sroberto if (T_EOC == token) 904258945Sroberto DPRINTF(4,("\t<end of command>\n")); 905258945Sroberto else 906258945Sroberto DPRINTF(4, ("yylex: lexeme '%s' -> %s\n", yytext, 907258945Sroberto token_name(token))); 908258945Sroberto 909258945Sroberto if (!yylval_was_set) 910258945Sroberto yylval.Integer = token; 911258945Sroberto 912258945Sroberto return token; 913258945Sroberto 914258945Srobertolex_too_long: 915258945Sroberto yytext[min(sizeof(yytext) - 1, 50)] = 0; 916258945Sroberto msyslog(LOG_ERR, 917258945Sroberto "configuration item on line %d longer than limit of %lu, began with '%s'", 918290000Sglebius lex_stack->curpos.nline, (u_long)min(sizeof(yytext) - 1, 50), 919280849Scy yytext); 920258945Sroberto 921258945Sroberto /* 922258945Sroberto * If we hit the length limit reading the startup configuration 923258945Sroberto * file, abort. 924258945Sroberto */ 925290000Sglebius if (lex_from_file()) 926258945Sroberto exit(sizeof(yytext) - 1); 927258945Sroberto 928258945Sroberto /* 929258945Sroberto * If it's runtime configuration via ntpq :config treat it as 930258945Sroberto * if the configuration text ended before the too-long lexeme, 931258945Sroberto * hostname, or string. 932258945Sroberto */ 933258945Sroberto yylval.Integer = 0; 934258945Sroberto return 0; 935258945Sroberto} 936