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