]> ncurses.scripts.mit.edu Git - ncurses.git/blob - ncurses/tinfo/parse_entry.c
ncurses 6.1 - patch 20190504
[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.96 2019/03/16 23:31:40 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 (_nc_curr_token.tk_valnumber > MAX_NUMBER) {
534                     entryp->tterm.Numbers[entry_ptr->nte_index] = MAX_NUMBER;
535                 } else {
536                     entryp->tterm.Numbers[entry_ptr->nte_index] =
537                         (NCURSES_INT2) _nc_curr_token.tk_valnumber;
538                 }
539                 break;
540
541             case STRING:
542                 ptr = _nc_curr_token.tk_valstring;
543                 if (_nc_syntax == SYN_TERMCAP)
544                     ptr = _nc_captoinfo(_nc_curr_token.tk_name,
545                                         ptr,
546                                         parametrized[entry_ptr->nte_index]);
547                 entryp->tterm.Strings[entry_ptr->nte_index] = _nc_save_str(ptr);
548                 break;
549
550             default:
551                 if (!silent)
552                     _nc_warning("unknown token type");
553                 _nc_panic_mode((char) ((_nc_syntax == SYN_TERMCAP) ? ':' : ','));
554                 continue;
555             }
556         }                       /* end else cur_token.name != "use" */
557       nexttok:
558         continue;               /* cannot have a label w/o statement */
559     }                           /* endwhile (not EOF and not NAMES) */
560
561     _nc_push_token(token_type);
562     _nc_set_type(_nc_first_name(entryp->tterm.term_names));
563
564     /*
565      * Try to deduce as much as possible from extension capabilities
566      * (this includes obsolete BSD capabilities).  Sigh...it would be more
567      * space-efficient to call this after use resolution, but it has
568      * to be done before entry allocation is wrapped up.
569      */
570     if (!literal) {
571         if (_nc_syntax == SYN_TERMCAP) {
572             bool has_base_entry = FALSE;
573
574             /*
575              * Don't insert defaults if this is a `+' entry meant only
576              * for inclusion in other entries (not sure termcap ever
577              * had these, actually).
578              */
579             if (strchr(entryp->tterm.term_names, '+')) {
580                 has_base_entry = TRUE;
581             } else {
582                 unsigned i;
583                 /*
584                  * Otherwise, look for a base entry that will already
585                  * have picked up defaults via translation.
586                  */
587                 for (i = 0; i < entryp->nuses; i++) {
588                     if (entryp->uses[i].name != 0
589                         && !strchr(entryp->uses[i].name, '+'))
590                         has_base_entry = TRUE;
591                 }
592             }
593
594             postprocess_termcap(&entryp->tterm, has_base_entry);
595         } else
596             postprocess_terminfo(&entryp->tterm);
597     }
598     _nc_wrap_entry(entryp, FALSE);
599
600     return (OK);
601 }
602
603 NCURSES_EXPORT(int)
604 _nc_capcmp(const char *s, const char *t)
605 /* compare two string capabilities, stripping out padding */
606 {
607     bool ok_s = VALID_STRING(s);
608     bool ok_t = VALID_STRING(t);
609
610     if (ok_s && ok_t) {
611         for (;;) {
612             if (s[0] == '$' && s[1] == '<') {
613                 for (s += 2;; s++) {
614                     if (!(isdigit(UChar(*s))
615                           || *s == '.'
616                           || *s == '*'
617                           || *s == '/'
618                           || *s == '>')) {
619                         break;
620                     }
621                 }
622             }
623
624             if (t[0] == '$' && t[1] == '<') {
625                 for (t += 2;; t++) {
626                     if (!(isdigit(UChar(*t))
627                           || *t == '.'
628                           || *t == '*'
629                           || *t == '/'
630                           || *t == '>')) {
631                         break;
632                     }
633                 }
634             }
635
636             /* we've now pushed s and t past any padding they pointed at */
637
638             if (*s == '\0' && *t == '\0')
639                 return (0);
640
641             if (*s != *t)
642                 return (*t - *s);
643
644             /* else *s == *t but one is not NUL, so continue */
645             s++, t++;
646         }
647     } else if (ok_s || ok_t) {
648         return 1;
649     }
650     return 0;
651 }
652
653 static void
654 append_acs0(string_desc * dst, int code, int src)
655 {
656     if (src != 0) {
657         char temp[3];
658         temp[0] = (char) code;
659         temp[1] = (char) src;
660         temp[2] = 0;
661         _nc_safe_strcat(dst, temp);
662     }
663 }
664
665 static void
666 append_acs(string_desc * dst, int code, char *src)
667 {
668     if (VALID_STRING(src) && strlen(src) == 1) {
669         append_acs0(dst, code, *src);
670     }
671 }
672
673 /*
674  * The ko capability, if present, consists of a comma-separated capability
675  * list.  For each capability, we may assume there is a keycap that sends the
676  * string which is the value of that capability.
677  */
678 #define DATA(from, to) { { from }, { to } }
679 typedef struct {
680     const char from[3];
681     const char to[6];
682 } assoc;
683 static assoc const ko_xlate[] =
684 {
685     DATA("al", "kil1"),         /* insert line key  -> KEY_IL    */
686     DATA("bt", "kcbt"),         /* back tab         -> KEY_BTAB  */
687     DATA("cd", "ked"),          /* clear-to-eos key -> KEY_EOL   */
688     DATA("ce", "kel"),          /* clear-to-eol key -> KEY_EOS   */
689     DATA("cl", "kclr"),         /* clear key        -> KEY_CLEAR */
690     DATA("ct", "tbc"),          /* clear all tabs   -> KEY_CATAB */
691     DATA("dc", "kdch1"),        /* delete char      -> KEY_DC    */
692     DATA("dl", "kdl1"),         /* delete line      -> KEY_DL    */
693     DATA("do", "kcud1"),        /* down key         -> KEY_DOWN  */
694     DATA("ei", "krmir"),        /* exit insert key  -> KEY_EIC   */
695     DATA("ho", "khome"),        /* home key         -> KEY_HOME  */
696     DATA("ic", "kich1"),        /* insert char key  -> KEY_IC    */
697     DATA("im", "kIC"),          /* insert-mode key  -> KEY_SIC   */
698     DATA("le", "kcub1"),        /* le key           -> KEY_LEFT  */
699     DATA("nd", "kcuf1"),        /* nd key           -> KEY_RIGHT */
700     DATA("nl", "kent"),         /* new line key     -> KEY_ENTER */
701     DATA("st", "khts"),         /* set-tab key      -> KEY_STAB  */
702     DATA("ta", ""),
703     DATA("up", "kcuu1"),        /* up-arrow key     -> KEY_UP    */
704 };
705
706 /*
707  * This routine fills in string caps that either had defaults under
708  * termcap or can be manufactured from obsolete termcap capabilities.
709  * It was lifted from Ross Ridge's mytinfo package.
710  */
711
712 static const char C_CR[] = "\r";
713 static const char C_LF[] = "\n";
714 static const char C_BS[] = "\b";
715 static const char C_HT[] = "\t";
716
717 /*
718  * This bit of legerdemain turns all the terminfo variable names into
719  * references to locations in the arrays Booleans, Numbers, and Strings ---
720  * precisely what's needed.
721  */
722
723 #undef CUR
724 #define CUR tp->
725
726 static void
727 postprocess_termcap(TERMTYPE2 *tp, bool has_base)
728 {
729     char buf[MAX_LINE * 2 + 2];
730     string_desc result;
731
732     /*
733      * TERMCAP DEFAULTS AND OBSOLETE-CAPABILITY TRANSLATIONS
734      *
735      * This first part of the code is the functional inverse of the
736      * fragment in capdefaults.c.
737      * ----------------------------------------------------------------------
738      */
739
740     /* if there was a tc entry, assume we picked up defaults via that */
741     if (!has_base) {
742         if (WANTED(init_3string) && PRESENT(termcap_init2))
743             init_3string = _nc_save_str(termcap_init2);
744
745         if (WANTED(reset_2string) && PRESENT(termcap_reset))
746             reset_2string = _nc_save_str(termcap_reset);
747
748         if (WANTED(carriage_return)) {
749             if (carriage_return_delay > 0) {
750                 _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
751                             "%s$<%d>", C_CR, carriage_return_delay);
752                 carriage_return = _nc_save_str(buf);
753             } else
754                 carriage_return = _nc_save_str(C_CR);
755         }
756         if (WANTED(cursor_left)) {
757             if (backspace_delay > 0) {
758                 _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
759                             "%s$<%d>", C_BS, backspace_delay);
760                 cursor_left = _nc_save_str(buf);
761             } else if (backspaces_with_bs == 1)
762                 cursor_left = _nc_save_str(C_BS);
763             else if (PRESENT(backspace_if_not_bs))
764                 cursor_left = backspace_if_not_bs;
765         }
766         /* vi doesn't use "do", but it does seem to use nl (or '\n') instead */
767         if (WANTED(cursor_down)) {
768             if (PRESENT(linefeed_if_not_lf))
769                 cursor_down = linefeed_if_not_lf;
770             else if (linefeed_is_newline != 1) {
771                 if (new_line_delay > 0) {
772                     _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
773                                 "%s$<%d>", C_LF, new_line_delay);
774                     cursor_down = _nc_save_str(buf);
775                 } else
776                     cursor_down = _nc_save_str(C_LF);
777             }
778         }
779         if (WANTED(scroll_forward) && crt_no_scrolling != 1) {
780             if (PRESENT(linefeed_if_not_lf))
781                 cursor_down = linefeed_if_not_lf;
782             else if (linefeed_is_newline != 1) {
783                 if (new_line_delay > 0) {
784                     _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
785                                 "%s$<%d>", C_LF, new_line_delay);
786                     scroll_forward = _nc_save_str(buf);
787                 } else
788                     scroll_forward = _nc_save_str(C_LF);
789             }
790         }
791         if (WANTED(newline)) {
792             if (linefeed_is_newline == 1) {
793                 if (new_line_delay > 0) {
794                     _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
795                                 "%s$<%d>", C_LF, new_line_delay);
796                     newline = _nc_save_str(buf);
797                 } else
798                     newline = _nc_save_str(C_LF);
799             } else if (PRESENT(carriage_return) && PRESENT(scroll_forward)) {
800                 _nc_str_init(&result, buf, sizeof(buf));
801                 if (_nc_safe_strcat(&result, carriage_return)
802                     && _nc_safe_strcat(&result, scroll_forward))
803                     newline = _nc_save_str(buf);
804             } else if (PRESENT(carriage_return) && PRESENT(cursor_down)) {
805                 _nc_str_init(&result, buf, sizeof(buf));
806                 if (_nc_safe_strcat(&result, carriage_return)
807                     && _nc_safe_strcat(&result, cursor_down))
808                     newline = _nc_save_str(buf);
809             }
810         }
811     }
812
813     /*
814      * Inverse of capdefaults.c code ends here.
815      * ----------------------------------------------------------------------
816      *
817      * TERMCAP-TO TERMINFO MAPPINGS FOR SOURCE TRANSLATION
818      *
819      * These translations will *not* be inverted by tgetent().
820      */
821
822     if (!has_base) {
823         /*
824          * We wait until now to decide if we've got a working cr because even
825          * one that doesn't work can be used for newline. Unfortunately the
826          * space allocated for it is wasted.
827          */
828         if (return_does_clr_eol == 1 || no_correctly_working_cr == 1)
829             carriage_return = ABSENT_STRING;
830
831         /*
832          * Supposedly most termcap entries have ta now and '\t' is no longer a
833          * default, but it doesn't seem to be true...
834          */
835         if (WANTED(tab)) {
836             if (horizontal_tab_delay > 0) {
837                 _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
838                             "%s$<%d>", C_HT, horizontal_tab_delay);
839                 tab = _nc_save_str(buf);
840             } else
841                 tab = _nc_save_str(C_HT);
842         }
843         if (init_tabs == ABSENT_NUMERIC && has_hardware_tabs == TRUE)
844             init_tabs = 8;
845
846         /*
847          * Assume we can beep with ^G unless we're given bl@.
848          */
849         if (WANTED(bell))
850             bell = _nc_save_str("\007");
851     }
852
853     /*
854      * Translate the old termcap :pt: capability to it#8 + ht=\t
855      */
856     if (has_hardware_tabs == TRUE) {
857         if (init_tabs != 8 && init_tabs != ABSENT_NUMERIC)
858             _nc_warning("hardware tabs with a width other than 8: %d", init_tabs);
859         else {
860             if (PRESENT(tab) && _nc_capcmp(tab, C_HT))
861                 _nc_warning("hardware tabs with a non-^I tab string %s",
862                             _nc_visbuf(tab));
863             else {
864                 if (WANTED(tab))
865                     tab = _nc_save_str(C_HT);
866                 init_tabs = 8;
867             }
868         }
869     }
870     /*
871      * Now translate the ko capability, if there is one.  This
872      * isn't from mytinfo...
873      */
874     if (PRESENT(other_non_function_keys)) {
875         char *base;
876         char *bp, *cp, *dp;
877         struct name_table_entry const *from_ptr;
878         struct name_table_entry const *to_ptr;
879         char buf2[MAX_TERMINFO_LENGTH];
880         bool foundim;
881
882         /* we're going to use this for a special case later */
883         dp = strchr(other_non_function_keys, 'i');
884         foundim = (dp != 0) && (dp[1] == 'm');
885
886         /* look at each comma-separated capability in the ko string... */
887         for (base = other_non_function_keys;
888              (cp = strchr(base, ',')) != 0;
889              base = cp + 1) {
890             size_t len = (unsigned) (cp - base);
891             size_t n;
892             assoc const *ap = 0;
893
894             for (n = 0; n < SIZEOF(ko_xlate); ++n) {
895                 if (len == strlen(ko_xlate[n].from)
896                     && strncmp(ko_xlate[n].from, base, len) == 0) {
897                     ap = ko_xlate + n;
898                     break;
899                 }
900             }
901             if (ap == 0) {
902                 _nc_warning("unknown capability `%.*s' in ko string",
903                             (int) len, base);
904                 continue;
905             } else if (ap->to[0] == '\0')       /* ignore it */
906                 continue;
907
908             /* now we know we found a match in ko_table, so... */
909
910             from_ptr = _nc_find_entry(ap->from, _nc_get_hash_table(TRUE));
911             to_ptr = _nc_find_entry(ap->to, _nc_get_hash_table(FALSE));
912
913             if (!from_ptr || !to_ptr)   /* should never happen! */
914                 _nc_err_abort("ko translation table is invalid, I give up");
915
916             if (WANTED(tp->Strings[from_ptr->nte_index])) {
917                 _nc_warning("no value for ko capability %s", ap->from);
918                 continue;
919             }
920
921             if (tp->Strings[to_ptr->nte_index]) {
922                 const char *s = tp->Strings[from_ptr->nte_index];
923                 const char *t = tp->Strings[to_ptr->nte_index];
924                 /* There's no point in warning about it if it's the same
925                  * string; that's just an inefficiency.
926                  */
927                 if (VALID_STRING(s) && VALID_STRING(t) && strcmp(s, t) != 0)
928                     _nc_warning("%s (%s) already has an explicit value %s, ignoring ko",
929                                 ap->to, ap->from, t);
930                 continue;
931             }
932
933             /*
934              * The magic moment -- copy the mapped key string over,
935              * stripping out padding.
936              */
937             bp = tp->Strings[from_ptr->nte_index];
938             if (VALID_STRING(bp)) {
939                 for (dp = buf2; *bp; bp++) {
940                     if (bp[0] == '$' && bp[1] == '<') {
941                         while (*bp && *bp != '>') {
942                             ++bp;
943                         }
944                     } else
945                         *dp++ = *bp;
946                 }
947                 *dp = '\0';
948
949                 tp->Strings[to_ptr->nte_index] = _nc_save_str(buf2);
950             } else {
951                 tp->Strings[to_ptr->nte_index] = bp;
952             }
953         }
954
955         /*
956          * Note: ko=im and ko=ic both want to grab the `Insert'
957          * keycap.  There's a kich1 but no ksmir, so the ic capability
958          * got mapped to kich1 and im to kIC to avoid a collision.
959          * If the description has im but not ic, hack kIC back to kich1.
960          */
961         if (foundim && WANTED(key_ic) && PRESENT(key_sic)) {
962             key_ic = key_sic;
963             key_sic = ABSENT_STRING;
964         }
965     }
966
967     if (!has_base) {
968         if (!hard_copy) {
969             if (WANTED(key_backspace))
970                 key_backspace = _nc_save_str(C_BS);
971             if (WANTED(key_left))
972                 key_left = _nc_save_str(C_BS);
973             if (WANTED(key_down))
974                 key_down = _nc_save_str(C_LF);
975         }
976     }
977
978     /*
979      * Translate XENIX forms characters.
980      */
981     if (PRESENT(acs_ulcorner) ||
982         PRESENT(acs_llcorner) ||
983         PRESENT(acs_urcorner) ||
984         PRESENT(acs_lrcorner) ||
985         PRESENT(acs_ltee) ||
986         PRESENT(acs_rtee) ||
987         PRESENT(acs_btee) ||
988         PRESENT(acs_ttee) ||
989         PRESENT(acs_hline) ||
990         PRESENT(acs_vline) ||
991         PRESENT(acs_plus)) {
992         char buf2[MAX_TERMCAP_LENGTH];
993
994         _nc_str_init(&result, buf2, sizeof(buf2));
995         _nc_safe_strcat(&result, acs_chars);
996
997         append_acs(&result, 'j', acs_lrcorner);
998         append_acs(&result, 'k', acs_urcorner);
999         append_acs(&result, 'l', acs_ulcorner);
1000         append_acs(&result, 'm', acs_llcorner);
1001         append_acs(&result, 'n', acs_plus);
1002         append_acs(&result, 'q', acs_hline);
1003         append_acs(&result, 't', acs_ltee);
1004         append_acs(&result, 'u', acs_rtee);
1005         append_acs(&result, 'v', acs_btee);
1006         append_acs(&result, 'w', acs_ttee);
1007         append_acs(&result, 'x', acs_vline);
1008
1009         if (buf2[0]) {
1010             acs_chars = _nc_save_str(buf2);
1011             _nc_warning("acsc string synthesized from XENIX capabilities");
1012         }
1013     } else if (acs_chars == ABSENT_STRING
1014                && PRESENT(enter_alt_charset_mode)
1015                && PRESENT(exit_alt_charset_mode)) {
1016         acs_chars = _nc_save_str(VT_ACSC);
1017     }
1018 }
1019
1020 static void
1021 postprocess_terminfo(TERMTYPE2 *tp)
1022 {
1023     /*
1024      * TERMINFO-TO-TERMINFO MAPPINGS FOR SOURCE TRANSLATION
1025      * ----------------------------------------------------------------------
1026      */
1027
1028     /*
1029      * Translate AIX forms characters.
1030      */
1031     if (PRESENT(box_chars_1)) {
1032         char buf2[MAX_TERMCAP_LENGTH];
1033         string_desc result;
1034
1035         _nc_str_init(&result, buf2, sizeof(buf2));
1036         _nc_safe_strcat(&result, acs_chars);
1037
1038         append_acs0(&result, 'l', box_chars_1[0]);      /* ACS_ULCORNER */
1039         append_acs0(&result, 'q', box_chars_1[1]);      /* ACS_HLINE */
1040         append_acs0(&result, 'k', box_chars_1[2]);      /* ACS_URCORNER */
1041         append_acs0(&result, 'x', box_chars_1[3]);      /* ACS_VLINE */
1042         append_acs0(&result, 'j', box_chars_1[4]);      /* ACS_LRCORNER */
1043         append_acs0(&result, 'm', box_chars_1[5]);      /* ACS_LLCORNER */
1044         append_acs0(&result, 'w', box_chars_1[6]);      /* ACS_TTEE */
1045         append_acs0(&result, 'u', box_chars_1[7]);      /* ACS_RTEE */
1046         append_acs0(&result, 'v', box_chars_1[8]);      /* ACS_BTEE */
1047         append_acs0(&result, 't', box_chars_1[9]);      /* ACS_LTEE */
1048         append_acs0(&result, 'n', box_chars_1[10]);     /* ACS_PLUS */
1049
1050         if (buf2[0]) {
1051             acs_chars = _nc_save_str(buf2);
1052             _nc_warning("acsc string synthesized from AIX capabilities");
1053             box_chars_1 = ABSENT_STRING;
1054         }
1055     }
1056     /*
1057      * ----------------------------------------------------------------------
1058      */
1059 }
1060
1061 /*
1062  * Do a linear search through the terminfo tables to find a given full-name.
1063  * We don't expect to do this often, so there's no hashing function.
1064  *
1065  * In effect, this scans through the 3 lists of full-names, and looks them
1066  * up in _nc_info_table, which is organized so that the nte_index fields are
1067  * sorted, but the nte_type fields are not necessarily grouped together.
1068  */
1069 static struct name_table_entry const *
1070 lookup_fullname(const char *find)
1071 {
1072     int state = -1;
1073
1074     for (;;) {
1075         int count = 0;
1076         NCURSES_CONST char *const *names;
1077
1078         switch (++state) {
1079         case BOOLEAN:
1080             names = boolfnames;
1081             break;
1082         case STRING:
1083             names = strfnames;
1084             break;
1085         case NUMBER:
1086             names = numfnames;
1087             break;
1088         default:
1089             return NOTFOUND;
1090         }
1091
1092         for (count = 0; names[count] != 0; count++) {
1093             if (!strcmp(names[count], find)) {
1094                 struct name_table_entry const *entry_ptr = _nc_get_table(FALSE);
1095                 while (entry_ptr->nte_type != state
1096                        || entry_ptr->nte_index != count)
1097                     entry_ptr++;
1098                 return entry_ptr;
1099             }
1100         }
1101     }
1102 }
1103
1104 /* parse_entry.c ends here */