Coverage Report

Created: 2023-05-19 06:16

/src/ntp-dev/ntpd/ntp_scanner.c
Line
Count
Source (jump to first uncovered line)
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 "ntpd.h"
23
#include "ntp_config.h"
24
#include "ntpsim.h"
25
#include "ntp_scanner.h"
26
#include "ntp_parser.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
u_int32 conf_file_sum;    /* Simple sum of characters read */
40
41
static struct FILE_INFO * lex_stack = NULL;
42
43
44
45
/* CONSTANTS 
46
 * ---------
47
 */
48
49
50
/* SCANNER GLOBAL VARIABLES 
51
 * ------------------------
52
 */
53
const char special_chars[] = "{}(),;|=";
54
55
56
/* FUNCTIONS
57
 * ---------
58
 */
59
60
static int is_keyword(char *lexeme, follby *pfollowedby);
61
62
63
/*
64
 * keyword() - Return the keyword associated with token T_ identifier.
65
 *         See also token_name() for the string-ized T_ identifier.
66
 *         Example: keyword(T_Server) returns "server"
67
 *      token_name(T_Server) returns "T_Server"
68
 */
69
const char *
70
keyword(
71
  int token
72
  )
73
0
{
74
0
  size_t i;
75
0
  const char *text;
76
77
0
  i = token - LOWEST_KEYWORD_ID;
78
79
0
  if (i < COUNTOF(keyword_text))
80
0
    text = keyword_text[i];
81
0
  else
82
0
    text = NULL;
83
84
0
  return (text != NULL)
85
0
       ? text
86
0
       : "(keyword not found)";
87
0
}
88
89
90
/* FILE & STRING BUFFER INTERFACE
91
 * ------------------------------
92
 *
93
 * This set out as a couple of wrapper functions around the standard C
94
 * fgetc and ungetc functions in order to include positional
95
 * bookkeeping. Alas, this is no longer a good solution with nested
96
 * input files and the possibility to send configuration commands via
97
 * 'ntpdc' and 'ntpq'.
98
 *
99
 * Now there are a few functions to maintain a stack of nested input
100
 * sources (though nesting is only allowd for disk files) and from the
101
 * scanner / parser point of view there's no difference between both
102
 * types of sources.
103
 *
104
 * The 'fgetc()' / 'ungetc()' replacements now operate on a FILE_INFO
105
 * structure. Instead of trying different 'ungetc()' strategies for file
106
 * and buffer based parsing, we keep the backup char in our own
107
 * FILE_INFO structure. This is sufficient, as the parser does *not*
108
 * jump around via 'seek' or the like, and there's no need to
109
 * check/clear the backup store in other places than 'lex_getch()'.
110
 */
111
112
/*
113
 * Allocate an info structure and attach it to a file.
114
 *
115
 * Note: When 'mode' is NULL, then the INFO block will be set up to
116
 * contain a NULL file pointer, as suited for remote config command
117
 * parsing. Otherwise having a NULL file pointer is considered an error,
118
 * and a NULL info block pointer is returned to indicate failure!
119
 *
120
 * Note: We use a variable-sized structure to hold a copy of the file
121
 * name (or, more proper, the input source description). This is more
122
 * secure than keeping a reference to some other storage that might go
123
 * out of scope.
124
 */
125
static struct FILE_INFO *
126
lex_open(
127
  const char *path,
128
  const char *mode
129
  )
130
0
{
131
0
  struct FILE_INFO *stream;
132
0
  size_t            nnambuf;
133
134
0
  nnambuf = strlen(path);
135
0
  stream = emalloc_zero(sizeof(*stream) + nnambuf);
136
0
  stream->curpos.nline = 1;
137
0
  stream->backch = EOF;
138
  /* copy name with memcpy -- trailing NUL already there! */
139
0
  memcpy(stream->fname, path, nnambuf);
140
141
0
  if (NULL != mode) {
142
0
    stream->fpi = fopen(path, mode);
143
0
    if (NULL == stream->fpi) {
144
0
      free(stream);
145
0
      stream = NULL;
146
0
    }
147
0
  }
148
0
  return stream;
149
0
}
150
151
/* get next character from buffer or file. This will return any putback
152
 * character first; it will also make sure the last line is at least
153
 * virtually terminated with a '\n'.
154
 */
155
static int
156
lex_getch(
157
  struct FILE_INFO *stream
158
  )
