<net/if_poll.h>: Add license. It was based on sys/net/if_poll.c.
[dragonfly.git] / contrib / ncurses / progs / dump_entry.c
blob7be9b9127b82535c8e25f8c87315bed3672ab730
1 /****************************************************************************
2 * Copyright (c) 1998-2014,2015 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 ****************************************************************************/
29 /****************************************************************************
30 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
31 * and: Eric S. Raymond <esr@snark.thyrsus.com> *
32 * and: Thomas E. Dickey 1996 on *
33 ****************************************************************************/
35 #define __INTERNAL_CAPS_VISIBLE
36 #include <progs.priv.h>
38 #include "dump_entry.h"
39 #include "termsort.c" /* this C file is generated */
40 #include <parametrized.h> /* so is this */
42 MODULE_ID("$Id: dump_entry.c,v 1.124 2015/11/28 22:54:33 tom Exp $")
44 #define DISCARD(string) string = ABSENT_STRING
45 #define PRINTF (void) printf
47 #define OkIndex(index,array) ((int)(index) >= 0 && (int)(index) < (int) SIZEOF(array))
49 typedef struct {
50 char *text;
51 size_t used;
52 size_t size;
53 } DYNBUF;
55 static int tversion; /* terminfo version */
56 static int outform; /* output format to use */
57 static int sortmode; /* sort mode to use */
58 static int width = 60; /* max line width for listings */
59 static int height = 65535; /* max number of lines for listings */
60 static int column; /* current column, limited by 'width' */
61 static int oldcol; /* last value of column before wrap */
62 static bool pretty; /* true if we format if-then-else strings */
63 static bool checking; /* true if we are checking for tic */
64 static int quickdump; /* true if we are dumping compiled data */
66 static char *save_sgr;
68 static DYNBUF outbuf;
69 static DYNBUF tmpbuf;
71 /* indirection pointers for implementing sort and display modes */
72 static const PredIdx *bool_indirect, *num_indirect, *str_indirect;
73 static NCURSES_CONST char *const *bool_names;
74 static NCURSES_CONST char *const *num_names;
75 static NCURSES_CONST char *const *str_names;
77 static const char *separator = "", *trailer = "";
78 static int indent = 8;
80 /* cover various ports and variants of terminfo */
81 #define V_ALLCAPS 0 /* all capabilities (SVr4, XSI, ncurses) */
82 #define V_SVR1 1 /* SVR1, Ultrix */
83 #define V_HPUX 2 /* HP/UX */
84 #define V_AIX 3 /* AIX */
85 #define V_BSD 4 /* BSD */
87 #if NCURSES_XNAMES
88 #define OBSOLETE(n) (!_nc_user_definable && (n[0] == 'O' && n[1] == 'T'))
89 #else
90 #define OBSOLETE(n) (n[0] == 'O' && n[1] == 'T')
91 #endif
93 #define isObsolete(f,n) ((f == F_TERMINFO || f == F_VARIABLE) && OBSOLETE(n))
95 #if NCURSES_XNAMES
96 #define BoolIndirect(j) ((j >= BOOLCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : bool_indirect[j]))
97 #define NumIndirect(j) ((j >= NUMCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : num_indirect[j]))
98 #define StrIndirect(j) ((j >= STRCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : str_indirect[j]))
99 #else
100 #define BoolIndirect(j) ((sortmode == S_NOSORT) ? (j) : bool_indirect[j])
101 #define NumIndirect(j) ((sortmode == S_NOSORT) ? (j) : num_indirect[j])
102 #define StrIndirect(j) ((sortmode == S_NOSORT) ? (j) : str_indirect[j])
103 #endif
105 static void failed(const char *) GCC_NORETURN;
107 static void
108 failed(const char *s)
110 perror(s);
111 ExitProgram(EXIT_FAILURE);
114 static void
115 strncpy_DYN(DYNBUF * dst, const char *src, size_t need)
117 size_t want = need + dst->used + 1;
118 if (want > dst->size) {
119 dst->size += (want + 1024); /* be generous */
120 dst->text = typeRealloc(char, dst->size, dst->text);
121 if (dst->text == 0)
122 failed("strncpy_DYN");
124 (void) strncpy(dst->text + dst->used, src, need);
125 dst->used += need;
126 dst->text[dst->used] = 0;
129 static void
130 strcpy_DYN(DYNBUF * dst, const char *src)
132 if (src == 0) {
133 dst->used = 0;
134 strcpy_DYN(dst, "");
135 } else {
136 strncpy_DYN(dst, src, strlen(src));
140 #if NO_LEAKS
141 static void
142 free_DYN(DYNBUF * p)
144 if (p->text != 0)
145 free(p->text);
146 p->text = 0;
147 p->size = 0;
148 p->used = 0;
151 void
152 _nc_leaks_dump_entry(void)
154 free_DYN(&outbuf);
155 free_DYN(&tmpbuf);
157 #endif
159 #define NameTrans(check,result) \
160 if ((np->nte_index <= OK_ ## check) \
161 && check[np->nte_index]) \
162 return (result[np->nte_index])
164 NCURSES_CONST char *
165 nametrans(const char *name)
166 /* translate a capability name to termcap from terminfo */
168 const struct name_table_entry *np;
170 if ((np = _nc_find_entry(name, _nc_get_hash_table(0))) != 0) {
171 switch (np->nte_type) {
172 case BOOLEAN:
173 NameTrans(bool_from_termcap, boolcodes);
174 break;
176 case NUMBER:
177 NameTrans(num_from_termcap, numcodes);
178 break;
180 case STRING:
181 NameTrans(str_from_termcap, strcodes);
182 break;
186 return (0);
189 void
190 dump_init(const char *version,
191 int mode,
192 int sort,
193 int twidth,
194 int theight,
195 unsigned traceval,
196 bool formatted,
197 bool check,
198 int quick)
199 /* set up for entry display */
201 width = twidth;
202 height = theight;
203 pretty = formatted;
204 checking = check;
205 quickdump = (quick & 3);
207 /* versions */
208 if (version == 0)
209 tversion = V_ALLCAPS;
210 else if (!strcmp(version, "SVr1") || !strcmp(version, "SVR1")
211 || !strcmp(version, "Ultrix"))
212 tversion = V_SVR1;
213 else if (!strcmp(version, "HP"))
214 tversion = V_HPUX;
215 else if (!strcmp(version, "AIX"))
216 tversion = V_AIX;
217 else if (!strcmp(version, "BSD"))
218 tversion = V_BSD;
219 else
220 tversion = V_ALLCAPS;
222 /* implement display modes */
223 switch (outform = mode) {
224 case F_LITERAL:
225 case F_TERMINFO:
226 bool_names = boolnames;
227 num_names = numnames;
228 str_names = strnames;
229 separator = (twidth > 0 && theight > 1) ? ", " : ",";
230 trailer = "\n\t";
231 break;
233 case F_VARIABLE:
234 bool_names = boolfnames;
235 num_names = numfnames;
236 str_names = strfnames;
237 separator = (twidth > 0 && theight > 1) ? ", " : ",";
238 trailer = "\n\t";
239 break;
241 case F_TERMCAP:
242 case F_TCONVERR:
243 bool_names = boolcodes;
244 num_names = numcodes;
245 str_names = strcodes;
246 separator = ":";
247 trailer = "\\\n\t:";
248 break;
250 indent = 8;
252 /* implement sort modes */
253 switch (sortmode = sort) {
254 case S_NOSORT:
255 if (traceval)
256 (void) fprintf(stderr,
257 "%s: sorting by term structure order\n", _nc_progname);
258 break;
260 case S_TERMINFO:
261 if (traceval)
262 (void) fprintf(stderr,
263 "%s: sorting by terminfo name order\n", _nc_progname);
264 bool_indirect = bool_terminfo_sort;
265 num_indirect = num_terminfo_sort;
266 str_indirect = str_terminfo_sort;
267 break;
269 case S_VARIABLE:
270 if (traceval)
271 (void) fprintf(stderr,
272 "%s: sorting by C variable order\n", _nc_progname);
273 bool_indirect = bool_variable_sort;
274 num_indirect = num_variable_sort;
275 str_indirect = str_variable_sort;
276 break;
278 case S_TERMCAP:
279 if (traceval)
280 (void) fprintf(stderr,
281 "%s: sorting by termcap name order\n", _nc_progname);
282 bool_indirect = bool_termcap_sort;
283 num_indirect = num_termcap_sort;
284 str_indirect = str_termcap_sort;
285 break;
288 if (traceval)
289 (void) fprintf(stderr,
290 "%s: width = %d, tversion = %d, outform = %d\n",
291 _nc_progname, width, tversion, outform);
294 static TERMTYPE *cur_type;
296 static int
297 dump_predicate(PredType type, PredIdx idx)
298 /* predicate function to use for ordinary decompilation */
300 switch (type) {
301 case BOOLEAN:
302 return (cur_type->Booleans[idx] == FALSE)
303 ? FAIL : cur_type->Booleans[idx];
305 case NUMBER:
306 return (cur_type->Numbers[idx] == ABSENT_NUMERIC)
307 ? FAIL : cur_type->Numbers[idx];
309 case STRING:
310 return (cur_type->Strings[idx] != ABSENT_STRING)
311 ? (int) TRUE : FAIL;
314 return (FALSE); /* pacify compiler */
317 static void set_obsolete_termcaps(TERMTYPE *tp);
319 /* is this the index of a function key string? */
320 #define FNKEY(i) \
321 (((i) >= STR_IDX(key_f0) && \
322 (i) <= STR_IDX(key_f9)) || \
323 ((i) >= STR_IDX(key_f11) && \
324 (i) <= STR_IDX(key_f63)))
327 * If we configure with a different Caps file, the offsets into the arrays
328 * will change. So we use an address expression.
330 #define BOOL_IDX(name) (PredType) (&(name) - &(CUR Booleans[0]))
331 #define NUM_IDX(name) (PredType) (&(name) - &(CUR Numbers[0]))
332 #define STR_IDX(name) (PredType) (&(name) - &(CUR Strings[0]))
334 static bool
335 version_filter(PredType type, PredIdx idx)
336 /* filter out capabilities we may want to suppress */
338 switch (tversion) {
339 case V_ALLCAPS: /* SVr4, XSI Curses */
340 return (TRUE);
342 case V_SVR1: /* System V Release 1, Ultrix */
343 switch (type) {
344 case BOOLEAN:
345 return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE);
346 case NUMBER:
347 return ((idx <= NUM_IDX(width_status_line)) ? TRUE : FALSE);
348 case STRING:
349 return ((idx <= STR_IDX(prtr_non)) ? TRUE : FALSE);
351 break;
353 case V_HPUX: /* Hewlett-Packard */
354 switch (type) {
355 case BOOLEAN:
356 return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE);
357 case NUMBER:
358 return ((idx <= NUM_IDX(label_width)) ? TRUE : FALSE);
359 case STRING:
360 if (idx <= STR_IDX(prtr_non))
361 return (TRUE);
362 else if (FNKEY(idx)) /* function keys */
363 return (TRUE);
364 else if (idx == STR_IDX(plab_norm)
365 || idx == STR_IDX(label_on)
366 || idx == STR_IDX(label_off))
367 return (TRUE);
368 else
369 return (FALSE);
371 break;
373 case V_AIX: /* AIX */
374 switch (type) {
375 case BOOLEAN:
376 return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE);
377 case NUMBER:
378 return ((idx <= NUM_IDX(width_status_line)) ? TRUE : FALSE);
379 case STRING:
380 if (idx <= STR_IDX(prtr_non))
381 return (TRUE);
382 else if (FNKEY(idx)) /* function keys */
383 return (TRUE);
384 else
385 return (FALSE);
387 break;
389 #define is_termcap(type) (OkIndex(idx, type##_from_termcap) && \
390 type##_from_termcap[idx])
392 case V_BSD: /* BSD */
393 switch (type) {
394 case BOOLEAN:
395 return is_termcap(bool);
396 case NUMBER:
397 return is_termcap(num);
398 case STRING:
399 return is_termcap(str);
401 break;
404 return (FALSE); /* pacify the compiler */
407 static void
408 trim_trailing(void)
410 while (outbuf.used > 0 && outbuf.text[outbuf.used - 1] == ' ')
411 outbuf.text[--outbuf.used] = '\0';
414 static void
415 force_wrap(void)
417 oldcol = column;
418 trim_trailing();
419 strcpy_DYN(&outbuf, trailer);
420 column = indent;
423 static void
424 wrap_concat(const char *src)
426 size_t need = strlen(src);
427 size_t want = strlen(separator) + need;
429 if (column > indent
430 && column + (int) want > width) {
431 force_wrap();
433 strcpy_DYN(&outbuf, src);
434 strcpy_DYN(&outbuf, separator);
435 column += (int) need;
438 #define IGNORE_SEP_TRAIL(first,last,sep_trail) \
439 if ((size_t)(last - first) > sizeof(sep_trail)-1 \
440 && !strncmp(first, sep_trail, sizeof(sep_trail)-1)) \
441 first += sizeof(sep_trail)-2
443 /* Returns the nominal length of the buffer assuming it is termcap format,
444 * i.e., the continuation sequence is treated as a single character ":".
446 * There are several implementations of termcap which read the text into a
447 * fixed-size buffer. Generally they strip the newlines from the text, but may
448 * not do it until after the buffer is read. Also, "tc=" resolution may be
449 * expanded in the same buffer. This function is useful for measuring the size
450 * of the best fixed-buffer implementation; the worst case may be much worse.
452 #ifdef TEST_TERMCAP_LENGTH
453 static int
454 termcap_length(const char *src)
456 static const char pattern[] = ":\\\n\t:";
458 int len = 0;
459 const char *const t = src + strlen(src);
461 while (*src != '\0') {
462 IGNORE_SEP_TRAIL(src, t, pattern);
463 src++;
464 len++;
466 return len;
468 #else
469 #define termcap_length(src) strlen(src)
470 #endif
472 static void
473 indent_DYN(DYNBUF * buffer, int level)
475 int n;
477 for (n = 0; n < level; n++)
478 strncpy_DYN(buffer, "\t", (size_t) 1);
481 bool
482 has_params(const char *src)
484 bool result = FALSE;
485 int len = (int) strlen(src);
486 int n;
487 bool ifthen = FALSE;
488 bool params = FALSE;
490 for (n = 0; n < len - 1; ++n) {
491 if (!strncmp(src + n, "%p", (size_t) 2)) {
492 params = TRUE;
493 } else if (!strncmp(src + n, "%;", (size_t) 2)) {
494 ifthen = TRUE;
495 result = params;
496 break;
499 if (!ifthen) {
500 result = ((len > 50) && params);
502 return result;
505 static char *
506 fmt_complex(TERMTYPE *tterm, const char *capability, char *src, int level)
508 bool percent = FALSE;
509 bool params = has_params(src);
511 while (*src != '\0') {
512 switch (*src) {
513 case '^':
514 percent = FALSE;
515 strncpy_DYN(&tmpbuf, src++, (size_t) 1);
516 break;
517 case '\\':
518 percent = FALSE;
519 strncpy_DYN(&tmpbuf, src++, (size_t) 1);
520 break;
521 case '%':
522 percent = TRUE;
523 break;
524 case '?': /* "if" */
525 case 't': /* "then" */
526 case 'e': /* "else" */
527 if (percent) {
528 percent = FALSE;
529 tmpbuf.text[tmpbuf.used - 1] = '\n';
530 /* treat a "%e" as else-if, on the same level */
531 if (*src == 'e') {
532 indent_DYN(&tmpbuf, level);
533 strncpy_DYN(&tmpbuf, "%", (size_t) 1);
534 strncpy_DYN(&tmpbuf, src, (size_t) 1);
535 src++;
536 params = has_params(src);
537 if (!params && *src != '\0' && *src != '%') {
538 strncpy_DYN(&tmpbuf, "\n", (size_t) 1);
539 indent_DYN(&tmpbuf, level + 1);
541 } else {
542 indent_DYN(&tmpbuf, level + 1);
543 strncpy_DYN(&tmpbuf, "%", (size_t) 1);
544 strncpy_DYN(&tmpbuf, src, (size_t) 1);
545 if (*src++ == '?') {
546 src = fmt_complex(tterm, capability, src, level + 1);
547 if (*src != '\0' && *src != '%') {
548 strncpy_DYN(&tmpbuf, "\n", (size_t) 1);
549 indent_DYN(&tmpbuf, level + 1);
551 } else if (level == 1) {
552 if (checking)
553 _nc_warning("%s: %%%c without %%? in %s",
554 _nc_first_name(tterm->term_names),
555 *src, capability);
558 continue;
560 break;
561 case ';': /* "endif" */
562 if (percent) {
563 percent = FALSE;
564 if (level > 1) {
565 tmpbuf.text[tmpbuf.used - 1] = '\n';
566 indent_DYN(&tmpbuf, level);
567 strncpy_DYN(&tmpbuf, "%", (size_t) 1);
568 strncpy_DYN(&tmpbuf, src++, (size_t) 1);
569 if (src[0] == '%'
570 && src[1] != '\0'
571 && (strchr("?e;", src[1])) == 0) {
572 tmpbuf.text[tmpbuf.used++] = '\n';
573 indent_DYN(&tmpbuf, level);
575 return src;
577 if (checking)
578 _nc_warning("%s: %%; without %%? in %s",
579 _nc_first_name(tterm->term_names),
580 capability);
582 break;
583 case 'p':
584 if (percent && params) {
585 tmpbuf.text[tmpbuf.used - 1] = '\n';
586 indent_DYN(&tmpbuf, level + 1);
587 strncpy_DYN(&tmpbuf, "%", (size_t) 1);
589 params = FALSE;
590 percent = FALSE;
591 break;
592 case ' ':
593 strncpy_DYN(&tmpbuf, "\\s", (size_t) 2);
594 ++src;
595 continue;
596 default:
597 percent = FALSE;
598 break;
600 strncpy_DYN(&tmpbuf, src++, (size_t) 1);
602 return src;
605 #define SAME_CAP(n,cap) (&tterm->Strings[n] == &cap)
606 #define EXTRA_CAP 20
609 fmt_entry(TERMTYPE *tterm,
610 PredFunc pred,
611 int content_only,
612 int suppress_untranslatable,
613 int infodump,
614 int numbers)
616 PredIdx i, j;
617 char buffer[MAX_TERMINFO_LENGTH + EXTRA_CAP];
618 char *capability;
619 NCURSES_CONST char *name;
620 int predval, len;
621 PredIdx num_bools = 0;
622 PredIdx num_values = 0;
623 PredIdx num_strings = 0;
624 bool outcount = 0;
626 #define WRAP_CONCAT \
627 wrap_concat(buffer); \
628 outcount = TRUE
630 len = 12; /* terminfo file-header */
632 if (pred == 0) {
633 cur_type = tterm;
634 pred = dump_predicate;
637 strcpy_DYN(&outbuf, 0);
638 if (content_only) {
639 column = indent; /* FIXME: workaround to prevent empty lines */
640 } else {
641 strcpy_DYN(&outbuf, tterm->term_names);
644 * Colon is legal in terminfo descriptions, but not in termcap.
646 if (!infodump) {
647 char *p = outbuf.text;
648 while (*p) {
649 if (*p == ':') {
650 *p = '=';
652 ++p;
655 strcpy_DYN(&outbuf, separator);
656 column = (int) outbuf.used;
657 if (height > 1)
658 force_wrap();
661 for_each_boolean(j, tterm) {
662 i = BoolIndirect(j);
663 name = ExtBoolname(tterm, (int) i, bool_names);
664 assert(strlen(name) < sizeof(buffer) - EXTRA_CAP);
666 if (!version_filter(BOOLEAN, i))
667 continue;
668 else if (isObsolete(outform, name))
669 continue;
671 predval = pred(BOOLEAN, i);
672 if (predval != FAIL) {
673 _nc_STRCPY(buffer, name, sizeof(buffer));
674 if (predval <= 0)
675 _nc_STRCAT(buffer, "@", sizeof(buffer));
676 else if (i + 1 > num_bools)
677 num_bools = i + 1;
678 WRAP_CONCAT;
682 if (column != indent && height > 1)
683 force_wrap();
685 for_each_number(j, tterm) {
686 i = NumIndirect(j);
687 name = ExtNumname(tterm, (int) i, num_names);
688 assert(strlen(name) < sizeof(buffer) - EXTRA_CAP);
690 if (!version_filter(NUMBER, i))
691 continue;
692 else if (isObsolete(outform, name))
693 continue;
695 predval = pred(NUMBER, i);
696 if (predval != FAIL) {
697 if (tterm->Numbers[i] < 0) {
698 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
699 "%s@", name);
700 } else {
701 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
702 "%s#%d", name, tterm->Numbers[i]);
703 if (i + 1 > num_values)
704 num_values = i + 1;
706 WRAP_CONCAT;
710 if (column != indent && height > 1)
711 force_wrap();
713 len += (int) (num_bools
714 + num_values * 2
715 + strlen(tterm->term_names) + 1);
716 if (len & 1)
717 len++;
719 #undef CUR
720 #define CUR tterm->
721 if (outform == F_TERMCAP) {
722 if (termcap_reset != ABSENT_STRING) {
723 if (init_3string != ABSENT_STRING
724 && !strcmp(init_3string, termcap_reset))
725 DISCARD(init_3string);
727 if (reset_2string != ABSENT_STRING
728 && !strcmp(reset_2string, termcap_reset))
729 DISCARD(reset_2string);
733 for_each_string(j, tterm) {
734 i = StrIndirect(j);
735 name = ExtStrname(tterm, (int) i, str_names);
736 assert(strlen(name) < sizeof(buffer) - EXTRA_CAP);
738 capability = tterm->Strings[i];
740 if (!version_filter(STRING, i))
741 continue;
742 else if (isObsolete(outform, name))
743 continue;
745 #if NCURSES_XNAMES
747 * Extended names can be longer than 2 characters, but termcap programs
748 * cannot read those (filter them out).
750 if (outform == F_TERMCAP && (strlen(name) > 2))
751 continue;
752 #endif
754 if (outform == F_TERMCAP) {
756 * Some older versions of vi want rmir/smir to be defined
757 * for ich/ich1 to work. If they're not defined, force
758 * them to be output as defined and empty.
760 if (PRESENT(insert_character) || PRESENT(parm_ich)) {
761 if (SAME_CAP(i, enter_insert_mode)
762 && enter_insert_mode == ABSENT_STRING) {
763 _nc_STRCPY(buffer, "im=", sizeof(buffer));
764 WRAP_CONCAT;
765 continue;
768 if (SAME_CAP(i, exit_insert_mode)
769 && exit_insert_mode == ABSENT_STRING) {
770 _nc_STRCPY(buffer, "ei=", sizeof(buffer));
771 WRAP_CONCAT;
772 continue;
776 * termcap applications such as screen will be confused if sgr0
777 * is translated to a string containing rmacs. Filter that out.
779 if (PRESENT(exit_attribute_mode)) {
780 if (SAME_CAP(i, exit_attribute_mode)) {
781 char *trimmed_sgr0;
782 char *my_sgr = set_attributes;
784 set_attributes = save_sgr;
786 trimmed_sgr0 = _nc_trim_sgr0(tterm);
787 if (strcmp(capability, trimmed_sgr0))
788 capability = trimmed_sgr0;
789 else {
790 if (trimmed_sgr0 != exit_attribute_mode)
791 free(trimmed_sgr0);
794 set_attributes = my_sgr;
799 predval = pred(STRING, i);
800 buffer[0] = '\0';
802 if (predval != FAIL) {
803 if (capability != ABSENT_STRING
804 && i + 1 > num_strings)
805 num_strings = i + 1;
807 if (!VALID_STRING(capability)) {
808 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
809 "%s@", name);
810 WRAP_CONCAT;
811 } else if (outform == F_TERMCAP || outform == F_TCONVERR) {
812 char *srccap = _nc_tic_expand(capability, TRUE, numbers);
813 int params = (((i < (int) SIZEOF(parametrized)) &&
814 (i < STRCOUNT))
815 ? parametrized[i]
816 : ((*srccap == 'k')
818 : has_params(srccap)));
819 char *cv = _nc_infotocap(name, srccap, params);
821 if (cv == 0) {
822 if (outform == F_TCONVERR) {
823 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
824 "%s=!!! %s WILL NOT CONVERT !!!",
825 name, srccap);
826 } else if (suppress_untranslatable) {
827 continue;
828 } else {
829 char *s = srccap, *d = buffer;
830 _nc_SPRINTF(d, _nc_SLIMIT(sizeof(buffer)) "..%s=", name);
831 d += strlen(d);
832 while ((*d = *s++) != 0) {
833 if (*d == ':') {
834 *d++ = '\\';
835 *d = ':';
836 } else if (*d == '\\') {
837 *++d = *s++;
839 d++;
842 } else {
843 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
844 "%s=%s", name, cv);
846 len += (int) strlen(capability) + 1;
847 WRAP_CONCAT;
848 } else {
849 char *src = _nc_tic_expand(capability,
850 outform == F_TERMINFO, numbers);
852 strcpy_DYN(&tmpbuf, 0);
853 strcpy_DYN(&tmpbuf, name);
854 strcpy_DYN(&tmpbuf, "=");
855 if (pretty
856 && (outform == F_TERMINFO
857 || outform == F_VARIABLE)) {
858 fmt_complex(tterm, name, src, 1);
859 } else {
860 strcpy_DYN(&tmpbuf, src);
862 len += (int) strlen(capability) + 1;
863 wrap_concat(tmpbuf.text);
864 outcount = TRUE;
867 /* e.g., trimmed_sgr0 */
868 if (capability != ABSENT_STRING &&
869 capability != CANCELLED_STRING &&
870 capability != tterm->Strings[i])
871 free(capability);
873 len += (int) (num_strings * 2);
876 * This piece of code should be an effective inverse of the functions
877 * postprocess_terminfo() and postprocess_terminfo() in parse_entry.c.
878 * Much more work should be done on this to support dumping termcaps.
880 if (tversion == V_HPUX) {
881 if (VALID_STRING(memory_lock)) {
882 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
883 "meml=%s", memory_lock);
884 WRAP_CONCAT;
886 if (VALID_STRING(memory_unlock)) {
887 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
888 "memu=%s", memory_unlock);
889 WRAP_CONCAT;
891 } else if (tversion == V_AIX) {
892 if (VALID_STRING(acs_chars)) {
893 bool box_ok = TRUE;
894 const char *acstrans = "lqkxjmwuvtn";
895 const char *cp;
896 char *tp, *sp, boxchars[11];
898 tp = boxchars;
899 for (cp = acstrans; *cp; cp++) {
900 sp = (strchr) (acs_chars, *cp);
901 if (sp)
902 *tp++ = sp[1];
903 else {
904 box_ok = FALSE;
905 break;
908 tp[0] = '\0';
910 if (box_ok) {
911 char *tmp = _nc_tic_expand(boxchars,
912 (outform == F_TERMINFO),
913 numbers);
914 _nc_STRCPY(buffer, "box1=", sizeof(buffer));
915 while (*tmp != '\0') {
916 size_t have = strlen(buffer);
917 size_t next = strlen(tmp);
918 size_t want = have + next + 1;
919 size_t last = next;
920 char save = '\0';
923 * If the expanded string is too long for the buffer,
924 * chop it off and save the location where we chopped it.
926 if (want >= sizeof(buffer)) {
927 save = tmp[last];
928 tmp[last] = '\0';
930 _nc_STRCAT(buffer, tmp, sizeof(buffer));
933 * If we chopped the buffer, replace the missing piece and
934 * shift everything to append the remainder.
936 if (save != '\0') {
937 next = 0;
938 tmp[last] = save;
939 while ((tmp[next] = tmp[last + next]) != '\0') {
940 ++next;
942 } else {
943 break;
946 WRAP_CONCAT;
952 * kludge: trim off trailer to avoid an extra blank line
953 * in infocmp -u output when there are no string differences
955 if (outcount) {
956 bool trimmed = FALSE;
957 j = (PredIdx) outbuf.used;
958 if (j >= 2
959 && outbuf.text[j - 1] == '\t'
960 && outbuf.text[j - 2] == '\n') {
961 outbuf.used -= 2;
962 trimmed = TRUE;
963 } else if (j >= 4
964 && outbuf.text[j - 1] == ':'
965 && outbuf.text[j - 2] == '\t'
966 && outbuf.text[j - 3] == '\n'
967 && outbuf.text[j - 4] == '\\') {
968 outbuf.used -= 4;
969 trimmed = TRUE;
971 if (trimmed) {
972 outbuf.text[outbuf.used] = '\0';
973 column = oldcol;
974 strcpy_DYN(&outbuf, " ");
977 #if 0
978 fprintf(stderr, "num_bools = %d\n", num_bools);
979 fprintf(stderr, "num_values = %d\n", num_values);
980 fprintf(stderr, "num_strings = %d\n", num_strings);
981 fprintf(stderr, "term_names=%s, len=%d, strlen(outbuf)=%d, outbuf=%s\n",
982 tterm->term_names, len, outbuf.used, outbuf.text);
983 #endif
985 * Here's where we use infodump to trigger a more stringent length check
986 * for termcap-translation purposes.
987 * Return the length of the raw entry, without tc= expansions,
988 * It gives an idea of which entries are deadly to even *scan past*,
989 * as opposed to *use*.
991 return (infodump ? len : (int) termcap_length(outbuf.text));
994 static bool
995 kill_string(TERMTYPE *tterm, char *cap)
997 unsigned n;
998 for (n = 0; n < NUM_STRINGS(tterm); ++n) {
999 if (cap == tterm->Strings[n]) {
1000 tterm->Strings[n] = ABSENT_STRING;
1001 return TRUE;
1004 return FALSE;
1007 static char *
1008 find_string(TERMTYPE *tterm, char *name)
1010 PredIdx n;
1011 for (n = 0; n < NUM_STRINGS(tterm); ++n) {
1012 if (version_filter(STRING, n)
1013 && !strcmp(name, strnames[n])) {
1014 char *cap = tterm->Strings[n];
1015 if (VALID_STRING(cap)) {
1016 return cap;
1018 break;
1021 return ABSENT_STRING;
1025 * This is used to remove function-key labels from a termcap entry to
1026 * make it smaller.
1028 static int
1029 kill_labels(TERMTYPE *tterm, int target)
1031 int n;
1032 int result = 0;
1033 char *cap;
1034 char name[10];
1036 for (n = 0; n <= 10; ++n) {
1037 _nc_SPRINTF(name, _nc_SLIMIT(sizeof(name)) "lf%d", n);
1038 if ((cap = find_string(tterm, name)) != ABSENT_STRING
1039 && kill_string(tterm, cap)) {
1040 target -= (int) (strlen(cap) + 5);
1041 ++result;
1042 if (target < 0)
1043 break;
1046 return result;
1050 * This is used to remove function-key definitions from a termcap entry to
1051 * make it smaller.
1053 static int
1054 kill_fkeys(TERMTYPE *tterm, int target)
1056 int n;
1057 int result = 0;
1058 char *cap;
1059 char name[10];
1061 for (n = 60; n >= 0; --n) {
1062 _nc_SPRINTF(name, _nc_SLIMIT(sizeof(name)) "kf%d", n);
1063 if ((cap = find_string(tterm, name)) != ABSENT_STRING
1064 && kill_string(tterm, cap)) {
1065 target -= (int) (strlen(cap) + 5);
1066 ++result;
1067 if (target < 0)
1068 break;
1071 return result;
1075 * Check if the given acsc string is a 1-1 mapping, i.e., just-like-vt100.
1076 * Also, since this is for termcap, we only care about the line-drawing map.
1078 #define isLine(c) (strchr("lmkjtuvwqxn", c) != 0)
1080 static bool
1081 one_one_mapping(const char *mapping)
1083 bool result = TRUE;
1085 if (mapping != ABSENT_STRING) {
1086 int n = 0;
1087 while (mapping[n] != '\0') {
1088 if (isLine(mapping[n]) &&
1089 mapping[n] != mapping[n + 1]) {
1090 result = FALSE;
1091 break;
1093 n += 2;
1096 return result;
1099 #define FMT_ENTRY() \
1100 fmt_entry(tterm, pred, \
1101 0, \
1102 suppress_untranslatable, \
1103 infodump, numbers)
1105 #define SHOW_WHY PRINTF
1107 static bool
1108 purged_acs(TERMTYPE *tterm)
1110 bool result = FALSE;
1112 if (VALID_STRING(acs_chars)) {
1113 if (!one_one_mapping(acs_chars)) {
1114 enter_alt_charset_mode = ABSENT_STRING;
1115 exit_alt_charset_mode = ABSENT_STRING;
1116 SHOW_WHY("# (rmacs/smacs removed for consistency)\n");
1118 result = TRUE;
1120 return result;
1123 static void
1124 encode_b64(char *target, char *source, unsigned state, int *saved)
1126 /* RFC-4648 */
1127 static const char data[] =
1128 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1129 "abcdefghijklmnopqrstuvwxyz"
1130 "0123456789" "-_";
1131 int ch = UChar(source[state]);
1133 switch (state % 3) {
1134 case 0:
1135 *target++ = data[ch & 077];
1136 *saved = (ch >> 6) & 3;
1137 break;
1138 case 1:
1139 *target++ = data[((ch << 2) | *saved) & 077];
1140 *saved = (ch >> 4) & 017;
1141 break;
1142 case 2:
1143 *target++ = data[((ch << 4) | *saved) & 077];
1144 *target++ = data[(ch >> 2) & 077];
1145 *saved = 0;
1146 break;
1148 *target = '\0';
1152 * Dump a single entry.
1154 void
1155 dump_entry(TERMTYPE *tterm,
1156 int suppress_untranslatable,
1157 int limited,
1158 int numbers,
1159 PredFunc pred)
1161 TERMTYPE save_tterm;
1162 int len, critlen;
1163 const char *legend;
1164 bool infodump;
1166 if (quickdump) {
1167 char bigbuf[65536];
1168 unsigned n;
1169 unsigned offset = 0;
1170 separator = "";
1171 trailer = "\n";
1172 indent = 0;
1173 if (_nc_write_object(tterm, bigbuf, &offset, sizeof(bigbuf)) == OK) {
1174 char numbuf[80];
1175 if (quickdump & 1) {
1176 if (outbuf.used)
1177 wrap_concat("\n");
1178 wrap_concat("hex:");
1179 for (n = 0; n < offset; ++n) {
1180 sprintf(numbuf, "%02X", UChar(bigbuf[n]));
1181 wrap_concat(numbuf);
1184 if (quickdump & 2) {
1185 int value = 0;
1186 if (outbuf.used)
1187 wrap_concat("\n");
1188 wrap_concat("b64:");
1189 for (n = 0; n < offset; ++n) {
1190 encode_b64(numbuf, bigbuf, n, &value);
1191 wrap_concat(numbuf);
1193 switch (n % 3) {
1194 case 0:
1195 break;
1196 case 1:
1197 wrap_concat("===");
1198 break;
1199 case 2:
1200 wrap_concat("==");
1201 break;
1205 return;
1208 if (outform == F_TERMCAP || outform == F_TCONVERR) {
1209 critlen = MAX_TERMCAP_LENGTH;
1210 legend = "older termcap";
1211 infodump = FALSE;
1212 set_obsolete_termcaps(tterm);
1213 } else {
1214 critlen = MAX_TERMINFO_LENGTH;
1215 legend = "terminfo";
1216 infodump = TRUE;
1219 save_sgr = set_attributes;
1221 if ((FMT_ENTRY() > critlen)
1222 && limited) {
1224 save_tterm = *tterm;
1225 if (!suppress_untranslatable) {
1226 SHOW_WHY("# (untranslatable capabilities removed to fit entry within %d bytes)\n",
1227 critlen);
1228 suppress_untranslatable = TRUE;
1230 if (FMT_ENTRY() > critlen) {
1232 * We pick on sgr because it's a nice long string capability that
1233 * is really just an optimization hack. Another good candidate is
1234 * acsc since it is both long and unused by BSD termcap.
1236 bool changed = FALSE;
1238 #if NCURSES_XNAMES
1240 * Extended names are most likely function-key definitions. Drop
1241 * those first.
1243 unsigned n;
1244 for (n = STRCOUNT; n < NUM_STRINGS(tterm); n++) {
1245 const char *name = ExtStrname(tterm, (int) n, strnames);
1247 if (VALID_STRING(tterm->Strings[n])) {
1248 set_attributes = ABSENT_STRING;
1249 /* we remove long names anyway - only report the short */
1250 if (strlen(name) <= 2) {
1251 SHOW_WHY("# (%s removed to fit entry within %d bytes)\n",
1252 name,
1253 critlen);
1255 changed = TRUE;
1256 if (FMT_ENTRY() <= critlen)
1257 break;
1260 #endif
1261 if (VALID_STRING(set_attributes)) {
1262 set_attributes = ABSENT_STRING;
1263 SHOW_WHY("# (sgr removed to fit entry within %d bytes)\n",
1264 critlen);
1265 changed = TRUE;
1267 if (!changed || (FMT_ENTRY() > critlen)) {
1268 if (purged_acs(tterm)) {
1269 acs_chars = ABSENT_STRING;
1270 SHOW_WHY("# (acsc removed to fit entry within %d bytes)\n",
1271 critlen);
1272 changed = TRUE;
1275 if (!changed || (FMT_ENTRY() > critlen)) {
1276 int oldversion = tversion;
1278 tversion = V_BSD;
1279 SHOW_WHY("# (terminfo-only capabilities suppressed to fit entry within %d bytes)\n",
1280 critlen);
1282 len = FMT_ENTRY();
1283 if (len > critlen
1284 && kill_labels(tterm, len - critlen)) {
1285 SHOW_WHY("# (some labels capabilities suppressed to fit entry within %d bytes)\n",
1286 critlen);
1287 len = FMT_ENTRY();
1289 if (len > critlen
1290 && kill_fkeys(tterm, len - critlen)) {
1291 SHOW_WHY("# (some function-key capabilities suppressed to fit entry within %d bytes)\n",
1292 critlen);
1293 len = FMT_ENTRY();
1295 if (len > critlen) {
1296 (void) fprintf(stderr,
1297 "warning: %s entry is %d bytes long\n",
1298 _nc_first_name(tterm->term_names),
1299 len);
1300 SHOW_WHY("# WARNING: this entry, %d bytes long, may core-dump %s libraries!\n",
1301 len, legend);
1303 tversion = oldversion;
1305 set_attributes = save_sgr;
1306 *tterm = save_tterm;
1308 } else if (!version_filter(STRING, STR_IDX(acs_chars))) {
1309 save_tterm = *tterm;
1310 if (purged_acs(tterm)) {
1311 (void) FMT_ENTRY();
1313 *tterm = save_tterm;
1317 void
1318 dump_uses(const char *name, bool infodump)
1319 /* dump "use=" clauses in the appropriate format */
1321 char buffer[MAX_TERMINFO_LENGTH];
1323 if (outform == F_TERMCAP || outform == F_TCONVERR)
1324 trim_trailing();
1325 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
1326 "%s%s", infodump ? "use=" : "tc=", name);
1327 wrap_concat(buffer);
1331 show_entry(void)
1334 * Trim any remaining whitespace.
1336 if (outbuf.used != 0) {
1337 bool infodump = (outform != F_TERMCAP && outform != F_TCONVERR);
1338 char delim = (char) (infodump ? ',' : ':');
1339 int j;
1341 for (j = (int) outbuf.used - 1; j > 0; --j) {
1342 char ch = outbuf.text[j];
1343 if (ch == '\n') {
1345 } else if (isspace(UChar(ch))) {
1346 outbuf.used = (size_t) j;
1347 } else if (!infodump && ch == '\\') {
1348 outbuf.used = (size_t) j;
1349 } else if (ch == delim && (j == 0 || outbuf.text[j - 1] != '\\')) {
1350 outbuf.used = (size_t) (j + 1);
1351 } else {
1352 break;
1355 outbuf.text[outbuf.used] = '\0';
1357 if (outbuf.text != 0) {
1358 (void) fputs(outbuf.text, stdout);
1359 putchar('\n');
1361 return (int) outbuf.used;
1364 void
1365 compare_entry(PredHook hook,
1366 TERMTYPE *tp GCC_UNUSED,
1367 bool quiet)
1368 /* compare two entries */
1370 PredIdx i, j;
1371 NCURSES_CONST char *name;
1373 if (!quiet)
1374 fputs(" comparing booleans.\n", stdout);
1375 for_each_boolean(j, tp) {
1376 i = BoolIndirect(j);
1377 name = ExtBoolname(tp, (int) i, bool_names);
1379 if (isObsolete(outform, name))
1380 continue;
1382 (*hook) (CMP_BOOLEAN, i, name);
1385 if (!quiet)
1386 fputs(" comparing numbers.\n", stdout);
1387 for_each_number(j, tp) {
1388 i = NumIndirect(j);
1389 name = ExtNumname(tp, (int) i, num_names);
1391 if (isObsolete(outform, name))
1392 continue;
1394 (*hook) (CMP_NUMBER, i, name);
1397 if (!quiet)
1398 fputs(" comparing strings.\n", stdout);
1399 for_each_string(j, tp) {
1400 i = StrIndirect(j);
1401 name = ExtStrname(tp, (int) i, str_names);
1403 if (isObsolete(outform, name))
1404 continue;
1406 (*hook) (CMP_STRING, i, name);
1409 /* (void) fputs(" comparing use entries.\n", stdout); */
1410 (*hook) (CMP_USE, 0, "use");
1414 #define NOTSET(s) ((s) == 0)
1417 * This bit of legerdemain turns all the terminfo variable names into
1418 * references to locations in the arrays Booleans, Numbers, and Strings ---
1419 * precisely what's needed.
1421 #undef CUR
1422 #define CUR tp->
1424 static void
1425 set_obsolete_termcaps(TERMTYPE *tp)
1427 #include "capdefaults.c"
1431 * Convert an alternate-character-set string to canonical form: sorted and
1432 * unique.
1434 void
1435 repair_acsc(TERMTYPE *tp)
1437 if (VALID_STRING(acs_chars)) {
1438 size_t n, m;
1439 char mapped[256];
1440 char extra = 0;
1441 unsigned source;
1442 unsigned target;
1443 bool fix_needed = FALSE;
1445 for (n = 0, source = 0; acs_chars[n] != 0; n++) {
1446 target = UChar(acs_chars[n]);
1447 if (source >= target) {
1448 fix_needed = TRUE;
1449 break;
1451 source = target;
1452 if (acs_chars[n + 1])
1453 n++;
1455 if (fix_needed) {
1456 memset(mapped, 0, sizeof(mapped));
1457 for (n = 0; acs_chars[n] != 0; n++) {
1458 source = UChar(acs_chars[n]);
1459 if ((target = (unsigned char) acs_chars[n + 1]) != 0) {
1460 mapped[source] = (char) target;
1461 n++;
1462 } else {
1463 extra = (char) source;
1466 for (n = m = 0; n < sizeof(mapped); n++) {
1467 if (mapped[n]) {
1468 acs_chars[m++] = (char) n;
1469 acs_chars[m++] = mapped[n];
1472 if (extra)
1473 acs_chars[m++] = extra; /* garbage in, garbage out */
1474 acs_chars[m] = 0;