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