Annotation of embedaddon/ntp/ntpd/ntp_scanner.c, revision 1.1
1.1 ! misho 1:
! 2: /* ntp_scanner.c
! 3: *
! 4: * The source code for a simple lexical analyzer.
! 5: *
! 6: * Written By: Sachin Kamboj
! 7: * University of Delaware
! 8: * Newark, DE 19711
! 9: * Copyright (c) 2006
! 10: */
! 11:
! 12: #ifdef HAVE_CONFIG_H
! 13: # include <config.h>
! 14: #endif
! 15:
! 16: #include <stdio.h>
! 17: #include <ctype.h>
! 18: #include <stdlib.h>
! 19: #include <errno.h>
! 20: #include <string.h>
! 21:
! 22: #include "ntp_config.h"
! 23: #include "ntpsim.h"
! 24: #include "ntp_scanner.h"
! 25: #include "ntp_parser.h"
! 26: #include "ntp_debug.h"
! 27:
! 28: /* ntp_keyword.h declares finite state machine and token text */
! 29: #include "ntp_keyword.h"
! 30:
! 31:
! 32:
! 33: /* SCANNER GLOBAL VARIABLES
! 34: * ------------------------
! 35: */
! 36:
! 37: #define MAX_LEXEME (1024 + 1) /* The maximum size of a lexeme */
! 38: char yytext[MAX_LEXEME]; /* Buffer for storing the input text/lexeme */
! 39: extern int input_from_file;
! 40:
! 41:
! 42:
! 43:
! 44: /* CONSTANTS
! 45: * ---------
! 46: */
! 47:
! 48:
! 49: /* SCANNER GLOBAL VARIABLES
! 50: * ------------------------
! 51: */
! 52: const char special_chars[] = "{}(),;|=";
! 53:
! 54:
! 55: /* FUNCTIONS
! 56: * ---------
! 57: */
! 58:
! 59: int get_next_char(void);
! 60: static int is_keyword(char *lexeme, follby *pfollowedby);
! 61:
! 62:
! 63:
! 64: /*
! 65: * keyword() - Return the keyword associated with token T_ identifier.
! 66: * See also token_name() for the string-ized T_ identifier.
! 67: * Example: keyword(T_Server) returns "server"
! 68: * token_name(T_Server) returns "T_Server"
! 69: */
! 70: const char *
! 71: keyword(
! 72: int token
! 73: )
! 74: {
! 75: int i;
! 76: const char *text;
! 77:
! 78: i = token - LOWEST_KEYWORD_ID;
! 79:
! 80: if (i >= 0 && i < COUNTOF(keyword_text))
! 81: text = keyword_text[i];
! 82: else
! 83: text = NULL;
! 84:
! 85: return (text != NULL)
! 86: ? text
! 87: : "(keyword not found)";
! 88: }
! 89:
! 90:
! 91: /* FILE INTERFACE
! 92: * --------------
! 93: * We define a couple of wrapper functions around the standard C fgetc
! 94: * and ungetc functions in order to include positional bookkeeping
! 95: */
! 96:
! 97: struct FILE_INFO *
! 98: F_OPEN(
! 99: const char *path,
! 100: const char *mode
! 101: )
! 102: {
! 103: struct FILE_INFO *my_info;
! 104:
! 105: my_info = emalloc(sizeof *my_info);
! 106:
! 107: my_info->line_no = 1;
! 108: my_info->col_no = 0;
! 109: my_info->prev_line_col_no = 0;
! 110: my_info->prev_token_col_no = 0;
! 111: my_info->fname = path;
! 112:
! 113: my_info->fd = fopen(path, mode);
! 114: if (NULL == my_info->fd) {
! 115: free(my_info);
! 116: return NULL;
! 117: }
! 118: return my_info;
! 119: }
! 120:
! 121: int
! 122: FGETC(
! 123: struct FILE_INFO *stream
! 124: )
! 125: {
! 126: int ch = fgetc(stream->fd);
! 127:
! 128: ++stream->col_no;
! 129: if (ch == '\n') {
! 130: stream->prev_line_col_no = stream->col_no;
! 131: ++stream->line_no;
! 132: stream->col_no = 1;
! 133: }
! 134: return ch;
! 135: }
! 136:
! 137: /* BUGS: 1. Function will fail on more than one line of pushback
! 138: * 2. No error checking is done to see if ungetc fails
! 139: * SK: I don't think its worth fixing these bugs for our purposes ;-)
! 140: */
! 141: int
! 142: UNGETC(
! 143: int ch,
! 144: struct FILE_INFO *stream
! 145: )
! 146: {
! 147: if (ch == '\n') {
! 148: stream->col_no = stream->prev_line_col_no;
! 149: stream->prev_line_col_no = -1;
! 150: --stream->line_no;
! 151: }
! 152: --stream->col_no;
! 153: return ungetc(ch, stream->fd);
! 154: }
! 155:
! 156: int
! 157: FCLOSE(
! 158: struct FILE_INFO *stream
! 159: )
! 160: {
! 161: int ret_val = fclose(stream->fd);
! 162:
! 163: if (!ret_val)
! 164: free(stream);
! 165: return ret_val;
! 166: }
! 167:
! 168: /* STREAM INTERFACE
! 169: * ----------------
! 170: * Provide a wrapper for the stream functions so that the
! 171: * stream can either read from a file or from a character
! 172: * array.
! 173: * NOTE: This is not very efficient for reading from character
! 174: * arrays, but needed to allow remote configuration where the
! 175: * configuration command is provided through ntpq.
! 176: *
! 177: * The behavior of there two functions is determined by the
! 178: * input_from_file flag.
! 179: */
! 180:
! 181: int
! 182: get_next_char(
! 183: void
! 184: )
! 185: {
! 186: char ch;
! 187:
! 188: if (input_from_file)
! 189: return FGETC(ip_file);
! 190: else {
! 191: if (remote_config.buffer[remote_config.pos] == '\0')
! 192: return EOF;
! 193: else {
! 194: ip_file->col_no++;
! 195: ch = remote_config.buffer[remote_config.pos++];
! 196: if (ch == '\n') {
! 197: ip_file->prev_line_col_no = ip_file->col_no;
! 198: ++ip_file->line_no;
! 199: ip_file->col_no = 1;
! 200: }
! 201: return ch;
! 202: }
! 203: }
! 204: }
! 205:
! 206: void
! 207: push_back_char(
! 208: int ch
! 209: )
! 210: {
! 211: if (input_from_file)
! 212: UNGETC(ch, ip_file);
! 213: else {
! 214: if (ch == '\n') {
! 215: ip_file->col_no = ip_file->prev_line_col_no;
! 216: ip_file->prev_line_col_no = -1;
! 217: --ip_file->line_no;
! 218: }
! 219: --ip_file->col_no;
! 220:
! 221: remote_config.pos--;
! 222: }
! 223: }
! 224:
! 225:
! 226:
! 227: /* STATE MACHINES
! 228: * --------------
! 229: */
! 230:
! 231: /* Keywords */
! 232: static int
! 233: is_keyword(
! 234: char *lexeme,
! 235: follby *pfollowedby
! 236: )
! 237: {
! 238: follby fb;
! 239: int curr_s; /* current state index */
! 240: int token;
! 241: int i;
! 242:
! 243: curr_s = SCANNER_INIT_S;
! 244: token = 0;
! 245:
! 246: for (i = 0; lexeme[i]; i++) {
! 247: while (curr_s && (lexeme[i] != SS_CH(sst[curr_s])))
! 248: curr_s = SS_OTHER_N(sst[curr_s]);
! 249:
! 250: if (curr_s && (lexeme[i] == SS_CH(sst[curr_s]))) {
! 251: if ('\0' == lexeme[i + 1]
! 252: && FOLLBY_NON_ACCEPTING
! 253: != SS_FB(sst[curr_s])) {
! 254: fb = SS_FB(sst[curr_s]);
! 255: *pfollowedby = fb;
! 256: token = curr_s;
! 257: break;
! 258: }
! 259: curr_s = SS_MATCH_N(sst[curr_s]);
! 260: } else
! 261: break;
! 262: }
! 263:
! 264: return token;
! 265: }
! 266:
! 267:
! 268: /* Integer */
! 269: static int
! 270: is_integer(
! 271: char *lexeme
! 272: )
! 273: {
! 274: int i = 0;
! 275:
! 276: /* Allow a leading minus sign */
! 277: if (lexeme[i] == '-')
! 278: ++i;
! 279:
! 280: /* Check that all the remaining characters are digits */
! 281: for (; lexeme[i]; ++i) {
! 282: if (!isdigit(lexeme[i]))
! 283: return 0;
! 284: }
! 285: return 1;
! 286: }
! 287:
! 288:
! 289: /* Double */
! 290: static int
! 291: is_double(
! 292: char *lexeme
! 293: )
! 294: {
! 295: u_int num_digits = 0; /* Number of digits read */
! 296: u_int i;
! 297:
! 298: i = 0;
! 299:
! 300: /* Check for an optional '+' or '-' */
! 301: if ('+' == lexeme[i] || '-' == lexeme[i])
! 302: i++;
! 303:
! 304: /* Read the integer part */
! 305: for (; lexeme[i] && isdigit(lexeme[i]); i++)
! 306: num_digits++;
! 307:
! 308: /* Check for the required decimal point */
! 309: if ('.' == lexeme[i])
! 310: i++;
! 311: else
! 312: return 0;
! 313:
! 314: /* Check for any digits after the decimal point */
! 315: for (; lexeme[i] && isdigit(lexeme[i]); i++)
! 316: num_digits++;
! 317:
! 318: /*
! 319: * The number of digits in both the decimal part and the
! 320: * fraction part must not be zero at this point
! 321: */
! 322: if (!num_digits)
! 323: return 0;
! 324:
! 325: /* Check if we are done */
! 326: if (!lexeme[i])
! 327: return 1;
! 328:
! 329: /* There is still more input, read the exponent */
! 330: if ('e' == tolower(lexeme[i]))
! 331: i++;
! 332: else
! 333: return 0;
! 334:
! 335: /* Read an optional Sign */
! 336: if ('+' == lexeme[i] || '-' == lexeme[i])
! 337: i++;
! 338:
! 339: /* Now read the exponent part */
! 340: while (lexeme[i] && isdigit(lexeme[i]))
! 341: i++;
! 342:
! 343: /* Check if we are done */
! 344: if (!lexeme[i])
! 345: return 1;
! 346: else
! 347: return 0;
! 348: }
! 349:
! 350:
! 351: /* is_special() - Test whether a character is a token */
! 352: static inline int
! 353: is_special(
! 354: int ch
! 355: )
! 356: {
! 357: return (int)strchr(special_chars, ch);
! 358: }
! 359:
! 360:
! 361: static int
! 362: is_EOC(
! 363: int ch
! 364: )
! 365: {
! 366: if ((old_config_style && (ch == '\n')) ||
! 367: (!old_config_style && (ch == ';')))
! 368: return 1;
! 369: return 0;
! 370: }
! 371:
! 372:
! 373: char *
! 374: quote_if_needed(char *str)
! 375: {
! 376: char *ret;
! 377: size_t len;
! 378: size_t octets;
! 379:
! 380: len = strlen(str);
! 381: octets = len + 2 + 1;
! 382: ret = emalloc(octets);
! 383: if ('"' != str[0]
! 384: && (strcspn(str, special_chars) < len
! 385: || strchr(str, ' ') != NULL)) {
! 386: snprintf(ret, octets, "\"%s\"", str);
! 387: } else
! 388: strncpy(ret, str, octets);
! 389:
! 390: return ret;
! 391: }
! 392:
! 393:
! 394: static int
! 395: create_string_token(
! 396: char *lexeme
! 397: )
! 398: {
! 399: char *pch;
! 400:
! 401: /*
! 402: * ignore end of line whitespace
! 403: */
! 404: pch = lexeme;
! 405: while (*pch && isspace(*pch))
! 406: pch++;
! 407:
! 408: if (!*pch) {
! 409: yylval.Integer = T_EOC;
! 410: return yylval.Integer;
! 411: }
! 412:
! 413: yylval.String = estrdup(lexeme);
! 414: return T_String;
! 415: }
! 416:
! 417:
! 418: /*
! 419: * yylex() - function that does the actual scanning.
! 420: * Bison expects this function to be called yylex and for it to take no
! 421: * input and return an int.
! 422: * Conceptually yylex "returns" yylval as well as the actual return
! 423: * value representing the token or type.
! 424: */
! 425: int
! 426: yylex(
! 427: void
! 428: )
! 429: {
! 430: int i, instring = 0;
! 431: int yylval_was_set = 0;
! 432: int token; /* The return value/the recognized token */
! 433: int ch;
! 434: static follby followedby = FOLLBY_TOKEN;
! 435:
! 436: do {
! 437: /* Ignore whitespace at the beginning */
! 438: while (EOF != (ch = get_next_char()) &&
! 439: isspace(ch) &&
! 440: !is_EOC(ch))
! 441: ; /* Null Statement */
! 442:
! 443: if (EOF == ch) {
! 444:
! 445: if (!input_from_file || !curr_include_level)
! 446: return 0;
! 447:
! 448: FCLOSE(fp[curr_include_level]);
! 449: ip_file = fp[--curr_include_level];
! 450: token = T_EOC;
! 451: goto normal_return;
! 452:
! 453: } else if (is_EOC(ch)) {
! 454:
! 455: /* end FOLLBY_STRINGS_TO_EOC effect */
! 456: followedby = FOLLBY_TOKEN;
! 457: token = T_EOC;
! 458: goto normal_return;
! 459:
! 460: } else if (is_special(ch) && FOLLBY_TOKEN == followedby) {
! 461: /* special chars are their own token values */
! 462: token = ch;
! 463: /*
! 464: * '=' implies a single string following as in:
! 465: * setvar Owner = "The Boss" default
! 466: * This could alternatively be handled by
! 467: * removing '=' from special_chars and adding
! 468: * it to the keyword table.
! 469: */
! 470: if ('=' == ch)
! 471: followedby = FOLLBY_STRING;
! 472: yytext[0] = (char)ch;
! 473: yytext[1] = '\0';
! 474: goto normal_return;
! 475: } else
! 476: push_back_char(ch);
! 477:
! 478: /* save the position of start of the token */
! 479: ip_file->prev_token_line_no = ip_file->line_no;
! 480: ip_file->prev_token_col_no = ip_file->col_no;
! 481:
! 482: /* Read in the lexeme */
! 483: i = 0;
! 484: while (EOF != (ch = get_next_char())) {
! 485:
! 486: yytext[i] = (char)ch;
! 487:
! 488: /* Break on whitespace or a special character */
! 489: if (isspace(ch) || is_EOC(ch)
! 490: || '"' == ch
! 491: || (FOLLBY_TOKEN == followedby
! 492: && is_special(ch)))
! 493: break;
! 494:
! 495: /* Read the rest of the line on reading a start
! 496: of comment character */
! 497: if ('#' == ch) {
! 498: while (EOF != (ch = get_next_char())
! 499: && '\n' != ch)
! 500: ; /* Null Statement */
! 501: break;
! 502: }
! 503:
! 504: i++;
! 505: if (i >= COUNTOF(yytext))
! 506: goto lex_too_long;
! 507: }
! 508: /* Pick up all of the string inside between " marks, to
! 509: * end of line. If we make it to EOL without a
! 510: * terminating " assume it for them.
! 511: *
! 512: * XXX - HMS: I'm not sure we want to assume the closing "
! 513: */
! 514: if ('"' == ch) {
! 515: instring = 1;
! 516: while (EOF != (ch = get_next_char()) &&
! 517: ch != '"' && ch != '\n') {
! 518: yytext[i++] = (char)ch;
! 519: if (i >= COUNTOF(yytext))
! 520: goto lex_too_long;
! 521: }
! 522: /*
! 523: * yytext[i] will be pushed back as not part of
! 524: * this lexeme, but any closing quote should
! 525: * not be pushed back, so we read another char.
! 526: */
! 527: if ('"' == ch)
! 528: ch = get_next_char();
! 529: }
! 530: /* Pushback the last character read that is not a part
! 531: * of this lexeme.
! 532: * If the last character read was an EOF, pushback a
! 533: * newline character. This is to prevent a parse error
! 534: * when there is no newline at the end of a file.
! 535: */
! 536: if (EOF == ch)
! 537: push_back_char('\n');
! 538: else
! 539: push_back_char(ch);
! 540: yytext[i] = '\0';
! 541: } while (i == 0);
! 542:
! 543: /* Now return the desired token */
! 544:
! 545: /* First make sure that the parser is *not* expecting a string
! 546: * as the next token (based on the previous token that was
! 547: * returned) and that we haven't read a string.
! 548: */
! 549:
! 550: if (followedby == FOLLBY_TOKEN && !instring) {
! 551: token = is_keyword(yytext, &followedby);
! 552: if (token)
! 553: goto normal_return;
! 554: else if (is_integer(yytext)) {
! 555: yylval_was_set = 1;
! 556: errno = 0;
! 557: if ((yylval.Integer = strtol(yytext, NULL, 10)) == 0
! 558: && ((errno == EINVAL) || (errno == ERANGE))) {
! 559: msyslog(LOG_ERR,
! 560: "Integer cannot be represented: %s",
! 561: yytext);
! 562: exit(1);
! 563: } else {
! 564: token = T_Integer;
! 565: goto normal_return;
! 566: }
! 567: }
! 568: else if (is_double(yytext)) {
! 569: yylval_was_set = 1;
! 570: errno = 0;
! 571: if ((yylval.Double = atof(yytext)) == 0 && errno == ERANGE) {
! 572: msyslog(LOG_ERR,
! 573: "Double too large to represent: %s",
! 574: yytext);
! 575: exit(1);
! 576: } else {
! 577: token = T_Double;
! 578: goto normal_return;
! 579: }
! 580: } else {
! 581: /* Default: Everything is a string */
! 582: yylval_was_set = 1;
! 583: token = create_string_token(yytext);
! 584: goto normal_return;
! 585: }
! 586: }
! 587:
! 588: /*
! 589: * Either followedby is not FOLLBY_TOKEN or this lexeme is part
! 590: * of a string. Hence, we need to return T_String.
! 591: *
! 592: * _Except_ we might have a -4 or -6 flag on a an association
! 593: * configuration line (server, peer, pool, etc.).
! 594: *
! 595: * This is a terrible hack, but the grammar is ambiguous so we
! 596: * don't have a choice. [SK]
! 597: *
! 598: * The ambiguity is in the keyword scanner, not ntp_parser.y.
! 599: * We do not require server addresses be quoted in ntp.conf,
! 600: * complicating the scanner's job. To avoid trying (and
! 601: * failing) to match an IP address or DNS name to a keyword,
! 602: * the association keywords use FOLLBY_STRING in the keyword
! 603: * table, which tells the scanner to force the next token to be
! 604: * a T_String, so it does not try to match a keyword but rather
! 605: * expects a string when -4/-6 modifiers to server, peer, etc.
! 606: * are encountered.
! 607: * restrict -4 and restrict -6 parsing works correctly without
! 608: * this hack, as restrict uses FOLLBY_TOKEN. [DH]
! 609: */
! 610: if ('-' == yytext[0]) {
! 611: if ('4' == yytext[1]) {
! 612: token = T_Ipv4_flag;
! 613: goto normal_return;
! 614: } else if ('6' == yytext[1]) {
! 615: token = T_Ipv6_flag;
! 616: goto normal_return;
! 617: }
! 618: }
! 619:
! 620: instring = 0;
! 621: if (FOLLBY_STRING == followedby)
! 622: followedby = FOLLBY_TOKEN;
! 623:
! 624: yylval_was_set = 1;
! 625: token = create_string_token(yytext);
! 626:
! 627: normal_return:
! 628: if (T_EOC == token)
! 629: DPRINTF(4,("\t<end of command>\n"));
! 630: else
! 631: DPRINTF(4, ("yylex: lexeme '%s' -> %s\n", yytext,
! 632: token_name(token)));
! 633:
! 634: if (!yylval_was_set)
! 635: yylval.Integer = token;
! 636:
! 637: return token;
! 638:
! 639: lex_too_long:
! 640: yytext[min(sizeof(yytext) - 1, 50)] = 0;
! 641: msyslog(LOG_ERR,
! 642: "configuration item on line %d longer than limit of %lu, began with '%s'",
! 643: ip_file->line_no, (u_long)(sizeof(yytext) - 1), yytext);
! 644:
! 645: /*
! 646: * If we hit the length limit reading the startup configuration
! 647: * file, abort.
! 648: */
! 649: if (input_from_file)
! 650: exit(sizeof(yytext) - 1);
! 651:
! 652: /*
! 653: * If it's runtime configuration via ntpq :config treat it as
! 654: * if the configuration text ended before the too-long lexeme,
! 655: * hostname, or string.
! 656: */
! 657: yylval.Integer = 0;
! 658: return 0;
! 659: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>