1 /****************************************************************************
2 * Copyright (c) 1998-2004,2005 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 * and: Thomas E. Dickey 1996 on *
33 ****************************************************************************/
34
35 #define __INTERNAL_CAPS_VISIBLE
36 #include <progs.priv.h>
37
38 #include "dump_entry.h"
39 #include "termsort.c" /* this C file is generated */
40 #include <parametrized.h> /* so is this */
41
42 MODULE_ID("$Id: dump_entry.c,v 1.70 2005/07/23 20:03:30 tom Exp $")
43 #ifdef __MirBSD__
44 __RCSID("$MirOS: src/lib/libncurses/src/progs/dump_entry.c,v 1.4 2009/09/06 12:46:48 tg Exp $");
45 #endif
46
47 #define INDENT 8
48 #define DISCARD(string) string = ABSENT_STRING
49 #define PRINTF (void) printf
50
51 typedef struct {
52 char *text;
53 size_t used;
54 size_t size;
55 } DYNBUF;
56
57 static int tversion; /* terminfo version */
58 static int outform; /* output format to use */
59 static int sortmode; /* sort mode to use */
60 static int width = 60; /* max line width for listings */
61 static int column; /* current column, limited by 'width' */
62 static int oldcol; /* last value of column before wrap */
63 static bool pretty; /* true if we format if-then-else strings */
64
65 static char *save_sgr;
66 static char *save_acsc;
67
68 static DYNBUF outbuf;
69 static DYNBUF tmpbuf;
70
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;
76
77 static const char *separator, *trailer;
78
79 /* cover various ports and variants of terminfo */
80 #define V_ALLCAPS 0 /* all capabilities (SVr4, XSI, ncurses) */
81 #define V_SVR1 1 /* SVR1, Ultrix */
82 #define V_HPUX 2 /* HP/UX */
83 #define V_AIX 3 /* AIX */
84 #define V_BSD 4 /* BSD */
85
86 #if NCURSES_XNAMES
87 #define OBSOLETE(n) (!_nc_user_definable && (n[0] == 'O' && n[1] == 'T'))
88 #else
89 #define OBSOLETE(n) (n[0] == 'O' && n[1] == 'T')
90 #endif
91
92 #define isObsolete(f,n) ((f == F_TERMINFO || f == F_VARIABLE) && OBSOLETE(n))
93
94 #if NCURSES_XNAMES
95 #define BoolIndirect(j) ((j >= BOOLCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : bool_indirect[j]))
96 #define NumIndirect(j) ((j >= NUMCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : num_indirect[j]))
97 #define StrIndirect(j) ((j >= STRCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : str_indirect[j]))
98 #else
99 #define BoolIndirect(j) ((sortmode == S_NOSORT) ? (j) : bool_indirect[j])
100 #define NumIndirect(j) ((sortmode == S_NOSORT) ? (j) : num_indirect[j])
101 #define StrIndirect(j) ((sortmode == S_NOSORT) ? (j) : str_indirect[j])
102 #endif
103
104 static void
strncpy_DYN(DYNBUF * dst,const char * src,size_t need)105 strncpy_DYN(DYNBUF * dst, const char *src, size_t need)
106 {
107 size_t want = need + dst->used + 1;
108 if (want > dst->size) {
109 dst->size += (want + 1024); /* be generous */
110 dst->text = typeRealloc(char, dst->size, dst->text);
111 }
112 (void) strncpy(dst->text + dst->used, src, need);
113 dst->used += need;
114 dst->text[dst->used] = 0;
115 }
116
117 static void
strcpy_DYN(DYNBUF * dst,const char * src)118 strcpy_DYN(DYNBUF * dst, const char *src)
119 {
120 if (src == 0) {
121 dst->used = 0;
122 strcpy_DYN(dst, "");
123 } else {
124 strncpy_DYN(dst, src, strlen(src));
125 }
126 }
127
128 #if NO_LEAKS
129 static void
free_DYN(DYNBUF * p)130 free_DYN(DYNBUF * p)
131 {
132 if (p->text != 0)
133 free(p->text);
134 p->text = 0;
135 p->size = 0;
136 p->used = 0;
137 }
138
139 void
_nc_leaks_dump_entry(void)140 _nc_leaks_dump_entry(void)
141 {
142 free_DYN(&outbuf);
143 free_DYN(&tmpbuf);
144 }
145 #endif
146
147 NCURSES_CONST char *
nametrans(const char * name)148 nametrans(const char *name)
149 /* translate a capability name from termcap to terminfo */
150 {
151 const struct name_table_entry *np;
152
153 if ((np = _nc_find_entry(name, _nc_get_hash_table(0))) != 0)
154 switch (np->nte_type) {
155 case BOOLEAN:
156 if (bool_from_termcap[np->nte_index])
157 return (boolcodes[np->nte_index]);
158 break;
159
160 case NUMBER:
161 if (num_from_termcap[np->nte_index])
162 return (numcodes[np->nte_index]);
163 break;
164
165 case STRING:
166 if (str_from_termcap[np->nte_index])
167 return (strcodes[np->nte_index]);
168 break;
169 }
170
171 return (0);
172 }
173
174 void
dump_init(const char * version,int mode,int sort,int twidth,int traceval,bool formatted)175 dump_init(const char *version, int mode, int sort, int twidth, int traceval,
176 bool formatted)
177 /* set up for entry display */
178 {
179 width = twidth;
180 pretty = formatted;
181
182 /* versions */
183 if (version == 0)
184 tversion = V_ALLCAPS;
185 else if (!strcmp(version, "SVr1") || !strcmp(version, "SVR1")
186 || !strcmp(version, "Ultrix"))
187 tversion = V_SVR1;
188 else if (!strcmp(version, "HP"))
189 tversion = V_HPUX;
190 else if (!strcmp(version, "AIX"))
191 tversion = V_AIX;
192 else if (!strcmp(version, "BSD"))
193 tversion = V_BSD;
194 else
195 tversion = V_ALLCAPS;
196
197 /* implement display modes */
198 switch (outform = mode) {
199 case F_LITERAL:
200 case F_TERMINFO:
201 bool_names = boolnames;
202 num_names = numnames;
203 str_names = strnames;
204 separator = twidth ? ", " : ",";
205 trailer = "\n\t";
206 break;
207
208 case F_VARIABLE:
209 bool_names = boolfnames;
210 num_names = numfnames;
211 str_names = strfnames;
212 separator = twidth ? ", " : ",";
213 trailer = "\n\t";
214 break;
215
216 case F_TERMCAP:
217 case F_TCONVERR:
218 bool_names = boolcodes;
219 num_names = numcodes;
220 str_names = strcodes;
221 separator = ":";
222 trailer = "\\\n\t:";
223 break;
224 }
225
226 /* implement sort modes */
227 switch (sortmode = sort) {
228 case S_NOSORT:
229 if (traceval)
230 (void) fprintf(stderr,
231 "%s: sorting by term structure order\n", _nc_progname);
232 break;
233
234 case S_TERMINFO:
235 if (traceval)
236 (void) fprintf(stderr,
237 "%s: sorting by terminfo name order\n", _nc_progname);
238 bool_indirect = bool_terminfo_sort;
239 num_indirect = num_terminfo_sort;
240 str_indirect = str_terminfo_sort;
241 break;
242
243 case S_VARIABLE:
244 if (traceval)
245 (void) fprintf(stderr,
246 "%s: sorting by C variable order\n", _nc_progname);
247 bool_indirect = bool_variable_sort;
248 num_indirect = num_variable_sort;
249 str_indirect = str_variable_sort;
250 break;
251
252 case S_TERMCAP:
253 if (traceval)
254 (void) fprintf(stderr,
255 "%s: sorting by termcap name order\n", _nc_progname);
256 bool_indirect = bool_termcap_sort;
257 num_indirect = num_termcap_sort;
258 str_indirect = str_termcap_sort;
259 break;
260 }
261
262 if (traceval)
263 (void) fprintf(stderr,
264 "%s: width = %d, tversion = %d, outform = %d\n",
265 _nc_progname, width, tversion, outform);
266 }
267
268 static TERMTYPE *cur_type;
269
270 static int
dump_predicate(PredType type,PredIdx idx)271 dump_predicate(PredType type, PredIdx idx)
272 /* predicate function to use for ordinary decompilation */
273 {
274 switch (type) {
275 case BOOLEAN:
276 return (cur_type->Booleans[idx] == FALSE)
277 ? FAIL : cur_type->Booleans[idx];
278
279 case NUMBER:
280 return (cur_type->Numbers[idx] == ABSENT_NUMERIC)
281 ? FAIL : cur_type->Numbers[idx];
282
283 case STRING:
284 return (cur_type->Strings[idx] != ABSENT_STRING)
285 ? (int) TRUE : FAIL;
286 }
287
288 return (FALSE); /* pacify compiler */
289 }
290
291 static void set_obsolete_termcaps(TERMTYPE *tp);
292
293 /* is this the index of a function key string? */
294 #define FNKEY(i) (((i)<= 65 && (i)>= 75) || ((i)<= 216 && (i)>= 268))
295
296 /*
297 * If we configure with a different Caps file, the offsets into the arrays
298 * will change. So we use an address expression.
299 */
300 #define BOOL_IDX(name) (&(name) - &(CUR Booleans[0]))
301 #define NUM_IDX(name) (&(name) - &(CUR Numbers[0]))
302 #define STR_IDX(name) (&(name) - &(CUR Strings[0]))
303
304 static bool
version_filter(PredType type,PredIdx idx)305 version_filter(PredType type, PredIdx idx)
306 /* filter out capabilities we may want to suppress */
307 {
308 switch (tversion) {
309 case V_ALLCAPS: /* SVr4, XSI Curses */
310 return (TRUE);
311
312 case V_SVR1: /* System V Release 1, Ultrix */
313 switch (type) {
314 case BOOLEAN:
315 return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE);
316 case NUMBER:
317 return ((idx <= NUM_IDX(width_status_line)) ? TRUE : FALSE);
318 case STRING:
319 return ((idx <= STR_IDX(prtr_non)) ? TRUE : FALSE);
320 }
321 break;
322
323 case V_HPUX: /* Hewlett-Packard */
324 switch (type) {
325 case BOOLEAN:
326 return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE);
327 case NUMBER:
328 return ((idx <= NUM_IDX(label_width)) ? TRUE : FALSE);
329 case STRING:
330 if (idx <= STR_IDX(prtr_non))
331 return (TRUE);
332 else if (FNKEY(idx)) /* function keys */
333 return (TRUE);
334 else if (idx == STR_IDX(plab_norm)
335 || idx == STR_IDX(label_on)
336 || idx == STR_IDX(label_off))
337 return (TRUE);
338 else
339 return (FALSE);
340 }
341 break;
342
343 case V_AIX: /* AIX */
344 switch (type) {
345 case BOOLEAN:
346 return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE);
347 case NUMBER:
348 return ((idx <= NUM_IDX(width_status_line)) ? TRUE : FALSE);
349 case STRING:
350 if (idx <= STR_IDX(prtr_non))
351 return (TRUE);
352 else if (FNKEY(idx)) /* function keys */
353 return (TRUE);
354 else
355 return (FALSE);
356 }
357 break;
358
359 case V_BSD: /* BSD */
360 switch (type) {
361 case BOOLEAN:
362 return bool_from_termcap[idx];
363 case NUMBER:
364 return num_from_termcap[idx];
365 case STRING:
366 return str_from_termcap[idx];
367 }
368 break;
369 }
370
371 return (FALSE); /* pacify the compiler */
372 }
373
374 static void
force_wrap(void)375 force_wrap(void)
376 {
377 oldcol = column;
378 strcpy_DYN(&outbuf, trailer);
379 column = INDENT;
380 }
381
382 static void
wrap_concat(const char * src)383 wrap_concat(const char *src)
384 {
385 int need = strlen(src);
386 int want = strlen(separator) + need;
387
388 if (column > INDENT
389 && column + want > width) {
390 force_wrap();
391 }
392 strcpy_DYN(&outbuf, src);
393 strcpy_DYN(&outbuf, separator);
394 column += need;
395 }
396
397 #define IGNORE_SEP_TRAIL(first,last,sep_trail) \
398 if ((size_t)(last - first) > sizeof(sep_trail)-1 \
399 && !strncmp(first, sep_trail, sizeof(sep_trail)-1)) \
400 first += sizeof(sep_trail)-2
401
402 /* Returns the nominal length of the buffer assuming it is termcap format,
403 * i.e., the continuation sequence is treated as a single character ":".
404 *
405 * There are several implementations of termcap which read the text into a
406 * fixed-size buffer. Generally they strip the newlines from the text, but may
407 * not do it until after the buffer is read. Also, "tc=" resolution may be
408 * expanded in the same buffer. This function is useful for measuring the size
409 * of the best fixed-buffer implementation; the worst case may be much worse.
410 */
411 #ifdef TEST_TERMCAP_LENGTH
412 static int
termcap_length(const char * src)413 termcap_length(const char *src)
414 {
415 static const char pattern[] = ":\\\n\t:";
416
417 int len = 0;
418 const char *const t = src + strlen(src);
419
420 while (*src != '\0') {
421 IGNORE_SEP_TRAIL(src, t, pattern);
422 src++;
423 len++;
424 }
425 return len;
426 }
427 #else
428 #define termcap_length(src) strlen(src)
429 #endif
430
431 static char *
fmt_complex(char * src,int level)432 fmt_complex(char *src, int level)
433 {
434 int percent = 0;
435 int n;
436 bool if_then = strstr(src, "%?") != 0;
437 bool params = !if_then && (strlen(src) > 50) && (strstr(src, "%p") != 0);
438
439 while (*src != '\0') {
440 switch (*src) {
441 case '\\':
442 percent = 0;
443 strncpy_DYN(&tmpbuf, src++, 1);
444 break;
445 case '%':
446 percent = 1;
447 break;
448 case '?': /* "if" */
449 case 't': /* "then" */
450 case 'e': /* "else" */
451 if (percent) {
452 percent = 0;
453 tmpbuf.text[tmpbuf.used - 1] = '\n';
454 /* treat a "%e%?" as else-if, on the same level */
455 if (!strncmp(src, "e%?", 3)) {
456 for (n = 0; n < level; n++)
457 strncpy_DYN(&tmpbuf, "\t", 1);
458 strncpy_DYN(&tmpbuf, "%", 1);
459 strncpy_DYN(&tmpbuf, src, 3);
460 src += 3;
461 } else {
462 for (n = 0; n <= level; n++)
463 strncpy_DYN(&tmpbuf, "\t", 1);
464 strncpy_DYN(&tmpbuf, "%", 1);
465 strncpy_DYN(&tmpbuf, src, 1);
466 if (*src++ == '?') {
467 src = fmt_complex(src, level + 1);
468 } else if (level == 1) {
469 _nc_warning("%%%c without %%?", *src);
470 }
471 }
472 continue;
473 }
474 break;
475 case ';': /* "endif" */
476 if (percent) {
477 percent = 0;
478 if (level > 1) {
479 tmpbuf.text[tmpbuf.used - 1] = '\n';
480 for (n = 0; n < level; n++)
481 strncpy_DYN(&tmpbuf, "\t", 1);
482 strncpy_DYN(&tmpbuf, "%", 1);
483 strncpy_DYN(&tmpbuf, src++, 1);
484 return src;
485 }
486 _nc_warning("%%; without %%?");
487 }
488 break;
489 case 'p':
490 if (percent && params) {
491 tmpbuf.text[tmpbuf.used - 1] = '\n';
492 for (n = 0; n <= level; n++)
493 strncpy_DYN(&tmpbuf, "\t", 1);
494 strncpy_DYN(&tmpbuf, "%", 1);
495 }
496 percent = 0;
497 break;
498 default:
499 percent = 0;
500 break;
501 }
502 strncpy_DYN(&tmpbuf, src++, 1);
503 }
504 return src;
505 }
506
507 #define SAME_CAP(n,cap) (&tterm->Strings[n] == &cap)
508
509 int
fmt_entry(TERMTYPE * tterm,PredFunc pred,bool content_only,bool suppress_untranslatable,bool infodump,int numbers)510 fmt_entry(TERMTYPE *tterm,
511 PredFunc pred,
512 bool content_only,
513 bool suppress_untranslatable,
514 bool infodump,
515 int numbers)
516 {
517 PredIdx i, j;
518 char buffer[MAX_TERMINFO_LENGTH];
519 char *capability;
520 NCURSES_CONST char *name;
521 int predval, len;
522 PredIdx num_bools = 0;
523 PredIdx num_values = 0;
524 PredIdx num_strings = 0;
525 bool outcount = 0;
526
527 #define WRAP_CONCAT \
528 wrap_concat(buffer); \
529 outcount = TRUE
530
531 len = 12; /* terminfo file-header */
532
533 if (pred == 0) {
534 cur_type = tterm;
535 pred = dump_predicate;
536 }
537
538 strcpy_DYN(&outbuf, 0);
539 if (content_only) {
540 column = INDENT; /* FIXME: workaround to prevent empty lines */
541 } else {
542 strcpy_DYN(&outbuf, tterm->term_names);
543 strcpy_DYN(&outbuf, separator);
544 column = outbuf.used;
545 force_wrap();
546 }
547
548 for_each_boolean(j, tterm) {
549 i = BoolIndirect(j);
550 name = ExtBoolname(tterm, i, bool_names);
551
552 if (!version_filter(BOOLEAN, i))
553 continue;
554 else if (isObsolete(outform, name))
555 continue;
556
557 predval = pred(BOOLEAN, i);
558 if (predval != FAIL) {
559 (void) strcpy(buffer, name);
560 if (predval <= 0)
561 (void) strcat(buffer, "@");
562 else if (i + 1 > num_bools)
563 num_bools = i + 1;
564 WRAP_CONCAT;
565 }
566 }
567
568 if (column != INDENT)
569 force_wrap();
570
571 for_each_number(j, tterm) {
572 i = NumIndirect(j);
573 name = ExtNumname(tterm, i, num_names);
574
575 if (!version_filter(NUMBER, i))
576 continue;
577 else if (isObsolete(outform, name))
578 continue;
579
580 predval = pred(NUMBER, i);
581 if (predval != FAIL) {
582 if (tterm->Numbers[i] < 0) {
583 sprintf(buffer, "%s@", name);
584 } else {
585 sprintf(buffer, "%s#%d", name, tterm->Numbers[i]);
586 if (i + 1 > num_values)
587 num_values = i + 1;
588 }
589 WRAP_CONCAT;
590 }
591 }
592
593 if (column != INDENT)
594 force_wrap();
595
596 len += num_bools
597 + num_values * 2
598 + strlen(tterm->term_names) + 1;
599 if (len & 1)
600 len++;
601
602 #undef CUR
603 #define CUR tterm->
604 if (outform == F_TERMCAP) {
605 if (termcap_reset != ABSENT_STRING) {
606 if (init_3string != ABSENT_STRING
607 && !strcmp(init_3string, termcap_reset))
608 DISCARD(init_3string);
609
610 if (reset_2string != ABSENT_STRING
611 && !strcmp(reset_2string, termcap_reset))
612 DISCARD(reset_2string);
613 }
614 }
615
616 for_each_string(j, tterm) {
617 i = StrIndirect(j);
618 name = ExtStrname(tterm, i, str_names);
619 capability = tterm->Strings[i];
620
621 if (!version_filter(STRING, i))
622 continue;
623 else if (isObsolete(outform, name))
624 continue;
625
626 #if NCURSES_XNAMES
627 /*
628 * Extended names can be longer than 2 characters, but termcap programs
629 * cannot read those (filter them out).
630 */
631 if (outform == F_TERMCAP && (strlen(name) > 2))
632 continue;
633 #endif
634
635 if (outform == F_TERMCAP) {
636 /*
637 * Some older versions of vi want rmir/smir to be defined
638 * for ich/ich1 to work. If they're not defined, force
639 * them to be output as defined and empty.
640 */
641 if (PRESENT(insert_character) || PRESENT(parm_ich)) {
642 if (SAME_CAP(i, enter_insert_mode)
643 && enter_insert_mode == ABSENT_STRING) {
644 (void) strcpy(buffer, "im=");
645 WRAP_CONCAT;
646 continue;
647 }
648
649 if (SAME_CAP(i, exit_insert_mode)
650 && exit_insert_mode == ABSENT_STRING) {
651 (void) strcpy(buffer, "ei=");
652 WRAP_CONCAT;
653 continue;
654 }
655 }
656 /*
657 * termcap applications such as screen will be confused if sgr0
658 * is translated to a string containing rmacs. Filter that out.
659 */
660 if (PRESENT(exit_attribute_mode)) {
661 if (SAME_CAP(i, exit_attribute_mode)) {
662 char *trimmed_sgr0;
663 char *my_sgr = set_attributes;
664
665 set_attributes = save_sgr;
666
667 trimmed_sgr0 = _nc_trim_sgr0(tterm);
668 if (strcmp(capability, trimmed_sgr0))
669 capability = trimmed_sgr0;
670
671 set_attributes = my_sgr;
672 }
673 }
674 }
675
676 predval = pred(STRING, i);
677 buffer[0] = '\0';
678
679 if (predval != FAIL) {
680 if (capability != ABSENT_STRING
681 && i + 1 > num_strings)
682 num_strings = i + 1;
683
684 if (!VALID_STRING(capability)) {
685 sprintf(buffer, "%s@", name);
686 WRAP_CONCAT;
687 } else if (outform == F_TERMCAP || outform == F_TCONVERR) {
688 int params = ((i < (int) SIZEOF(parametrized))
689 ? parametrized[i]
690 : 0);
691 char *srccap = _nc_tic_expand(capability, TRUE, numbers);
692 char *cv = _nc_infotocap(name, srccap, params);
693
694 if (cv == 0) {
695 if (outform == F_TCONVERR) {
696 sprintf(buffer, "%s=!!! %s WILL NOT CONVERT !!!",
697 name, srccap);
698 } else if (suppress_untranslatable) {
699 continue;
700 } else {
701 char *s = srccap, *d = buffer;
702 sprintf(d, "..%s=", name);
703 d += strlen(d);
704 while ((*d = *s++) != 0) {
705 if (*d == ':') {
706 *d++ = '\\';
707 *d = ':';
708 } else if (*d == '\\') {
709 *++d = *s++;
710 }
711 d++;
712 }
713 }
714 } else {
715 sprintf(buffer, "%s=%s", name, cv);
716 }
717 len += strlen(capability) + 1;
718 WRAP_CONCAT;
719 } else {
720 char *src = _nc_tic_expand(capability,
721 outform == F_TERMINFO, numbers);
722
723 strcpy_DYN(&tmpbuf, 0);
724 strcpy_DYN(&tmpbuf, name);
725 strcpy_DYN(&tmpbuf, "=");
726 if (pretty
727 && (outform == F_TERMINFO
728 || outform == F_VARIABLE)) {
729 fmt_complex(src, 1);
730 } else {
731 strcpy_DYN(&tmpbuf, src);
732 }
733 len += strlen(capability) + 1;
734 wrap_concat(tmpbuf.text);
735 outcount = TRUE;
736 }
737 }
738 /* e.g., trimmed_sgr0 */
739 if (capability != tterm->Strings[i])
740 free(capability);
741 }
742 len += num_strings * 2;
743
744 /*
745 * This piece of code should be an effective inverse of the functions
746 * postprocess_terminfo and postprocess_terminfo in parse_entry.c.
747 * Much more work should be done on this to support dumping termcaps.
748 */
749 if (tversion == V_HPUX) {
750 if (memory_lock) {
751 (void) sprintf(buffer, "meml=%s", memory_lock);
752 WRAP_CONCAT;
753 }
754 if (memory_unlock) {
755 (void) sprintf(buffer, "memu=%s", memory_unlock);
756 WRAP_CONCAT;
757 }
758 } else if (tversion == V_AIX) {
759 if (VALID_STRING(acs_chars)) {
760 bool box_ok = TRUE;
761 const char *acstrans = "lqkxjmwuvtn";
762 const char *cp;
763 char *tp, *sp, boxchars[11];
764
765 tp = boxchars;
766 for (cp = acstrans; *cp; cp++) {
767 sp = strchr(acs_chars, *cp);
768 if (sp)
769 *tp++ = sp[1];
770 else {
771 box_ok = FALSE;
772 break;
773 }
774 }
775 tp[0] = '\0';
776
777 if (box_ok) {
778 (void) strcpy(buffer, "box1=");
779 (void) strcat(buffer, _nc_tic_expand(boxchars,
780 outform == F_TERMINFO, numbers));
781 WRAP_CONCAT;
782 }
783 }
784 }
785
786 /*
787 * kludge: trim off trailer to avoid an extra blank line
788 * in infocmp -u output when there are no string differences
789 */
790 if (outcount) {
791 bool trimmed = FALSE;
792 j = outbuf.used;
793 if (j >= 2
794 && outbuf.text[j - 1] == '\t'
795 && outbuf.text[j - 2] == '\n') {
796 outbuf.used -= 2;
797 trimmed = TRUE;
798 } else if (j >= 4
799 && outbuf.text[j - 1] == ':'
800 && outbuf.text[j - 2] == '\t'
801 && outbuf.text[j - 3] == '\n'
802 && outbuf.text[j - 4] == '\\') {
803 outbuf.used -= 4;
804 trimmed = TRUE;
805 }
806 if (trimmed) {
807 outbuf.text[outbuf.used] = '\0';
808 column = oldcol;
809 }
810 }
811 #if 0
812 fprintf(stderr, "num_bools = %d\n", num_bools);
813 fprintf(stderr, "num_values = %d\n", num_values);
814 fprintf(stderr, "num_strings = %d\n", num_strings);
815 fprintf(stderr, "term_names=%s, len=%d, strlen(outbuf)=%d, outbuf=%s\n",
816 tterm->term_names, len, outbuf.used, outbuf.text);
817 #endif
818 /*
819 * Here's where we use infodump to trigger a more stringent length check
820 * for termcap-translation purposes.
821 * Return the length of the raw entry, without tc= expansions,
822 * It gives an idea of which entries are deadly to even *scan past*,
823 * as opposed to *use*.
824 */
825 return (infodump ? len : (int) termcap_length(outbuf.text));
826 }
827
828 static bool
kill_string(TERMTYPE * tterm,char * cap)829 kill_string(TERMTYPE *tterm, char *cap)
830 {
831 int n;
832 for (n = 0; n < NUM_STRINGS(tterm); ++n) {
833 if (cap == tterm->Strings[n]) {
834 tterm->Strings[n] = ABSENT_STRING;
835 return TRUE;
836 }
837 }
838 return FALSE;
839 }
840
841 static char *
find_string(TERMTYPE * tterm,char * name)842 find_string(TERMTYPE *tterm, char *name)
843 {
844 PredIdx n;
845 for (n = 0; n < NUM_STRINGS(tterm); ++n) {
846 if (version_filter(STRING, n)
847 && !strcmp(name, strnames[n])) {
848 char *cap = tterm->Strings[n];
849 if (VALID_STRING(cap)) {
850 return cap;
851 }
852 break;
853 }
854 }
855 return ABSENT_STRING;
856 }
857
858 /*
859 * This is used to remove function-key labels from a termcap entry to
860 * make it smaller.
861 */
862 static int
kill_labels(TERMTYPE * tterm,int target)863 kill_labels(TERMTYPE *tterm, int target)
864 {
865 int n;
866 int result = 0;
867 char *cap;
868 char name[10];
869
870 for (n = 0; n <= 10; ++n) {
871 sprintf(name, "lf%d", n);
872 if ((cap = find_string(tterm, name)) != ABSENT_STRING
873 && kill_string(tterm, cap)) {
874 target -= (strlen(cap) + 5);
875 ++result;
876 if (target < 0)
877 break;
878 }
879 }
880 return result;
881 }
882
883 /*
884 * This is used to remove function-key definitions from a termcap entry to
885 * make it smaller.
886 */
887 static int
kill_fkeys(TERMTYPE * tterm,int target)888 kill_fkeys(TERMTYPE *tterm, int target)
889 {
890 int n;
891 int result = 0;
892 char *cap;
893 char name[10];
894
895 for (n = 60; n >= 0; --n) {
896 sprintf(name, "kf%d", n);
897 if ((cap = find_string(tterm, name)) != ABSENT_STRING
898 && kill_string(tterm, cap)) {
899 target -= (strlen(cap) + 5);
900 ++result;
901 if (target < 0)
902 break;
903 }
904 }
905 return result;
906 }
907
908 #define FMT_ENTRY() \
909 fmt_entry(tterm, pred, \
910 (already_used > 0), \
911 suppress_untranslatable, \
912 infodump, numbers)
913
914 #define SHOW_WHY if (!already_used) PRINTF
915
916 int
dump_entry(TERMTYPE * tterm,bool suppress_untranslatable,bool limited,int already_used,int numbers,PredFunc pred)917 dump_entry(TERMTYPE *tterm,
918 bool suppress_untranslatable,
919 bool limited,
920 int already_used,
921 int numbers,
922 PredFunc pred)
923 /* dump a single entry */
924 {
925 int len, critlen;
926 const char *legend;
927 bool infodump;
928
929 if (outform == F_TERMCAP || outform == F_TCONVERR) {
930 critlen = MAX_TERMCAP_LENGTH;
931 legend = "older termcap";
932 infodump = FALSE;
933 set_obsolete_termcaps(tterm);
934 } else {
935 critlen = MAX_TERMINFO_LENGTH;
936 legend = "terminfo";
937 infodump = TRUE;
938 }
939 critlen -= already_used;
940
941 save_sgr = set_attributes;
942 save_acsc = acs_chars;
943
944 if (((len = FMT_ENTRY()) > critlen)
945 && limited) {
946 if (!suppress_untranslatable) {
947 SHOW_WHY("# (untranslatable capabilities removed to fit entry within %d bytes)\n",
948 critlen);
949 suppress_untranslatable = TRUE;
950 }
951 if ((len = FMT_ENTRY()) > critlen) {
952 /*
953 * We pick on sgr because it's a nice long string capability that
954 * is really just an optimization hack. Another good candidate is
955 * acsc since it is both long and unused by BSD termcap.
956 */
957 bool changed = FALSE;
958
959 #if NCURSES_XNAMES
960 /*
961 * Extended names are most likely function-key definitions. Drop
962 * those first.
963 */
964 int n;
965 for (n = STRCOUNT; n < NUM_STRINGS(tterm); n++) {
966 const char *name = ExtStrname(tterm, n, strnames);
967
968 if (VALID_STRING(tterm->Strings[n])) {
969 set_attributes = ABSENT_STRING;
970 /* we remove long names anyway - only report the short */
971 if (strlen(name) <= 2) {
972 SHOW_WHY("# (%s removed to fit entry within %d bytes)\n",
973 name,
974 critlen);
975 }
976 changed = TRUE;
977 if ((len = FMT_ENTRY()) <= critlen)
978 break;
979 }
980 }
981 #endif
982 if (VALID_STRING(set_attributes)) {
983 set_attributes = ABSENT_STRING;
984 SHOW_WHY("# (sgr removed to fit entry within %d bytes)\n",
985 critlen);
986 changed = TRUE;
987 }
988 if (!changed || ((len = FMT_ENTRY()) > critlen)) {
989 if (VALID_STRING(acs_chars)) {
990 acs_chars = ABSENT_STRING;
991 SHOW_WHY("# (acsc removed to fit entry within %d bytes)\n",
992 critlen);
993 changed = TRUE;
994 }
995 }
996 if (!changed || ((len = FMT_ENTRY()) > critlen)) {
997 int oldversion = tversion;
998
999 tversion = V_BSD;
1000 SHOW_WHY("# (terminfo-only capabilities suppressed to fit entry within %d bytes)\n",
1001 critlen);
1002
1003 len = FMT_ENTRY();
1004 if (len > critlen
1005 && kill_labels(tterm, len - critlen)) {
1006 SHOW_WHY("# (some labels capabilities suppressed to fit entry within %d bytes)\n",
1007 critlen);
1008 len = FMT_ENTRY();
1009 }
1010 if (len > critlen
1011 && kill_fkeys(tterm, len - critlen)) {
1012 SHOW_WHY("# (some function-key capabilities suppressed to fit entry within %d bytes)\n",
1013 critlen);
1014 len = FMT_ENTRY();
1015 }
1016 if (len > critlen && !already_used) {
1017 (void) fprintf(stderr,
1018 "warning: %s entry is %d bytes long\n",
1019 _nc_first_name(tterm->term_names),
1020 len);
1021 SHOW_WHY("# WARNING: this entry, %d bytes long, may core-dump %s libraries!\n",
1022 already_used + len, legend);
1023 }
1024 tversion = oldversion;
1025 }
1026 set_attributes = save_sgr;
1027 acs_chars = save_acsc;
1028 }
1029 }
1030
1031 (void) fputs(outbuf.text, stdout);
1032 return len;
1033 }
1034
1035 int
dump_uses(const char * name,bool infodump)1036 dump_uses(const char *name, bool infodump)
1037 /* dump "use=" clauses in the appropriate format */
1038 {
1039 char buffer[MAX_TERMINFO_LENGTH];
1040
1041 strcpy_DYN(&outbuf, 0);
1042 (void) sprintf(buffer, "%s%s", infodump ? "use=" : "tc=", name);
1043 wrap_concat(buffer);
1044 (void) fputs(outbuf.text, stdout);
1045 return outbuf.used;
1046 }
1047
1048 void
compare_entry(void (* hook)(PredType t,PredIdx i,const char * name),TERMTYPE * tp GCC_UNUSED,bool quiet)1049 compare_entry(void (*hook) (PredType t, PredIdx i, const char *name),
1050 TERMTYPE *tp GCC_UNUSED,
1051 bool quiet)
1052 /* compare two entries */
1053 {
1054 PredIdx i, j;
1055 NCURSES_CONST char *name;
1056
1057 if (!quiet)
1058 fputs(" comparing booleans.\n", stdout);
1059 for_each_boolean(j, tp) {
1060 i = BoolIndirect(j);
1061 name = ExtBoolname(tp, i, bool_names);
1062
1063 if (isObsolete(outform, name))
1064 continue;
1065
1066 (*hook) (CMP_BOOLEAN, i, name);
1067 }
1068
1069 if (!quiet)
1070 fputs(" comparing numbers.\n", stdout);
1071 for_each_number(j, tp) {
1072 i = NumIndirect(j);
1073 name = ExtNumname(tp, i, num_names);
1074
1075 if (isObsolete(outform, name))
1076 continue;
1077
1078 (*hook) (CMP_NUMBER, i, name);
1079 }
1080
1081 if (!quiet)
1082 fputs(" comparing strings.\n", stdout);
1083 for_each_string(j, tp) {
1084 i = StrIndirect(j);
1085 name = ExtStrname(tp, i, str_names);
1086
1087 if (isObsolete(outform, name))
1088 continue;
1089
1090 (*hook) (CMP_STRING, i, name);
1091 }
1092
1093 /* (void) fputs(" comparing use entries.\n", stdout); */
1094 (*hook) (CMP_USE, 0, "use");
1095
1096 }
1097
1098 #define NOTSET(s) ((s) == 0)
1099
1100 /*
1101 * This bit of legerdemain turns all the terminfo variable names into
1102 * references to locations in the arrays Booleans, Numbers, and Strings ---
1103 * precisely what's needed.
1104 */
1105 #undef CUR
1106 #define CUR tp->
1107
1108 static void
set_obsolete_termcaps(TERMTYPE * tp)1109 set_obsolete_termcaps(TERMTYPE *tp)
1110 {
1111 #include "capdefaults.c"
1112 }
1113
1114 /*
1115 * Convert an alternate-character-set string to canonical form: sorted and
1116 * unique.
1117 */
1118 void
repair_acsc(TERMTYPE * tp)1119 repair_acsc(TERMTYPE *tp)
1120 {
1121 if (VALID_STRING(acs_chars)) {
1122 size_t n, m;
1123 char mapped[256];
1124 char extra = 0;
1125 unsigned source;
1126 unsigned target;
1127 bool fix_needed = FALSE;
1128
1129 for (n = 0, source = 0; acs_chars[n] != 0; n++) {
1130 target = acs_chars[n];
1131 if (source >= target) {
1132 fix_needed = TRUE;
1133 break;
1134 }
1135 source = target;
1136 if (acs_chars[n + 1])
1137 n++;
1138 }
1139 if (fix_needed) {
1140 memset(mapped, 0, sizeof(mapped));
1141 for (n = 0; acs_chars[n] != 0; n++) {
1142 source = acs_chars[n];
1143 if ((target = (unsigned char) acs_chars[n + 1]) != 0) {
1144 mapped[source] = target;
1145 n++;
1146 } else {
1147 extra = source;
1148 }
1149 }
1150 for (n = m = 0; n < sizeof(mapped); n++) {
1151 if (mapped[n]) {
1152 acs_chars[m++] = n;
1153 acs_chars[m++] = mapped[n];
1154 }
1155 }
1156 if (extra)
1157 acs_chars[m++] = extra; /* garbage in, garbage out */
1158 acs_chars[m] = 0;
1159 }
1160 }
1161 }
1162