GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tic/dump_entry.c Lines: 0 514 0.0 %
Date: 2017-11-07 Branches: 0 608 0.0 %

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