GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libcurses/tinfo/comp_scan.c Lines: 0 313 0.0 %
Date: 2017-11-13 Branches: 0 290 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: comp_scan.c,v 1.15 2010/01/12 23:22:06 nicm Exp $ */
2
3
/****************************************************************************
4
 * Copyright (c) 1998-2006,2008 Free Software Foundation, Inc.              *
5
 *                                                                          *
6
 * Permission is hereby granted, free of charge, to any person obtaining a  *
7
 * copy of this software and associated documentation files (the            *
8
 * "Software"), to deal in the Software without restriction, including      *
9
 * without limitation the rights to use, copy, modify, merge, publish,      *
10
 * distribute, distribute with modifications, sublicense, and/or sell       *
11
 * copies of the Software, and to permit persons to whom the Software is    *
12
 * furnished to do so, subject to the following conditions:                 *
13
 *                                                                          *
14
 * The above copyright notice and this permission notice shall be included  *
15
 * in all copies or substantial portions of the Software.                   *
16
 *                                                                          *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
18
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
19
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
20
 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
21
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
22
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
23
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
24
 *                                                                          *
25
 * Except as contained in this notice, the name(s) of the above copyright   *
26
 * holders shall not be used in advertising or otherwise to promote the     *
27
 * sale, use or other dealings in this Software without prior written       *
28
 * authorization.                                                           *
29
 ****************************************************************************/
30
31
/****************************************************************************
32
 *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
33
 *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
34
 *     and: Thomas E. Dickey                        1996 on                 *
35
 ****************************************************************************/
36
37
/*
38
 *	comp_scan.c --- Lexical scanner for terminfo compiler.
39
 *
40
 *	_nc_reset_input()
41
 *	_nc_get_token()
42
 *	_nc_panic_mode()
43
 *	int _nc_syntax;
44
 *	int _nc_curr_line;
45
 *	long _nc_curr_file_pos;
46
 *	long _nc_comment_start;
47
 *	long _nc_comment_end;
48
 */
49
50
#include <curses.priv.h>
51
52
#include <ctype.h>
53
#include <term_entry.h>
54
#include <tic.h>
55
56
MODULE_ID("$Id: comp_scan.c,v 1.15 2010/01/12 23:22:06 nicm Exp $")
57
58
/*
59
 * Maximum length of string capability we'll accept before raising an error.
60
 * Yes, there is a real capability in /etc/termcap this long, an "is".
61
 */
62
#define MAXCAPLEN	600
63
64
#define iswhite(ch)	(ch == ' '  ||  ch == '\t')
65
66
NCURSES_EXPORT_VAR(int)
67
_nc_syntax = 0;			/* termcap or terminfo? */
68
NCURSES_EXPORT_VAR(long)
69
_nc_curr_file_pos = 0;		/* file offset of current line */
70
NCURSES_EXPORT_VAR(long)
71
_nc_comment_start = 0;		/* start of comment range before name */
72
NCURSES_EXPORT_VAR(long)
73
_nc_comment_end = 0;		/* end of comment range before name */
74
NCURSES_EXPORT_VAR(long)
75
_nc_start_line = 0;		/* start line of current entry */
76
77
NCURSES_EXPORT_VAR(struct token)
78
_nc_curr_token =
79
{
80
    0, 0, 0
81
};
82
83
/*****************************************************************************
84
 *
85
 * Token-grabbing machinery
86
 *
87
 *****************************************************************************/
88
89
static bool first_column;	/* See 'next_char()' below */
90
static bool had_newline;
91
static char separator;		/* capability separator */
92
static int pushtype;		/* type of pushback token */
93
static char *pushname;
94
95
#if NCURSES_EXT_FUNCS
96
NCURSES_EXPORT_VAR(bool)
97
_nc_disable_period = FALSE;	/* used by tic -a option */
98
#endif
99
100
/*****************************************************************************
101
 *
102
 * Character-stream handling
103
 *
104
 *****************************************************************************/
