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>