159
0
{
160
0
  int ch;
161
162
0
  if (NULL == stream || stream->force_eof)
163
0
    return EOF;
164
165
0
  if (EOF != stream->backch) {
166
0
    ch = stream->backch;
167
0
    stream->backch = EOF;
168
0
    if (stream->fpi)
169
0
      conf_file_sum += ch;
170
0
    stream->curpos.ncol++;
171
0
  } else if (stream->fpi) {
172
    /* fetch next 7-bit ASCII char (or EOF) from file */
173
0
    while ((ch = fgetc(stream->fpi)) != EOF && ch > SCHAR_MAX)
174
0
      stream->curpos.ncol++;
175
0
    if (EOF != ch) {
176
0
      conf_file_sum += ch;
177
0
      stream->curpos.ncol++;
178
0
    }
179
0
  } else {
180
    /* fetch next 7-bit ASCII char from buffer */
181
0
    const char * scan;
182
0
    scan = &remote_config.buffer[remote_config.pos];
183
0
    while ((ch = (u_char)*scan) > SCHAR_MAX) {
184
0
      scan++;
185
0
      stream->curpos.ncol++;
186
0
    }
187
0
    if ('\0' != ch) {
188
0
      scan++;
189
0
      stream->curpos.ncol++;
190
0
    } else {
191
0
      ch = EOF;
192
0
    }
193
0
    remote_config.pos = (int)(scan - remote_config.buffer);
194
0
  }
195
196
  /* If the last line ends without '\n', generate one. This
197
   * happens most likely on Windows, where editors often have a
198
   * sloppy concept of a line.
199
   */
200
0
  if (EOF == ch && stream->curpos.ncol != 0)
201
0
    ch = '\n';
202
203
  /* update scan position tallies */
204
0
  if (ch == '\n') {
205
0
    stream->bakpos = stream->curpos;
206
0
    stream->curpos.nline++;
207
0
    stream->curpos.ncol = 0;
208
0
  }
209
210
0
  return ch;
211
0
}
212
213
/* Note: lex_ungetch will fail to track more than one line of push
214
 * back. But since it guarantees only one char of back storage anyway,
215
 * this should not be a problem.
216
 */
217
static int
218
lex_ungetch(
219
  int ch,
220
  struct FILE_INFO *stream
221
  )
222
0
{
223
  /* check preconditions */
224
0
  if (NULL == stream || stream->force_eof)
225
0
    return EOF;
226
0
  if (EOF != stream->backch || EOF == ch)
227
0
    return EOF;
228
229
  /* keep for later reference and update checksum */
230
0
  stream->backch = (u_char)ch;
231
0
  if (stream->fpi)
232
0
    conf_file_sum -= stream->backch;
233
234
  /* update position */
235
0
  if (stream->backch == '\n') {
236
0
      stream->curpos = stream->bakpos;
237
0
      stream->bakpos.ncol = -1;
238
0
  }
239
0
  stream->curpos.ncol--;
240
0
  return stream->backch;
241
0
}
242
243
/* dispose of an input structure. If the file pointer is not NULL, close
244
 * the file. This function does not check the result of 'fclose()'.
245
 */
246
static void
247
lex_close(
248
  struct FILE_INFO *stream
249
  )
250
0
{
251
0
  if (NULL != stream) {
252
0
    if (NULL != stream->fpi)
253
0
      fclose(stream->fpi);   
254
0
    free(stream);
255
0
  }
256
0
}
257
258
/* INPUT STACK
259
 * -----------
260
 *
261
 * Nested input sources are a bit tricky at first glance. We deal with
262
 * this problem using a stack of input sources, that is, a forward
263
 * linked list of FILE_INFO structs.
264
 *
265
 * This stack is never empty during parsing; while an encounter with EOF
266
 * can and will remove nested input sources, removing the last element
267
 * in the stack will not work during parsing, and the EOF condition of
268
 * the outermost input file remains until the parser folds up.
269
 */
270
271
static struct FILE_INFO *
272
_drop_stack_do(
273
  struct FILE_INFO * head
274
  )
275
0
{
276
0
  struct FILE_INFO * tail;
277
0
  while (NULL != head) {
278
0
    tail = head->st_next;
279
0
    lex_close(head);
280
0
    head = tail;
281
0
  }
282
0
  return head;
283
0
}
284
285
286
287
/* Create a singleton input source on an empty lexer stack. This will
288
 * fail if there is already an input source, or if the underlying disk
289
 * file cannot be opened.
290
 *
291
 * Returns TRUE if a new input object was successfully created.
292
 */