105
106
#define LEXBUFSIZ	1024
107
108
static char *bufptr;		/* otherwise, the input buffer pointer */
109
static char *bufstart;		/* start of buffer so we can compute offsets */
110
static FILE *yyin;		/* scanner's input file descriptor */
111
112
/*
113
 *	_nc_reset_input()
114
 *
115
 *	Resets the input-reading routines.  Used on initialization,
116
 *	or after a seek has been done.  Exactly one argument must be
117
 *	non-null.
118
 */
119
120
NCURSES_EXPORT(void)
121
_nc_reset_input(FILE *fp, char *buf)
122
{
123
    pushtype = NO_PUSHBACK;
124
    if (pushname != 0)
125
	pushname[0] = '\0';
126
    yyin = fp;
127
    bufstart = bufptr = buf;
128
    _nc_curr_file_pos = 0L;
129
    if (fp != 0)
130
	_nc_curr_line = 0;
131
    _nc_curr_col = 0;
132
}
133
134
/*
135
 *	int last_char()
136
 *
137
 *	Returns the final nonblank character on the current input buffer
138
 */
139
static int
140
last_char(void)
141
{
142
    size_t len = strlen(bufptr);
143
    while (len--) {
144
	if (!isspace(UChar(bufptr[len])))
145
	    return bufptr[len];
146
    }
147
    return 0;
148
}
149
150
/*
151
 *	int next_char()
152
 *
153
 *	Returns the next character in the input stream.  Comments and leading
154
 *	white space are stripped.
155
 *
156
 *	The global state variable 'firstcolumn' is set TRUE if the character
157
 *	returned is from the first column of the input line.
158
 *
159
 *	The global variable _nc_curr_line is incremented for each new line.
160
 *	The global variable _nc_curr_file_pos is set to the file offset of the
161
 *	beginning of each line.
162
 */
