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