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