163
164
static int
165
next_char(void)
166
{
167
    static char *result;
168
    static size_t allocated;
169
    int the_char;
170
171
    if (!yyin) {
172
	if (result != 0) {
173
	    FreeAndNull(result);
174
	    FreeAndNull(pushname);
175
	    allocated = 0;
176
	}
177
	/*
178
	 * An string with an embedded null will truncate the input.  This is
179
	 * intentional (we don't read binary files here).
180
	 */
181
	if (bufptr == 0 || *bufptr == '\0')
182
	    return (EOF);
183
	if (*bufptr == '\n') {
184
	    _nc_curr_line++;
185
	    _nc_curr_col = 0;
186
	} else if (*bufptr == '\t') {
187
	    _nc_curr_col = (_nc_curr_col | 7);
188
	}
189
    } else if (!bufptr || !*bufptr) {
190
	/*
191
	 * In theory this could be recoded to do its I/O one character at a
192
	 * time, saving the buffer space.  In practice, this turns out to be
193
	 * quite hard to get completely right.  Try it and see.  If you
194
	 * succeed, don't forget to hack push_back() correspondingly.
195
	 */
196
	size_t used;
197
	size_t len;
198
199
	do {
200
	    bufstart = 0;
201
	    used = 0;
202
	    do {
203
		if (used + (LEXBUFSIZ / 4) >= allocated) {
204
		    allocated += (allocated + LEXBUFSIZ);
205
		    result = typeRealloc(char, allocated, result);
206
		    if (result == 0)
207
			return (EOF);
208
		    bufstart = result;
209
		}
210
		if (used == 0)
211
		    _nc_curr_file_pos = ftell(yyin);
212
213
		if (fgets(result + used, (int) (allocated - used), yyin) != 0) {
214
		    bufstart = result;
215
		    if (used == 0) {
216
			_nc_curr_line++;
217
			_nc_curr_col = 0;
218
		    }
219
		} else {
220
		    if (used != 0)
221
			strlcat(result, "\n", allocated);
222
		}
223
		if ((bufptr = bufstart) != 0) {
224
		    used = strlen(bufptr);
225
		    while (iswhite(*bufptr)) {
226
			if (*bufptr == '\t') {
227
			    _nc_curr_col = (_nc_curr_col | 7) + 1;
228
			} else {
229
			    _nc_curr_col++;
230
			}
231
			bufptr++;
232
		    }
233
234
		    /*
235
		     * Treat a trailing <cr><lf> the same as a <newline> so we
236
		     * can read files on OS/2, etc.
237
		     */
238
		    if ((len = strlen(bufptr)) > 1) {
239
			if (bufptr[len - 1] == '\n'
240
			    && bufptr[len - 2] == '\r') {
241
			    len--;
242
			    bufptr[len - 1] = '\n';
243
			    bufptr[len] = '\0';
244
			}
245
		    }
246
		} else {
247
		    return (EOF);
248
		}
249
	    } while (bufptr[len - 1] != '\n');	/* complete a line */
250
	} while (result[0] == '#');	/* ignore comments */
251
    } else if (*bufptr == '\t') {
252
	_nc_curr_col = (_nc_curr_col | 7);
253
    }
254
255
    first_column = (bufptr == bufstart);
256
    if (first_column)
257
	had_newline = FALSE;
258
259
    _nc_curr_col++;
260
    the_char = *bufptr++;
261
    return UChar(the_char);
262
}
263
264
static void
265
push_back(char c)
266
/* push a character back onto the input stream */
267
{
268
    if (bufptr == bufstart)
269
	_nc_syserr_abort("Can't backspace off beginning of line");
270
    *--bufptr = c;
271
    _nc_curr_col--;
272
}
273
274
static long
275
stream_pos(void)
276
/* return our current character position in the input stream */
277
{
278
    return (yyin ? ftell(yyin) : (bufptr ? bufptr - bufstart : 0));
279
}
280
281
static bool
282
end_of_stream(void)
283
/* are we at end of input? */
284
{
285
    return ((yyin ? feof(yyin) : (bufptr && *bufptr == '\0'))
286
	    ? TRUE : FALSE);
287
}
288
289
/* Assume we may be looking at a termcap-style continuation */
290
static NCURSES_INLINE int
291
eat_escaped_newline(int ch)
292
{
293
    if (ch == '\\')
294
	while ((ch = next_char()) == '\n' || iswhite(ch))
295
	    continue;
296
    return ch;
297
}
298
299
#define TOK_BUF_SIZE MAX_ENTRY_SIZE
300
301
#define OkToAdd() \
302
	((tok_ptr - tok_buf) < (TOK_BUF_SIZE - 2))
303
304
#define AddCh(ch) \
305
	*tok_ptr++ = (char) ch; \
306
	*tok_ptr = '\0'
307
308
/*
309
 *	int
310
 *	get_token()
311
 *
312
 *	Scans the input for the next token, storing the specifics in the
313
 *	global structure 'curr_token' and returning one of the following:
314
 *
315
 *		NAMES		A line beginning in column 1.  'name'
316
 *				will be set to point to everything up to but
317
 *				not including the first separator on the line.
318
 *		BOOLEAN		An entry consisting of a name followed by
319
 *				a separator.  'name' will be set to point to
320
 *				the name of the capability.
321
 *		NUMBER		An entry of the form
322
 *					name#digits,
323
 *				'name' will be set to point to the capability
324
 *				name and 'valnumber' to the number given.
325
 *		STRING		An entry of the form
326
 *					name=characters,
327
 *				'name' is set to the capability name and
328
 *				'valstring' to the string of characters, with
329
 *				input translations done.
330
 *		CANCEL		An entry of the form
331
 *					name@,
332
 *				'name' is set to the capability name and
333
 *				'valnumber' to -1.
334
 *		EOF		The end of the file has been reached.
335
 *
336
 *	A `separator' is either a comma or a semicolon, depending on whether
337
 *	we are in termcap or terminfo mode.
338
 *
339
 */