293
int/*BOOL*/
294
lex_init_stack(
295
  const char * path,
296
  const char * mode
297
  )
298
0
{
299
0
  if (NULL != lex_stack || NULL == path)
300
0
    return FALSE;
301
302
0
  lex_stack = lex_open(path, mode);
303
0
  return (NULL != lex_stack);
304
0
}
305
306
/* This removes *all* input sources from the stack, leaving the head
307
 * pointer as NULL. Any attempt to parse in that state is likely to bomb
308
 * with segmentation faults or the like.
309
 *
310
 * In other words: Use this to clean up after parsing, and do not parse
311
 * anything until the next 'lex_init_stack()' succeeded.
312
 */
313
void
314
lex_drop_stack()
315
0
{
316
0
  lex_stack = _drop_stack_do(lex_stack);
317
0
}
318
319
/* Flush the lexer input stack: This will nip all input objects on the
320
 * stack (but keeps the current top-of-stack) and marks the top-of-stack
321
 * as inactive. Any further calls to lex_getch yield only EOF, and it's
322
 * no longer possible to push something back.
323
 *
324
 * Returns TRUE if there is a head element (top-of-stack) that was not
325
 * in the force-eof mode before this call.
326
 */
327
int/*BOOL*/
328
lex_flush_stack()
329
0
{
330
0
  int retv = FALSE;
331
332
0
  if (NULL != lex_stack) {
333
0
    retv = !lex_stack->force_eof;
334
0
    lex_stack->force_eof = TRUE;
335
0
    lex_stack->st_next = _drop_stack_do(
336
0
          lex_stack->st_next);
337
0
  }
338
0
  return retv;
339
0
}
340
341
/* Push another file on the parsing stack. If the mode is NULL, create a
342
 * FILE_INFO suitable for in-memory parsing; otherwise, create a
343
 * FILE_INFO that is bound to a local/disc file. Note that 'path' must
344
 * not be NULL, or the function will fail.
345
 *
346
 * Returns TRUE if a new info record was pushed onto the stack.
347
 */
348
int/*BOOL*/ lex_push_file(
349
  const char * path,
350
  const char * mode
351
  )
352
0
{
353
0
  struct FILE_INFO * next = NULL;
354
355
0
  if (NULL != path) {
356
0
    next = lex_open(path, mode);
357
0
    if (NULL != next) {
358
0
      next->st_next = lex_stack;
359
0
      lex_stack = next;
360
0
    }
361
0
  }
362
0
  return (NULL != next);
363
0
}
364
365
/* Pop, close & free the top of the include stack, unless the stack
366
 * contains only a singleton input object. In that case the function
367
 * fails, because the parser does not expect the input stack to be
368
 * empty.
369
 *
370
 * Returns TRUE if an object was successfuly popped from the stack.
371
 */
372
int/*BOOL*/
373
lex_pop_file(void)
374
0
{
375
0
  struct FILE_INFO * head = lex_stack;
376
0
  struct FILE_INFO * tail = NULL; 
377
  
378
0
  if (NULL != head) {
379
0
    tail = head->st_next;
380
0
    if (NULL != tail) {
381
0
      lex_stack = tail;
382
0
      lex_close(head);
383
0
    }
384
0
  }
385
0
  return (NULL != tail);
386
0
}
387
388
/* Get include nesting level. This currently loops over the stack and
389
 * counts elements; but since this is of concern only with an include
390
 * statement and the nesting depth has a small limit, there's no
391
 * bottleneck expected here.
392
 *
393
 * Returns the nesting level of includes, that is, the current depth of
394
 * the lexer input stack.
395
 *
396
 * Note: 
397
 */
398
size_t
399
lex_level(void)
400
0
{
401
0
  size_t            cnt = 0;
402
0
  struct FILE_INFO *ipf = lex_stack;
403
404
0
  while (NULL != ipf) {
405
0
    cnt++;
406
0
    ipf = ipf->st_next;
407
0
  }
408
0
  return cnt;
409
0
}
410
411
/* check if the current input is from a file */ 
412
int/*BOOL*/
413
lex_from_file(void)
414
0
{
415
0
  return (NULL != lex_stack) && (NULL != lex_stack->fpi);
416
0
}
417
418
struct FILE_INFO *
419
lex_current()
420
0
{
421
  /* this became so simple, it could be a macro. But then,
422
   * lex_stack needed to be global...
423
   */
424
0
  return lex_stack;
425
0
}
426
427
428
/* STATE MACHINES 
429
 * --------------
430
 */
