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