340
341
NCURSES_EXPORT(int)
342
_nc_get_token(bool silent)
343
{
344
    static const char terminfo_punct[] = "@%&*!#";
345
    static char *tok_buf;
346
347
    char *after_list;
348
    char *after_name;
349
    char *numchk;
350
    char *tok_ptr;
351
    char *s;
352
    char numbuf[80];
353
    int ch;
354
    int dot_flag = FALSE;
355
    int type;
356
    long number;
357
    long token_start;
358
    unsigned found;
359
#ifdef TRACE
360
    int old_line;
361
    int old_col;
362
#endif
363
364
    if (pushtype != NO_PUSHBACK) {
365
	int retval = pushtype;
366
367
	_nc_set_type(pushname != 0 ? pushname : "");
368
	DEBUG(3, ("pushed-back token: `%s', class %d",
369
		  _nc_curr_token.tk_name, pushtype));
370
371
	pushtype = NO_PUSHBACK;
372
	if (pushname != 0)
373
	    pushname[0] = '\0';
374
375
	/* currtok wasn't altered by _nc_push_token() */
376
	return (retval);
377
    }
378
379
    if (end_of_stream()) {
380
	yyin = 0;
381
	next_char();		/* frees its allocated memory */
382
	if (tok_buf != 0) {
383
	    if (_nc_curr_token.tk_name == tok_buf)
384
		_nc_curr_token.tk_name = 0;
385
	    FreeAndNull(tok_buf);
386
	}
387
	return (EOF);
388
    }
389
390
  start_token:
391
    token_start = stream_pos();
392
    while ((ch = next_char()) == '\n' || iswhite(ch)) {
393
	if (ch == '\n')
394
	    had_newline = TRUE;
395
	continue;
396
    }
397
398
    ch = eat_escaped_newline(ch);
399
400
#ifdef TRACE
401
    old_line = _nc_curr_line;
402
    old_col = _nc_curr_col;
403
#endif
404
    if (ch == EOF)
405
	type = EOF;
406
    else {
407
	/* if this is a termcap entry, skip a leading separator */
408
	if (separator == ':' && ch == ':')
409
	    ch = next_char();
410
411
	if (ch == '.'
412
#if NCURSES_EXT_FUNCS
413
	    && !_nc_disable_period
414
#endif
415
	    ) {
416
	    dot_flag = TRUE;
417
	    DEBUG(8, ("dot-flag set"));
418
419
	    while ((ch = next_char()) == '.' || iswhite(ch))
420
		continue;
421
	}
422
423
	if (ch == EOF) {
424
	    type = EOF;
425
	    goto end_of_token;
426
	}
427
428
	/* have to make some punctuation chars legal for terminfo */
429
	if (!isalnum(UChar(ch))
430
#if NCURSES_EXT_FUNCS
431
	    && !(ch == '.' && _nc_disable_period)
432
#endif
433
	    && !strchr(terminfo_punct, (char) ch)) {
434
	    if (!silent)
435
		_nc_warning("Illegal character (expected alphanumeric or %s) - '%s'",
436
			    terminfo_punct, unctrl((chtype) ch));
437
	    _nc_panic_mode(separator);
438
	    goto start_token;
439
	}
440
441
	if (tok_buf == 0)
442
	    tok_buf = typeMalloc(char, TOK_BUF_SIZE);
443
444
#ifdef TRACE
445
	old_line = _nc_curr_line;
446
	old_col = _nc_curr_col;
447
#endif
448
	tok_ptr = tok_buf;
449
	AddCh(ch);
450
451
	if (first_column) {
452
	    _nc_comment_start = token_start;
453
	    _nc_comment_end = _nc_curr_file_pos;
454
	    _nc_start_line = _nc_curr_line;
455
456
	    _nc_syntax = ERR;
457
	    after_name = 0;
458
	    after_list = 0;
459
	    while ((ch = next_char()) != '\n') {
460
		if (ch == EOF) {
461
		    _nc_err_abort(MSG_NO_INPUTS);
462
		} else if (ch == '|') {
463
		    after_list = tok_ptr;
464
		    if (after_name == 0)
465
			after_name = tok_ptr;
466
		} else if (ch == ':' && last_char() != ',') {
467
		    _nc_syntax = SYN_TERMCAP;
468
		    separator = ':';
469
		    break;
470
		} else if (ch == ',') {
471
		    _nc_syntax = SYN_TERMINFO;
472
		    separator = ',';
473
		    /*
474
		     * If we did not see a '|', then we found a name with no
475
		     * aliases or description.
476
		     */
477
		    if (after_name == 0)
478
			break;
479
		    /*
480
		     * If we see a comma, we assume this is terminfo unless we
481
		     * subsequently run into a colon.  But we don't stop
482
		     * looking for a colon until hitting a newline.  This
483
		     * allows commas to be embedded in description fields of
484
		     * either syntax.
485
		     */
486
		} else
487
		    ch = eat_escaped_newline(ch);
488
489
		if (OkToAdd()) {
490
		    AddCh(ch);
491
		} else {
492
		    ch = EOF;
493
		    break;
494
		}
495
	    }
496
	    *tok_ptr = '\0';
497
	    if (_nc_syntax == ERR) {
498
		/*
499
		 * Grrr...what we ought to do here is barf, complaining that
500
		 * the entry is malformed.  But because a couple of name fields
501
		 * in the 8.2 termcap file end with |\, we just have to assume
502
		 * it's termcap syntax.
503
		 */
504
		_nc_syntax = SYN_TERMCAP;
505
		separator = ':';
506
	    } else if (_nc_syntax == SYN_TERMINFO) {
507
		/* throw away trailing /, *$/ */
508
		for (--tok_ptr;
509
		     iswhite(*tok_ptr) || *tok_ptr == ',';
510
		     tok_ptr--)
511
		    continue;
512
		tok_ptr[1] = '\0';
513
	    }
514
515
	    /*
516
	     * This is the soonest we have the terminal name fetched.  Set up
517
	     * for following warning messages.  If there's no '|', then there
518
	     * is no description.
519
	     */
520
	    if (after_name != 0) {
521
		ch = *after_name;
522
		*after_name = '\0';
523
		_nc_set_type(tok_buf);
524
		*after_name = (char) ch;
525
	    }
526
527
	    /*
528
	     * Compute the boundary between the aliases and the description
529
	     * field for syntax-checking purposes.
530
	     */
531
	    if (after_list != 0) {
532
		if (!silent) {
533
		    if (*after_list == '\0')
534
			_nc_warning("empty longname field");
535
		    else if (strchr(after_list, ' ') == 0)
536
			_nc_warning("older tic versions may treat the description field as an alias");
537
		}
538
	    } else {
539
		after_list = tok_buf + strlen(tok_buf);
540
		DEBUG(1, ("missing description"));
541
	    }
542
543
	    /*
544
	     * Whitespace in a name field other than the long name can confuse
545
	     * rdist and some termcap tools.  Slashes are a no-no.  Other
546
	     * special characters can be dangerous due to shell expansion.
547
	     */
548
	    for (s = tok_buf; s < after_list; ++s) {
549
		if (isspace(UChar(*s))) {
550
		    if (!silent)
551
			_nc_warning("whitespace in name or alias field");
552
		    break;
553
		} else if (*s == '/') {
554
		    if (!silent)
555
			_nc_warning("slashes aren't allowed in names or aliases");
556
		    break;
557
		} else if (strchr("$[]!*?", *s)) {
558
		    if (!silent)
559
			_nc_warning("dubious character `%c' in name or alias field", *s);
560
		    break;
561
		}
562
	    }
563
564
	    _nc_curr_token.tk_name = tok_buf;
565
	    type = NAMES;
566
	} else {
567
	    if (had_newline && _nc_syntax == SYN_TERMCAP) {
568
		_nc_warning("Missing backslash before newline");
569
		had_newline = FALSE;
570
	    }
571
	    while ((ch = next_char()) != EOF) {
572
		if (!isalnum(UChar(ch))) {
573
		    if (_nc_syntax == SYN_TERMINFO) {
574
			if (ch != '_')
575
			    break;
576
		    } else {	/* allow ';' for "k;" */
577
			if (ch != ';')
578
			    break;
579
		    }
580
		}
581
		if (OkToAdd()) {
582
		    AddCh(ch);
583
		} else {
584
		    ch = EOF;
585
		    break;
586
		}
587
	    }
588
589
	    *tok_ptr++ = '\0';	/* separate name/value in buffer */
590
	    switch (ch) {
591
	    case ',':
592
	    case ':':
593
		if (ch != separator)
594
		    _nc_err_abort("Separator inconsistent with syntax");
595
		_nc_curr_token.tk_name = tok_buf;
596
		type = BOOLEAN;
597
		break;
598
	    case '@':
599
		if ((ch = next_char()) != separator && !silent)
600
		    _nc_warning("Missing separator after `%s', have %s",
601
				tok_buf, unctrl((chtype) ch));
602
		_nc_curr_token.tk_name = tok_buf;
603
		type = CANCEL;
604
		break;
605
606
	    case '#':
607
		found = 0;
608
		while (isalnum(ch = next_char())) {
609
		    numbuf[found++] = (char) ch;
610
		    if (found >= sizeof(numbuf) - 1)
611
			break;
612
		}
613
		numbuf[found] = '\0';
614
		number = strtol(numbuf, &numchk, 0);
615
		if (!silent) {
616
		    if (numchk == numbuf)
617
			_nc_warning("no value given for `%s'", tok_buf);
618
		    if ((*numchk != '\0') || (ch != separator))
619
			_nc_warning("Missing separator");
620
		}
621
		_nc_curr_token.tk_name = tok_buf;
622
		_nc_curr_token.tk_valnumber = number;
623
		type = NUMBER;
624
		break;
625
626
	    case '=':
627
		ch = _nc_trans_string(tok_ptr, tok_buf + TOK_BUF_SIZE);
628
		if (!silent && ch != separator)
629
		    _nc_warning("Missing separator");
630
		_nc_curr_token.tk_name = tok_buf;
631
		_nc_curr_token.tk_valstring = tok_ptr;
632
		type = STRING;
633
		break;
634
635
	    case EOF:
636
		type = EOF;
637
		break;
638
	    default:
639
		/* just to get rid of the compiler warning */
640
		type = UNDEF;
641
		if (!silent)
642
		    _nc_warning("Illegal character - '%s'", unctrl((chtype) ch));
643
	    }
644
	}			/* end else (first_column == FALSE) */
645
    }				/* end else (ch != EOF) */
646
647
  end_of_token:
648
649
#ifdef TRACE
650
    if (dot_flag == TRUE)
651
	DEBUG(8, ("Commented out "));
652
653
    if (_nc_tracing >= DEBUG_LEVEL(8)) {
654
	_tracef("parsed %d.%d to %d.%d",
655
		old_line, old_col,
656
		_nc_curr_line, _nc_curr_col);
657
    }
658
    if (_nc_tracing >= DEBUG_LEVEL(7)) {
659
	switch (type) {
660
	case BOOLEAN:
661
	    _tracef("Token: Boolean; name='%s'",
662
		    _nc_curr_token.tk_name);
663
	    break;
664
665
	case NUMBER:
666
	    _tracef("Token: Number;  name='%s', value=%d",
667
		    _nc_curr_token.tk_name,
668
		    _nc_curr_token.tk_valnumber);
669
	    break;
670
671
	case STRING:
672
	    _tracef("Token: String;  name='%s', value=%s",
673
		    _nc_curr_token.tk_name,
674
		    _nc_visbuf(_nc_curr_token.tk_valstring));
675
	    break;
676
677
	case CANCEL:
678
	    _tracef("Token: Cancel; name='%s'",
679
		    _nc_curr_token.tk_name);
680
	    break;
681
682
	case NAMES:
683
684
	    _tracef("Token: Names; value='%s'",
685
		    _nc_curr_token.tk_name);
686
	    break;
687
688
	case EOF:
689
	    _tracef("Token: End of file");
690
	    break;
691
692
	default:
693
	    _nc_warning("Bad token type");
694
	}
695
    }
696
#endif
697
698
    if (dot_flag == TRUE)	/* if commented out, use the next one */
699
	type = _nc_get_token(silent);
700
701
    DEBUG(3, ("token: `%s', class %d",
702
	      ((_nc_curr_token.tk_name != 0)
703
	       ? _nc_curr_token.tk_name
704
	       : "<null>"),
705
	      type));
706
707
    return (type);
708
}
709
710
/*
711
 *	char
712
 *	trans_string(ptr)
713
 *
714
 *	Reads characters using next_char() until encountering a separator, nl,
715
 *	or end-of-file.  The returned value is the character which caused
716
 *	reading to stop.  The following translations are done on the input:
717
 *
718
 *		^X  goes to  ctrl-X (i.e. X & 037)
719
 *		{\E,\n,\r,\b,\t,\f}  go to
720
 *			{ESCAPE,newline,carriage-return,backspace,tab,formfeed}
721
 *		{\^,\\}  go to  {carat,backslash}
722
 *		\ddd (for ddd = up to three octal digits)  goes to the character ddd
723
 *
724
 *		\e == \E
725
 *		\0 == \200
726
 *
727
 */
