ncurses 5.3
[ncurses.git] / ncurses / tinfo / parse_entry.c
1 /****************************************************************************
2  * Copyright (c) 1998-2001,2002 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  ****************************************************************************/
33
34 /*
35  *      parse_entry.c -- compile one terminfo or termcap entry
36  *
37  *      Get an exact in-core representation of an entry.  Don't
38  *      try to resolve use or tc capabilities, that is someone
39  *      else's job.  Depends on the lexical analyzer to get tokens
40  *      from the input stream.
41  */
42
43 #define __INTERNAL_CAPS_VISIBLE
44 #include <curses.priv.h>
45
46 #include <ctype.h>
47 #include <tic.h>
48 #include <term_entry.h>
49
50 MODULE_ID("$Id: parse_entry.c,v 1.57 2002/08/31 17:02:02 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(TERMTYPE *, bool);
60 static void postprocess_terminfo(TERMTYPE *);
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, char *name, int token_type)
67 {
68     static struct name_table_entry temp;
69     TERMTYPE *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 = tp->ext_Booleans + tp->ext_Numbers;
87         tindex = tp->num_Numbers;
88         break;
89     case STRING:
90         first = tp->ext_Booleans + tp->ext_Numbers;
91         last = tp->ext_Strings + first;
92         offset = 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     if (!found) {
140         switch (token_type) {
141         case BOOLEAN:
142             tp->ext_Booleans += 1;
143             tp->num_Booleans += 1;
144             tp->Booleans = typeRealloc(char, tp->num_Booleans, tp->Booleans);
145             for (last = tp->num_Booleans - 1; last > tindex; last--)
146                 tp->Booleans[last] = tp->Booleans[last - 1];
147             break;
148         case NUMBER:
149             tp->ext_Numbers += 1;
150             tp->num_Numbers += 1;
151             tp->Numbers = typeRealloc(short, tp->num_Numbers, tp->Numbers);
152             for (last = tp->num_Numbers - 1; last > tindex; last--)
153                 tp->Numbers[last] = tp->Numbers[last - 1];
154             break;
155         case STRING:
156             tp->ext_Strings += 1;
157             tp->num_Strings += 1;
158             tp->Strings = typeRealloc(char *, tp->num_Strings, tp->Strings);
159             for (last = tp->num_Strings - 1; last > tindex; last--)
160                 tp->Strings[last] = tp->Strings[last - 1];
161             break;
162         }
163         actual = NUM_EXT_NAMES(tp);
164         tp->ext_Names = typeRealloc(char *, actual, tp->ext_Names);
165         while (--actual > offset)
166             tp->ext_Names[actual] = tp->ext_Names[actual - 1];
167         tp->ext_Names[offset] = _nc_save_str(name);
168     }
169
170     temp.nte_name = tp->ext_Names[offset];
171     temp.nte_type = token_type;
172     temp.nte_index = tindex;
173     temp.nte_link = -1;
174
175     return &temp;
176 }
177 #endif /* NCURSES_XNAMES */
178
179 /*
180  *      int
181  *      _nc_parse_entry(entry, literal, silent)
182  *
183  *      Compile one entry.  Doesn't try to resolve use or tc capabilities.
184  *
185  *      found-forward-use = FALSE
186  *      re-initialise internal arrays
187  *      get_token();
188  *      if the token was not a name in column 1, complain and die
189  *      save names in entry's string table
190  *      while (get_token() is not EOF and not NAMES)
191  *              check for existance and type-correctness
192  *              enter cap into structure
193  *              if STRING
194  *                  save string in entry's string table
195  *      push back token
196  */
197
198 NCURSES_EXPORT(int)
199 _nc_parse_entry
200 (struct entry *entryp, int literal, bool silent)
201 {
202     int token_type;
203     struct name_table_entry const *entry_ptr;
204     char *ptr, *base;
205
206     token_type = _nc_get_token(silent);
207
208     if (token_type == EOF)
209         return (EOF);
210     if (token_type != NAMES)
211         _nc_err_abort("Entry does not start with terminal names in column one");
212
213     _nc_init_entry(&entryp->tterm);
214
215     entryp->cstart = _nc_comment_start;
216     entryp->cend = _nc_comment_end;
217     entryp->startline = _nc_start_line;
218     DEBUG(2, ("Comment range is %ld to %ld", entryp->cstart, entryp->cend));
219
220     /* junk the 2-character termcap name, if present */
221     ptr = _nc_curr_token.tk_name;
222     if (ptr[2] == '|') {
223         ptr = _nc_curr_token.tk_name + 3;
224         _nc_curr_token.tk_name[2] = '\0';
225     }
226
227     entryp->tterm.str_table = entryp->tterm.term_names = _nc_save_str(ptr);
228
229     DEBUG(1, ("Starting '%s'", ptr));
230
231     /*
232      * We do this because the one-token lookahead in the parse loop
233      * results in the terminal type getting prematurely set to correspond
234      * to that of the next entry.
235      */
236     _nc_set_type(_nc_first_name(entryp->tterm.term_names));
237
238     /* check for overly-long names and aliases */
239     for (base = entryp->tterm.term_names; (ptr = strchr(base, '|')) != 0;
240          base = ptr + 1) {
241         if (ptr - base > MAX_ALIAS) {
242             _nc_warning("%s `%.*s' may be too long",
243                         (base == entryp->tterm.term_names)
244                         ? "primary name"
245                         : "alias",
246                         (int) (ptr - base), base);
247         }
248     }
249
250     entryp->nuses = 0;
251
252     for (token_type = _nc_get_token(silent);
253          token_type != EOF && token_type != NAMES;
254          token_type = _nc_get_token(silent)) {
255         if (strcmp(_nc_curr_token.tk_name, "use") == 0
256             || strcmp(_nc_curr_token.tk_name, "tc") == 0) {
257             entryp->uses[entryp->nuses].name = _nc_save_str(_nc_curr_token.tk_valstring);
258             entryp->uses[entryp->nuses].line = _nc_curr_line;
259             entryp->nuses++;
260         } else {
261             /* normal token lookup */
262             entry_ptr = _nc_find_entry(_nc_curr_token.tk_name,
263                                        _nc_syntax ? _nc_cap_hash_table : _nc_info_hash_table);
264
265             /*
266              * Our kluge to handle aliasing.  The reason it's done
267              * this ugly way, with a linear search, is so the hashing
268              * machinery doesn't have to be made really complicated
269              * (also we get better warnings this way).  No point in
270              * making this case fast, aliased caps aren't common now
271              * and will get rarer.
272              */
273             if (entry_ptr == NOTFOUND) {
274                 const struct alias *ap;
275
276                 if (_nc_syntax == SYN_TERMCAP) {
277                     for (ap = _nc_capalias_table; ap->from; ap++)
278                         if (strcmp(ap->from, _nc_curr_token.tk_name) == 0) {
279                             if (ap->to == (char *) 0) {
280                                 _nc_warning("%s (%s termcap extension) ignored",
281                                             ap->from, ap->source);
282                                 goto nexttok;
283                             }
284
285                             entry_ptr = _nc_find_entry(ap->to, _nc_cap_hash_table);
286                             if (entry_ptr && !silent)
287                                 _nc_warning("%s (%s termcap extension) aliased to %s",
288                                             ap->from, ap->source, ap->to);
289                             break;
290                         }
291                 } else {        /* if (_nc_syntax == SYN_TERMINFO) */
292                     for (ap = _nc_infoalias_table; ap->from; ap++)
293                         if (strcmp(ap->from, _nc_curr_token.tk_name) == 0) {
294                             if (ap->to == (char *) 0) {
295                                 _nc_warning("%s (%s terminfo extension) ignored",
296                                             ap->from, ap->source);
297                                 goto nexttok;
298                             }
299
300                             entry_ptr = _nc_find_entry(ap->to, _nc_info_hash_table);
301                             if (entry_ptr && !silent)
302                                 _nc_warning("%s (%s terminfo extension) aliased to %s",
303                                             ap->from, ap->source, ap->to);
304                             break;
305                         }
306
307                     if (entry_ptr == NOTFOUND) {
308                         entry_ptr = lookup_fullname(_nc_curr_token.tk_name);
309                     }
310                 }
311             }
312 #if NCURSES_XNAMES
313             /*
314              * If we have extended-names active, we will automatically
315              * define a name based on its context.
316              */
317             if (entry_ptr == NOTFOUND
318                 && _nc_user_definable
319                 && (entry_ptr = _nc_extend_names(entryp,
320                                                  _nc_curr_token.tk_name,
321                                                  token_type)) != 0) {
322                 if (_nc_tracing >= DEBUG_LEVEL(1))
323                     _nc_warning("extended capability '%s'", _nc_curr_token.tk_name);
324             }
325 #endif /* NCURSES_XNAMES */
326
327             /* can't find this cap name, not even as an alias */
328             if (entry_ptr == NOTFOUND) {
329                 if (!silent)
330                     _nc_warning("unknown capability '%s'",
331                                 _nc_curr_token.tk_name);
332                 continue;
333             }
334
335             /* deal with bad type/value combinations. */
336             if (token_type != CANCEL && entry_ptr->nte_type != token_type) {
337                 /*
338                  * Nasty special cases here handle situations in which type
339                  * information can resolve name clashes.  Normal lookup
340                  * finds the last instance in the capability table of a
341                  * given name, regardless of type.  find_type_entry looks
342                  * for a first matching instance with given type.  So as
343                  * long as all ambiguous names occur in pairs of distinct
344                  * type, this will do the job.
345                  */
346
347                 /* tell max_attributes from arrow_key_map */
348                 if (token_type == NUMBER && !strcmp("ma", _nc_curr_token.tk_name))
349                     entry_ptr = _nc_find_type_entry("ma", NUMBER,
350                                                     _nc_get_table(_nc_syntax
351                                                                   != 0));
352
353                 /* map terminfo's string MT to MT */
354                 else if (token_type == STRING && !strcmp("MT", _nc_curr_token.tk_name))
355                     entry_ptr = _nc_find_type_entry("MT", STRING,
356                                                     _nc_get_table(_nc_syntax
357                                                                   != 0));
358
359                 /* treat strings without following "=" as empty strings */
360                 else if (token_type == BOOLEAN && entry_ptr->nte_type == STRING)
361                     token_type = STRING;
362                 /* we couldn't recover; skip this token */
363                 else {
364                     if (!silent) {
365                         const char *type_name;
366                         switch (entry_ptr->nte_type) {
367                         case BOOLEAN:
368                             type_name = "boolean";
369                             break;
370                         case STRING:
371                             type_name = "string";
372                             break;
373                         case NUMBER:
374                             type_name = "numeric";
375                             break;
376                         default:
377                             type_name = "unknown";
378                             break;
379                         }
380                         _nc_warning("wrong type used for %s capability '%s'",
381                                     type_name, _nc_curr_token.tk_name);
382                     }
383                     continue;
384                 }
385             }
386
387             /* now we know that the type/value combination is OK */
388             switch (token_type) {
389             case CANCEL:
390                 switch (entry_ptr->nte_type) {
391                 case BOOLEAN:
392                     entryp->tterm.Booleans[entry_ptr->nte_index] = CANCELLED_BOOLEAN;
393                     break;
394
395                 case NUMBER:
396                     entryp->tterm.Numbers[entry_ptr->nte_index] = CANCELLED_NUMERIC;
397                     break;
398
399                 case STRING:
400                     entryp->tterm.Strings[entry_ptr->nte_index] = CANCELLED_STRING;
401                     break;
402                 }
403                 break;
404
405             case BOOLEAN:
406                 entryp->tterm.Booleans[entry_ptr->nte_index] = TRUE;
407                 break;
408
409             case NUMBER:
410                 entryp->tterm.Numbers[entry_ptr->nte_index] =
411                     _nc_curr_token.tk_valnumber;
412                 break;
413
414             case STRING:
415                 ptr = _nc_curr_token.tk_valstring;
416                 if (_nc_syntax == SYN_TERMCAP)
417                     ptr = _nc_captoinfo(_nc_curr_token.tk_name,
418                                         ptr,
419                                         parametrized[entry_ptr->nte_index]);
420                 entryp->tterm.Strings[entry_ptr->nte_index] = _nc_save_str(ptr);
421                 break;
422
423             default:
424                 if (!silent)
425                     _nc_warning("unknown token type");
426                 _nc_panic_mode((_nc_syntax == SYN_TERMCAP) ? ':' : ',');
427                 continue;
428             }
429         }                       /* end else cur_token.name != "use" */
430       nexttok:
431         continue;               /* cannot have a label w/o statement */
432     }                           /* endwhile (not EOF and not NAMES) */
433
434     _nc_push_token(token_type);
435     _nc_set_type(_nc_first_name(entryp->tterm.term_names));
436
437     /*
438      * Try to deduce as much as possible from extension capabilities
439      * (this includes obsolete BSD capabilities).  Sigh...it would be more
440      * space-efficient to call this after use resolution, but it has
441      * to be done before entry allocation is wrapped up.
442      */
443     if (!literal) {
444         if (_nc_syntax == SYN_TERMCAP) {
445             bool has_base_entry = FALSE;
446             int i;
447
448             /*
449              * Don't insert defaults if this is a `+' entry meant only
450              * for inclusion in other entries (not sure termcap ever
451              * had these, actually).
452              */
453             if (strchr(entryp->tterm.term_names, '+'))
454                 has_base_entry = TRUE;
455             else
456                 /*
457                  * Otherwise, look for a base entry that will already
458                  * have picked up defaults via translation.
459                  */
460                 for (i = 0; i < entryp->nuses; i++)
461                     if (!strchr((char *) entryp->uses[i].name, '+'))
462                         has_base_entry = TRUE;
463
464             postprocess_termcap(&entryp->tterm, has_base_entry);
465         } else
466             postprocess_terminfo(&entryp->tterm);
467     }
468     _nc_wrap_entry(entryp, FALSE);
469
470     return (OK);
471 }
472
473 NCURSES_EXPORT(int)
474 _nc_capcmp(const char *s, const char *t)
475 /* compare two string capabilities, stripping out padding */
476 {
477     if (!s && !t)
478         return (0);
479     else if (!s || !t)
480         return (1);
481
482     for (;;) {
483         if (s[0] == '$' && s[1] == '<') {
484             for (s += 2;; s++)
485                 if (!(isdigit(UChar(*s))
486                       || *s == '.'
487                       || *s == '*'
488                       || *s == '/'
489                       || *s == '>'))
490                     break;
491         }
492
493         if (t[0] == '$' && t[1] == '<') {
494             for (t += 2;; t++)
495                 if (!(isdigit(UChar(*t))
496                       || *t == '.'
497                       || *t == '*'
498                       || *t == '/'
499                       || *t == '>'))
500                     break;
501         }
502
503         /* we've now pushed s and t past any padding they were pointing at */
504
505         if (*s == '\0' && *t == '\0')
506             return (0);
507
508         if (*s != *t)
509             return (*t - *s);
510
511         /* else *s == *t but one is not NUL, so continue */
512         s++, t++;
513     }
514 }
515
516 static void
517 append_acs0(string_desc * dst, int code, int src)
518 {
519     if (src != 0) {
520         char temp[3];
521         temp[0] = code;
522         temp[1] = src;
523         temp[2] = 0;
524         _nc_safe_strcat(dst, temp);
525     }
526 }
527
528 static void
529 append_acs(string_desc * dst, int code, char *src)
530 {
531     if (src != 0 && strlen(src) == 1) {
532         append_acs0(dst, code, *src);
533     }
534 }
535
536 /*
537  * The ko capability, if present, consists of a comma-separated capability
538  * list.  For each capability, we may assume there is a keycap that sends the
539  * string which is the value of that capability.
540  */
541 typedef struct {
542     const char *from;
543     const char *to;
544 } assoc;
545 static assoc const ko_xlate[] =
546 {
547     {"al", "kil1"},             /* insert line key  -> KEY_IL    */
548     {"bt", "kcbt"},             /* back tab         -> KEY_BTAB  */
549     {"cd", "ked"},              /* clear-to-eos key -> KEY_EOL   */
550     {"ce", "kel"},              /* clear-to-eol key -> KEY_EOS   */
551     {"cl", "kclr"},             /* clear key        -> KEY_CLEAR */
552     {"ct", "tbc"},              /* clear all tabs   -> KEY_CATAB */
553     {"dc", "kdch1"},            /* delete char      -> KEY_DC    */
554     {"dl", "kdl1"},             /* delete line      -> KEY_DL    */
555     {"do", "kcud1"},            /* down key         -> KEY_DOWN  */
556     {"ei", "krmir"},            /* exit insert key  -> KEY_EIC   */
557     {"ho", "khome"},            /* home key         -> KEY_HOME  */
558     {"ic", "kich1"},            /* insert char key  -> KEY_IC    */
559     {"im", "kIC"},              /* insert-mode key  -> KEY_SIC   */
560     {"le", "kcub1"},            /* le key           -> KEY_LEFT  */
561     {"nd", "kcuf1"},            /* nd key           -> KEY_RIGHT */
562     {"nl", "kent"},             /* new line key     -> KEY_ENTER */
563     {"st", "khts"},             /* set-tab key      -> KEY_STAB  */
564     {"ta", CANCELLED_STRING},
565     {"up", "kcuu1"},            /* up-arrow key     -> KEY_UP    */
566     {(char *) 0, (char *) 0},
567 };
568
569 /*
570  * This routine fills in string caps that either had defaults under
571  * termcap or can be manufactured from obsolete termcap capabilities.
572  * It was lifted from Ross Ridge's mytinfo package.
573  */
574
575 static const char C_CR[] = "\r";
576 static const char C_LF[] = "\n";
577 static const char C_BS[] = "\b";
578 static const char C_HT[] = "\t";
579
580 /*
581  * Note that WANTED and PRESENT are not simple inverses!  If a capability
582  * has been explicitly cancelled, it's not considered WANTED.
583  */
584 #define WANTED(s)       ((s) == ABSENT_STRING)
585 #define PRESENT(s)      (((s) != ABSENT_STRING) && ((s) != CANCELLED_STRING))
586
587 /*
588  * This bit of legerdemain turns all the terminfo variable names into
589  * references to locations in the arrays Booleans, Numbers, and Strings ---
590  * precisely what's needed.
591  */
592
593 #undef CUR
594 #define CUR tp->
595
596 static void
597 postprocess_termcap(TERMTYPE * tp, bool has_base)
598 {
599     char buf[MAX_LINE * 2 + 2];
600     string_desc result;
601
602     /*
603      * TERMCAP DEFAULTS AND OBSOLETE-CAPABILITY TRANSLATIONS
604      *
605      * This first part of the code is the functional inverse of the
606      * fragment in capdefaults.c.
607      * ----------------------------------------------------------------------
608      */
609
610     /* if there was a tc entry, assume we picked up defaults via that */
611     if (!has_base) {
612         if (WANTED(init_3string) && termcap_init2)
613             init_3string = _nc_save_str(termcap_init2);
614
615         if (WANTED(reset_2string) && termcap_reset)
616             reset_2string = _nc_save_str(termcap_reset);
617
618         if (WANTED(carriage_return)) {
619             if (carriage_return_delay > 0) {
620                 sprintf(buf, "%s$<%d>", C_CR, carriage_return_delay);
621                 carriage_return = _nc_save_str(buf);
622             } else
623                 carriage_return = _nc_save_str(C_CR);
624         }
625         if (WANTED(cursor_left)) {
626             if (backspace_delay > 0) {
627                 sprintf(buf, "%s$<%d>", C_BS, backspace_delay);
628                 cursor_left = _nc_save_str(buf);
629             } else if (backspaces_with_bs == 1)
630                 cursor_left = _nc_save_str(C_BS);
631             else if (PRESENT(backspace_if_not_bs))
632                 cursor_left = backspace_if_not_bs;
633         }
634         /* vi doesn't use "do", but it does seems to use nl (or '\n') instead */
635         if (WANTED(cursor_down)) {
636             if (PRESENT(linefeed_if_not_lf))
637                 cursor_down = linefeed_if_not_lf;
638             else if (linefeed_is_newline != 1) {
639                 if (new_line_delay > 0) {
640                     sprintf(buf, "%s$<%d>", C_LF, new_line_delay);
641                     cursor_down = _nc_save_str(buf);
642                 } else
643                     cursor_down = _nc_save_str(C_LF);
644             }
645         }
646         if (WANTED(scroll_forward) && crt_no_scrolling != 1) {
647             if (PRESENT(linefeed_if_not_lf))
648                 cursor_down = linefeed_if_not_lf;
649             else if (linefeed_is_newline != 1) {
650                 if (new_line_delay > 0) {
651                     sprintf(buf, "%s$<%d>", C_LF, new_line_delay);
652                     scroll_forward = _nc_save_str(buf);
653                 } else
654                     scroll_forward = _nc_save_str(C_LF);
655             }
656         }
657         if (WANTED(newline)) {
658             if (linefeed_is_newline == 1) {
659                 if (new_line_delay > 0) {
660                     sprintf(buf, "%s$<%d>", C_LF, new_line_delay);
661                     newline = _nc_save_str(buf);
662                 } else
663                     newline = _nc_save_str(C_LF);
664             } else if (PRESENT(carriage_return) && PRESENT(scroll_forward)) {
665                 _nc_str_init(&result, buf, sizeof(buf));
666                 if (_nc_safe_strcat(&result, carriage_return)
667                     && _nc_safe_strcat(&result, scroll_forward))
668                     newline = _nc_save_str(buf);
669             } else if (PRESENT(carriage_return) && PRESENT(cursor_down)) {
670                 _nc_str_init(&result, buf, sizeof(buf));
671                 if (_nc_safe_strcat(&result, carriage_return)
672                     && _nc_safe_strcat(&result, cursor_down))
673                     newline = _nc_save_str(buf);
674             }
675         }
676     }
677
678     /*
679      * Inverse of capdefaults.c code ends here.
680      * ----------------------------------------------------------------------
681      *
682      * TERMCAP-TO TERMINFO MAPPINGS FOR SOURCE TRANSLATION
683      *
684      * These translations will *not* be inverted by tgetent().
685      */
686
687     if (!has_base) {
688         /*
689          * We wait until now to decide if we've got a working cr because even
690          * one that doesn't work can be used for newline. Unfortunately the
691          * space allocated for it is wasted.
692          */
693         if (return_does_clr_eol == 1 || no_correctly_working_cr == 1)
694             carriage_return = ABSENT_STRING;
695
696         /*
697          * Supposedly most termcap entries have ta now and '\t' is no longer a
698          * default, but it doesn't seem to be true...
699          */
700         if (WANTED(tab)) {
701             if (horizontal_tab_delay > 0) {
702                 sprintf(buf, "%s$<%d>", C_HT, horizontal_tab_delay);
703                 tab = _nc_save_str(buf);
704             } else
705                 tab = _nc_save_str(C_HT);
706         }
707         if (init_tabs == ABSENT_NUMERIC && has_hardware_tabs == TRUE)
708             init_tabs = 8;
709
710         /*
711          * Assume we can beep with ^G unless we're given bl@.
712          */
713         if (WANTED(bell))
714             bell = _nc_save_str("\007");
715     }
716
717     /*
718      * Translate the old termcap :pt: capability to it#8 + ht=\t
719      */
720     if (has_hardware_tabs == TRUE) {
721         if (init_tabs != 8 && init_tabs != ABSENT_NUMERIC)
722             _nc_warning("hardware tabs with a width other than 8: %d", init_tabs);
723         else {
724             if (tab && _nc_capcmp(tab, C_HT))
725                 _nc_warning("hardware tabs with a non-^I tab string %s",
726                             _nc_visbuf(tab));
727             else {
728                 if (WANTED(tab))
729                     tab = _nc_save_str(C_HT);
730                 init_tabs = 8;
731             }
732         }
733     }
734     /*
735      * Now translate the ko capability, if there is one.  This
736      * isn't from mytinfo...
737      */
738     if (PRESENT(other_non_function_keys)) {
739         char *base = other_non_function_keys;
740         char *bp, *cp, *dp;
741         struct name_table_entry const *from_ptr;
742         struct name_table_entry const *to_ptr;
743         assoc const *ap;
744         char buf2[MAX_TERMINFO_LENGTH];
745         bool foundim;
746
747         /* we're going to use this for a special case later */
748         dp = strchr(other_non_function_keys, 'i');
749         foundim = (dp != 0) && (dp[1] == 'm');
750
751         /* look at each comma-separated capability in the ko string... */
752         for (base = other_non_function_keys;
753              (cp = strchr(base, ',')) != 0;
754              base = cp + 1) {
755             size_t len = cp - base;
756
757             for (ap = ko_xlate; ap->from; ap++)
758                 if (len == strlen(ap->from)
759                     && strncmp(ap->from, base, len) == 0)
760                     break;
761             if (!ap->to) {
762                 _nc_warning("unknown capability `%.*s' in ko string",
763                             (int) len, base);
764                 continue;
765             } else if (ap->to == CANCELLED_STRING)      /* ignore it */
766                 continue;
767
768             /* now we know we found a match in ko_table, so... */
769
770             from_ptr = _nc_find_entry(ap->from, _nc_cap_hash_table);
771             to_ptr = _nc_find_entry(ap->to, _nc_info_hash_table);
772
773             if (!from_ptr || !to_ptr)   /* should never happen! */
774                 _nc_err_abort("ko translation table is invalid, I give up");
775
776             if (WANTED(tp->Strings[from_ptr->nte_index])) {
777                 _nc_warning("no value for ko capability %s", ap->from);
778                 continue;
779             }
780
781             if (tp->Strings[to_ptr->nte_index]) {
782                 /* There's no point in warning about it if it's the same
783                  * string; that's just an inefficiency.
784                  */
785                 if (strcmp(
786                               tp->Strings[from_ptr->nte_index],
787                               tp->Strings[to_ptr->nte_index]) != 0)
788                     _nc_warning("%s (%s) already has an explicit value %s, ignoring ko",
789                                 ap->to, ap->from,
790                                 _nc_visbuf(tp->Strings[to_ptr->nte_index]));
791                 continue;
792             }
793
794             /*
795              * The magic moment -- copy the mapped key string over,
796              * stripping out padding.
797              */
798             for (dp = buf2, bp = tp->Strings[from_ptr->nte_index]; *bp; bp++) {
799                 if (bp[0] == '$' && bp[1] == '<') {
800                     while (*bp && *bp != '>') {
801                         ++bp;
802                     }
803                 } else
804                     *dp++ = *bp;
805             }
806             *dp++ = '\0';
807
808             tp->Strings[to_ptr->nte_index] = _nc_save_str(buf2);
809         }
810
811         /*
812          * Note: ko=im and ko=ic both want to grab the `Insert'
813          * keycap.  There's a kich1 but no ksmir, so the ic capability
814          * got mapped to kich1 and im to kIC to avoid a collision.
815          * If the description has im but not ic, hack kIC back to kich1.
816          */
817         if (foundim && WANTED(key_ic) && key_sic) {
818             key_ic = key_sic;
819             key_sic = ABSENT_STRING;
820         }
821     }
822
823     if (!has_base) {
824         if (!hard_copy) {
825             if (WANTED(key_backspace))
826                 key_backspace = _nc_save_str(C_BS);
827             if (WANTED(key_left))
828                 key_left = _nc_save_str(C_BS);
829             if (WANTED(key_down))
830                 key_down = _nc_save_str(C_LF);
831         }
832     }
833
834     /*
835      * Translate XENIX forms characters.
836      */
837     if (PRESENT(acs_ulcorner) ||
838         PRESENT(acs_llcorner) ||
839         PRESENT(acs_urcorner) ||
840         PRESENT(acs_lrcorner) ||
841         PRESENT(acs_ltee) ||
842         PRESENT(acs_rtee) ||
843         PRESENT(acs_btee) ||
844         PRESENT(acs_ttee) ||
845         PRESENT(acs_hline) ||
846         PRESENT(acs_vline) ||
847         PRESENT(acs_plus)) {
848         char buf2[MAX_TERMCAP_LENGTH];
849
850         _nc_str_init(&result, buf2, sizeof(buf2));
851         _nc_safe_strcat(&result, acs_chars);
852
853         append_acs(&result, 'j', acs_lrcorner);
854         append_acs(&result, 'k', acs_urcorner);
855         append_acs(&result, 'l', acs_ulcorner);
856         append_acs(&result, 'm', acs_llcorner);
857         append_acs(&result, 'n', acs_plus);
858         append_acs(&result, 'q', acs_hline);
859         append_acs(&result, 't', acs_ltee);
860         append_acs(&result, 'u', acs_rtee);
861         append_acs(&result, 'v', acs_btee);
862         append_acs(&result, 'w', acs_ttee);
863         append_acs(&result, 'x', acs_vline);
864
865         if (buf2[0]) {
866             acs_chars = _nc_save_str(buf2);
867             _nc_warning("acsc string synthesized from XENIX capabilities");
868         }
869     } else if (acs_chars == 0
870                && enter_alt_charset_mode != 0
871                && exit_alt_charset_mode != 0) {
872         acs_chars = _nc_save_str(VT_ACSC);
873     }
874 }
875
876 static void
877 postprocess_terminfo(TERMTYPE * tp)
878 {
879     /*
880      * TERMINFO-TO-TERMINFO MAPPINGS FOR SOURCE TRANSLATION
881      * ----------------------------------------------------------------------
882      */
883
884     /*
885      * Translate AIX forms characters.
886      */
887     if (PRESENT(box_chars_1)) {
888         char buf2[MAX_TERMCAP_LENGTH];
889         string_desc result;
890
891         _nc_str_init(&result, buf2, sizeof(buf2));
892         _nc_safe_strcat(&result, acs_chars);
893
894         append_acs0(&result, 'l', box_chars_1[0]);      /* ACS_ULCORNER */
895         append_acs0(&result, 'q', box_chars_1[1]);      /* ACS_HLINE */
896         append_acs0(&result, 'k', box_chars_1[2]);      /* ACS_URCORNER */
897         append_acs0(&result, 'x', box_chars_1[3]);      /* ACS_VLINE */
898         append_acs0(&result, 'j', box_chars_1[4]);      /* ACS_LRCORNER */
899         append_acs0(&result, 'm', box_chars_1[5]);      /* ACS_LLCORNER */
900         append_acs0(&result, 'w', box_chars_1[6]);      /* ACS_TTEE */
901         append_acs0(&result, 'u', box_chars_1[7]);      /* ACS_RTEE */
902         append_acs0(&result, 'v', box_chars_1[8]);      /* ACS_BTEE */
903         append_acs0(&result, 't', box_chars_1[9]);      /* ACS_LTEE */
904         append_acs0(&result, 'n', box_chars_1[10]);     /* ACS_PLUS */
905
906         if (buf2[0]) {
907             acs_chars = _nc_save_str(buf2);
908             _nc_warning("acsc string synthesized from AIX capabilities");
909             box_chars_1 = ABSENT_STRING;
910         }
911     }
912     /*
913      * ----------------------------------------------------------------------
914      */
915 }
916
917 /*
918  * Do a linear search through the terminfo tables to find a given full-name.
919  * We don't expect to do this often, so there's no hashing function.
920  *
921  * In effect, this scans through the 3 lists of full-names, and looks them
922  * up in _nc_info_table, which is organized so that the nte_index fields are
923  * sorted, but the nte_type fields are not necessarily grouped together.
924  */
925 static struct name_table_entry const *
926 lookup_fullname(const char *find)
927 {
928     int state = -1;
929
930     for (;;) {
931         int count = 0;
932         NCURSES_CONST char *const *names;
933
934         switch (++state) {
935         case BOOLEAN:
936             names = boolfnames;
937             break;
938         case STRING:
939             names = strfnames;
940             break;
941         case NUMBER:
942             names = numfnames;
943             break;
944         default:
945             return NOTFOUND;
946         }
947
948         for (count = 0; names[count] != 0; count++) {
949             if (!strcmp(names[count], find)) {
950                 struct name_table_entry const *entry_ptr = _nc_get_table(FALSE);
951                 while (entry_ptr->nte_type != state
952                        || entry_ptr->nte_index != count)
953                     entry_ptr++;
954                 return entry_ptr;
955             }
956         }
957     }
958 }
959
960 /* parse_entry.c ends here */