605b14c7c515af959ea26003691f469d31fdfe09
[ncurses.git] / progs / dump_entry.c
1 /****************************************************************************
2  * Copyright (c) 1998 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 #define __INTERNAL_CAPS_VISIBLE
35 #include <progs.priv.h>
36
37 #include "dump_entry.h"
38 #include "termsort.c"           /* this C file is generated */
39 #include "parametrized.h"       /* so is this */
40
41 MODULE_ID("$Id: dump_entry.c,v 1.25 1998/02/11 12:14:02 tom Exp $")
42
43 #define INDENT                  8
44
45 static int tversion;            /* terminfo version */
46 static int outform;             /* output format to use */
47 static int sortmode;            /* sort mode to use */
48 static int width = 60;          /* max line width for listings */
49 static int column;              /* current column, limited by 'width' */
50 static int oldcol;              /* last value of column before wrap */
51 static int tracelevel;          /* level of debug output */
52
53 static char *outbuf;            /* the output-buffer */
54 static size_t out_used;         /* ...its current length */
55 static size_t out_size;         /* ...and its allocated length */
56
57 /* indirection pointers for implementing sort and display modes */
58 static const int *bool_indirect, *num_indirect, *str_indirect;
59 static NCURSES_CONST char * const *bool_names;
60 static NCURSES_CONST char * const *num_names;
61 static NCURSES_CONST char * const *str_names;
62
63 static const char *separator, *trailer;
64
65 /* cover various ports and variants of terminfo */
66 #define V_ALLCAPS       0       /* all capabilities (SVr4, XSI, ncurses) */
67 #define V_SVR1          1       /* SVR1, Ultrix */
68 #define V_HPUX          2       /* HP/UX */
69 #define V_AIX           3       /* AIX */
70 #define V_BSD           4       /* BSD */
71
72 #define OBSOLETE(n) (n[0] == 'O' && n[1] == 'T')
73
74 #if NO_LEAKS
75 void _nc_leaks_dump_entry(void)
76 {
77         if (outbuf != 0) {
78                 free(outbuf);
79                 outbuf = 0;
80         }
81 }
82 #endif
83
84 NCURSES_CONST char *nametrans(const char *name)
85 /* translate a capability name from termcap to terminfo */
86 {
87     const struct name_table_entry       *np;
88
89     if ((np = _nc_find_entry(name, _nc_info_hash_table)) != NULL)
90         switch(np->nte_type)
91         {
92         case BOOLEAN:
93             if (bool_from_termcap[np->nte_index])
94                 return(boolcodes[np->nte_index]);
95             break;
96
97         case NUMBER:
98             if (num_from_termcap[np->nte_index])
99                 return(numcodes[np->nte_index]);
100             break;
101
102         case STRING:
103             if (str_from_termcap[np->nte_index])
104                 return(strcodes[np->nte_index]);
105             break;
106         }
107
108     return((char *)NULL);
109 }
110
111 void dump_init(const char *version, int mode, int sort, int twidth, int traceval)
112 /* set up for entry display */
113 {
114     width = twidth;
115     tracelevel = traceval;
116
117     /* versions */
118     if (version == (char *)NULL)
119         tversion = V_ALLCAPS;
120     else if (!strcmp(version, "SVr1") || !strcmp(version, "SVR1")
121                                         || !strcmp(version, "Ultrix"))
122         tversion = V_SVR1;
123     else if (!strcmp(version, "HP"))
124         tversion = V_HPUX;
125     else if (!strcmp(version, "AIX"))
126         tversion = V_AIX;
127     else if (!strcmp(version, "BSD"))
128         tversion = V_BSD;
129     else
130         tversion = V_ALLCAPS;
131
132     /* implement display modes */
133     switch (outform = mode)
134     {
135     case F_LITERAL:
136     case F_TERMINFO:
137         bool_names = boolnames;
138         num_names = numnames;
139         str_names = strnames;
140         separator = twidth ? ", " : ",";
141         trailer = "\n\t";
142         break;
143
144     case F_VARIABLE:
145         bool_names = boolfnames;
146         num_names = numfnames;
147         str_names = strfnames;
148         separator = twidth ? ", " : ",";
149         trailer = "\n\t";
150         break;
151
152     case F_TERMCAP:
153     case F_TCONVERR:
154         bool_names = boolcodes;
155         num_names = numcodes;
156         str_names = strcodes;
157         separator = ":";
158         trailer = "\\\n\t:";
159         break;
160     }
161
162     /* implement sort modes */
163     switch(sortmode = sort)
164     { 
165     case S_NOSORT:
166         if (traceval)
167             (void) fprintf(stderr,
168                            "%s: sorting by term structure order\n", _nc_progname);
169         break;
170
171     case S_TERMINFO:
172         if (traceval)
173             (void) fprintf(stderr,
174                            "%s: sorting by terminfo name order\n", _nc_progname);
175         bool_indirect = bool_terminfo_sort;
176         num_indirect = num_terminfo_sort;
177         str_indirect = str_terminfo_sort;
178         break;
179
180     case S_VARIABLE:
181         if (traceval)
182             (void) fprintf(stderr,
183                            "%s: sorting by C variable order\n", _nc_progname);
184         bool_indirect = bool_variable_sort;
185         num_indirect = num_variable_sort;
186         str_indirect = str_variable_sort;
187         break;
188
189     case S_TERMCAP:
190         if (traceval)
191             (void) fprintf(stderr,
192                            "%s: sorting by termcap name order\n", _nc_progname);
193         bool_indirect = bool_termcap_sort;
194         num_indirect = num_termcap_sort;
195         str_indirect = str_termcap_sort;
196         break;
197     }
198
199     if (traceval)
200         (void) fprintf(stderr,
201                        "%s: width = %d, tversion = %d, outform = %d\n",
202                        _nc_progname, width, tversion, outform);
203 }
204
205 static TERMTYPE *cur_type;
206
207 static int dump_predicate(int type, int idx)
208 /* predicate function to use for ordinary decompilation */
209 {
210         switch(type) {
211         case BOOLEAN:
212                 return (cur_type->Booleans[idx] == FALSE)
213                     ? FAIL : cur_type->Booleans[idx];
214
215         case NUMBER:
216                 return (cur_type->Numbers[idx] == ABSENT_NUMERIC)
217                     ? FAIL : cur_type->Numbers[idx];
218
219         case STRING:
220                 return (cur_type->Strings[idx] != ABSENT_STRING)
221                     ? (int)TRUE : FAIL;
222         }
223
224         return(FALSE);  /* pacify compiler */
225 }
226
227 static void set_obsolete_termcaps(TERMTYPE *tp);
228 static void repair_acsc(TERMTYPE *tp);
229
230 /* is this the index of a function key string? */
231 #define FNKEY(i)        (((i)<= 65 && (i)>= 75) || ((i)<= 216 && (i)>= 268))
232
233 static bool version_filter(int type, int idx)
234 /* filter out capabilities we may want to suppress */
235 {
236     switch (tversion)
237     {
238     case V_ALLCAPS:     /* SVr4, XSI Curses */
239         return(TRUE);
240
241     case V_SVR1:        /* System V Release 1, Ultrix */
242         switch (type)
243         {
244         case BOOLEAN:
245             /* below and including xon_xoff */
246             return ((idx <= 20) ? TRUE : FALSE);
247         case NUMBER:
248             /* below and including width_status_line */
249             return ((idx <= 7) ? TRUE : FALSE);
250         case STRING:
251             /* below and including prtr_non */
252             return ((idx <= 144) ? TRUE : FALSE);
253         }
254         break;
255
256     case V_HPUX:                /* Hewlett-Packard */
257         switch (type)
258         {
259         case BOOLEAN:
260             /* below and including xon_xoff */
261             return ((idx <= 20) ? TRUE : FALSE);
262         case NUMBER:
263             /* below and including label_width */
264             return ((idx <= 10) ? TRUE : FALSE);
265         case STRING:
266             if (idx <= 144)     /* below and including prtr_non */
267                 return(TRUE);
268             else if (FNKEY(idx))        /* function keys */
269                 return(TRUE);
270             else if (idx==147||idx==156||idx==157) /* plab_norm,label_on,label_off */
271                 return(TRUE);
272             else
273                 return(FALSE);
274         }
275         break;
276
277     case V_AIX:         /* AIX */
278         switch (type)
279         {
280         case BOOLEAN:
281             /* below and including xon_xoff */
282             return ((idx <= 20) ? TRUE : FALSE);
283         case NUMBER:
284             /* below and including width_status_line */
285             return ((idx <= 7) ? TRUE : FALSE);
286         case STRING:
287             if (idx <= 144)     /* below and including prtr_non */
288                 return(TRUE);
289             else if (FNKEY(idx))        /* function keys */
290                 return(TRUE);
291             else
292                 return(FALSE);
293         }
294         break;
295
296     case V_BSD:         /* BSD */
297         switch (type)
298         {
299         case BOOLEAN:
300             return bool_from_termcap[idx];
301         case NUMBER:
302             return num_from_termcap[idx];
303         case STRING:
304             return str_from_termcap[idx];
305         }
306         break;
307     }
308
309     return(FALSE);      /* pacify the compiler */
310 }
311
312 static
313 void append_output (const char *src)
314 {
315         if (src == 0) {
316                 out_used = 0;
317                 append_output("");
318         } else {
319                 size_t need = strlen(src);
320                 size_t want = need + out_used + 1;
321                 if (want > out_size) {
322                         out_size += want;       /* be generous */
323                         if (outbuf == 0)
324                                 outbuf = malloc(out_size);
325                         else
326                                 outbuf = realloc(outbuf, out_size);
327                 }
328                 (void)strcpy(outbuf + out_used, src);
329                 out_used += need;
330         }
331 }
332
333 static
334 void force_wrap(void)
335 {
336         oldcol = column;
337         append_output(trailer);
338         column = INDENT;
339 }
340
341 static
342 void wrap_concat(const char *src)
343 {
344         int need = strlen(src);
345         int want = strlen(separator) + need;
346
347         if (column > INDENT
348          && column + want > width) {
349                 force_wrap();
350         }
351         append_output(src);
352         append_output(separator);
353         column += need;
354 }
355
356 #define IGNORE_SEP_TRAIL(first,last,sep_trail) \
357         if ((size_t)(last - first) > sizeof(sep_trail)-1 \
358          && !strncmp(first, sep_trail, sizeof(sep_trail)-1)) \
359                 first += sizeof(sep_trail)-2
360
361 /* Returns the nominal length of the buffer assuming it is termcap format,
362  * i.e., the continuation sequence is treated as a single character ":".
363  *
364  * There are several implementations of termcap which read the text into a
365  * fixed-size buffer.  Generally they strip the newlines from the text, but may
366  * not do it until after the buffer is read.  Also, "tc=" resolution may be
367  * expanded in the same buffer.  This function is useful for measuring the size
368  * of the best fixed-buffer implementation; the worst case may be much worse.
369  */
370 #ifdef TEST_TERMCAP_LENGTH
371 static int termcap_length(const char *src)
372 {
373         static const char pattern[] = ":\\\n\t:";
374
375         int len = 0;
376         const char *const t = src + strlen(src);
377
378         while (*src != '\0') {
379                 IGNORE_SEP_TRAIL(src, t, pattern);
380                 src++;
381                 len++;
382         }
383         return len;
384 }
385 #else
386 #define termcap_length(src) strlen(src)
387 #endif
388
389 int fmt_entry(TERMTYPE *tterm,
390                            int (*pred)(int type, int idx),
391                            bool suppress_untranslatable,
392                            bool infodump)
393 {
394 int     i, j;
395 char    buffer[MAX_TERMINFO_LENGTH];
396 int     predval, len;
397 int     num_bools = 0;
398 int     num_values = 0;
399 int     num_strings = 0;
400 bool    outcount = 0;
401
402 #define WRAP_CONCAT     \
403         wrap_concat(buffer); \
404         outcount = TRUE
405
406     len = 12;                   /* terminfo file-header */
407
408     if (pred == NULL) {
409         cur_type = tterm;
410         pred = dump_predicate;
411     }
412
413     append_output(NULL);
414     append_output(tterm->term_names);
415     append_output(separator);
416     column = out_used;
417     force_wrap();
418
419     for (j=0; j < BOOLCOUNT; j++) {
420         if (sortmode == S_NOSORT)
421             i = j;
422         else
423             i = bool_indirect[j];
424
425         if (!version_filter(BOOLEAN, i))
426             continue;
427         else if ((outform == F_LITERAL || outform == F_TERMINFO || outform == F_VARIABLE)
428                  && (OBSOLETE(bool_names[i]) && outform != F_LITERAL))
429             continue;
430
431         predval = pred(BOOLEAN, i);
432         if (predval != FAIL) {
433             (void) strcpy(buffer, bool_names[i]);
434             if (predval <= 0)
435                 (void) strcat(buffer, "@");
436             else if (i + 1 > num_bools)
437                 num_bools = i + 1;
438             WRAP_CONCAT;
439         }
440     }
441
442     if (column != INDENT)
443         force_wrap();
444
445     for (j=0; j < NUMCOUNT; j++) {
446         if (sortmode == S_NOSORT)
447             i = j;
448         else
449             i = num_indirect[j];
450
451         if (!version_filter(NUMBER, i))
452             continue;
453         else if ((outform == F_LITERAL || outform == F_TERMINFO || outform == F_VARIABLE)
454                  && (OBSOLETE(num_names[i]) && outform != F_LITERAL))
455             continue;
456
457         predval = pred(NUMBER, i);
458         if (predval != FAIL) {
459             if (tterm->Numbers[i] < 0) {
460                 sprintf(buffer, "%s@", num_names[i]);
461             } else {
462                 sprintf(buffer, "%s#%d", num_names[i], tterm->Numbers[i]);
463                 if (i + 1 > num_values)
464                     num_values = i + 1;
465             }
466             WRAP_CONCAT;
467         }
468     }
469
470     if (column != INDENT)
471         force_wrap();
472
473     len += num_bools
474         + num_values * 2
475         + strlen(tterm->term_names) + 1;
476     if (len & 1)
477         len++;
478
479     repair_acsc(tterm);
480     for (j=0; j < STRCOUNT; j++) {
481         if (sortmode == S_NOSORT)
482             i = j;
483         else
484             i = str_indirect[j];
485
486         if (!version_filter(STRING, i))
487             continue;
488         else if ((outform == F_LITERAL || outform == F_TERMINFO || outform == F_VARIABLE)
489                  && (OBSOLETE(str_names[i]) && outform != F_LITERAL))
490             continue;
491
492         /*
493          * Some older versions of vi want rmir/smir to be defined
494          * for ich/ich1 to work.  If they're not defined, force
495          * them to be output as defined and empty.
496          */
497         if (outform==F_TERMCAP)
498 #undef CUR
499 #define CUR tterm->
500             if (insert_character || parm_ich)
501             {
502                 if (&tterm->Strings[i] == &enter_insert_mode
503                     && enter_insert_mode == ABSENT_STRING)
504                 {
505                     (void) strcpy(buffer, "im=");
506                     goto catenate;
507                 }
508
509                 if (&tterm->Strings[i] == &exit_insert_mode
510                     && exit_insert_mode == ABSENT_STRING)
511                 {
512                     (void) strcpy(buffer, "ei=");
513                     goto catenate;
514                 }
515             }
516
517         predval = pred(STRING, i);
518         buffer[0] = '\0';
519         if (predval != FAIL) {
520             if (tterm->Strings[i] != ABSENT_STRING
521              && i + 1 > num_strings)
522                 num_strings = i + 1;
523             if (!VALID_STRING(tterm->Strings[i]))
524                 sprintf(buffer, "%s@", str_names[i]);
525             else if (outform == F_TERMCAP || outform == F_TCONVERR)
526             {
527                 char *srccap = _nc_tic_expand(tterm->Strings[i], FALSE);
528                 char *cv = _nc_infotocap(str_names[i], srccap, parametrized[i]);
529
530                 if (cv == (char *)NULL)
531                 {
532                     if (outform == F_TCONVERR)
533                         sprintf(buffer, "%s=!!! %s WILL NOT CONVERT !!!", str_names[i], srccap);
534                     else if (suppress_untranslatable)
535                         continue;
536                     else
537                         sprintf(buffer, "..%s=%s", str_names[i], srccap);
538                 }
539                 else
540                     sprintf(buffer, "%s=%s", str_names[i], cv);
541                 len += strlen(tterm->Strings[i]) + 1;
542             }
543             else
544             {
545                 sprintf(buffer, "%s=%s", str_names[i], _nc_tic_expand(tterm->Strings[i], outform==F_TERMINFO));
546                 len += strlen(tterm->Strings[i]) + 1;
547             }
548
549         catenate:
550             WRAP_CONCAT;
551         }
552     }
553     len += num_strings * 2;
554
555     /*
556      * This piece of code should be an effective inverse of the functions
557      * postprocess_terminfo and postprocess_terminfo in parse_entry.c.
558      * Much more work should be done on this to support dumping termcaps.
559      */
560     if (tversion == V_HPUX)
561     {
562         if (memory_lock)
563         {
564             (void) sprintf(buffer, "meml=%s", memory_lock);
565             WRAP_CONCAT;
566         }
567         if (memory_unlock)
568         {
569             (void) sprintf(buffer, "memu=%s", memory_unlock);
570             WRAP_CONCAT;
571         }
572     }
573     else if (tversion == V_AIX)
574     {
575         if (VALID_STRING(acs_chars))
576         {
577             bool        box_ok = TRUE;
578             const char  *acstrans = "lqkxjmwuvtn";
579             const char  *cp;
580             char        *tp, *sp, boxchars[11];
581
582             tp = boxchars;
583             for (cp = acstrans; *cp; cp++)
584             {
585                 sp = strchr(acs_chars, *cp);
586                 if (sp)
587                     *tp++ = sp[1];
588                 else
589                 {
590                     box_ok = FALSE;
591                     break;
592                 }
593             }
594             tp[0] = '\0';
595
596             if (box_ok)
597             {
598                 (void) strcpy(buffer, "box1=");
599                 (void) strcat(buffer, _nc_tic_expand(boxchars, outform==F_TERMINFO));
600                 WRAP_CONCAT;
601             }
602         }
603     }
604
605     /*
606      * kludge: trim off trailer to avoid an extra blank line
607      * in infocmp -u output when there are no string differences
608      */
609     if (outcount)
610     {
611         j = out_used;
612         if (j >= 2
613          && outbuf[j-1] == '\t'
614          && outbuf[j-2] == '\n') {
615             out_used -= 2;
616         } else if (j >= 4
617          && outbuf[j-1] == ':'
618          && outbuf[j-2] == '\t'
619          && outbuf[j-3] == '\n'
620          && outbuf[j-4] == '\\') {
621             out_used -= 4;
622         }
623         outbuf[out_used] = '\0';
624         column = oldcol;
625     }
626
627 #if 0
628     fprintf(stderr, "num_bools = %d\n", num_bools);
629     fprintf(stderr, "num_values = %d\n", num_values);
630     fprintf(stderr, "num_strings = %d\n", num_strings);
631     fprintf(stderr, "term_names=%s, len=%d, strlen(outbuf)=%d, outbuf=%s\n",
632             tterm->term_names, len, out_used, outbuf);
633 #endif
634     /*
635      * Here's where we use infodump to trigger a more stringent length check
636      * for termcap-translation purposes.
637      * Return the length of the raw entry, without tc= expansions,
638      * It gives an idea of which entries are deadly to even *scan past*,
639      * as opposed to *use*.
640      */
641     return(infodump ? len : termcap_length(outbuf));
642 }
643
644 int dump_entry(TERMTYPE *tterm, bool limited, int (*pred)(int type, int idx))
645 /* dump a single entry */
646 {
647     int len, critlen;
648     const char  *legend;
649     bool        infodump;
650
651     if (outform==F_TERMCAP || outform==F_TCONVERR)
652     {
653         critlen = MAX_TERMCAP_LENGTH;
654         legend = "older termcap";
655         infodump = FALSE;
656         set_obsolete_termcaps(tterm);
657     }
658     else
659     {
660         critlen = MAX_TERMINFO_LENGTH;
661         legend = "terminfo";
662         infodump = TRUE;
663     }
664
665     if (((len = fmt_entry(tterm, pred, FALSE, infodump)) > critlen) && limited)
666     {
667         (void) printf("# (untranslatable capabilities removed to fit entry within %d bytes)\n",
668                       critlen);
669         if ((len = fmt_entry(tterm, pred, TRUE, infodump)) > critlen)
670         {
671             /*
672              * We pick on sgr because it's a nice long string capability that
673              * is really just an optimization hack.
674              */
675             char *oldsgr = set_attributes;
676             set_attributes = ABSENT_STRING; 
677             (void) printf("# (sgr removed to fit entry within %d bytes)\n",
678                           critlen);
679             if ((len = fmt_entry(tterm, pred, TRUE, infodump)) > critlen)
680             {
681                 int oldversion = tversion;
682
683                 tversion = V_BSD;
684                 (void) printf("# (terminfo-only capabilities suppressed to fit entry within %d bytes)\n",
685                               critlen);
686
687                 if ((len = fmt_entry(tterm, pred, TRUE, infodump)) > critlen)
688                 {
689                     (void) fprintf(stderr,
690                                "warning: %s entry is %d bytes long\n",
691                                _nc_first_name(tterm->term_names),
692                                len);
693                     (void) printf(
694                               "# WARNING: this entry, %d bytes long, may core-dump %s libraries!\n",
695                               len, legend);
696                 }
697                 tversion = oldversion;
698             }
699             set_attributes = oldsgr;
700         }
701     }
702
703     (void) fputs(outbuf, stdout);
704     return len;
705 }
706
707 int dump_uses(const char *name, bool infodump)
708 /* dump "use=" clauses in the appropriate format */
709 {
710     char buffer[MAX_TERMINFO_LENGTH];
711
712     append_output(NULL);
713     (void)sprintf(buffer, "%s%s", infodump ? "use=" : "tc=", name);
714     wrap_concat(buffer);
715     (void) fputs(outbuf, stdout);
716     return out_used;
717 }
718
719 void compare_entry(void (*hook)(int t, int i, const char *name))
720 /* compare two entries */
721 {
722     int i, j;
723
724     (void) fputs("    comparing booleans.\n", stdout);
725     for (j=0; j < BOOLCOUNT; j++)
726     {
727         if (sortmode == S_NOSORT)
728             i = j;
729         else
730             i = bool_indirect[j];
731
732         if ((outform == F_LITERAL || outform == F_TERMINFO || outform == F_VARIABLE)
733                  && (OBSOLETE(bool_names[i]) && outform != F_LITERAL))
734             continue;
735
736         (*hook)(BOOLEAN, i, bool_names[i]);
737     }
738
739     (void) fputs("    comparing numbers.\n", stdout);
740     for (j=0; j < NUMCOUNT; j++)
741     {
742         if (sortmode == S_NOSORT)
743             i = j;
744         else
745             i = num_indirect[j];
746
747         if ((outform==F_LITERAL || outform==F_TERMINFO || outform==F_VARIABLE)
748                  && (OBSOLETE(num_names[i]) && outform != F_LITERAL))
749             continue;
750
751         (*hook)(NUMBER, i, num_names[i]);
752     }
753
754     (void) fputs("    comparing strings.\n", stdout);
755     for (j=0; j < STRCOUNT; j++)
756     {
757         if (sortmode == S_NOSORT)
758             i = j;
759         else
760             i = str_indirect[j];
761
762         if ((outform==F_LITERAL || outform==F_TERMINFO || outform==F_VARIABLE)
763                  && (OBSOLETE(str_names[i]) && outform != F_LITERAL))
764             continue;
765
766         (*hook)(STRING, i, str_names[i]);
767     }
768 }
769
770 #define NOTSET(s)       ((s) == (char *)NULL)
771
772 /*
773  * This bit of legerdemain turns all the terminfo variable names into
774  * references to locations in the arrays Booleans, Numbers, and Strings ---
775  * precisely what's needed.
776  */
777 #undef CUR
778 #define CUR tp->
779
780 static void set_obsolete_termcaps(TERMTYPE *tp)
781 {
782 #include "capdefaults.c"
783 }
784
785 /*
786  * Convert an alternate-character-set string to canonical form: sorted and
787  * unique.
788  */
789 static void repair_acsc(TERMTYPE *tp)
790 {
791         if (VALID_STRING(acs_chars)) {
792             size_t n, m;
793             char mapped[256];
794             char extra = 0;
795             unsigned source;
796             unsigned target;
797             bool fix_needed = FALSE;
798
799             for (n = 0, source = 0; acs_chars[n] != 0; n++) {
800                 target = acs_chars[n];
801                 if (source >= target) {
802                     fix_needed = TRUE;
803                     break;
804                 }
805                 source = target;
806                 if (acs_chars[n+1])
807                     n++;
808             }
809             if (fix_needed) {
810                 memset(mapped, 0, sizeof(mapped));
811                 for (n = 0; acs_chars[n] != 0; n++) {
812                     source = acs_chars[n];
813                     if ((target = (unsigned char)acs_chars[n+1]) != 0) {
814                         mapped[source] = target;
815                         n++;
816                     } else {
817                         extra = source;
818                     }
819                 }
820                 for (n = m = 0; n < sizeof(mapped); n++) {
821                     if (mapped[n]) {
822                         acs_chars[m++] = n;
823                         acs_chars[m++] = mapped[n];
824                     }
825                 }
826                 if (extra)
827                     acs_chars[m++] = extra;     /* garbage in, garbage out */
828                 acs_chars[m] = 0;
829             }
830         }
831 }