728
729
NCURSES_EXPORT(int)
730
_nc_trans_string(char *ptr, char *last)
731
{
732
    int count = 0;
733
    int number = 0;
734
    int i, c;
735
    chtype ch, last_ch = '\0';
736
    bool ignored = FALSE;
737
    bool long_warning = FALSE;
738
739
    while ((ch = c = next_char()) != (chtype) separator && c != EOF) {
740
	if (ptr >= (last - 1)) {
741
	    if (c != EOF) {
742
		while ((c = next_char()) != separator && c != EOF) {
743
		    ;
744
		}
745
		ch = c;
746
	    }
747
	    break;
748
	}
749
	if ((_nc_syntax == SYN_TERMCAP) && c == '\n')
750
	    break;
751
	if (ch == '^' && last_ch != '%') {
752
	    ch = c = next_char();
753
	    if (c == EOF)
754
		_nc_err_abort(MSG_NO_INPUTS);
755
756
	    if (!(is7bits(ch) && isprint(ch))) {
757
		_nc_warning("Illegal ^ character - '%s'", unctrl(ch));
758
	    }
759
	    if (ch == '?') {
760
		*(ptr++) = '\177';
761
		if (_nc_tracing)
762
		    _nc_warning("Allow ^? as synonym for \\177");
763
	    } else {
764
		if ((ch &= 037) == 0)
765
		    ch = 128;
766
		*(ptr++) = (char) (ch);
767
	    }
768
	} else if (ch == '\\') {
769
	    ch = c = next_char();
770
	    if (c == EOF)
771
		_nc_err_abort(MSG_NO_INPUTS);
772
773
	    if (ch >= '0' && ch <= '7') {
774
		number = ch - '0';
775
		for (i = 0; i < 2; i++) {
776
		    ch = c = next_char();
777
		    if (c == EOF)
778
			_nc_err_abort(MSG_NO_INPUTS);
779
780
		    if (c < '0' || c > '7') {
781
			if (isdigit(c)) {
782
			    _nc_warning("Non-octal digit `%c' in \\ sequence", c);
783
			    /* allow the digit; it'll do less harm */
784
			} else {
785
			    push_back((char) c);
786
			    break;
787
			}
788
		    }
789
790
		    number = number * 8 + c - '0';
791
		}
792
793
		if (number == 0)
794
		    number = 0200;
795
		*(ptr++) = (char) number;
796
	    } else {
797
		switch (c) {
798
		case 'E':
799
		case 'e':
800
		    *(ptr++) = '\033';
801
		    break;
802
803
		case 'a':
804
		    *(ptr++) = '\007';
805
		    break;
806
807
		case 'l':
808
		case 'n':
809
		    *(ptr++) = '\n';
810
		    break;
811
812
		case 'r':
813
		    *(ptr++) = '\r';
814
		    break;
815
816
		case 'b':
817
		    *(ptr++) = '\010';
818
		    break;
819
820
		case 's':
821
		    *(ptr++) = ' ';
822
		    break;
823
824
		case 'f':
825
		    *(ptr++) = '\014';
826
		    break;
827
828
		case 't':
829
		    *(ptr++) = '\t';
830
		    break;
831
832
		case '\\':
833
		    *(ptr++) = '\\';
834
		    break;
835
836
		case '^':
837
		    *(ptr++) = '^';
838
		    break;
839
840
		case ',':
841
		    *(ptr++) = ',';
842
		    break;
843
844
		case ':':
845
		    *(ptr++) = ':';
846
		    break;
847
848
		case '\n':
849
		    continue;
850
851
		default:
852
		    _nc_warning("Illegal character '%s' in \\ sequence",
853
				unctrl(ch));
854
		    /* FALLTHRU */
855
		case '|':
856
		    *(ptr++) = (char) ch;
857
		}		/* endswitch (ch) */
858
	    }			/* endelse (ch < '0' ||  ch > '7') */
859
	}
860
	/* end else if (ch == '\\') */
861
	else if (ch == '\n' && (_nc_syntax == SYN_TERMINFO)) {
862
	    /*
863
	     * Newlines embedded in a terminfo string are ignored, provided
864
	     * that the next line begins with whitespace.
865
	     */
866
	    ignored = TRUE;
867
	} else {
868
	    *(ptr++) = (char) ch;
869
	}
870
871
	if (!ignored) {
872
	    if (_nc_curr_col <= 1) {
873
		push_back((char) ch);
874
		ch = '\n';
875
		break;
876
	    }
877
	    last_ch = ch;
878
	    count++;
879
	}
880
	ignored = FALSE;
881
882
	if (count > MAXCAPLEN && !long_warning) {
883
	    _nc_warning("Very long string found.  Missing separator?");
884
	    long_warning = TRUE;
885
	}
886
    }				/* end while */
887
888
    *ptr = '\0';
889
890
    return (ch);
891
}
892
893
/*
894
 *	_nc_push_token()
895
 *
896
 *	Push a token of given type so that it will be reread by the next
897
 *	get_token() call.
898
 */
