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