• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2  * Copyright 2019-2022,2023 Thomas E. Dickey                                *
3  * Copyright 2009-2016,2017 Free Software Foundation, Inc.                  *
4  *                                                                          *
5  * Permission is hereby granted, free of charge, to any person obtaining a  *
6  * copy of this software and associated documentation files (the            *
7  * "Software"), to deal in the Software without restriction, including      *
8  * without limitation the rights to use, copy, modify, merge, publish,      *
9  * distribute, distribute with modifications, sublicense, and/or sell       *
10  * copies of the Software, and to permit persons to whom the Software is    *
11  * furnished to do so, subject to the following conditions:                 *
12  *                                                                          *
13  * The above copyright notice and this permission notice shall be included  *
14  * in all copies or substantial portions of the Software.                   *
15  *                                                                          *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
19  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
20  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
21  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
23  *                                                                          *
24  * Except as contained in this notice, the name(s) of the above copyright   *
25  * holders shall not be used in advertising or otherwise to promote the     *
26  * sale, use or other dealings in this Software without prior written       *
27  * authorization.                                                           *
28  ****************************************************************************/
29 
30 /*
31  * Author: Thomas E. Dickey
32  *
33  * $Id: demo_terminfo.c,v 1.57 2023/05/27 20:13:10 tom Exp $
34  *
35  * A simple demo of the terminfo interface.
36  */
37 #define USE_TINFO
38 #include <test.priv.h>
39 #include <sys/stat.h>
40 
41 #if NCURSES_XNAMES
42 #if HAVE_TERM_ENTRY_H
43 #include <term_entry.h>
44 #else
45 #undef NCURSES_XNAMES
46 #define NCURSES_XNAMES 0
47 #endif
48 #endif
49 
50 static GCC_NORETURN void failed(const char *);
51 
52 static void
failed(const char * msg)53 failed(const char *msg)
54 {
55     fprintf(stderr, "%s\n", msg);
56     ExitProgram(EXIT_FAILURE);
57 }
58 
59 #if HAVE_TIGETSTR
60 
61 #if defined(HAVE_CURSES_DATA_BOOLNAMES) || defined(DECL_CURSES_DATA_BOOLNAMES)
62 #define USE_CODE_LISTS 1
63 #else
64 #define USE_CODE_LISTS 0
65 #endif
66 
67 static bool a_opt = FALSE;
68 static bool b_opt = FALSE;
69 static bool f_opt = FALSE;
70 static bool n_opt = FALSE;
71 static bool q_opt = FALSE;
72 static bool s_opt = FALSE;
73 #ifdef NCURSES_VERSION
74 static bool x_opt = FALSE;
75 static bool y_opt = FALSE;
76 #endif
77 
78 static char *d_opt;
79 static char *e_opt;
80 static char **db_list;
81 static int db_item;
82 
83 static char *my_blob;
84 static char **my_boolcodes;
85 static char **my_numcodes;
86 static char **my_numvalues;
87 static char **my_strcodes;
88 static char **my_strvalues;
89 
90 static long total_values;
91 static long total_b_values;
92 static long total_n_values;
93 static long total_s_values;
94 
95 #define FCOLS 8
96 #define FNAME(type) "%s %-*s = ", #type, f_opt ? 24 : FCOLS
97 
98 static char *
make_dbitem(const char * const p,const char * const q)99 make_dbitem(const char *const p, const char *const q)
100 {
101     size_t need = strlen(e_opt) + 2 + (size_t) (p - q);
102     char *result = malloc(need);
103     _nc_SPRINTF(result, _nc_SLIMIT(need) "%s=%.*s", e_opt, (int) (p - q), q);
104     return result;
105 }
106 
107 static void
make_dblist(void)108 make_dblist(void)
109 {
110     if (d_opt && e_opt) {
111 	int pass;
112 
113 	for (pass = 0; pass < 2; ++pass) {
114 	    char *p, *q;
115 	    size_t count = 0;
116 
117 	    for (p = q = d_opt; *p != '\0'; ++p) {
118 		if (*p == ':') {
119 		    if (p != q + 1) {
120 			if (pass) {
121 			    db_list[count] = make_dbitem(p, q);
122 			}
123 			count++;
124 		    }
125 		    q = p + 1;
126 		}
127 	    }
128 	    if (p != q + 1) {
129 		if (pass) {
130 		    db_list[count] = make_dbitem(p, q);
131 		}
132 		count++;
133 	    }
134 	    if (!pass) {
135 		db_list = typeCalloc(char *, count + 1);
136 	    }
137 	}
138     }
139 }
140 
141 static char *
next_dbitem(void)142 next_dbitem(void)
143 {
144     char *result = 0;
145 
146     if (db_list) {
147 	if ((result = db_list[db_item]) == 0) {
148 	    db_item = 0;
149 	    result = db_list[0];
150 	} else {
151 	    db_item++;
152 	}
153     }
154     if (result != 0)
155 	printf("** %s\n", result);
156     return result;
157 }
158 
159 #if NO_LEAKS
160 static void
free_dblist(void)161 free_dblist(void)
162 {
163     if (db_list) {
164 	int n;
165 	for (n = 0; db_list[n]; ++n)
166 	    free(db_list[n]);
167 	free(db_list);
168 	db_list = 0;
169     }
170 }
171 #endif
172 
173 static void
dumpit(NCURSES_CONST char * cap,const char * show)174 dumpit(NCURSES_CONST char *cap, const char *show)
175 {
176     const char *str;
177     int num;
178 
179     if ((str = tigetstr(cap)) != 0 && (str != (char *) -1)) {
180 	total_values++;
181 	total_s_values++;
182 	if (!q_opt) {
183 	    printf(FNAME(str), show ? show : cap);
184 	    while (*str != 0) {
185 		int ch = UChar(*str++);
186 		switch (ch) {
187 		case '\177':
188 		    fputs("^?", stdout);
189 		    break;
190 		case '\033':
191 		    fputs("\\E", stdout);
192 		    break;
193 		case '\b':
194 		    fputs("\\b", stdout);
195 		    break;
196 		case '\f':
197 		    fputs("\\f", stdout);
198 		    break;
199 		case '\n':
200 		    fputs("\\n", stdout);
201 		    break;
202 		case '\r':
203 		    fputs("\\r", stdout);
204 		    break;
205 		case ' ':
206 		    fputs("\\s", stdout);
207 		    break;
208 		case '\t':
209 		    fputs("\\t", stdout);
210 		    break;
211 		case '^':
212 		    fputs("\\^", stdout);
213 		    break;
214 		case ':':
215 		    fputs("\\072", stdout);
216 		    break;
217 		case '\\':
218 		    fputs("\\\\", stdout);
219 		    break;
220 		default:
221 		    if (isgraph(ch))
222 			fputc(ch, stdout);
223 		    else if (ch < 32)
224 			printf("^%c", ch + '@');
225 		    else
226 			printf("\\%03o", ch);
227 		    break;
228 		}
229 	    }
230 	    printf("\n");
231 	}
232     } else if ((num = tigetnum(cap)) >= 0) {
233 	total_values++;
234 	total_n_values++;
235 	if (!q_opt) {
236 	    printf(FNAME(num), show ? show : cap);
237 	    printf(" %d\n", num);
238 	}
239     } else if ((num = tigetflag(cap)) >= 0) {
240 	total_values++;
241 	total_b_values++;
242 	if (!q_opt) {
243 	    printf(FNAME(flg), show ? show : cap);
244 	    printf("%s\n", num ? "true" : "false");
245 	}
246     }
247 
248     if (!q_opt)
249 	fflush(stdout);
250 }
251 
252 #define isCapName(c) (isalnum(UChar(c)) || ((c) == '_'))
253 #define LegalItem(c,n) (n)
254 
255 static void
brute_force(const char * name)256 brute_force(const char *name)
257 {
258 #define MAX_FORCE 5		/* omit "colors", since CPU-time is a problem */
259     static const char legal[] = "\
260 0123456789\
261 ABCDEFGHIJKLMNOPQRSTUVWXYZ\
262 abcdefghijklmnopqrstuvwxyz_";
263     int length;
264     int j, k;
265     bool carry;
266     bool changed;
267     char cap[MAX_FORCE + 1];
268     int item[MAX_FORCE + 1];
269 
270     if (db_list) {
271 	putenv(next_dbitem());
272     }
273     if (!q_opt)
274 	printf("Terminal type \"%s\"\n", name);
275     setupterm((NCURSES_CONST char *) name, 1, (int *) 0);
276     if (!q_opt) {
277 	if (strcmp(name, ttytype))
278 	    printf("... actual \"%s\"\n", ttytype);
279     }
280 
281     for (length = 1; length <= MAX_FORCE; ++length) {
282 	/* set all digits to zeros */
283 	for (j = 0; j < length; ++j) {
284 	    item[j] = LegalItem(j, 0);
285 	}
286 
287 	do {
288 	    changed = FALSE;
289 	    /* copy digits to cap-name */
290 	    for (j = 0; j < length; ++j) {
291 		cap[j] = legal[item[j]];
292 	    }
293 	    cap[length] = '\0';
294 	    dumpit(cap, NULL);
295 
296 	    k = length - 1;
297 	    do {
298 		carry = FALSE;
299 		for (; k >= 0; --k) {
300 		    item[k] += 1;
301 		    if (legal[item[k]]) {
302 			changed = TRUE;
303 			break;
304 		    }
305 		    if (k > 0 &&
306 			legal[item[k - 1] + 1]) {
307 			for (j = k; j < length; ++j) {
308 			    item[j] = LegalItem(j, 0);
309 			}
310 			carry = TRUE;
311 			changed = TRUE;
312 		    }
313 		}
314 	    } while (carry);
315 	} while (changed);
316     }
317     del_curterm(cur_term);
318 }
319 
320 #if USE_CODE_LISTS
321 #define fullname(type,n) f_opt ? type##fnames[n] : cap
322 #else
323 #define fullname(type,n) cap
324 #endif
325 
326 static void
demo_terminfo(char * name)327 demo_terminfo(char *name)
328 {
329     unsigned n;
330     NCURSES_CONST char *cap;
331 
332     if (db_list) {
333 	putenv(next_dbitem());
334     }
335     if (!q_opt)
336 	printf("Terminal type \"%s\"\n", name);
337     setupterm(name, 1, (int *) 0);
338 
339     if (b_opt) {
340 	for (n = 0;; ++n) {
341 	    cap = my_boolcodes[n];
342 	    if (cap == 0)
343 		break;
344 	    dumpit(cap, fullname(bool, n));
345 	}
346     }
347 
348     if (n_opt) {
349 	for (n = 0;; ++n) {
350 	    cap = my_numcodes[n];
351 	    if (cap == 0)
352 		break;
353 	    dumpit(cap, fullname(num, n));
354 	}
355     }
356 
357     if (s_opt) {
358 	for (n = 0;; ++n) {
359 	    cap = my_strcodes[n];
360 	    if (cap == 0)
361 		break;
362 	    dumpit(cap, fullname(str, n));
363 	}
364     }
365 #ifdef NCURSES_VERSION
366     if (x_opt && (my_blob == 0)) {
367 	if (y_opt) {
368 #if NCURSES_XNAMES
369 	    TERMTYPE *term = (TERMTYPE *) cur_term;
370 	    if (term != 0
371 		&& ((NUM_BOOLEANS(term) != BOOLCOUNT)
372 		    || (NUM_NUMBERS(term) != NUMCOUNT)
373 		    || (NUM_STRINGS(term) != STRCOUNT))) {
374 		for (n = BOOLCOUNT; n < NUM_BOOLEANS(term); ++n) {
375 		    dumpit(ExtBoolname(term, (int) n, boolnames), NULL);
376 		}
377 		for (n = NUMCOUNT; n < NUM_NUMBERS(term); ++n) {
378 		    dumpit(ExtNumname(term, (int) n, numnames), NULL);
379 		}
380 		for (n = STRCOUNT; n < NUM_STRINGS(term); ++n) {
381 		    dumpit(ExtStrname(term, (int) n, strnames), NULL);
382 		}
383 	    }
384 #endif
385 	} else {
386 	    char temp[80];
387 	    static const char *xterm_keys[] =
388 	    {
389 		"kDC", "kDN", "kEND", "kHOM", "kIC",
390 		"kLFT", "kNXT", "kPRV", "kRIT", "kUP",
391 	    };
392 	    for (n = 0; n < SIZEOF(xterm_keys); ++n) {
393 		int mod;
394 		for (mod = 0; mod < 8; ++mod) {
395 		    if (mod == 0) {
396 			/* these happen to be standard - avoid duplicates */
397 			if (!strcmp(xterm_keys[n], "kDC") ||
398 			    !strcmp(xterm_keys[n], "kEND") ||
399 			    !strcmp(xterm_keys[n], "kHOM") ||
400 			    !strcmp(xterm_keys[n], "kLFT") ||
401 			    !strcmp(xterm_keys[n], "kRIT")) {
402 			    continue;
403 			}
404 			_nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
405 				    "%.*s", 8, xterm_keys[n]);
406 		    } else {
407 			_nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
408 				    "%.*s%d", 8, xterm_keys[n], mod);
409 		    }
410 		    dumpit(temp, NULL);
411 		}
412 	    }
413 	}
414     }
415 #endif
416     del_curterm(cur_term);
417 }
418 
419 typedef enum {
420     pDefault = 0
421     ,pComment
422     ,pDescription
423     ,pEscaped
424     ,pNewline
425     ,pName
426     ,pNumber
427     ,pString
428 } STATE;
429 
430 static void
parse_description(const char * input_name)431 parse_description(const char *input_name)
432 {
433     static char empty[1] =
434     {0};
435 
436     FILE *fp;
437     struct stat sb;
438     size_t count_bools = 0;
439     size_t count_nums = 0;
440     size_t count_strs = 0;
441     size_t len;
442     size_t j, k, jl;
443     STATE state;
444 
445     if (stat(input_name, &sb) != 0
446 	|| (sb.st_mode & S_IFMT) != S_IFREG) {
447 	failed("input is not a file");
448     }
449 
450     if (sb.st_size == 0) {
451 	failed("input is empty");
452     }
453 
454     /*
455      * None of the arrays could be larger than the input-file, and since it
456      * is small, just allocate the maximum for simplicity.
457      */
458     if ((my_blob = malloc((size_t) sb.st_size + 1)) == 0 ||
459 	(my_boolcodes = typeCalloc(char *, sb.st_size)) == 0 ||
460 	  (my_numcodes = typeCalloc(char *, sb.st_size)) == 0 ||
461 	  (my_numvalues = typeCalloc(char *, sb.st_size)) == 0 ||
462 	  (my_strcodes = typeCalloc(char *, sb.st_size)) == 0 ||
463 	  (my_strvalues = typeCalloc(char *, sb.st_size)) == 0) {
464 	failed("cannot allocate memory for input-file");
465     }
466 
467     if ((fp = fopen(input_name, "r")) == 0) {
468 	failed("cannot open input-file");
469     } else {
470 	len = fread(my_blob, sizeof(char), (size_t) sb.st_size, fp);
471 	my_blob[sb.st_size] = '\0';
472 	fclose(fp);
473     }
474 
475     /*
476      * First, get rid of comments and escaped newlines, as well as repeated
477      * colons to construct a canonical entry.
478      */
479     state = pNewline;
480     for (j = k = 0; j < len; ++j) {
481 	int ch = my_blob[j];
482 	if (ch == '\t') {
483 	    ch = ' ';
484 	}
485 	switch (state) {
486 	case pNewline:
487 	    if (ch == ' ') {
488 		continue;
489 	    }
490 	    if (ch == '#') {
491 		state = pComment;
492 		continue;
493 	    }
494 	    state = pDefault;
495 	    /* FALLTHRU */
496 	case pDefault:
497 	    switch (ch) {
498 	    case '|':
499 		state = pDescription;
500 		continue;
501 	    case '\\':
502 		state = pEscaped;
503 		continue;
504 	    case '\n':
505 		state = pNewline;
506 		continue;
507 	    case ' ':
508 		break;
509 	    case ',':
510 		my_blob[k++] = (char) ch;
511 		break;
512 	    default:
513 		if (isalpha(UChar(ch)))
514 		    state = pName;
515 		else
516 		    fprintf(stderr, "OOPS @%d:%.20s\n", __LINE__, my_blob + j);
517 		my_blob[k++] = (char) ch;
518 		break;
519 	    }
520 	    break;
521 	case pComment:
522 	    if (ch == '\n')
523 		state = pNewline;
524 	    break;
525 	case pDescription:
526 	    switch (ch) {
527 	    case ',':
528 		state = pDefault;
529 		break;
530 	    case '\n':
531 		state = pNewline;
532 		break;
533 	    }
534 	    break;
535 	case pEscaped:
536 	    if (ch != '\n') {
537 		my_blob[k++] = (char) ch;
538 		state = pDefault;
539 	    } else {
540 		state = pNewline;
541 	    }
542 	    break;
543 	case pName:
544 	    switch (ch) {
545 	    case '\n':
546 		state = pNewline;
547 		continue;
548 	    case ' ':
549 	    case ',':
550 		state = pDefault;
551 		break;
552 	    case '#':
553 		state = pNumber;
554 		break;
555 	    case '=':
556 		state = pString;
557 		break;
558 	    case '|':
559 		state = pDescription;
560 		continue;
561 	    }
562 	    my_blob[k++] = (char) ch;
563 	    break;
564 	case pNumber:
565 	    switch (ch) {
566 	    case '\n':
567 		state = pNewline;
568 		continue;
569 	    case ',':
570 		state = pDefault;
571 		break;
572 	    case ' ':
573 		state = pDefault;
574 		continue;
575 	    }
576 	    my_blob[k++] = (char) ch;
577 	    break;
578 	case pString:
579 	    switch (ch) {
580 	    case '\n':
581 		state = pNewline;
582 		break;
583 	    case ',':
584 		state = pDefault;
585 		my_blob[k++] = (char) ch;
586 		break;
587 	    default:
588 		my_blob[k++] = (char) ch;
589 		break;
590 	    }
591 	    break;
592 	default:
593 	    /* not used */
594 	    break;
595 	}
596     }
597     my_blob[k] = '\0';
598 
599     /*
600      * Then, parse what's left, making indexes of the names and values.
601      */
602     state = pDefault;
603     for (j = 0; my_blob[j] != '\0'; ++j) {
604 	switch (state) {
605 	case pDefault:
606 	    switch (my_blob[j]) {
607 	    case '\\':
608 		state = pEscaped;
609 		break;
610 	    case ',':
611 		my_blob[j] = '\0';
612 		if (my_blob[j + 1] != '\0' && my_blob[j + 1] != ',')
613 		    state = pName;
614 		break;
615 	    case ' ':
616 		break;
617 	    default:
618 		break;
619 	    }
620 	case pEscaped:
621 	    break;
622 	case pName:
623 	    state = pDefault;
624 	    if (isalpha(UChar(my_blob[j]))) {
625 		for (jl = 1; isalnum(UChar(my_blob[j + jl])); ++jl) {
626 		    ;
627 		}
628 	    } else {
629 		jl = 0;
630 	    }
631 	    if (jl != 0) {
632 		switch (my_blob[j + jl]) {
633 		case '#':
634 		    my_numvalues[count_nums] = &my_blob[j + jl + 1];
635 		    my_numcodes[count_nums++] = &my_blob[j];
636 		    my_blob[j + jl] = '\0';
637 		    state = pNumber;
638 		    j += jl;
639 		    break;
640 		case '=':
641 		    my_strvalues[count_strs] = &my_blob[j + jl + 1];
642 		    my_strcodes[count_strs++] = &my_blob[j];
643 		    my_blob[j + jl] = '\0';
644 		    state = pString;
645 		    j += jl;
646 		    break;
647 		default:
648 		    if (my_blob[j + jl] == '@') {
649 			/*
650 			 * We cannot get the type for a cancelled item
651 			 * directly, but can infer it assuming the input
652 			 * came from infocmp, which puts the data in a
653 			 * known order.
654 			 */
655 			if (count_strs) {
656 			    my_strvalues[count_strs] = empty;
657 			    my_strcodes[count_strs++] = &my_blob[j];
658 			} else if (count_nums) {
659 			    my_numvalues[count_nums] = empty;
660 			    my_numcodes[count_nums++] = &my_blob[j];
661 			} else {
662 			    my_boolcodes[count_bools++] = &my_blob[j];
663 			}
664 			my_blob[j + jl] = '\0';
665 			j += jl + 1;
666 		    } else {
667 			my_boolcodes[count_bools++] = &my_blob[j];
668 			my_blob[j + jl] = '\0';
669 			j += jl;
670 		    }
671 		    state = (isCapName(my_blob[j + 1])
672 			     ? pName
673 			     : pDefault);
674 		    break;
675 		}
676 	    }
677 	    break;
678 	case pNumber:
679 	    if (!isdigit(UChar(my_blob[j]))) {
680 		--j;
681 		state = pDefault;
682 	    }
683 	    break;
684 	case pString:
685 	    switch (my_blob[j]) {
686 	    case '\\':
687 		if (my_blob[j + 1] != '\0') {
688 		    ++j;
689 		} else {
690 		    --j;
691 		    state = pDefault;
692 		}
693 		break;
694 	    case ',':
695 		--j;
696 		state = pDefault;
697 		break;
698 	    }
699 	    break;
700 	case pNewline:
701 	case pComment:
702 	case pDescription:
703 	default:
704 	    break;
705 	}
706     }
707     my_boolcodes[count_bools] = 0;
708     my_numcodes[count_nums] = 0;
709     my_numvalues[count_nums] = 0;
710     my_strcodes[count_strs] = 0;
711     my_strvalues[count_strs] = 0;
712 
713 #if 0
714     printf("# bools:%d\n", (int) count_bools);
715     for (j = 0; my_boolcodes[j]; ++j)
716 	printf("\t%s,\n", my_boolcodes[j]);
717 
718     printf("# numbers:%d\n", (int) count_nums);
719     for (j = 0; my_numcodes[j]; ++j)
720 	printf("\t%s#%s,\n", my_numcodes[j], my_numvalues[j]);
721 
722     printf("# strings:%d\n", (int) count_strs);
723     for (j = 0; my_strcodes[j]; ++j)
724 	printf("\t%s=%s,\n", my_strcodes[j], my_strvalues[j]);
725 #endif
726 }
727 
728 #if USE_CODE_LISTS
729 static char **
copy_code_list(NCURSES_CONST char * const * list)730 copy_code_list(NCURSES_CONST char *const *list)
731 {
732     int pass;
733     size_t count;
734     size_t length = 1;
735     char **result = 0;
736     char *unused = 0;
737 
738     for (pass = 0; pass < 2; ++pass) {
739 	for (count = 0; list[count] != 0; ++count) {
740 	    size_t chunk = strlen(list[count]) + 1;
741 	    if (pass == 0) {
742 		length += chunk;
743 	    } else {
744 		result[count] = unused;
745 		_nc_STRCPY(unused, list[count], length);
746 		unused += chunk;
747 	    }
748 	}
749 	if (pass == 0) {
750 	    char *blob = malloc(length);
751 	    result = typeCalloc(char *, count + 1);
752 	    unused = blob;
753 	    if (blob == 0 || result == 0)
754 		failed("copy_code_list failed");
755 	}
756     }
757 
758     return result;
759 }
760 
761 #if NO_LEAKS
762 static void
free_code_list(char ** list)763 free_code_list(char **list)
764 {
765     if (list) {
766 	free(list[0]);
767 	free(list);
768     }
769 }
770 #endif
771 #endif /* USE_CODE_LISTS */
772 
773 static void
usage(int ok)774 usage(int ok)
775 {
776     static const char *msg[] =
777     {
778 	"Usage: demo_terminfo [options] [terminal]"
779 	,""
780 	,"If no options are given, print all (boolean, numeric, string)"
781 	,"capabilities for the given terminal, using short names."
782 	,""
783 	,USAGE_COMMON
784 	,"Options:"
785 	," -a       try all names, print capabilities found"
786 	," -b       print boolean-capabilities"
787 	," -d LIST  colon-separated list of databases to use"
788 	," -e NAME  environment variable to set with -d option"
789 	," -f       print full names"
790 	," -i NAME  terminal description to use as names for \"-a\" option"
791 	," -n       print numeric-capabilities"
792 	," -q       quiet (prints only counts)"
793 	," -r COUNT repeat for given count"
794 	," -s       print string-capabilities"
795 #ifdef NCURSES_VERSION
796 	," -x       print extended capabilities"
797 	," -y       direct-lookup names of extended capabilities"
798 #endif
799     };
800     unsigned n;
801     for (n = 0; n < SIZEOF(msg); ++n) {
802 	fprintf(stderr, "%s\n", msg[n]);
803     }
804     ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
805 }
806 /* *INDENT-OFF* */
VERSION_COMMON()807 VERSION_COMMON()
808 /* *INDENT-ON* */
809 
810 int
811 main(int argc, char *argv[])
812 {
813     int ch;
814     int n;
815     int repeat;
816     char *name;
817     int r_opt = 1;
818     char *input_name = 0;
819 
820     while ((ch = getopt(argc, argv, OPTS_COMMON "abd:e:fi:nqr:sxy")) != -1) {
821 	switch (ch) {
822 	case 'a':
823 	    a_opt = TRUE;
824 	    break;
825 	case 'b':
826 	    b_opt = TRUE;
827 	    break;
828 	case 'd':
829 	    d_opt = optarg;
830 	    break;
831 	case 'e':
832 	    e_opt = optarg;
833 	    break;
834 	case 'f':
835 	    f_opt = TRUE;
836 	    break;
837 	case 'i':
838 	    input_name = optarg;
839 	    break;
840 	case 'n':
841 	    n_opt = TRUE;
842 	    break;
843 	case 'q':
844 	    q_opt = TRUE;
845 	    break;
846 	case 'r':
847 	    if ((r_opt = atoi(optarg)) <= 0)
848 		usage(FALSE);
849 	    break;
850 	case 's':
851 	    s_opt = TRUE;
852 	    break;
853 	case 'x':
854 #ifdef NCURSES_VERSION
855 	    x_opt = TRUE;
856 #endif
857 	    break;
858 #ifdef NCURSES_VERSION
859 	case 'y':
860 	    y_opt = TRUE;
861 	    x_opt = TRUE;
862 	    break;
863 #endif
864 	case OPTS_VERSION:
865 	    show_version(argv);
866 	    ExitProgram(EXIT_SUCCESS);
867 	default:
868 	    usage(ch == OPTS_USAGE);
869 	    /* NOTREACHED */
870 	}
871     }
872 
873 #if HAVE_USE_EXTENDED_NAMES
874     use_extended_names(x_opt);
875 #endif
876 
877     if (!(b_opt || n_opt || s_opt)) {
878 	b_opt = TRUE;
879 	n_opt = TRUE;
880 	s_opt = TRUE;
881     }
882 
883     make_dblist();
884 
885     if (a_opt) {
886 	for (repeat = 0; repeat < r_opt; ++repeat) {
887 	    if (optind < argc) {
888 		for (n = optind; n < argc; ++n) {
889 		    brute_force(argv[n]);
890 		}
891 	    } else if ((name = getenv("TERM")) != 0) {
892 		brute_force(name);
893 	    } else {
894 		static char dumb[] = "dumb";
895 		brute_force(dumb);
896 	    }
897 	}
898     } else {
899 	if (input_name != 0) {
900 	    parse_description(input_name);
901 	}
902 #if USE_CODE_LISTS
903 	else {
904 	    my_boolcodes = copy_code_list(boolnames);
905 	    my_numcodes = copy_code_list(numnames);
906 	    my_strcodes = copy_code_list(strnames);
907 	}
908 #else
909 	else {
910 	    failed("no capability-lists available (use -i option)");
911 	}
912 #endif /* USE_CODE_LISTS */
913 	for (repeat = 0; repeat < r_opt; ++repeat) {
914 	    if (optind < argc) {
915 		for (n = optind; n < argc; ++n) {
916 		    demo_terminfo(argv[n]);
917 		}
918 	    } else if ((name = getenv("TERM")) != 0) {
919 		demo_terminfo(name);
920 	    } else {
921 		static char dumb[] = "dumb";
922 		demo_terminfo(dumb);
923 	    }
924 	}
925     }
926 
927 #define PLURAL(n) n, (n != 1) ? "s" : ""
928     printf("%ld value%s (%ld boolean%s, %ld number%s, %ld string%s)\n",
929 	   PLURAL(total_values),
930 	   PLURAL(total_b_values),
931 	   PLURAL(total_n_values),
932 	   PLURAL(total_s_values));
933 
934 #if NO_LEAKS
935     free_dblist();
936     if (input_name != 0) {
937 	if (my_blob != 0) {
938 	    free(my_blob);
939 	    free(my_boolcodes);
940 	    free(my_numcodes);
941 	    free(my_numvalues);
942 	    free(my_strcodes);
943 	    free(my_strvalues);
944 	}
945     }
946 #if USE_CODE_LISTS
947     else {
948 	free_code_list(my_boolcodes);
949 	free_code_list(my_numcodes);
950 	free_code_list(my_strcodes);
951     }
952 #endif
953 #endif /* NO_LEAKS */
954 
955     ExitProgram(EXIT_SUCCESS);
956 }
957 
958 #else /* !HAVE_TIGETSTR */
959 int
main(void)960 main(void)
961 {
962     failed("This program requires the terminfo functions such as tigetstr");
963     ExitProgram(EXIT_FAILURE);
964 }
965 #endif /* HAVE_TIGETSTR */
966