899
900
NCURSES_EXPORT(void)
901
_nc_push_token(int tokclass)
902
{
903
    /*
904
     * This implementation is kind of bogus, it will fail if we ever do more
905
     * than one pushback at a time between get_token() calls.  It relies on the
906
     * fact that _nc_curr_token is static storage that nothing but
907
     * _nc_get_token() touches.
908
     */
909
    pushtype = tokclass;
910
    if (pushname == 0)
911
	pushname = typeMalloc(char, MAX_NAME_SIZE + 1);
912
    _nc_get_type(pushname);
913
914
    DEBUG(3, ("pushing token: `%s', class %d",
915
	      ((_nc_curr_token.tk_name != 0)
916
	       ? _nc_curr_token.tk_name
917
	       : "<null>"),
918
	      pushtype));
919
}
920
921
/*
922
 * Panic mode error recovery - skip everything until a "ch" is found.
923
 */
924
NCURSES_EXPORT(void)
925
_nc_panic_mode(char ch)
926
{
927
    int c;
928
929
    for (;;) {
930
	c = next_char();
931
	if (c == ch)
932
	    return;
933
	if (c == EOF)
934
	    return;
935
    }
936
}
937
938
#if NO_LEAKS
939
NCURSES_EXPORT(void)
940
_nc_comp_scan_leaks(void)
941
{
942
    if (pushname != 0) {
943
	FreeAndNull(pushname);
944
    }
945
}
946
#endif