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