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