431
432
/* Keywords */
433
static int
434
is_keyword(
435
  char *lexeme,
436
  follby *pfollowedby
437
  )
438
0
{
439
0
  follby fb;
440
0
  int curr_s;   /* current state index */
441
0
  int token;
442
0
  int i;
443
444
0
  curr_s = SCANNER_INIT_S;
445
0
  token = 0;
446
447
0
  for (i = 0; lexeme[i]; i++) {
448
0
    while (curr_s && (lexeme[i] != SS_CH(sst[curr_s])))
449
0
      curr_s = SS_OTHER_N(sst[curr_s]);
450
451
0
    if (curr_s && (lexeme[i] == SS_CH(sst[curr_s]))) {
452
0
      if ('\0' == lexeme[i + 1]
453
0
          && FOLLBY_NON_ACCEPTING 
454
0
             != SS_FB(sst[curr_s])) {
455
0
        fb = SS_FB(sst[curr_s]);
456
0
        *pfollowedby = fb;
457
0
        token = curr_s;
458
0
        break;
459
0
      }
460
0
      curr_s = SS_MATCH_N(sst[curr_s]);
461
0
    } else
462
0
      break;
463
0
  }
464
465
0
  return token;
466
0
}
467
468
469
/* Integer */
470
static int
471
is_integer(
472
  char *lexeme
473
  )
474
0
{
475
0
  int i;
476
0
  int is_neg;
477
0
  u_int u_val;
478
  
479
0
  i = 0;
480
481
  /* Allow a leading minus sign */
482
0
  if (lexeme[i] == '-') {
483
0
    i++;
484
0
    is_neg = TRUE;
485
0
  } else {
486
0
    is_neg = FALSE;
487
0
  }
488
489
  /* Check that all the remaining characters are digits */
490
0
  for (; lexeme[i] != '\0'; i++) {
491
0
    if (!isdigit((u_char)lexeme[i]))
492
0
      return FALSE;
493
0
  }
494
495
0
  if (is_neg)
496
0
    return TRUE;
497
498
  /* Reject numbers that fit in unsigned but not in signed int */
499
0
  if (1 == sscanf(lexeme, "%u", &u_val))
500
0
    return (u_val <= INT_MAX);
501
0
  else
502
0
    return FALSE;
503
0
}
504
505
506
/* U_int -- assumes is_integer() has returned FALSE */
507
static int
508
is_u_int(
509
  char *lexeme
510
  )
511
0
{
512
0
  int i;
513
0
  int is_hex;
514
  
515
0
  i = 0;
516
0
  if ('0' == lexeme[i] && 'x' == tolower((u_char)lexeme[i + 1])) {
517
0
    i += 2;
518
0
    is_hex = TRUE;
519
0
  } else {
520
0
    is_hex = FALSE;
521
0
  }
522
523
  /* Check that all the remaining characters are digits */
524
0
  for (; lexeme[i] != '\0'; i++) {
525
0
    if (is_hex && !isxdigit((u_char)lexeme[i]))
526
0
      return FALSE;
527
0
    if (!is_hex && !isdigit((u_char)lexeme[i]))
528
0
      return FALSE;
529
0
  }
530
531
0
  return TRUE;
532
0
}
533
534
535
/* Double */
536
static int
537
is_double(
538
  char *lexeme
539
  )
