]> ncurses.scripts.mit.edu Git - ncurses.git/blob - ncurses/tinfo/parse_entry.c
ncurses 6.1 - patch 20191005
[ncurses.git] / ncurses / tinfo / parse_entry.c
1 /****************************************************************************
2  * Copyright (c) 1998-2018,2019 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28
29 /****************************************************************************
30  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
31  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32  *     and: Thomas E. Dickey                        1996-on                 *
33  ****************************************************************************/
34
35 /*
36  *      parse_entry.c -- compile one terminfo or termcap entry
37  *
38  *      Get an exact in-core representation of an entry.  Don't
39  *      try to resolve use or tc capabilities, that is someone
40  *      else's job.  Depends on the lexical analyzer to get tokens
41  *      from the input stream.
42  */
43
44 #define __INTERNAL_CAPS_VISIBLE
45 #include <curses.priv.h>
46
47 #include <ctype.h>
48 #include <tic.h>
49
50 MODULE_ID("$Id: parse_entry.c,v 1.97 2019/08/03 23:10:38 tom Exp $")
51
52 #ifdef LINT
53 static short const parametrized[] =
54 {0};
55 #else
56 #include <parametrized.h>
57 #endif
58
59 static void postprocess_termcap(TERMTYPE2 *, bool);
60 static void postprocess_terminfo(TERMTYPE2 *);
61 static struct name_table_entry const *lookup_fullname(const char *name);
62
63 #if NCURSES_XNAMES
64
65 static struct name_table_entry const *
66 _nc_extend_names(ENTRY * entryp, const char *name, int token_type)
67 {
68     static struct name_table_entry temp;
69     TERMTYPE2 *tp = &(entryp->tterm);
70     unsigned offset = 0;
71     unsigned actual;
72     unsigned tindex;
73     unsigned first, last, n;
74     bool found;
75
76     switch (token_type) {
77     case BOOLEAN:
78         first = 0;
79         last = tp->ext_Booleans;
80         offset = tp->ext_Booleans;
81         tindex = tp->num_Booleans;
82         break;
83     case NUMBER:
84         first = tp->ext_Booleans;
85         last = tp->ext_Numbers + first;
86         offset = (unsigned) (tp->ext_Booleans + tp->ext_Numbers);
87         tindex = tp->num_Numbers;
88         break;
89     case STRING:
90         first = (unsigned) (tp->ext_Booleans + tp->ext_Numbers);
91         last = tp->ext_Strings + first;
92         offset = (unsigned) (tp->ext_Booleans + tp->ext_Numbers + tp->ext_Strings);
93         tindex = tp->num_Strings;
94         break;
95     case CANCEL:
96         actual = NUM_EXT_NAMES(tp);
97         for (n = 0; n < actual; n++) {
98             if (!strcmp(name, tp->ext_Names[n])) {
99                 if (n > (unsigned) (tp->ext_Booleans + tp->ext_Numbers)) {
100                     token_type = STRING;
101                 } else if (n > tp->ext_Booleans) {
102                     token_type = NUMBER;
103                 } else {
104                     token_type = BOOLEAN;
105                 }
106                 return _nc_extend_names(entryp, name, token_type);
107             }
108         }
109         /* Well, we are given a cancel for a name that we don't recognize */
110         return _nc_extend_names(entryp, name, STRING);
111     default:
112         return 0;
113     }
114
115     /* Adjust the 'offset' (insertion-point) to keep the lists of extended
116      * names sorted.
117      */
118     for (n = first, found = FALSE; n < last; n++) {
119         int cmp = strcmp(tp->ext_Names[n], name);
120         if (cmp == 0)
121             found = TRUE;
122         if (cmp >= 0) {
123             offset = n;
124             tindex = n - first;
125             switch (token_type) {
126             case BOOLEAN:
127                 tindex += BOOLCOUNT;
128                 break;
129             case NUMBER:
130                 tindex += NUMCOUNT;
131                 break;
132             case STRING:
133                 tindex += STRCOUNT;
134                 break;
135             }
136             break;
137         }
138     }
139
140 #define for_each_value(max) \
141         for (last = (unsigned) (max - 1); last > tindex; last--)
142
143     if (!found) {
144         switch (token_type) {
145         case BOOLEAN:
146             tp->ext_Booleans++;
147             tp->num_Booleans++;
148             TYPE_REALLOC(NCURSES_SBOOL, tp->num_Booleans, tp->Booleans);
149             for_each_value(tp->num_Booleans)
150                 tp->Booleans[last] = tp->Booleans[last - 1];
151             break;
152         case NUMBER:
153             tp->ext_Numbers++;
154             tp->num_Numbers++;
155             TYPE_REALLOC(NCURSES_INT2, tp->num_Numbers, tp->Numbers);
156             for_each_value(tp->num_Numbers)
157                 tp->Numbers[last] = tp->Numbers[last - 1];
158             break;
159         case STRING:
160             tp->ext_Strings++;
161             tp->num_Strings++;
162             TYPE_REALLOC(char *, tp->num_Strings, tp->Strings);
163             for_each_value(tp->num_Strings)
164                 tp->Strings[last] = tp->Strings[last - 1];
165             break;
166         }
167         actual = NUM_EXT_NAMES(tp);
168         TYPE_REALLOC(char *, actual, tp->ext_Names);
169         while (--actual > offset)
170             tp->ext_Names[actual] = tp->ext_Names[actual - 1];
171         tp->ext_Names[offset] = _nc_save_str(name);
172     }
173
174     temp.nte_name = tp->ext_Names[offset];
175     temp.nte_type = token_type;
176     temp.nte_index = (short) tindex;
177     temp.nte_link = -1;
178
179     return &temp;
180 }
181
182 static const char *
183 usertype2s(int mask)
184 {
185     const char *result = "unknown";
186     if (mask & (1 << BOOLEAN)) {
187         result = "boolean";
188     } else if (mask & (1 << NUMBER)) {
189         result = "number";
190     } else if (mask & (1 << STRING)) {
191         result = "string";
192     }
193     return result;
194 }
195
196 static bool
197 expected_type(const char *name, int token_type, bool silent)
198 {
199     struct user_table_entry const *entry = _nc_find_user_entry(name);
200     bool result = TRUE;
201     if ((entry != 0) && (token_type != CANCEL)) {
202         int have_type = (1 << token_type);
203         if (!(entry->ute_type & have_type)) {
204             if (!silent)
205                 _nc_warning("expected %s-type for %s, have %s",
206                             usertype2s(entry->ute_type),
207                             name,
208                             usertype2s(have_type));
209             result = FALSE;
210         }
211     }
212     return result;
213 }
214 #endif /* NCURSES_XNAMES */
215
216 static bool
217 valid_entryname(const char *name)
218 {
219     bool result = TRUE;
220     int ch;
221     while ((ch = UChar(*name++)) != '\0') {
222         if (ch <= ' ' || ch > '~' || ch == '/') {
223             result = FALSE;
224             break;
225         }
226     }
227     return result;
228 }
229
230 /*
231  *      int
232  *      _nc_parse_entry(entry, literal, silent)
233  *
234  *      Compile one entry.  Doesn't try to resolve use or tc capabilities.
235  *
236  *      found-forward-use = FALSE
237  *      re-initialise internal arrays
238  *      get_token();
239  *      if the token was not a name in column 1, complain and die
240  *      save names in entry's string table
241  *      while (get_token() is not EOF and not NAMES)
242  *              check for existence and type-correctness
243  *              enter cap into structure
244  *              if STRING
245  *                  save string in entry's string table
246  *      push back token
247  */
248
249 #define BAD_TC_USAGE if (!bad_tc_usage) \
250         { bad_tc_usage = TRUE; \
251          _nc_warning("Legacy termcap allows only a trailing tc= clause"); }
252
253 #define MAX_NUMBER MAX_OF_TYPE(NCURSES_INT2)
254
255 NCURSES_EXPORT(int)
256 _nc_parse_entry(ENTRY * entryp, int literal, bool silent)
257 {
258     int token_type;
259     struct name_table_entry const *entry_ptr;
260     char *ptr, *base;
261     const char *name;
262     bool bad_tc_usage = FALSE;
263
264     token_type = _nc_get_token(silent);
265
266     if (token_type == EOF)
267         return (EOF);
268     if (token_type != NAMES)
269         _nc_err_abort("Entry does not start with terminal names in column one");
270
271     _nc_init_entry(entryp);
272
273     entryp->cstart = _nc_comment_start;
274     entryp->cend = _nc_comment_end;
275     entryp->startline = _nc_start_line;
276     DEBUG(2, ("Comment range is %ld to %ld", entryp->cstart, entryp->cend));
277
278     /*
279      * Strip off the 2-character termcap name, if present.  Originally termcap
280      * used that as an indexing aid.  We can retain 2-character terminfo names,
281      * but note that they would be lost if we translate to/from termcap.  This
282      * feature is supposedly obsolete since "newer" BSD implementations do not
283      * use it; however our reference for this feature is SunOS 4.x, which
284      * implemented it.  Note that the resulting terminal type was never the
285      * 2-character name, but was instead the first alias after that.
286      */
287 #define ok_TC2(s) (isgraph(UChar(s)) && (s) != '|')
288     ptr = _nc_curr_token.tk_name;
289     if (_nc_syntax == SYN_TERMCAP
290 #if NCURSES_XNAMES
291         && !_nc_user_definable
292 #endif
293         ) {
294         if (ok_TC2(ptr[0]) && ok_TC2(ptr[1]) && (ptr[2] == '|')) {
295             ptr += 3;
296             _nc_curr_token.tk_name[2] = '\0';
297         }
298     }
299
300     entryp->tterm.str_table = entryp->tterm.term_names = _nc_save_str(ptr);
301
302     if (entryp->tterm.str_table == 0)
303         return (ERR);
304
305     DEBUG(1, ("Starting '%s'", ptr));
306
307     /*
308      * We do this because the one-token lookahead in the parse loop
309      * results in the terminal type getting prematurely set to correspond
310      * to that of the next entry.
311      */
312     name = _nc_first_name(entryp->tterm.term_names);
313     if (!valid_entryname(name)) {
314         _nc_warning("invalid entry name \"%s\"", name);
315         name = "invalid";
316     }
317     _nc_set_type(name);
318
319     /* check for overly-long names and aliases */
320     for (base = entryp->tterm.term_names; (ptr = strchr(base, '|')) != 0;
321          base = ptr + 1) {
322         if (ptr - base > MAX_ALIAS) {
323             _nc_warning("%s `%.*s' may be too long",
324                         (base == entryp->tterm.term_names)
325                         ? "primary name"
326                         : "alias",
327                         (int) (ptr - base), base);
328         }
329     }
330
331     entryp->nuses = 0;
332
333     for (token_type = _nc_get_token(silent);
334          token_type != EOF && token_type != NAMES;
335          token_type = _nc_get_token(silent)) {
336         bool is_use = (strcmp(_nc_curr_token.tk_name, "use") == 0);
337         bool is_tc = !is_use && (strcmp(_nc_curr_token.tk_name, "tc") == 0);
338         if (is_use || is_tc) {
339             if (!VALID_STRING(_nc_curr_token.tk_valstring)
340                 || _nc_curr_token.tk_valstring[0] == '\0') {
341                 _nc_warning("missing name for use-clause");
342                 continue;
343             } else if (!valid_entryname(_nc_curr_token.tk_valstring)) {
344                 _nc_warning("invalid name for use-clause \"%s\"",
345                             _nc_curr_token.tk_valstring);
346                 continue;
347             } else if (entryp->nuses >= MAX_USES) {
348                 _nc_warning("too many use-clauses, ignored \"%s\"",
349                             _nc_curr_token.tk_valstring);
350                 continue;
351             }
352             entryp->uses[entryp->nuses].name = _nc_save_str(_nc_curr_token.tk_valstring);
353             entryp->uses[entryp->nuses].line = _nc_curr_line;
354             entryp->nuses++;
355             if (entryp->nuses > 1 && is_tc) {
356                 BAD_TC_USAGE
357             }
358         } else {
359             /* normal token lookup */
360             entry_ptr = _nc_find_entry(_nc_curr_token.tk_name,
361                                        _nc_get_hash_table(_nc_syntax));
362
363             /*
364              * Our kluge to handle aliasing.  The reason it's done
365              * this ugly way, with a linear search, is so the hashing
366              * machinery doesn't have to be made really complicated
367              * (also we get better warnings this way).  No point in
368              * making this case fast, aliased caps aren't common now
369              * and will get rarer.
370              */
371             if (entry_ptr == NOTFOUND) {
372                 const struct alias *ap;
373
374                 if (_nc_syntax == SYN_TERMCAP) {
375                     if (entryp->nuses != 0) {
376                         BAD_TC_USAGE
377                     }
378                     for (ap = _nc_get_alias_table(TRUE); ap->from; ap++)
379                         if (strcmp(ap->from, _nc_curr_token.tk_name) == 0) {
380                             if (ap->to == (char *) 0) {
381                                 _nc_warning("%s (%s termcap extension) ignored",
382                                             ap->from, ap->source);
383                                 goto nexttok;
384                             }
385
386                             entry_ptr = _nc_find_entry(ap->to,
387                                                        _nc_get_hash_table(TRUE));
388                             if (entry_ptr && !silent)
389                                 _nc_warning("%s (%s termcap extension) aliased to %s",
390                                             ap->from, ap->source, ap->to);
391                             break;
392                         }
393                 } else {        /* if (_nc_syntax == SYN_TERMINFO) */
394                     for (ap = _nc_get_alias_table(FALSE); ap->from; ap++)
395                         if (strcmp(ap->from, _nc_curr_token.tk_name) == 0) {
396                             if (ap->to == (char *) 0) {
397                                 _nc_warning("%s (%s terminfo extension) ignored",
398                                             ap->from, ap->source);
399                                 goto nexttok;
400                             }
401
402                             entry_ptr = _nc_find_entry(ap->to,
403                                                        _nc_get_hash_table(FALSE));
404                             if (entry_ptr && !silent)
405                                 _nc_warning("%s (%s terminfo extension) aliased to %s",
406                                             ap->from, ap->source, ap->to);
407                             break;
408                         }
409
410                     if (entry_ptr == NOTFOUND) {
411                         entry_ptr = lookup_fullname(_nc_curr_token.tk_name);
412                     }
413                 }
414             }
415 #if NCURSES_XNAMES
416             /*
417              * If we have extended-names active, we will automatically
418              * define a name based on its context.
419              */
420             if (entry_ptr == NOTFOUND
421                 && _nc_user_definable) {
422                 if (expected_type(_nc_curr_token.tk_name, token_type, silent)) {
423                     if ((entry_ptr = _nc_extend_names(entryp,
424                                                       _nc_curr_token.tk_name,
425                                                       token_type)) != 0) {
426                         if (_nc_tracing >= DEBUG_LEVEL(1)) {
427                             _nc_warning("extended capability '%s'",
428                                         _nc_curr_token.tk_name);
429                         }
430                     }
431                 } else {
432                     /* ignore it: we have already printed error message */
433                     continue;
434                 }
435             }
436 #endif /* NCURSES_XNAMES */
437
438             /* can't find this cap name, not even as an alias */
439             if (entry_ptr == NOTFOUND) {
440                 if (!silent)
441                     _nc_warning("unknown capability '%s'",
442                                 _nc_curr_token.tk_name);
443                 continue;
444             }
445
446             /* deal with bad type/value combinations. */
447             if (token_type == CANCEL) {
448                 /*
449                  * Prefer terminfo in this (long-obsolete) ambiguity:
450                  */
451                 if (!strcmp("ma", _nc_curr_token.tk_name)) {
452                     entry_ptr = _nc_find_type_entry("ma", NUMBER,
453                                                     _nc_syntax != 0);
454                     assert(entry_ptr != 0);
455                 }
456             } else if (entry_ptr->nte_type != token_type) {
457                 /*
458                  * Nasty special cases here handle situations in which type
459                  * information can resolve name clashes.  Normal lookup
460                  * finds the last instance in the capability table of a
461                  * given name, regardless of type.  find_type_entry looks
462                  * for a first matching instance with given type.  So as
463                  * long as all ambiguous names occur in pairs of distinct
464                  * type, this will do the job.
465                  */
466
467                 if (token_type == NUMBER
468                     && !strcmp("ma", _nc_curr_token.tk_name)) {
469                     /* tell max_attributes from arrow_key_map */
470                     entry_ptr = _nc_find_type_entry("ma", NUMBER,
471                                                     _nc_syntax != 0);
472                     assert(entry_ptr != 0);
473
474                 } else if (token_type == STRING
475                            && !strcmp("MT", _nc_curr_token.tk_name)) {
476                     /* map terminfo's string MT to MT */
477                     entry_ptr = _nc_find_type_entry("MT", STRING,
478                                                     _nc_syntax != 0);
479                     assert(entry_ptr != 0);
480
481                 } else if (token_type == BOOLEAN
482                            && entry_ptr->nte_type == STRING) {
483                     /* treat strings without following "=" as empty strings */
484                     token_type = STRING;
485                 } else {
486                     /* we couldn't recover; skip this token */
487                     if (!silent) {
488                         const char *type_name;
489                         switch (entry_ptr->nte_type) {
490                         case BOOLEAN:
491                             type_name = "boolean";
492                             break;
493                         case STRING:
494                             type_name = "string";
495                             break;
496                         case NUMBER:
497                             type_name = "numeric";
498                             break;
499                         default:
500                             type_name = "unknown";
501                             break;
502                         }
503                         _nc_warning("wrong type used for %s capability '%s'",
504                                     type_name, _nc_curr_token.tk_name);
505                     }
506                     continue;
507                 }
508             }
509
510             /* now we know that the type/value combination is OK */
511             switch (token_type) {
512             case CANCEL:
513                 switch (entry_ptr->nte_type) {
514                 case BOOLEAN:
515                     entryp->tterm.Booleans[entry_ptr->nte_index] = CANCELLED_BOOLEAN;
516                     break;
517
518                 case NUMBER:
519                     entryp->tterm.Numbers[entry_ptr->nte_index] = CANCELLED_NUMERIC;
520                     break;
521
522                 case STRING:
523                     entryp->tterm.Strings[entry_ptr->nte_index] = CANCELLED_STRING;
524                     break;
525                 }
526                 break;
527
528             case BOOLEAN:
529                 entryp->tterm.Booleans[entry_ptr->nte_index] = TRUE;
530                 break;
531
532             case NUMBER:
533 #if !NCURSES_EXT_NUMBERS
534                 if (_nc_curr_token.tk_valnumber > MAX_NUMBER) {
535                     entryp->tterm.Numbers[entry_ptr->nte_index] = MAX_NUMBER;
536                 } else
537 #endif
538                 {
539                     entryp->tterm.Numbers[entry_ptr->nte_index] =
540                         (NCURSES_INT2) _nc_curr_token.tk_valnumber;
541                 }
542                 break;
543
544             case STRING:
545                 ptr = _nc_curr_token.tk_valstring;
546                 if (_nc_syntax == SYN_TERMCAP)
547                     ptr = _nc_captoinfo(_nc_curr_token.tk_name,
548                                         ptr,
549                                         parametrized[entry_ptr->nte_index]);
550                 entryp->tterm.Strings[entry_ptr->nte_index] = _nc_save_str(ptr);
551                 break;
552
553             default:
554                 if (!silent)
555                     _nc_warning("unknown token type");
556                 _nc_panic_mode((char) ((_nc_syntax == SYN_TERMCAP) ? ':' : ','));
557                 continue;
558             }
559         }                       /* end else cur_token.name != "use" */
560       nexttok:
561         continue;               /* cannot have a label w/o statement */
562     }                           /* endwhile (not EOF and not NAMES) */
563
564     _nc_push_token(token_type);
565     _nc_set_type(_nc_first_name(entryp->tterm.term_names));
566
567     /*
568      * Try to deduce as much as possible from extension capabilities
569      * (this includes obsolete BSD capabilities).  Sigh...it would be more
570      * space-efficient to call this after use resolution, but it has
571      * to be done before entry allocation is wrapped up.
572      */
573     if (!literal) {
574         if (_nc_syntax == SYN_TERMCAP) {
575             bool has_base_entry = FALSE;
576
577             /*
578              * Don't insert defaults if this is a `+' entry meant only
579              * for inclusion in other entries (not sure termcap ever
580              * had these, actually).
581              */
582             if (strchr(entryp->tterm.term_names, '+')) {
583                 has_base_entry = TRUE;
584             } else {
585                 unsigned i;
586                 /*
587                  * Otherwise, look for a base entry that will already
588                  * have picked up defaults via translation.
589                  */
590                 for (i = 0; i < entryp->nuses; i++) {
591                     if (entryp->uses[i].name != 0
592                         && !strchr(entryp->uses[i].name, '+'))
593                         has_base_entry = TRUE;
594                 }
595             }
596
597             postprocess_termcap(&entryp->tterm, has_base_entry);
598         } else
599             postprocess_terminfo(&entryp->tterm);
600     }
601     _nc_wrap_entry(entryp, FALSE);
602
603     return (OK);
604 }
605
606 NCURSES_EXPORT(int)
607 _nc_capcmp(const char *s, const char *t)
608 /* compare two string capabilities, stripping out padding */
609 {
610     bool ok_s = VALID_STRING(s);
611     bool ok_t = VALID_STRING(t);
612
613     if (ok_s && ok_t) {
614         for (;;) {
615             if (s[0] == '$' && s[1] == '<') {
616                 for (s += 2;; s++) {
617                     if (!(isdigit(UChar(*s))
618                           || *s == '.'
619                           || *s == '*'
620                           || *s == '/'
621                           || *s == '>')) {
622                         break;
623                     }
624                 }
625             }
626
627             if (t[0] == '$' && t[1] == '<') {
628                 for (t += 2;; t++) {
629                     if (!(isdigit(UChar(*t))
630                           || *t == '.'
631                           || *t == '*'
632                           || *t == '/'
633                           || *t == '>')) {
634                         break;
635                     }
636                 }
637             }
638
639             /* we've now pushed s and t past any padding they pointed at */
640
641             if (*s == '\0' && *t == '\0')
642                 return (0);
643
644             if (*s != *t)
645                 return (*t - *s);
646
647             /* else *s == *t but one is not NUL, so continue */
648             s++, t++;
649         }
650     } else if (ok_s || ok_t) {
651         return 1;
652     }
653     return 0;
654 }
655
656 static void
657 append_acs0(string_desc * dst, int code, int src)
658 {
659     if (src != 0) {
660         char temp[3];
661         temp[0] = (char) code;
662         temp[1] = (char) src;
663         temp[2] = 0;
664         _nc_safe_strcat(dst, temp);
665     }
666 }
667
668 static void
669 append_acs(string_desc * dst, int code, char *src)
670 {
671     if (VALID_STRING(src) && strlen(src) == 1) {
672         append_acs0(dst, code, *src);
673     }
674 }
675
676 /*
677  * The ko capability, if present, consists of a comma-separated capability
678  * list.  For each capability, we may assume there is a keycap that sends the
679  * string which is the value of that capability.
680  */
681 #define DATA(from, to) { { from }, { to } }
682 typedef struct {
683     const char from[3];
684     const char to[6];
685 } assoc;
686 static assoc const ko_xlate[] =
687 {
688     DATA("al", "kil1"),         /* insert line key  -> KEY_IL    */
689     DATA("bt", "kcbt"),         /* back tab         -> KEY_BTAB  */
690     DATA("cd", "ked"),          /* clear-to-eos key -> KEY_EOL   */
691     DATA("ce", "kel"),          /* clear-to-eol key -> KEY_EOS   */
692     DATA("cl", "kclr"),         /* clear key        -> KEY_CLEAR */
693     DATA("ct", "tbc"),          /* clear all tabs   -> KEY_CATAB */
694     DATA("dc", "kdch1"),        /* delete char      -> KEY_DC    */
695     DATA("dl", "kdl1"),         /* delete line      -> KEY_DL    */
696     DATA("do", "kcud1"),        /* down key         -> KEY_DOWN  */
697     DATA("ei", "krmir"),        /* exit insert key  -> KEY_EIC   */
698     DATA("ho", "khome"),        /* home key         -> KEY_HOME  */
699     DATA("ic", "kich1"),        /* insert char key  -> KEY_IC    */
700     DATA("im", "kIC"),          /* insert-mode key  -> KEY_SIC   */
701     DATA("le", "kcub1"),        /* le key           -> KEY_LEFT  */
702     DATA("nd", "kcuf1"),        /* nd key           -> KEY_RIGHT */
703     DATA("nl", "kent"),         /* new line key     -> KEY_ENTER */
704     DATA("st", "khts"),         /* set-tab key      -> KEY_STAB  */
705     DATA("ta", ""),
706     DATA("up", "kcuu1"),        /* up-arrow key     -> KEY_UP    */
707 };
708
709 /*
710  * This routine fills in string caps that either had defaults under
711  * termcap or can be manufactured from obsolete termcap capabilities.
712  * It was lifted from Ross Ridge's mytinfo package.
713  */
714
715 static const char C_CR[] = "\r";
716 static const char C_LF[] = "\n";
717 static const char C_BS[] = "\b";
718 static const char C_HT[] = "\t";
719
720 /*
721  * This bit of legerdemain turns all the terminfo variable names into
722  * references to locations in the arrays Booleans, Numbers, and Strings ---
723  * precisely what's needed.
724  */
725
726 #undef CUR
727 #define CUR tp->
728
729 static void
730 postprocess_termcap(TERMTYPE2 *tp, bool has_base)
731 {
732     char buf[MAX_LINE * 2 + 2];
733     string_desc result;
734
735     /*
736      * TERMCAP DEFAULTS AND OBSOLETE-CAPABILITY TRANSLATIONS
737      *
738      * This first part of the code is the functional inverse of the
739      * fragment in capdefaults.c.
740      * ----------------------------------------------------------------------
741      */
742
743     /* if there was a tc entry, assume we picked up defaults via that */
744     if (!has_base) {
745         if (WANTED(init_3string) && PRESENT(termcap_init2))
746             init_3string = _nc_save_str(termcap_init2);
747
748         if (WANTED(reset_2string) && PRESENT(termcap_reset))
749             reset_2string = _nc_save_str(termcap_reset);
750
751         if (WANTED(carriage_return)) {
752             if (carriage_return_delay > 0) {
753                 _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
754                             "%s$<%d>", C_CR, carriage_return_delay);
755                 carriage_return = _nc_save_str(buf);
756             } else
757                 carriage_return = _nc_save_str(C_CR);
758         }
759         if (WANTED(cursor_left)) {
760             if (backspace_delay > 0) {
761                 _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
762                             "%s$<%d>", C_BS, backspace_delay);
763                 cursor_left = _nc_save_str(buf);
764             } else if (backspaces_with_bs == 1)
765                 cursor_left = _nc_save_str(C_BS);
766             else if (PRESENT(backspace_if_not_bs))
767                 cursor_left = backspace_if_not_bs;
768         }
769         /* vi doesn't use "do", but it does seem to use nl (or '\n') instead */
770         if (WANTED(cursor_down)) {
771             if (PRESENT(linefeed_if_not_lf))
772                 cursor_down = linefeed_if_not_lf;
773             else if (linefeed_is_newline != 1) {
774                 if (new_line_delay > 0) {
775                     _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
776                                 "%s$<%d>", C_LF, new_line_delay);
777                     cursor_down = _nc_save_str(buf);
778                 } else
779                     cursor_down = _nc_save_str(C_LF);
780             }
781         }
782         if (WANTED(scroll_forward) && crt_no_scrolling != 1) {
783             if (PRESENT(linefeed_if_not_lf))
784                 cursor_down = linefeed_if_not_lf;
785             else if (linefeed_is_newline != 1) {
786                 if (new_line_delay > 0) {
787                     _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
788                                 "%s$<%d>", C_LF, new_line_delay);
789                     scroll_forward = _nc_save_str(buf);
790                 } else
791                     scroll_forward = _nc_save_str(C_LF);
792             }
793         }
794         if (WANTED(newline)) {
795             if (linefeed_is_newline == 1) {
796                 if (new_line_delay > 0) {
797                     _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
798                                 "%s$<%d>", C_LF, new_line_delay);
799                     newline = _nc_save_str(buf);
800                 } else
801                     newline = _nc_save_str(C_LF);
802             } else if (PRESENT(carriage_return) && PRESENT(scroll_forward)) {
803                 _nc_str_init(&result, buf, sizeof(buf));
804                 if (_nc_safe_strcat(&result, carriage_return)
805                     && _nc_safe_strcat(&result, scroll_forward))
806                     newline = _nc_save_str(buf);
807             } else if (PRESENT(carriage_return) && PRESENT(cursor_down)) {
808                 _nc_str_init(&result, buf, sizeof(buf));
809                 if (_nc_safe_strcat(&result, carriage_return)
810                     && _nc_safe_strcat(&result, cursor_down))
811                     newline = _nc_save_str(buf);
812             }
813         }
814     }
815
816     /*
817      * Inverse of capdefaults.c code ends here.
818      * ----------------------------------------------------------------------
819      *
820      * TERMCAP-TO TERMINFO MAPPINGS FOR SOURCE TRANSLATION
821      *
822      * These translations will *not* be inverted by tgetent().
823      */
824
825     if (!has_base) {
826         /*
827          * We wait until now to decide if we've got a working cr because even
828          * one that doesn't work can be used for newline. Unfortunately the
829          * space allocated for it is wasted.
830          */
831         if (return_does_clr_eol == 1 || no_correctly_working_cr == 1)
832             carriage_return = ABSENT_STRING;
833
834         /*
835          * Supposedly most termcap entries have ta now and '\t' is no longer a
836          * default, but it doesn't seem to be true...
837          */
838         if (WANTED(tab)) {
839             if (horizontal_tab_delay > 0) {
840                 _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
841                             "%s$<%d>", C_HT, horizontal_tab_delay);
842                 tab = _nc_save_str(buf);
843             } else
844                 tab = _nc_save_str(C_HT);
845         }
846         if (init_tabs == ABSENT_NUMERIC && has_hardware_tabs == TRUE)
847             init_tabs = 8;
848
849         /*
850          * Assume we can beep with ^G unless we're given bl@.
851          */
852         if (WANTED(bell))
853             bell = _nc_save_str("\007");
854     }
855
856     /*
857      * Translate the old termcap :pt: capability to it#8 + ht=\t
858      */
859     if (has_hardware_tabs == TRUE) {
860         if (init_tabs != 8 && init_tabs != ABSENT_NUMERIC)
861             _nc_warning("hardware tabs with a width other than 8: %d", init_tabs);
862         else {
863             if (PRESENT(tab) && _nc_capcmp(tab, C_HT))
864                 _nc_warning("hardware tabs with a non-^I tab string %s",
865                             _nc_visbuf(tab));
866             else {
867                 if (WANTED(tab))
868                     tab = _nc_save_str(C_HT);
869                 init_tabs = 8;
870             }
871         }
872     }
873     /*
874      * Now translate the ko capability, if there is one.  This
875      * isn't from mytinfo...
876      */
877     if (PRESENT(other_non_function_keys)) {
878         char *base;
879         char *bp, *cp, *dp;
880         struct name_table_entry const *from_ptr;
881         struct name_table_entry const *to_ptr;
882         char buf2[MAX_TERMINFO_LENGTH];
883         bool foundim;
884
885         /* we're going to use this for a special case later */
886         dp = strchr(other_non_function_keys, 'i');
887         foundim = (dp != 0) && (dp[1] == 'm');
888
889         /* look at each comma-separated capability in the ko string... */
890         for (base = other_non_function_keys;
891              (cp = strchr(base, ',')) != 0;
892              base = cp + 1) {
893             size_t len = (unsigned) (cp - base);
894             size_t n;
895             assoc const *ap = 0;
896
897             for (n = 0; n < SIZEOF(ko_xlate); ++n) {
898                 if (len == strlen(ko_xlate[n].from)
899                     && strncmp(ko_xlate[n].from, base, len) == 0) {
900                     ap = ko_xlate + n;
901                     break;
902                 }
903             }
904             if (ap == 0) {
905                 _nc_warning("unknown capability `%.*s' in ko string",
906                             (int) len, base);
907                 continue;
908             } else if (ap->to[0] == '\0')       /* ignore it */
909                 continue;
910
911             /* now we know we found a match in ko_table, so... */
912
913             from_ptr = _nc_find_entry(ap->from, _nc_get_hash_table(TRUE));
914             to_ptr = _nc_find_entry(ap->to, _nc_get_hash_table(FALSE));
915
916             if (!from_ptr || !to_ptr)   /* should never happen! */
917                 _nc_err_abort("ko translation table is invalid, I give up");
918
919             if (WANTED(tp->Strings[from_ptr->nte_index])) {
920                 _nc_warning("no value for ko capability %s", ap->from);
921                 continue;
922             }
923
924             if (tp->Strings[to_ptr->nte_index]) {
925                 const char *s = tp->Strings[from_ptr->nte_index];
926                 const char *t = tp->Strings[to_ptr->nte_index];
927                 /* There's no point in warning about it if it's the same
928                  * string; that's just an inefficiency.
929                  */
930                 if (VALID_STRING(s) && VALID_STRING(t) && strcmp(s, t) != 0)
931                     _nc_warning("%s (%s) already has an explicit value %s, ignoring ko",
932                                 ap->to, ap->from, t);
933                 continue;
934             }
935
936             /*
937              * The magic moment -- copy the mapped key string over,
938              * stripping out padding.
939              */
940             bp = tp->Strings[from_ptr->nte_index];
941             if (VALID_STRING(bp)) {
942                 for (dp = buf2; *bp; bp++) {
943                     if (bp[0] == '$' && bp[1] == '<') {
944                         while (*bp && *bp != '>') {
945                             ++bp;
946                         }
947                     } else
948                         *dp++ = *bp;
949                 }
950                 *dp = '\0';
951
952                 tp->Strings[to_ptr->nte_index] = _nc_save_str(buf2);
953             } else {
954                 tp->Strings[to_ptr->nte_index] = bp;
955             }
956         }
957
958         /*
959          * Note: ko=im and ko=ic both want to grab the `Insert'
960          * keycap.  There's a kich1 but no ksmir, so the ic capability
961          * got mapped to kich1 and im to kIC to avoid a collision.
962          * If the description has im but not ic, hack kIC back to kich1.
963          */
964         if (foundim && WANTED(key_ic) && PRESENT(key_sic)) {
965             key_ic = key_sic;
966             key_sic = ABSENT_STRING;
967         }
968     }
969
970     if (!has_base) {
971         if (!hard_copy) {
972             if (WANTED(key_backspace))
973                 key_backspace = _nc_save_str(C_BS);
974             if (WANTED(key_left))
975                 key_left = _nc_save_str(C_BS);
976             if (WANTED(key_down))
977                 key_down = _nc_save_str(C_LF);
978         }
979     }
980
981     /*
982      * Translate XENIX forms characters.
983      */
984     if (PRESENT(acs_ulcorner) ||
985         PRESENT(acs_llcorner) ||
986         PRESENT(acs_urcorner) ||
987         PRESENT(acs_lrcorner) ||
988         PRESENT(acs_ltee) ||
989         PRESENT(acs_rtee) ||
990         PRESENT(acs_btee) ||
991         PRESENT(acs_ttee) ||
992         PRESENT(acs_hline) ||
993         PRESENT(acs_vline) ||
994         PRESENT(acs_plus)) {
995         char buf2[MAX_TERMCAP_LENGTH];
996
997         _nc_str_init(&result, buf2, sizeof(buf2));
998         _nc_safe_strcat(&result, acs_chars);
999
1000         append_acs(&result, 'j', acs_lrcorner);
1001         append_acs(&result, 'k', acs_urcorner);
1002         append_acs(&result, 'l', acs_ulcorner);
1003         append_acs(&result, 'm', acs_llcorner);
1004         append_acs(&result, 'n', acs_plus);
1005         append_acs(&result, 'q', acs_hline);
1006         append_acs(&result, 't', acs_ltee);
1007         append_acs(&result, 'u', acs_rtee);
1008         append_acs(&result, 'v', acs_btee);
1009         append_acs(&result, 'w', acs_ttee);
1010         append_acs(&result, 'x', acs_vline);
1011
1012         if (buf2[0]) {
1013             acs_chars = _nc_save_str(buf2);
1014             _nc_warning("acsc string synthesized from XENIX capabilities");
1015         }
1016     } else if (acs_chars == ABSENT_STRING
1017                && PRESENT(enter_alt_charset_mode)
1018                && PRESENT(exit_alt_charset_mode)) {
1019         acs_chars = _nc_save_str(VT_ACSC);
1020     }
1021 }
1022
1023 static void
1024 postprocess_terminfo(TERMTYPE2 *tp)
1025 {
1026     /*
1027      * TERMINFO-TO-TERMINFO MAPPINGS FOR SOURCE TRANSLATION
1028      * ----------------------------------------------------------------------
1029      */
1030
1031     /*
1032      * Translate AIX forms characters.
1033      */
1034     if (PRESENT(box_chars_1)) {
1035         char buf2[MAX_TERMCAP_LENGTH];
1036         string_desc result;
1037
1038         _nc_str_init(&result, buf2, sizeof(buf2));
1039         _nc_safe_strcat(&result, acs_chars);
1040
1041         append_acs0(&result, 'l', box_chars_1[0]);      /* ACS_ULCORNER */
1042         append_acs0(&result, 'q', box_chars_1[1]);      /* ACS_HLINE */
1043         append_acs0(&result, 'k', box_chars_1[2]);      /* ACS_URCORNER */
1044         append_acs0(&result, 'x', box_chars_1[3]);      /* ACS_VLINE */
1045         append_acs0(&result, 'j', box_chars_1[4]);      /* ACS_LRCORNER */
1046         append_acs0(&result, 'm', box_chars_1[5]);      /* ACS_LLCORNER */
1047         append_acs0(&result, 'w', box_chars_1[6]);      /* ACS_TTEE */
1048         append_acs0(&result, 'u', box_chars_1[7]);      /* ACS_RTEE */
1049         append_acs0(&result, 'v', box_chars_1[8]);      /* ACS_BTEE */
1050         append_acs0(&result, 't', box_chars_1[9]);      /* ACS_LTEE */
1051         append_acs0(&result, 'n', box_chars_1[10]);     /* ACS_PLUS */
1052
1053         if (buf2[0]) {
1054             acs_chars = _nc_save_str(buf2);
1055             _nc_warning("acsc string synthesized from AIX capabilities");
1056             box_chars_1 = ABSENT_STRING;
1057         }
1058     }
1059     /*
1060      * ----------------------------------------------------------------------
1061      */
1062 }
1063
1064 /*
1065  * Do a linear search through the terminfo tables to find a given full-name.
1066  * We don't expect to do this often, so there's no hashing function.
1067  *
1068  * In effect, this scans through the 3 lists of full-names, and looks them
1069  * up in _nc_info_table, which is organized so that the nte_index fields are
1070  * sorted, but the nte_type fields are not necessarily grouped together.
1071  */
1072 static struct name_table_entry const *
1073 lookup_fullname(const char *find)
1074 {
1075     int state = -1;
1076
1077     for (;;) {
1078         int count = 0;
1079         NCURSES_CONST char *const *names;
1080
1081         switch (++state) {
1082         case BOOLEAN:
1083             names = boolfnames;
1084             break;
1085         case STRING:
1086             names = strfnames;
1087             break;
1088         case NUMBER:
1089             names = numfnames;
1090             break;
1091         default:
1092             return NOTFOUND;
1093         }
1094
1095         for (count = 0; names[count] != 0; count++) {
1096             if (!strcmp(names[count], find)) {
1097                 struct name_table_entry const *entry_ptr = _nc_get_table(FALSE);
1098                 while (entry_ptr->nte_type != state
1099                        || entry_ptr->nte_index != count)
1100                     entry_ptr++;
1101                 return entry_ptr;
1102             }
1103         }
1104     }
1105 }
1106
1107 /* parse_entry.c ends here */