1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1996, by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  *	ticscan.c		Terminal Information Compiler
31  *
32  *	Copyright 1990, 1992 by Mortice Kern Systems Inc.  All rights reserved.
33  *
34  *	Portions of this code Copyright 1982 by Pavel Curtis.
35  *
36  */
37 
38 #ifdef M_RCSID
39 #ifndef lint
40 static char rcsID[] = "$Header: /rd/src/tic/rcs/ticscan.c 1.13 1994/02/08 20:19:29 rog Exp $";
41 #endif
42 #endif
43 
44 #include "tic.h"
45 #include <limits.h>
46 #include <ctype.h>
47 
48 #define iswhite(ch)	(ch == ' ' || ch == '\t')
49 
50 
51 token curr_token;
52 long curr_file_pos;
53 int curr_column = -1;
54 char line[LINE_MAX+1];
55 static int first_column;		/* See 'next_char()' below */
56 
57 STATIC int next_char ANSI((void));
58 STATIC int trans_string ANSI((char *));
59 STATIC int escape ANSI((int));
60 STATIC void backspace ANSI((void));
61 
62 char early_eof[] = m_textstr(3122, "Premature EOF", "E");
63 char nl_middle[] = m_textstr(3123, "Newline in middle of terminal name", "E");
64 char ill_char[] = m_textstr(3124, "Illegal character - '%c'", "E char");
65 char ill_ctrl[] = m_textstr(3125, "Illegal control character - '%c'", "E char");
66 char off_beg[] = m_textstr(3126, "Backspaced off beginning of line", "E");
67 char no_comma[] = m_textstr(3127, "Missing comma", "E");
68 char very_long[] = m_textstr(3128, "Very long string found.  Missing comma?", "E");
69 char token_msg[] = m_textstr(3129, "Token: ", "I");
70 char bool_msg[] = m_textstr(3130, "Boolean;  name='%s'\n", "I string");
71 char num_msg[] = m_textstr(3131, "Number; name='%s', value=%d\n", "I name value");
72 char str_msg[] = m_textstr(3132, "String; name='%s', value='%s'\n", "I name value");
73 char cancel[] = m_textstr(3133, "Cancel; name='%s'\n", "I name");
74 char names[] = m_textstr(3134, "Names; value='%s'\n", "I value");
75 char eof_msg[] = m_textstr(3135, "End of file.\n", "I");
76 char bad_token[] = m_textstr(3136, "Bad token type", "E");
77 
78 
79 /*f
80  *	Scans the input for the next token, storing the specifics in the
81  *	global structure 'curr_token' and returning one of the following:
82  *
83  *		NAMES		A line beginning in column 1.  'name'
84  *				will be set to point to everything up to
85  *				but not including the first comma on the line.
86  *		BOOLEAN		An entry consisting of a name followed by
87  *				a comma.  'name' will be set to point to the
88  *				name of the capability.
89  *		NUMBER		An entry of the form
90  *					name#digits,
91  *				'name' will be set to point to the capability
92  *				name and 'valnumber' to the number given.
93  *		STRING		An entry of the form
94  *					name=characters,
95  *				'name' is set to the capability name and
96  *				'valstring' to the string of characters, with
97  *				input translations done.
98  *		CANCEL		An entry of the form
99  *					name@,
100  *				'name' is set to the capability name and
101  *				'valnumber' to -1.
102  *		EOF		The end of the file has been reached.
103  */
104 int
get_token()105 get_token()
106 {
107 	long		number;
108 	int		type;
109 	int             ch;
110 	static char	buffer[1024];
111 	register char	*ptr;
112 	int		dot_flag = 0;
113 
114 	while ((ch = next_char()) == '\n' || iswhite(ch)) {
115 		;
116 	}
117 
118 	if (ch == EOF)
119 	    type = EOF;
120 	else
121 	{
122 	    if (ch == '.')
123 	    {
124 		dot_flag = 1;
125 
126 		while ((ch = next_char()) == ' ' || ch == '\t')
127 		    ;
128 	    }
129 
130 	    if (! isalnum(ch)) {
131 			warning(m_strmsg(ill_char), ch);
132 			panic_mode(',');
133 	    }
134 
135 	    ptr = buffer;
136 	    *(ptr++) = ch;
137 
138 	    if (first_column)
139 	    {
140 		while ((ch = next_char()) != ',' && ch != '\n' && ch != EOF)
141 		    *(ptr++) = ch;
142 
143 		if (ch == EOF)
144 		    err_abort(m_strmsg(early_eof));
145 		else if (ch == '\n') {
146 		    warning(m_strmsg(nl_middle));
147 		    panic_mode(',');
148 		}
149 
150 		*ptr = '\0';
151 		curr_token.tk_name = buffer;
152 		type = NAMES;
153 	    }
154 	    else
155 	    {
156 		ch = next_char();
157 		while (isalnum(ch))
158 		{
159 		    *(ptr++) = ch;
160 		    ch = next_char();
161 		}
162 
163 		*ptr++ = '\0';
164 		switch (ch)
165 		{
166 		    case ',':
167 			curr_token.tk_name = buffer;
168 			type = BOOLEAN;
169 			break;
170 
171 		    case '@':
172 			if (next_char() != ',')
173 			    warning(m_strmsg(no_comma));
174 			curr_token.tk_name = buffer;
175 			type = CANCEL;
176 			break;
177 
178 		    case '#':
179 			number = 0;
180 			while (isdigit(ch = next_char()))
181 			    number = number * 10 + ch - '0';
182 			if (ch != ',')
183 			    warning(m_strmsg(no_comma));
184 			curr_token.tk_name = buffer;
185 			curr_token.tk_valnumber = number;
186 			type = NUMBER;
187 			break;
188 
189 		    case '=':
190 			ch = trans_string(ptr);
191 			if (ch != ',')
192 			    warning(m_strmsg(no_comma));
193 			curr_token.tk_name = buffer;
194 			curr_token.tk_valstring = ptr;
195 			type = STRING;
196 			break;
197 
198 		    default:
199 			warning(m_strmsg(ill_char), ch);
200 		}
201 	    } /* end else (first_column == 0) */
202 	} /* end else (ch != EOF) */
203 
204 	if (dot_flag == 1)
205 	    DEBUG(8, "Commented out ", "");
206 
207 	if (debug_level >= 8)
208 	{
209 	    fprintf(stderr, m_strmsg(token_msg));
210 	    switch (type)
211 	    {
212 		case BOOLEAN:
213 		    fprintf(stderr, m_strmsg(bool_msg), curr_token.tk_name);
214 		    break;
215 
216 		case NUMBER:
217 			fprintf(
218 				stderr, m_strmsg(num_msg),
219 				curr_token.tk_name, curr_token.tk_valnumber
220 			);
221 			break;
222 
223 		case STRING:
224 			fprintf(
225 				stderr, m_strmsg(str_msg),
226 				curr_token.tk_name, curr_token.tk_valstring
227 			);
228 			break;
229 
230 		case CANCEL:
231 		    fprintf(stderr, m_strmsg(cancel), curr_token.tk_name);
232 		    break;
233 
234 		case NAMES:
235 		    fprintf(stderr, m_strmsg(names), curr_token.tk_name);
236 		    break;
237 
238 		case EOF:
239 		    fprintf(stderr, m_strmsg(eof_msg));
240 		    break;
241 
242 		default:
243 		    warning(m_strmsg(bad_token));
244 	    }
245 	}
246 
247 	if (dot_flag == 1)		/* if commented out, use the next one */
248 	    type = get_token();
249 
250 	return(type);
251 }
252 
253 
254 /*f
255  *	Returns the next character in the input stream.  Comments and leading
256  *	white space are stripped.  The global state variable 'firstcolumn' is
257  *	set TRUE if the character returned is from the first column of the input
258  * 	line.  The global variable curr_line is incremented for each new line.
259  *	The global variable curr_file_pos is set to the file offset of the
260  *	beginning of each line.
261  */
262 STATIC int
next_char()263 next_char()
264 {
265 	char *rtn_value;
266 
267 	if (curr_column < 0 || LINE_MAX < curr_column
268 	|| line[curr_column] == '\0') {
269 		do {
270 			curr_file_pos = ftell(stdin);
271 			if ((rtn_value = fgets(line, LINE_MAX, stdin)) != NULL)
272 				curr_line++;
273 		} while (rtn_value != NULL && line[0] == '#');
274 
275 		if (rtn_value == NULL)
276 			return (EOF);
277 
278 		curr_column = 0;
279 		while (iswhite(line[curr_column]))
280 			curr_column++;
281 	}
282 	first_column = curr_column == 0 && *line != '\n';
283 	return (line[curr_column++]);
284 }
285 
286 
287 /*f
288  * go back one character
289  */
290 STATIC void
backspace()291 backspace()
292 {
293 	curr_column--;
294 
295 	if (curr_column < 0)
296 	    syserr_abort(m_strmsg(off_beg));
297 }
298 
299 
300 /*f
301  *	Resets the input-reading routines.  Used after a seek has been done.
302  */
303 void
reset_input()304 reset_input()
305 {
306 	curr_column = -1;
307 }
308 
309 /*f
310  *	Reads characters using next_char() until encountering a comma, newline
311  *	or end-of-file.  The returned value is the character which caused
312  *	reading to stop.  The following translations are done on the input:
313  *
314  *		^X  goes to  ctrl-X (i.e. X & 037)
315  *		{backslash-E,backslash-n,backslash-r,backslash-b,
316  *				backslash-t,backslash-f}  go to
317  *			{ESCAPE,newline,carriage-return,backspace,tab,formfeed}
318  *		{backslash-^,backslash-backslash}  go to  {carat,backslash}
319  *		backslash-ddd (for ddd = up to three octal digits)  goes to
320  *							the character ddd
321  *
322  *		backslash-e == backslash-E
323  *		backslash-0 == backslash-200
324  */
325 STATIC int
trans_string(ptr)326 trans_string(ptr)
327 char *ptr;
328 {
329 	int i, number, ch;
330 	register int count = 0;
331 
332 	while ((ch = next_char()) != ',' && ch != EOF) {
333 		if (ch == '^') {
334 			ch = next_char();
335 			if (ch == EOF)
336 				err_abort(m_strmsg(early_eof));
337 			if (!isprint(ch))
338 				warning(m_strmsg(ill_ctrl), ch);
339 			*(ptr++) = ch & 037;
340 		} else if (ch == '\\') {
341 			/* Try to read a three character octal number. */
342 			for (number = i = 0; i < 3; ++i) {
343 				ch = next_char();
344 				if (ch == EOF)
345 					err_abort(m_strmsg(early_eof));
346 				if (ch < '0' || '7' < ch) {
347 					backspace();
348 					break;
349 				}
350 				number = number * 8 + ch - '0';
351 			}
352 			if (0 < i) {
353 				/* Read an octal number. */
354 				*ptr++ = number == 0 ? 0200 : (char) number;
355 			} else {
356 				/* Escape mapping translation. */
357 				ch = escape(next_char());
358 				*ptr++ = ch;
359 			}
360 		} else {
361 			*(ptr++) = ch;
362 		}
363 		if (500 < ++count)
364 			warning(m_strmsg(very_long));
365 	}
366 	*ptr = '\0';
367 	return (ch);
368 }
369 
370 /*f
371  * Panic mode error recovery - skip everything until a "ch" is found.
372  */
373 void
panic_mode(ch)374 panic_mode(ch)
375 char ch;
376 {
377 	int c;
378 	for (;;) {
379 		c = next_char();
380 		if (c == ch)
381 			return;
382 		if (c == EOF);
383 			return;
384 	}
385 }
386 
387 /*f
388  *	This routine is a codeset independent method of specifying a translation
389  *	from an unambiguous printable form, to an internal binary value.
390  *	This mapping is defined by Table 2-13 in section 2-12 of POSIX.2.
391  *
392  * 	This table has been extended to account for tic/infocmp specification
393  *	of additional characters: <escape>, <space>, <colon>, <caret>, <comma>
394  *
395  *	Assume that the escape lead-in character has been processed and
396  *	any escaped octal sequence.
397  */
398 STATIC int
escape(c)399 escape(c)
400 int c;
401 {
402 	int i;
403 	static int cntl_code[] = {
404 		'\0', '\\', M_ALERT, '\b', '\f', '\n', '\r', '\t',
405 		M_VTAB, M_ESCAPE, M_ESCAPE, ' ', ':', '^', ',',
406 		-1
407 	};
408 	static int escape_char[] = {
409 		'\0', '\\', 'a', 'b', 'f', 'n', 'r', 't',
410 		'v', 'E', 'e', 's', ':', '^', ',',
411 		-1
412 	};
413 	for (i = 0; escape_char[i] != -1; ++i)
414 		if (c == escape_char[i])
415 			return (cntl_code[i]);
416 	return (c);
417 }
418