540
0
{
541
0
  u_int num_digits = 0;  /* Number of digits read */
542
0
  u_int i;
543
544
0
  i = 0;
545
546
  /* Check for an optional '+' or '-' */
547
0
  if ('+' == lexeme[i] || '-' == lexeme[i])
548
0
    i++;
549
550
  /* Read the integer part */
551
0
  for (; lexeme[i] && isdigit((u_char)lexeme[i]); i++)
552
0
    num_digits++;
553
554
  /* Check for the optional decimal point */
555
0
  if ('.' == lexeme[i]) {
556
0
    i++;
557
    /* Check for any digits after the decimal point */
558
0
    for (; lexeme[i] && isdigit((u_char)lexeme[i]); i++)
559
0
      num_digits++;
560
0
  }
561
562
  /*
563
   * The number of digits in both the decimal part and the
564
   * fraction part must not be zero at this point 
565
   */
566
0
  if (!num_digits)
567
0
    return 0;
568
569
  /* Check if we are done */
570
0
  if (!lexeme[i])
571
0
    return 1;
572
573
  /* There is still more input, read the exponent */
574
0
  if ('e' == tolower((u_char)lexeme[i]))
575
0
    i++;
576
0
  else
577
0
    return 0;
578
579
  /* Read an optional Sign */
580
0
  if ('+' == lexeme[i] || '-' == lexeme[i])
581
0
    i++;
582
583
  /* Now read the exponent part */
584
0
  while (lexeme[i] && isdigit((u_char)lexeme[i]))
585
0
    i++;
586
587
  /* Check if we are done */
588
0
  if (!lexeme[i])
589
0
    return 1;
590
0
  else
591
0
    return 0;
592
0
}
593
594
595
/* is_special() - Test whether a character is a token */
596
static inline int
597
is_special(
598
  int ch
599
  )
600
0
{
601
0
  return strchr(special_chars, ch) != NULL;
602
0
}
603
604
605
static int
606
is_EOC(
607
  int ch
608
  )
609
0
{
610
0
  if ((old_config_style && (ch == '\n')) ||
611
0
      (!old_config_style && (ch == ';')))
612
0
    return 1;
613
0
  return 0;
614
0
}
615
616
617
char *
618
quote_if_needed(char *str)
619
0
{
620
0
  char *ret;
621
0
  size_t len;
622
0
  size_t octets;
623
624
0
  len = strlen(str);
625
0
  octets = len + 2 + 1;
626
0
  ret = emalloc(octets);
627
0
  if ('"' != str[0] 
628
0
      && (strcspn(str, special_chars) < len 
629
0
    || strchr(str, ' ') != NULL)) {
630
0
    snprintf(ret, octets, "\"%s\"", str);
631
0
  } else
632
0
    strlcpy(ret, str, octets);
633
634
0
  return ret;
635
0
}
636
637
638
static int
639
create_string_token(
640
  char *lexeme
641
  )
642
0
{
643
0
  char *pch;
644
645
  /*
646
   * ignore end of line whitespace
647
   */
648
0
  pch = lexeme;
649
0
  while (*pch && isspace((u_char)*pch))
650
0
    pch++;
651
652
0
  if (!*pch) {
653
0
    yylval.Integer = T_EOC;
654
0
    return yylval.Integer;
655
0
  }
656
657
0
  yylval.String = estrdup(lexeme);
658
0
  return T_String;
659
0
}
660
661
662
/*
663
 * yylex() - function that does the actual scanning.
664
 * Bison expects this function to be called yylex and for it to take no
665
 * input and return an int.
666
 * Conceptually yylex "returns" yylval as well as the actual return
667
 * value representing the token or type.
668
 */
