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