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 41285169Scystatic 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; 76358659Scy static char sbuf[64]; 77258945Sroberto 78258945Sroberto i = token - LOWEST_KEYWORD_ID; 79258945Sroberto 80358659Scy switch (token) { 81358659Scy case T_ServerresponseFuzz: 82358659Scy text = "serverresponse fuzz"; 83358659Scy break; 84258945Sroberto 85358659Scy default: 86358659Scy if (i < COUNTOF(keyword_text)) { 87358659Scy text = keyword_text[i]; 88358659Scy } else { 89358659Scy snprintf(sbuf, sizeof sbuf, 90358659Scy "(keyword #%u not found)", token); 91358659Scy text = sbuf; 92358659Scy } 93358659Scy } 94358659Scy 95358659Scy return text; 96258945Sroberto} 97258945Sroberto 98258945Sroberto 99285169Scy/* FILE & STRING BUFFER INTERFACE 100285169Scy * ------------------------------ 101285169Scy * 102285169Scy * This set out as a couple of wrapper functions around the standard C 103285169Scy * fgetc and ungetc functions in order to include positional 104285169Scy * bookkeeping. Alas, this is no longer a good solution with nested 105285169Scy * input files and the possibility to send configuration commands via 106285169Scy * 'ntpdc' and 'ntpq'. 107285169Scy * 108285169Scy * Now there are a few functions to maintain a stack of nested input 109285169Scy * sources (though nesting is only allowd for disk files) and from the 110285169Scy * scanner / parser point of view there's no difference between both 111285169Scy * types of sources. 112285169Scy * 113285169Scy * The 'fgetc()' / 'ungetc()' replacements now operate on a FILE_INFO 114285169Scy * structure. Instead of trying different 'ungetc()' strategies for file 115285169Scy * and buffer based parsing, we keep the backup char in our own 116285169Scy * FILE_INFO structure. This is sufficient, as the parser does *not* 117285169Scy * jump around via 'seek' or the like, and there's no need to 118285169Scy * check/clear the backup store in other places than 'lex_getch()'. 119258945Sroberto */ 120258945Sroberto 121285169Scy/* 122285169Scy * Allocate an info structure and attach it to a file. 123285169Scy * 124285169Scy * Note: When 'mode' is NULL, then the INFO block will be set up to 125285169Scy * contain a NULL file pointer, as suited for remote config command 126285169Scy * parsing. Otherwise having a NULL file pointer is considered an error, 127285169Scy * and a NULL info block pointer is returned to indicate failure! 128285169Scy * 129285169Scy * Note: We use a variable-sized structure to hold a copy of the file 130285169Scy * name (or, more proper, the input source description). This is more 131285169Scy * secure than keeping a reference to some other storage that might go 132285169Scy * out of scope. 133285169Scy */ 134285169Scystatic struct FILE_INFO * 135285169Scylex_open( 136258945Sroberto const char *path, 137258945Sroberto const char *mode 138258945Sroberto ) 139258945Sroberto{ 140285169Scy struct FILE_INFO *stream; 141285169Scy size_t nnambuf; 142258945Sroberto 143285169Scy nnambuf = strlen(path); 144285169Scy stream = emalloc_zero(sizeof(*stream) + nnambuf); 145285169Scy stream->curpos.nline = 1; 146285169Scy stream->backch = EOF; 147285169Scy /* copy name with memcpy -- trailing NUL already there! */ 148285169Scy memcpy(stream->fname, path, nnambuf); 149258945Sroberto 150285169Scy if (NULL != mode) { 151285169Scy stream->fpi = fopen(path, mode); 152285169Scy if (NULL == stream->fpi) { 153285169Scy free(stream); 154285169Scy stream = NULL; 155285169Scy } 156258945Sroberto } 157285169Scy return stream; 158258945Sroberto} 159258945Sroberto 160285169Scy/* get next character from buffer or file. This will return any putback 161285169Scy * character first; it will also make sure the last line is at least 162285169Scy * virtually terminated with a '\n'. 163285169Scy */ 164285169Scystatic int 165285169Scylex_getch( 166258945Sroberto struct FILE_INFO *stream 167258945Sroberto ) 168258945Sroberto{ 169280849Scy int ch; 170258945Sroberto 171285169Scy if (NULL == stream || stream->force_eof) 172285169Scy return EOF; 173285169Scy 174285169Scy if (EOF != stream->backch) { 175285169Scy ch = stream->backch; 176285169Scy stream->backch = EOF; 177285169Scy if (stream->fpi) 178285169Scy conf_file_sum += ch; 179330106Sdelphij stream->curpos.ncol++; 180285169Scy } else if (stream->fpi) { 181285169Scy /* fetch next 7-bit ASCII char (or EOF) from file */ 182285169Scy while ((ch = fgetc(stream->fpi)) != EOF && ch > SCHAR_MAX) 183285169Scy stream->curpos.ncol++; 184285169Scy if (EOF != ch) { 185285169Scy conf_file_sum += ch; 186285169Scy stream->curpos.ncol++; 187280849Scy } 188285169Scy } else { 189285169Scy /* fetch next 7-bit ASCII char from buffer */ 190285169Scy const char * scan; 191285169Scy scan = &remote_config.buffer[remote_config.pos]; 192285169Scy while ((ch = (u_char)*scan) > SCHAR_MAX) { 193285169Scy scan++; 194285169Scy stream->curpos.ncol++; 195285169Scy } 196285169Scy if ('\0' != ch) { 197285169Scy scan++; 198285169Scy stream->curpos.ncol++; 199285169Scy } else { 200285169Scy ch = EOF; 201285169Scy } 202285169Scy remote_config.pos = (int)(scan - remote_config.buffer); 203258945Sroberto } 204280849Scy 205285169Scy /* If the last line ends without '\n', generate one. This 206285169Scy * happens most likely on Windows, where editors often have a 207285169Scy * sloppy concept of a line. 208285169Scy */ 209285169Scy if (EOF == ch && stream->curpos.ncol != 0) 210285169Scy ch = '\n'; 211285169Scy 212285169Scy /* update scan position tallies */ 213285169Scy if (ch == '\n') { 214285169Scy stream->bakpos = stream->curpos; 215285169Scy stream->curpos.nline++; 216285169Scy stream->curpos.ncol = 0; 217285169Scy } 218285169Scy 219258945Sroberto return ch; 220258945Sroberto} 221258945Sroberto 222285169Scy/* Note: lex_ungetch will fail to track more than one line of push 223285169Scy * back. But since it guarantees only one char of back storage anyway, 224285169Scy * this should not be a problem. 225258945Sroberto */ 226285169Scystatic int 227285169Scylex_ungetch( 228258945Sroberto int ch, 229258945Sroberto struct FILE_INFO *stream 230258945Sroberto ) 231258945Sroberto{ 232285169Scy /* check preconditions */ 233285169Scy if (NULL == stream || stream->force_eof) 234285169Scy return EOF; 235285169Scy if (EOF != stream->backch || EOF == ch) 236285169Scy return EOF; 237285169Scy 238285169Scy /* keep for later reference and update checksum */ 239285169Scy stream->backch = (u_char)ch; 240285169Scy if (stream->fpi) 241285169Scy conf_file_sum -= stream->backch; 242285169Scy 243285169Scy /* update position */ 244285169Scy if (stream->backch == '\n') { 245285169Scy stream->curpos = stream->bakpos; 246285169Scy stream->bakpos.ncol = -1; 247258945Sroberto } 248285169Scy stream->curpos.ncol--; 249285169Scy return stream->backch; 250258945Sroberto} 251258945Sroberto 252285169Scy/* dispose of an input structure. If the file pointer is not NULL, close 253285169Scy * the file. This function does not check the result of 'fclose()'. 254285169Scy */ 255285169Scystatic void 256285169Scylex_close( 257258945Sroberto struct FILE_INFO *stream 258258945Sroberto ) 259258945Sroberto{ 260285169Scy if (NULL != stream) { 261285169Scy if (NULL != stream->fpi) 262285169Scy fclose(stream->fpi); 263258945Sroberto free(stream); 264285169Scy } 265258945Sroberto} 266258945Sroberto 267285169Scy/* INPUT STACK 268285169Scy * ----------- 269285169Scy * 270285169Scy * Nested input sources are a bit tricky at first glance. We deal with 271285169Scy * this problem using a stack of input sources, that is, a forward 272285169Scy * linked list of FILE_INFO structs. 273285169Scy * 274285169Scy * This stack is never empty during parsing; while an encounter with EOF 275285169Scy * can and will remove nested input sources, removing the last element 276285169Scy * in the stack will not work during parsing, and the EOF condition of 277285169Scy * the outermost input file remains until the parser folds up. 278258945Sroberto */ 279258945Sroberto 280285169Scystatic struct FILE_INFO * 281285169Scy_drop_stack_do( 282285169Scy struct FILE_INFO * head 283258945Sroberto ) 284258945Sroberto{ 285285169Scy struct FILE_INFO * tail; 286285169Scy while (NULL != head) { 287285169Scy tail = head->st_next; 288285169Scy lex_close(head); 289285169Scy head = tail; 290285169Scy } 291285169Scy return head; 292285169Scy} 293258945Sroberto 294285169Scy 295285169Scy 296285169Scy/* Create a singleton input source on an empty lexer stack. This will 297285169Scy * fail if there is already an input source, or if the underlying disk 298285169Scy * file cannot be opened. 299285169Scy * 300285169Scy * Returns TRUE if a new input object was successfully created. 301285169Scy */ 302285169Scyint/*BOOL*/ 303285169Scylex_init_stack( 304285169Scy const char * path, 305285169Scy const char * mode 306285169Scy ) 307285169Scy{ 308285169Scy if (NULL != lex_stack || NULL == path) 309285169Scy return FALSE; 310285169Scy 311285169Scy lex_stack = lex_open(path, mode); 312285169Scy return (NULL != lex_stack); 313285169Scy} 314285169Scy 315285169Scy/* This removes *all* input sources from the stack, leaving the head 316285169Scy * pointer as NULL. Any attempt to parse in that state is likely to bomb 317285169Scy * with segmentation faults or the like. 318285169Scy * 319285169Scy * In other words: Use this to clean up after parsing, and do not parse 320285169Scy * anything until the next 'lex_init_stack()' succeeded. 321285169Scy */ 322285169Scyvoid 323285169Scylex_drop_stack() 324285169Scy{ 325285169Scy lex_stack = _drop_stack_do(lex_stack); 326285169Scy} 327285169Scy 328285169Scy/* Flush the lexer input stack: This will nip all input objects on the 329285169Scy * stack (but keeps the current top-of-stack) and marks the top-of-stack 330285169Scy * as inactive. Any further calls to lex_getch yield only EOF, and it's 331285169Scy * no longer possible to push something back. 332285169Scy * 333285169Scy * Returns TRUE if there is a head element (top-of-stack) that was not 334285169Scy * in the force-eof mode before this call. 335285169Scy */ 336285169Scyint/*BOOL*/ 337285169Scylex_flush_stack() 338285169Scy{ 339285169Scy int retv = FALSE; 340285169Scy 341285169Scy if (NULL != lex_stack) { 342285169Scy retv = !lex_stack->force_eof; 343285169Scy lex_stack->force_eof = TRUE; 344285169Scy lex_stack->st_next = _drop_stack_do( 345285169Scy lex_stack->st_next); 346258945Sroberto } 347285169Scy return retv; 348258945Sroberto} 349258945Sroberto 350285169Scy/* Push another file on the parsing stack. If the mode is NULL, create a 351285169Scy * FILE_INFO suitable for in-memory parsing; otherwise, create a 352285169Scy * FILE_INFO that is bound to a local/disc file. Note that 'path' must 353285169Scy * not be NULL, or the function will fail. 354285169Scy * 355285169Scy * Returns TRUE if a new info record was pushed onto the stack. 356285169Scy */ 357285169Scyint/*BOOL*/ lex_push_file( 358285169Scy const char * path, 359285169Scy const char * mode 360258945Sroberto ) 361258945Sroberto{ 362285169Scy struct FILE_INFO * next = NULL; 363285169Scy 364285169Scy if (NULL != path) { 365285169Scy next = lex_open(path, mode); 366285169Scy if (NULL != next) { 367285169Scy next->st_next = lex_stack; 368285169Scy lex_stack = next; 369258945Sroberto } 370285169Scy } 371285169Scy return (NULL != next); 372285169Scy} 373258945Sroberto 374285169Scy/* Pop, close & free the top of the include stack, unless the stack 375285169Scy * contains only a singleton input object. In that case the function 376285169Scy * fails, because the parser does not expect the input stack to be 377285169Scy * empty. 378285169Scy * 379285169Scy * Returns TRUE if an object was successfuly popped from the stack. 380285169Scy */ 381285169Scyint/*BOOL*/ 382285169Scylex_pop_file(void) 383285169Scy{ 384285169Scy struct FILE_INFO * head = lex_stack; 385285169Scy struct FILE_INFO * tail = NULL; 386285169Scy 387285169Scy if (NULL != head) { 388285169Scy tail = head->st_next; 389285169Scy if (NULL != tail) { 390285169Scy lex_stack = tail; 391285169Scy lex_close(head); 392285169Scy } 393258945Sroberto } 394285169Scy return (NULL != tail); 395258945Sroberto} 396258945Sroberto 397285169Scy/* Get include nesting level. This currently loops over the stack and 398285169Scy * counts elements; but since this is of concern only with an include 399285169Scy * statement and the nesting depth has a small limit, there's no 400285169Scy * bottleneck expected here. 401285169Scy * 402285169Scy * Returns the nesting level of includes, that is, the current depth of 403285169Scy * the lexer input stack. 404285169Scy * 405285169Scy * Note: 406285169Scy */ 407285169Scysize_t 408285169Scylex_level(void) 409285169Scy{ 410285169Scy size_t cnt = 0; 411285169Scy struct FILE_INFO *ipf = lex_stack; 412258945Sroberto 413285169Scy while (NULL != ipf) { 414285169Scy cnt++; 415285169Scy ipf = ipf->st_next; 416285169Scy } 417285169Scy return cnt; 418285169Scy} 419285169Scy 420285169Scy/* check if the current input is from a file */ 421285169Scyint/*BOOL*/ 422285169Scylex_from_file(void) 423285169Scy{ 424285169Scy return (NULL != lex_stack) && (NULL != lex_stack->fpi); 425285169Scy} 426285169Scy 427285169Scystruct FILE_INFO * 428285169Scylex_current() 429285169Scy{ 430285169Scy /* this became so simple, it could be a macro. But then, 431285169Scy * lex_stack needed to be global... 432285169Scy */ 433285169Scy return lex_stack; 434285169Scy} 435285169Scy 436285169Scy 437258945Sroberto/* STATE MACHINES 438258945Sroberto * -------------- 439258945Sroberto */ 440258945Sroberto 441258945Sroberto/* Keywords */ 442258945Srobertostatic int 443258945Srobertois_keyword( 444258945Sroberto char *lexeme, 445258945Sroberto follby *pfollowedby 446258945Sroberto ) 447258945Sroberto{ 448258945Sroberto follby fb; 449258945Sroberto int curr_s; /* current state index */ 450258945Sroberto int token; 451258945Sroberto int i; 452258945Sroberto 453258945Sroberto curr_s = SCANNER_INIT_S; 454258945Sroberto token = 0; 455258945Sroberto 456258945Sroberto for (i = 0; lexeme[i]; i++) { 457258945Sroberto while (curr_s && (lexeme[i] != SS_CH(sst[curr_s]))) 458258945Sroberto curr_s = SS_OTHER_N(sst[curr_s]); 459258945Sroberto 460258945Sroberto if (curr_s && (lexeme[i] == SS_CH(sst[curr_s]))) { 461258945Sroberto if ('\0' == lexeme[i + 1] 462258945Sroberto && FOLLBY_NON_ACCEPTING 463258945Sroberto != SS_FB(sst[curr_s])) { 464258945Sroberto fb = SS_FB(sst[curr_s]); 465258945Sroberto *pfollowedby = fb; 466258945Sroberto token = curr_s; 467258945Sroberto break; 468258945Sroberto } 469258945Sroberto curr_s = SS_MATCH_N(sst[curr_s]); 470258945Sroberto } else 471258945Sroberto break; 472258945Sroberto } 473258945Sroberto 474258945Sroberto return token; 475258945Sroberto} 476258945Sroberto 477258945Sroberto 478258945Sroberto/* Integer */ 479258945Srobertostatic int 480258945Srobertois_integer( 481258945Sroberto char *lexeme 482258945Sroberto ) 483258945Sroberto{ 484280849Scy int i; 485280849Scy int is_neg; 486280849Scy u_int u_val; 487280849Scy 488280849Scy i = 0; 489258945Sroberto 490258945Sroberto /* Allow a leading minus sign */ 491280849Scy if (lexeme[i] == '-') { 492280849Scy i++; 493280849Scy is_neg = TRUE; 494280849Scy } else { 495280849Scy is_neg = FALSE; 496280849Scy } 497258945Sroberto 498258945Sroberto /* Check that all the remaining characters are digits */ 499280849Scy for (; lexeme[i] != '\0'; i++) { 500285169Scy if (!isdigit((u_char)lexeme[i])) 501280849Scy return FALSE; 502258945Sroberto } 503280849Scy 504280849Scy if (is_neg) 505280849Scy return TRUE; 506280849Scy 507280849Scy /* Reject numbers that fit in unsigned but not in signed int */ 508280849Scy if (1 == sscanf(lexeme, "%u", &u_val)) 509280849Scy return (u_val <= INT_MAX); 510280849Scy else 511280849Scy return FALSE; 512258945Sroberto} 513258945Sroberto 514258945Sroberto 515280849Scy/* U_int -- assumes is_integer() has returned FALSE */ 516280849Scystatic int 517280849Scyis_u_int( 518280849Scy char *lexeme 519280849Scy ) 520280849Scy{ 521280849Scy int i; 522280849Scy int is_hex; 523280849Scy 524280849Scy i = 0; 525285169Scy if ('0' == lexeme[i] && 'x' == tolower((u_char)lexeme[i + 1])) { 526280849Scy i += 2; 527280849Scy is_hex = TRUE; 528280849Scy } else { 529280849Scy is_hex = FALSE; 530280849Scy } 531280849Scy 532280849Scy /* Check that all the remaining characters are digits */ 533280849Scy for (; lexeme[i] != '\0'; i++) { 534285169Scy if (is_hex && !isxdigit((u_char)lexeme[i])) 535280849Scy return FALSE; 536285169Scy if (!is_hex && !isdigit((u_char)lexeme[i])) 537280849Scy return FALSE; 538280849Scy } 539280849Scy 540280849Scy return TRUE; 541280849Scy} 542280849Scy 543280849Scy 544258945Sroberto/* Double */ 545258945Srobertostatic int 546258945Srobertois_double( 547258945Sroberto char *lexeme 548258945Sroberto ) 549258945Sroberto{ 550258945Sroberto u_int num_digits = 0; /* Number of digits read */ 551258945Sroberto u_int i; 552258945Sroberto 553258945Sroberto i = 0; 554258945Sroberto 555258945Sroberto /* Check for an optional '+' or '-' */ 556258945Sroberto if ('+' == lexeme[i] || '-' == lexeme[i]) 557258945Sroberto i++; 558258945Sroberto 559258945Sroberto /* Read the integer part */ 560285169Scy for (; lexeme[i] && isdigit((u_char)lexeme[i]); i++) 561258945Sroberto num_digits++; 562258945Sroberto 563280849Scy /* Check for the optional decimal point */ 564280849Scy if ('.' == lexeme[i]) { 565258945Sroberto i++; 566280849Scy /* Check for any digits after the decimal point */ 567285169Scy for (; lexeme[i] && isdigit((u_char)lexeme[i]); i++) 568280849Scy num_digits++; 569280849Scy } 570258945Sroberto 571258945Sroberto /* 572258945Sroberto * The number of digits in both the decimal part and the 573258945Sroberto * fraction part must not be zero at this point 574258945Sroberto */ 575258945Sroberto if (!num_digits) 576258945Sroberto return 0; 577258945Sroberto 578258945Sroberto /* Check if we are done */ 579258945Sroberto if (!lexeme[i]) 580258945Sroberto return 1; 581258945Sroberto 582258945Sroberto /* There is still more input, read the exponent */ 583285169Scy if ('e' == tolower((u_char)lexeme[i])) 584258945Sroberto i++; 585258945Sroberto else 586258945Sroberto return 0; 587258945Sroberto 588258945Sroberto /* Read an optional Sign */ 589258945Sroberto if ('+' == lexeme[i] || '-' == lexeme[i]) 590258945Sroberto i++; 591258945Sroberto 592258945Sroberto /* Now read the exponent part */ 593285169Scy while (lexeme[i] && isdigit((u_char)lexeme[i])) 594258945Sroberto i++; 595258945Sroberto 596258945Sroberto /* Check if we are done */ 597258945Sroberto if (!lexeme[i]) 598258945Sroberto return 1; 599258945Sroberto else 600258945Sroberto return 0; 601258945Sroberto} 602258945Sroberto 603258945Sroberto 604258945Sroberto/* is_special() - Test whether a character is a token */ 605258945Srobertostatic inline int 606258945Srobertois_special( 607258945Sroberto int ch 608258945Sroberto ) 609258945Sroberto{ 610280849Scy return strchr(special_chars, ch) != NULL; 611258945Sroberto} 612258945Sroberto 613258945Sroberto 614258945Srobertostatic int 615258945Srobertois_EOC( 616258945Sroberto int ch 617258945Sroberto ) 618258945Sroberto{ 619258945Sroberto if ((old_config_style && (ch == '\n')) || 620258945Sroberto (!old_config_style && (ch == ';'))) 621258945Sroberto return 1; 622258945Sroberto return 0; 623258945Sroberto} 624258945Sroberto 625258945Sroberto 626258945Srobertochar * 627258945Srobertoquote_if_needed(char *str) 628258945Sroberto{ 629258945Sroberto char *ret; 630258945Sroberto size_t len; 631258945Sroberto size_t octets; 632258945Sroberto 633258945Sroberto len = strlen(str); 634258945Sroberto octets = len + 2 + 1; 635258945Sroberto ret = emalloc(octets); 636258945Sroberto if ('"' != str[0] 637258945Sroberto && (strcspn(str, special_chars) < len 638258945Sroberto || strchr(str, ' ') != NULL)) { 639258945Sroberto snprintf(ret, octets, "\"%s\"", str); 640258945Sroberto } else 641280849Scy strlcpy(ret, str, octets); 642258945Sroberto 643258945Sroberto return ret; 644258945Sroberto} 645258945Sroberto 646258945Sroberto 647258945Srobertostatic int 648258945Srobertocreate_string_token( 649258945Sroberto char *lexeme 650258945Sroberto ) 651258945Sroberto{ 652258945Sroberto char *pch; 653258945Sroberto 654258945Sroberto /* 655258945Sroberto * ignore end of line whitespace 656258945Sroberto */ 657258945Sroberto pch = lexeme; 658285169Scy while (*pch && isspace((u_char)*pch)) 659258945Sroberto pch++; 660258945Sroberto 661258945Sroberto if (!*pch) { 662258945Sroberto yylval.Integer = T_EOC; 663258945Sroberto return yylval.Integer; 664258945Sroberto } 665258945Sroberto 666258945Sroberto yylval.String = estrdup(lexeme); 667258945Sroberto return T_String; 668258945Sroberto} 669258945Sroberto 670258945Sroberto 671258945Sroberto/* 672258945Sroberto * yylex() - function that does the actual scanning. 673258945Sroberto * Bison expects this function to be called yylex and for it to take no 674258945Sroberto * input and return an int. 675258945Sroberto * Conceptually yylex "returns" yylval as well as the actual return 676258945Sroberto * value representing the token or type. 677258945Sroberto */ 678258945Srobertoint 679285169Scyyylex(void) 680258945Sroberto{ 681280849Scy static follby followedby = FOLLBY_TOKEN; 682294554Sdelphij size_t i; 683280849Scy int instring; 684280849Scy int yylval_was_set; 685280849Scy int converted; 686280849Scy int token; /* The return value */ 687280849Scy int ch; 688258945Sroberto 689280849Scy instring = FALSE; 690280849Scy yylval_was_set = FALSE; 691280849Scy 692258945Sroberto do { 693258945Sroberto /* Ignore whitespace at the beginning */ 694285169Scy while (EOF != (ch = lex_getch(lex_stack)) && 695258945Sroberto isspace(ch) && 696258945Sroberto !is_EOC(ch)) 697285169Scy 698258945Sroberto ; /* Null Statement */ 699258945Sroberto 700258945Sroberto if (EOF == ch) { 701258945Sroberto 702285169Scy if ( ! lex_pop_file()) 703258945Sroberto return 0; 704258945Sroberto token = T_EOC; 705258945Sroberto goto normal_return; 706258945Sroberto 707258945Sroberto } else if (is_EOC(ch)) { 708258945Sroberto 709258945Sroberto /* end FOLLBY_STRINGS_TO_EOC effect */ 710258945Sroberto followedby = FOLLBY_TOKEN; 711258945Sroberto token = T_EOC; 712258945Sroberto goto normal_return; 713258945Sroberto 714258945Sroberto } else if (is_special(ch) && FOLLBY_TOKEN == followedby) { 715258945Sroberto /* special chars are their own token values */ 716258945Sroberto token = ch; 717258945Sroberto /* 718280849Scy * '=' outside simulator configuration implies 719280849Scy * a single string following as in: 720258945Sroberto * setvar Owner = "The Boss" default 721258945Sroberto */ 722280849Scy if ('=' == ch && old_config_style) 723258945Sroberto followedby = FOLLBY_STRING; 724258945Sroberto yytext[0] = (char)ch; 725258945Sroberto yytext[1] = '\0'; 726258945Sroberto goto normal_return; 727258945Sroberto } else 728285169Scy lex_ungetch(ch, lex_stack); 729258945Sroberto 730258945Sroberto /* save the position of start of the token */ 731285169Scy lex_stack->tokpos = lex_stack->curpos; 732258945Sroberto 733258945Sroberto /* Read in the lexeme */ 734258945Sroberto i = 0; 735285169Scy while (EOF != (ch = lex_getch(lex_stack))) { 736258945Sroberto 737258945Sroberto yytext[i] = (char)ch; 738258945Sroberto 739258945Sroberto /* Break on whitespace or a special character */ 740258945Sroberto if (isspace(ch) || is_EOC(ch) 741258945Sroberto || '"' == ch 742258945Sroberto || (FOLLBY_TOKEN == followedby 743258945Sroberto && is_special(ch))) 744258945Sroberto break; 745258945Sroberto 746258945Sroberto /* Read the rest of the line on reading a start 747258945Sroberto of comment character */ 748258945Sroberto if ('#' == ch) { 749285169Scy while (EOF != (ch = lex_getch(lex_stack)) 750258945Sroberto && '\n' != ch) 751258945Sroberto ; /* Null Statement */ 752258945Sroberto break; 753258945Sroberto } 754258945Sroberto 755258945Sroberto i++; 756258945Sroberto if (i >= COUNTOF(yytext)) 757258945Sroberto goto lex_too_long; 758258945Sroberto } 759258945Sroberto /* Pick up all of the string inside between " marks, to 760258945Sroberto * end of line. If we make it to EOL without a 761258945Sroberto * terminating " assume it for them. 762258945Sroberto * 763258945Sroberto * XXX - HMS: I'm not sure we want to assume the closing " 764258945Sroberto */ 765258945Sroberto if ('"' == ch) { 766280849Scy instring = TRUE; 767285169Scy while (EOF != (ch = lex_getch(lex_stack)) && 768258945Sroberto ch != '"' && ch != '\n') { 769258945Sroberto yytext[i++] = (char)ch; 770258945Sroberto if (i >= COUNTOF(yytext)) 771258945Sroberto goto lex_too_long; 772258945Sroberto } 773258945Sroberto /* 774258945Sroberto * yytext[i] will be pushed back as not part of 775258945Sroberto * this lexeme, but any closing quote should 776258945Sroberto * not be pushed back, so we read another char. 777258945Sroberto */ 778258945Sroberto if ('"' == ch) 779285169Scy ch = lex_getch(lex_stack); 780258945Sroberto } 781258945Sroberto /* Pushback the last character read that is not a part 782285169Scy * of this lexeme. This fails silently if ch is EOF, 783285169Scy * but then the EOF condition persists and is handled on 784285169Scy * the next turn by the include stack mechanism. 785258945Sroberto */ 786285169Scy lex_ungetch(ch, lex_stack); 787285169Scy 788258945Sroberto yytext[i] = '\0'; 789258945Sroberto } while (i == 0); 790258945Sroberto 791258945Sroberto /* Now return the desired token */ 792258945Sroberto 793258945Sroberto /* First make sure that the parser is *not* expecting a string 794258945Sroberto * as the next token (based on the previous token that was 795258945Sroberto * returned) and that we haven't read a string. 796258945Sroberto */ 797258945Sroberto 798258945Sroberto if (followedby == FOLLBY_TOKEN && !instring) { 799258945Sroberto token = is_keyword(yytext, &followedby); 800280849Scy if (token) { 801280849Scy /* 802280849Scy * T_Server is exceptional as it forces the 803280849Scy * following token to be a string in the 804280849Scy * non-simulator parts of the configuration, 805280849Scy * but in the simulator configuration section, 806280849Scy * "server" is followed by "=" which must be 807280849Scy * recognized as a token not a string. 808280849Scy */ 809280849Scy if (T_Server == token && !old_config_style) 810280849Scy followedby = FOLLBY_TOKEN; 811258945Sroberto goto normal_return; 812280849Scy } else if (is_integer(yytext)) { 813280849Scy yylval_was_set = TRUE; 814258945Sroberto errno = 0; 815258945Sroberto if ((yylval.Integer = strtol(yytext, NULL, 10)) == 0 816258945Sroberto && ((errno == EINVAL) || (errno == ERANGE))) { 817258945Sroberto msyslog(LOG_ERR, 818258945Sroberto "Integer cannot be represented: %s", 819258945Sroberto yytext); 820285169Scy if (lex_from_file()) { 821280849Scy exit(1); 822280849Scy } else { 823280849Scy /* force end of parsing */ 824280849Scy yylval.Integer = 0; 825280849Scy return 0; 826280849Scy } 827258945Sroberto } 828280849Scy token = T_Integer; 829280849Scy goto normal_return; 830280849Scy } else if (is_u_int(yytext)) { 831280849Scy yylval_was_set = TRUE; 832280849Scy if ('0' == yytext[0] && 833285169Scy 'x' == tolower((unsigned long)yytext[1])) 834280849Scy converted = sscanf(&yytext[2], "%x", 835280849Scy &yylval.U_int); 836280849Scy else 837280849Scy converted = sscanf(yytext, "%u", 838280849Scy &yylval.U_int); 839280849Scy if (1 != converted) { 840280849Scy msyslog(LOG_ERR, 841280849Scy "U_int cannot be represented: %s", 842280849Scy yytext); 843285169Scy if (lex_from_file()) { 844280849Scy exit(1); 845280849Scy } else { 846280849Scy /* force end of parsing */ 847280849Scy yylval.Integer = 0; 848280849Scy return 0; 849280849Scy } 850280849Scy } 851280849Scy token = T_U_int; 852280849Scy goto normal_return; 853280849Scy } else if (is_double(yytext)) { 854280849Scy yylval_was_set = TRUE; 855258945Sroberto errno = 0; 856258945Sroberto if ((yylval.Double = atof(yytext)) == 0 && errno == ERANGE) { 857258945Sroberto msyslog(LOG_ERR, 858258945Sroberto "Double too large to represent: %s", 859258945Sroberto yytext); 860258945Sroberto exit(1); 861258945Sroberto } else { 862258945Sroberto token = T_Double; 863258945Sroberto goto normal_return; 864258945Sroberto } 865258945Sroberto } else { 866258945Sroberto /* Default: Everything is a string */ 867280849Scy yylval_was_set = TRUE; 868258945Sroberto token = create_string_token(yytext); 869258945Sroberto goto normal_return; 870258945Sroberto } 871258945Sroberto } 872258945Sroberto 873258945Sroberto /* 874258945Sroberto * Either followedby is not FOLLBY_TOKEN or this lexeme is part 875258945Sroberto * of a string. Hence, we need to return T_String. 876258945Sroberto * 877258945Sroberto * _Except_ we might have a -4 or -6 flag on a an association 878258945Sroberto * configuration line (server, peer, pool, etc.). 879258945Sroberto * 880258945Sroberto * This is a terrible hack, but the grammar is ambiguous so we 881258945Sroberto * don't have a choice. [SK] 882258945Sroberto * 883258945Sroberto * The ambiguity is in the keyword scanner, not ntp_parser.y. 884258945Sroberto * We do not require server addresses be quoted in ntp.conf, 885258945Sroberto * complicating the scanner's job. To avoid trying (and 886258945Sroberto * failing) to match an IP address or DNS name to a keyword, 887258945Sroberto * the association keywords use FOLLBY_STRING in the keyword 888258945Sroberto * table, which tells the scanner to force the next token to be 889258945Sroberto * a T_String, so it does not try to match a keyword but rather 890258945Sroberto * expects a string when -4/-6 modifiers to server, peer, etc. 891258945Sroberto * are encountered. 892258945Sroberto * restrict -4 and restrict -6 parsing works correctly without 893258945Sroberto * this hack, as restrict uses FOLLBY_TOKEN. [DH] 894258945Sroberto */ 895258945Sroberto if ('-' == yytext[0]) { 896258945Sroberto if ('4' == yytext[1]) { 897258945Sroberto token = T_Ipv4_flag; 898258945Sroberto goto normal_return; 899258945Sroberto } else if ('6' == yytext[1]) { 900258945Sroberto token = T_Ipv6_flag; 901258945Sroberto goto normal_return; 902258945Sroberto } 903258945Sroberto } 904258945Sroberto 905258945Sroberto if (FOLLBY_STRING == followedby) 906258945Sroberto followedby = FOLLBY_TOKEN; 907258945Sroberto 908280849Scy yylval_was_set = TRUE; 909258945Sroberto token = create_string_token(yytext); 910258945Sroberto 911258945Srobertonormal_return: 912258945Sroberto if (T_EOC == token) 913258945Sroberto DPRINTF(4,("\t<end of command>\n")); 914258945Sroberto else 915258945Sroberto DPRINTF(4, ("yylex: lexeme '%s' -> %s\n", yytext, 916258945Sroberto token_name(token))); 917258945Sroberto 918258945Sroberto if (!yylval_was_set) 919258945Sroberto yylval.Integer = token; 920258945Sroberto 921258945Sroberto return token; 922258945Sroberto 923258945Srobertolex_too_long: 924258945Sroberto yytext[min(sizeof(yytext) - 1, 50)] = 0; 925258945Sroberto msyslog(LOG_ERR, 926258945Sroberto "configuration item on line %d longer than limit of %lu, began with '%s'", 927285169Scy lex_stack->curpos.nline, (u_long)min(sizeof(yytext) - 1, 50), 928280849Scy yytext); 929258945Sroberto 930258945Sroberto /* 931258945Sroberto * If we hit the length limit reading the startup configuration 932258945Sroberto * file, abort. 933258945Sroberto */ 934285169Scy if (lex_from_file()) 935258945Sroberto exit(sizeof(yytext) - 1); 936258945Sroberto 937258945Sroberto /* 938258945Sroberto * If it's runtime configuration via ntpq :config treat it as 939258945Sroberto * if the configuration text ended before the too-long lexeme, 940258945Sroberto * hostname, or string. 941258945Sroberto */ 942258945Sroberto yylval.Integer = 0; 943258945Sroberto return 0; 944258945Sroberto} 945