669
int
670
yylex(void)
671
0
{
672
0
  static follby followedby = FOLLBY_TOKEN;
673
0
  size_t    i;
674
0
  int   instring;
675
0
  int   yylval_was_set;
676
0
  int   converted;
677
0
  int   token;    /* The return value */
678
0
  int   ch;
679
680
0
  instring = FALSE;
681
0
  yylval_was_set = FALSE;
682
683
0
  do {
684
    /* Ignore whitespace at the beginning */
685
0
    while (EOF != (ch = lex_getch(lex_stack)) &&
686
0
           isspace(ch) &&
687
0
           !is_EOC(ch))
688
689
0
      ; /* Null Statement */
690
691
0
    if (EOF == ch) {
692
693
0
      if ( ! lex_pop_file())
694
0
        return 0;
695
0
      token = T_EOC;
696
0
      goto normal_return;
697
698
0
    } else if (is_EOC(ch)) {
699
700
      /* end FOLLBY_STRINGS_TO_EOC effect */
701
0
      followedby = FOLLBY_TOKEN;
702
0
      token = T_EOC;
703
0
      goto normal_return;
704
705
0
    } else if (is_special(ch) && FOLLBY_TOKEN == followedby) {
706
      /* special chars are their own token values */
707
0
      token = ch;
708
      /*
709
       * '=' outside simulator configuration implies
710
       * a single string following as in:
711
       * setvar Owner = "The Boss" default
712
       */
713
0
      if ('=' == ch && old_config_style)
714
0
        followedby = FOLLBY_STRING;
715
0
      yytext[0] = (char)ch;
716
0
      yytext[1] = '\0';
717
0
      goto normal_return;
718
0
    } else
719
0
      lex_ungetch(ch, lex_stack);
720
721
    /* save the position of start of the token */
722
0
    lex_stack->tokpos = lex_stack->curpos;
723
724
    /* Read in the lexeme */
725
0
    i = 0;
726
0
    while (EOF != (ch = lex_getch(lex_stack))) {
727
728
0
      yytext[i] = (char)ch;
729
730
      /* Break on whitespace or a special character */
731
0
      if (isspace(ch) || is_EOC(ch) 
732
0
          || '"' == ch
733
0
          || (FOLLBY_TOKEN == followedby
734
0
        && is_special(ch)))
735
0
        break;
736
737
      /* Read the rest of the line on reading a start
738
         of comment character */
739
0
      if ('#' == ch) {
740
0
        while (EOF != (ch = lex_getch(lex_stack))
741
0
               && '\n' != ch)
742
0
          ; /* Null Statement */
743
0
        break;
744
0
      }
745
746
0
      i++;
747
0
      if (i >= COUNTOF(yytext))
748
0
        goto lex_too_long;
749
0
    }
750
    /* Pick up all of the string inside between " marks, to
751
     * end of line.  If we make it to EOL without a
752
     * terminating " assume it for them.
753
     *
754
     * XXX - HMS: I'm not sure we want to assume the closing "
755
     */
756
0
    if ('"' == ch) {
757
0
      instring = TRUE;
758
0
      while (EOF != (ch = lex_getch(lex_stack)) &&
759
0
             ch != '"' && ch != '\n') {
760
0
        yytext[i++] = (char)ch;
761
0
        if (i >= COUNTOF(yytext))
762
0
          goto lex_too_long;
763
0
      }
764
      /*
765
       * yytext[i] will be pushed back as not part of
766
       * this lexeme, but any closing quote should
767
       * not be pushed back, so we read another char.
768
       */
769
0
      if ('"' == ch)
770
0
        ch = lex_getch(lex_stack);
771
0
    }
772
    /* Pushback the last character read that is not a part
773
     * of this lexeme. This fails silently if ch is EOF,
774
     * but then the EOF condition persists and is handled on
775
     * the next turn by the include stack mechanism.
776
     */
777
0
    lex_ungetch(ch, lex_stack);
778
779
0
    yytext[i] = '\0';
780
0
  } while (i == 0);
781
782
  /* Now return the desired token */
783
  
784
  /* First make sure that the parser is *not* expecting a string
785
   * as the next token (based on the previous token that was
786
   * returned) and that we haven't read a string.
787
   */
788
  
789
0
  if (followedby == FOLLBY_TOKEN && !instring) {
790
0
    token = is_keyword(yytext, &followedby);
791
0
    if (token) {
792
      /*
793
       * T_Server is exceptional as it forces the
794
       * following token to be a string in the
795
       * non-simulator parts of the configuration,
796
       * but in the simulator configuration section,
797
       * "server" is followed by "=" which must be
798
       * recognized as a token not a string.
799
       */
800
0
      if (T_Server == token && !old_config_style)
801
0
        followedby = FOLLBY_TOKEN;
802
0
      goto normal_return;
803
0
    } else if (is_integer(yytext)) {
804
0
      yylval_was_set = TRUE;
805
0
      errno = 0;
806
0
      if ((yylval.Integer = strtol(yytext, NULL, 10)) == 0
807
0
          && ((errno == EINVAL) || (errno == ERANGE))) {
808
0
        msyslog(LOG_ERR, 
809
0
          "Integer cannot be represented: %s",
810
0
          yytext);
811
0
        if (lex_from_file()) {
812
0
          exit(1);
813
0
        } else {
814
          /* force end of parsing */
815
0
          yylval.Integer = 0;
816
0
          return 0;
817
0
        }
818
0
      }
819
0
      token = T_Integer;
820
0
      goto normal_return;
821
0
    } else if (is_u_int(yytext)) {
822
0
      yylval_was_set = TRUE;
823
0
      if ('0' == yytext[0] &&
824
0
          'x' == tolower((unsigned long)yytext[1]))
825
0
        converted = sscanf(&yytext[2], "%x",
826
0
               &yylval.U_int);
827
0
      else
828
0
        converted = sscanf(yytext, "%u",
829
0
               &yylval.U_int);
830
0
      if (1 != converted) {
831
0
        msyslog(LOG_ERR, 
832
0
          "U_int cannot be represented: %s",
833
0
          yytext);
834
0
        if (lex_from_file()) {
835
0
          exit(1);
836
0
        } else {
837
          /* force end of parsing */
838
0
          yylval.Integer = 0;
839
0
          return 0;
840
0
        }
841
0
      }
842
0
      token = T_U_int;
843
0
      goto normal_return;
844
0
    } else if (is_double(yytext)) {
845
0
      yylval_was_set = TRUE;
846
0
      errno = 0;
847
0
      if ((yylval.Double = atof(yytext)) == 0 && errno == ERANGE) {
848
0
        msyslog(LOG_ERR,
849
0
          "Double too large to represent: %s",
850
0
          yytext);
851
0
        exit(1);
852
0
      } else {
853
0
        token = T_Double;
854
0
        goto normal_return;
855
0
      }
856
0
    } else {
857
      /* Default: Everything is a string */
858
0
      yylval_was_set = TRUE;
859
0
      token = create_string_token(yytext);
860
0
      goto normal_return;
861
0
    }
862
0
  }
863
864
  /*
865
   * Either followedby is not FOLLBY_TOKEN or this lexeme is part
866
   * of a string.  Hence, we need to return T_String.
867
   * 
868
   * _Except_ we might have a -4 or -6 flag on a an association
869
   * configuration line (server, peer, pool, etc.).
870
   *
871
   * This is a terrible hack, but the grammar is ambiguous so we
872
   * don't have a choice.  [SK]
873
   *
874
   * The ambiguity is in the keyword scanner, not ntp_parser.y.
875
   * We do not require server addresses be quoted in ntp.conf,
876
   * complicating the scanner's job.  To avoid trying (and
877
   * failing) to match an IP address or DNS name to a keyword,
878
   * the association keywords use FOLLBY_STRING in the keyword
879
   * table, which tells the scanner to force the next token to be
880
   * a T_String, so it does not try to match a keyword but rather
881
   * expects a string when -4/-6 modifiers to server, peer, etc.
882
   * are encountered.
883
   * restrict -4 and restrict -6 parsing works correctly without
884
   * this hack, as restrict uses FOLLBY_TOKEN.  [DH]
885
   */
886
0
  if ('-' == yytext[0]) {
887
0
    if ('4' == yytext[1]) {
888
0
      token = T_Ipv4_flag;
889
0
      goto normal_return;
890
0
    } else if ('6' == yytext[1]) {
891
0
      token = T_Ipv6_flag;
892
0
      goto normal_return;
893
0
    }
894
0
  }
895
896
0
  if (FOLLBY_STRING == followedby)
897
0
    followedby = FOLLBY_TOKEN;
898
899
0
  yylval_was_set = TRUE;
900
0
  token = create_string_token(yytext);
901
902
0
normal_return:
903
0
  if (T_EOC == token)
904
0
    DPRINTF(4,("\t<end of command>\n"));
905
0
  else
906
0
    DPRINTF(4, ("yylex: lexeme '%s' -> %s\n", yytext,
907
0
          token_name(token)));
908
909
0
  if (!yylval_was_set)
910
0
    yylval.Integer = token;
911
912
0
  return token;
913
914
0
lex_too_long:
915
0
  yytext[min(sizeof(yytext) - 1, 50)] = 0;
916
0
  msyslog(LOG_ERR, 
917
0
    "configuration item on line %d longer than limit of %lu, began with '%s'",
918
0
    lex_stack->curpos.nline, (u_long)min(sizeof(yytext) - 1, 50),
919
0
    yytext);
920
921
  /*
922
   * If we hit the length limit reading the startup configuration
923
   * file, abort.
924
   */
925
0
  if (lex_from_file())
926
0
    exit(sizeof(yytext) - 1);
927
928
  /*
929
   * If it's runtime configuration via ntpq :config treat it as
930
   * if the configuration text ended before the too-long lexeme,
931
   * hostname, or string.
932
   */
933
0
  yylval.Integer = 0;
934
0
  return 0;
935
0
}