• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2  * Copyright 2019-2022,2023 Thomas E. Dickey                                *
3  * Copyright 2005-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_termcap.c,v 1.65 2023/05/27 20:13:10 tom Exp $
34  *
35  * A simple demo of the termcap 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 #if defined(NCURSES_VERSION)
51 #if HAVE_NCURSES_TERMCAP_H
52 #include <ncurses/termcap.h>
53 #elif HAVE_TERMCAP_H
54 #include <termcap.h>
55 #endif
56 #endif
57 
58 static GCC_NORETURN void failed(const char *);
59 
60 static void
failed(const char * msg)61 failed(const char *msg)
62 {
63     fprintf(stderr, "%s\n", msg);
64     ExitProgram(EXIT_FAILURE);
65 }
66 
67 #if HAVE_TGETENT
68 
69 #if defined(HAVE_CURSES_DATA_BOOLNAMES) || defined(DECL_CURSES_DATA_BOOLNAMES)
70 #define USE_CODE_LISTS 1
71 #else
72 #define USE_CODE_LISTS 0
73 #endif
74 
75 #define FCOLS 8
76 #define FNAME(type) "%s %-*s = ", #type, FCOLS
77 
78 static bool b_opt = FALSE;
79 static bool n_opt = FALSE;
80 static bool s_opt = FALSE;
81 static bool q_opt = FALSE;
82 #ifdef NCURSES_VERSION
83 static bool x_opt = FALSE;
84 static bool y_opt = FALSE;
85 #endif
86 
87 static char *d_opt;
88 static char *e_opt;
89 static char **db_list;
90 static int db_item;
91 
92 static char *my_blob;
93 static char **my_boolcodes;
94 static char **my_numcodes;
95 static char **my_numvalues;
96 static char **my_strcodes;
97 static char **my_strvalues;
98 
99 static long total_values;
100 static long total_b_values;
101 static long total_n_values;
102 static long total_s_values;
103 
104 #define isCapName(c) (isgraph(c) && strchr("^=:\\", c) == 0)
105 #define EachCapName(n) n = 33; n < 127; ++n
106 
107 static char *
make_dbitem(const char * const p,const char * const q)108 make_dbitem(const char *const p, const char *const q)
109 {
110     size_t need = strlen(e_opt) + 2 + (size_t) (p - q);
111     char *result = malloc(need);
112     _nc_SPRINTF(result, _nc_SLIMIT(need) "%s=%.*s", e_opt, (int) (p - q), q);
113     return result;
114 }
115 
116 static void
make_dblist(void)117 make_dblist(void)
118 {
119     if (d_opt && e_opt) {
120 	int pass;
121 
122 	for (pass = 0; pass < 2; ++pass) {
123 	    char *p, *q;
124 	    size_t count = 0;
125 
126 	    for (p = q = d_opt; *p != '\0'; ++p) {
127 		if (*p == ':') {
128 		    if (p != q + 1) {
129 			if (pass) {
130 			    db_list[count] = make_dbitem(p, q);
131 			}
132 			count++;
133 		    }
134 		    q = p + 1;
135 		}
136 	    }
137 	    if (p != q + 1) {
138 		if (pass) {
139 		    db_list[count] = make_dbitem(p, q);
140 		}
141 		count++;
142 	    }
143 	    if (!pass) {
144 		db_list = typeCalloc(char *, count + 1);
145 	    }
146 	}
147     }
148 }
149 
150 static char *
next_dbitem(void)151 next_dbitem(void)
152 {
153     char *result = 0;
154 
155     if (db_list) {
156 	if ((result = db_list[db_item]) == 0) {
157 	    db_item = 0;
158 	    result = db_list[0];
159 	} else {
160 	    db_item++;
161 	}
162     }
163     if (result != 0)
164 	printf("** %s\n", result);
165     return result;
166 }
167 
168 #if NO_LEAKS
169 static void
free_dblist(void)170 free_dblist(void)
171 {
172     if (db_list) {
173 	int n;
174 	for (n = 0; db_list[n]; ++n)
175 	    free(db_list[n]);
176 	free(db_list);
177 	db_list = 0;
178     }
179 }
180 #endif /* NO_LEAKS */
181 
182 static void
show_string(const char * name,const char * value)183 show_string(const char *name, const char *value)
184 {
185     printf(FNAME(str), name);
186     if (value == ((char *) -1)) {
187 	printf("CANCELLED");
188     } else if (value == ((char *) 0)) {
189 	printf("ABSENT");
190     } else {
191 	while (*value != 0) {
192 	    int ch = UChar(*value++);
193 	    switch (ch) {
194 	    case '\177':
195 		fputs("^?", stdout);
196 		break;
197 	    case '\033':
198 		fputs("\\E", stdout);
199 		break;
200 	    case '\b':
201 		fputs("\\b", stdout);
202 		break;
203 	    case '\f':
204 		fputs("\\f", stdout);
205 		break;
206 	    case '\n':
207 		fputs("\\n", stdout);
208 		break;
209 	    case '\r':
210 		fputs("\\r", stdout);
211 		break;
212 	    case ' ':
213 		fputs("\\s", stdout);
214 		break;
215 	    case '\t':
216 		fputs("\\t", stdout);
217 		break;
218 	    case '^':
219 		fputs("\\^", stdout);
220 		break;
221 	    case ':':
222 		fputs("\\072", stdout);
223 		break;
224 	    case '\\':
225 		fputs("\\\\", stdout);
226 		break;
227 	    default:
228 		if (isgraph(ch))
229 		    fputc(ch, stdout);
230 		else if (ch < 32)
231 		    printf("^%c", ch + '@');
232 		else
233 		    printf("\\%03o", ch);
234 		break;
235 	    }
236 	}
237     }
238     printf("\n");
239 }
240 
241 static void
show_number(const char * name,int value)242 show_number(const char *name, int value)
243 {
244     printf(FNAME(num), name);
245     printf(" %d\n", value);
246 }
247 
248 static void
dumpit(NCURSES_CONST char * cap)249 dumpit(NCURSES_CONST char *cap)
250 {
251     /*
252      * One of the limitations of the termcap interface is that the library
253      * cannot determine the size of the buffer passed via tgetstr(), nor the
254      * amount of space remaining.  This demo simply reuses the whole buffer
255      * for each call; a normal termcap application would try to use the buffer
256      * to hold all of the strings extracted from the terminal entry.
257      */
258     char area[1024], *ap = area;
259     char *str;
260     int num;
261 
262     if ((str = tgetstr(cap, &ap)) != 0) {
263 	total_values++;
264 	total_s_values++;
265 	if (!q_opt) {
266 	    /*
267 	     * Note that the strings returned are mostly terminfo format, since
268 	     * ncurses does not convert except for a handful of special cases.
269 	     */
270 	    show_string(cap, str);
271 	}
272     } else if ((num = tgetnum(cap)) >= 0) {
273 	total_values++;
274 	total_n_values++;
275 	if (!q_opt) {
276 	    show_number(cap, num);
277 	}
278     } else if (tgetflag(cap) > 0) {
279 	total_values++;
280 	total_b_values++;
281 	if (!q_opt) {
282 	    printf(FNAME(flg), cap);
283 	    printf("%s\n", "true");
284 	}
285     }
286 
287     if (!q_opt)
288 	fflush(stdout);
289 }
290 
291 static void
brute_force(const char * name)292 brute_force(const char *name)
293 {
294     char buffer[1024];
295 
296     if (db_list) {
297 	putenv(next_dbitem());
298     }
299     if (!q_opt)
300 	printf("Terminal type \"%s\"\n", name);
301     if (tgetent(buffer, name) >= 0) {
302 	char cap[3];
303 	int c1, c2;
304 
305 	cap[2] = 0;
306 	for (EachCapName(c1)) {
307 	    cap[0] = (char) c1;
308 	    if (isCapName(c1)) {
309 		for (EachCapName(c2)) {
310 		    cap[1] = (char) c2;
311 		    if (isCapName(c2)) {
312 			dumpit(cap);
313 		    }
314 		}
315 	    }
316 	}
317     }
318 }
319 
320 #if NCURSES_XNAMES
321 static void
dump_xname(NCURSES_CONST char * cap)322 dump_xname(NCURSES_CONST char *cap)
323 {
324     if (strlen(cap) == 2)
325 	dumpit(cap);
326 }
327 #endif
328 
329 static void
demo_termcap(NCURSES_CONST char * name)330 demo_termcap(NCURSES_CONST char *name)
331 {
332     char buffer[1024];
333 
334     if (db_list) {
335 	putenv(next_dbitem());
336     }
337     if (!q_opt)
338 	printf("Terminal type \"%s\"\n", name);
339     if (tgetent(buffer, name) >= 0) {
340 	NCURSES_CONST char *cap;
341 	unsigned n;
342 
343 	if (b_opt) {
344 	    for (n = 0;; ++n) {
345 		cap = my_boolcodes[n];
346 		if (cap == 0)
347 		    break;
348 		dumpit(cap);
349 	    }
350 	}
351 
352 	if (n_opt) {
353 	    for (n = 0;; ++n) {
354 		cap = my_numcodes[n];
355 		if (cap == 0)
356 		    break;
357 		dumpit(cap);
358 	    }
359 	}
360 
361 	if (s_opt) {
362 	    for (n = 0;; ++n) {
363 		cap = my_strcodes[n];
364 		if (cap == 0)
365 		    break;
366 		dumpit(cap);
367 	    }
368 	}
369 #ifdef NCURSES_VERSION
370 	if (x_opt && (my_blob == 0) && y_opt) {
371 #if NCURSES_XNAMES
372 	    TERMTYPE *term = (TERMTYPE *) cur_term;
373 	    if (term != 0
374 		&& ((NUM_BOOLEANS(term) != BOOLCOUNT)
375 		    || (NUM_NUMBERS(term) != NUMCOUNT)
376 		    || (NUM_STRINGS(term) != STRCOUNT))) {
377 		for (n = BOOLCOUNT; n < NUM_BOOLEANS(term); ++n) {
378 		    dump_xname(ExtBoolname(term, (int) n, boolnames));
379 		}
380 		for (n = NUMCOUNT; n < NUM_NUMBERS(term); ++n) {
381 		    dump_xname(ExtNumname(term, (int) n, numnames));
382 		}
383 		for (n = STRCOUNT; n < NUM_STRINGS(term); ++n) {
384 		    dump_xname(ExtStrname(term, (int) n, strnames));
385 		}
386 	    }
387 #endif
388 	}
389 #endif
390     }
391 }
392 
393 typedef enum {
394     pDefault = 0
395     ,pComment
396     ,pDescription
397     ,pEscaped
398     ,pNewline
399     ,pName
400     ,pNumber
401     ,pString
402 } STATE;
403 
404 static void
parse_description(const char * input_name)405 parse_description(const char *input_name)
406 {
407     static char empty[1] =
408     {0};
409 
410     FILE *fp;
411     struct stat sb;
412     size_t count_bools = 0;
413     size_t count_nums = 0;
414     size_t count_strs = 0;
415     size_t len;
416     size_t j, k;
417     STATE state;
418 
419     if (stat(input_name, &sb) != 0
420 	|| (sb.st_mode & S_IFMT) != S_IFREG) {
421 	failed("input is not a file");
422     }
423 
424     if (sb.st_size == 0) {
425 	failed("input is empty");
426     }
427 
428     /*
429      * None of the arrays could be larger than the input-file, and since it
430      * is small, just allocate the maximum for simplicity.
431      */
432     if ((my_blob = malloc((size_t) sb.st_size + 1)) == 0 ||
433 	(my_boolcodes = typeCalloc(char *, sb.st_size)) == 0 ||
434 	  (my_numcodes = typeCalloc(char *, sb.st_size)) == 0 ||
435 	  (my_numvalues = typeCalloc(char *, sb.st_size)) == 0 ||
436 	  (my_strcodes = typeCalloc(char *, sb.st_size)) == 0 ||
437 	  (my_strvalues = typeCalloc(char *, sb.st_size)) == 0) {
438 	failed("cannot allocate memory for input-file");
439     }
440 
441     if ((fp = fopen(input_name, "r")) == 0) {
442 	failed("cannot open input-file");
443     } else {
444 	len = fread(my_blob, sizeof(char), (size_t) sb.st_size, fp);
445 	my_blob[sb.st_size] = '\0';
446 	fclose(fp);
447     }
448 
449     /*
450      * First, get rid of comments and escaped newlines, as well as repeated
451      * colons to construct a canonical entry.
452      *
453      * FIXME: actually this should make an additional pass just to strip
454      * comment-lines and escaped newlines.  But it is workable for infocmp
455      * output.
456      */
457     state = pNewline;
458     for (j = k = 0; j < len; ++j) {
459 	int ch = my_blob[j];
460 	if (ch == '\t') {
461 	    ch = ' ';
462 	}
463 	switch (state) {
464 	case pNewline:
465 	    if (ch == ' ') {
466 		continue;
467 	    }
468 	    if (ch == '#') {
469 		state = pComment;
470 		continue;
471 	    }
472 	    state = pDefault;
473 	    /* FALLTHRU */
474 	case pDefault:
475 	    switch (ch) {
476 	    case '|':
477 		state = pDescription;
478 		continue;
479 	    case '\\':
480 		state = pEscaped;
481 		continue;
482 	    case '\n':
483 		state = pNewline;
484 		continue;
485 	    case ' ':
486 	    case ':':
487 		break;
488 	    default:
489 		state = pName;
490 		break;
491 	    }
492 	    my_blob[k++] = (char) ch;
493 	    break;
494 	case pComment:
495 	    if (ch == '\n')
496 		state = pNewline;
497 	    break;
498 	case pDescription:
499 	    switch (ch) {
500 	    case ':':
501 		state = pDefault;
502 		break;
503 	    case '\n':
504 		state = pNewline;
505 		break;
506 	    }
507 	    break;
508 	case pEscaped:
509 	    if (ch != '\n') {
510 		my_blob[k++] = (char) ch;
511 		state = pDefault;
512 	    } else {
513 		state = pNewline;
514 	    }
515 	    break;
516 	case pName:
517 	    switch (ch) {
518 	    case '\n':
519 		state = pNewline;
520 		continue;
521 	    case ' ':
522 	    case ':':
523 		state = pDefault;
524 		break;
525 	    case '#':
526 		state = pNumber;
527 		break;
528 	    case '|':
529 		state = pDescription;
530 		continue;
531 	    }
532 	    my_blob[k++] = (char) ch;
533 	    break;
534 	case pNumber:
535 	    switch (ch) {
536 	    case '\n':
537 		state = pNewline;
538 		continue;
539 	    case ':':
540 		state = pDefault;
541 		break;
542 	    case ' ':
543 		state = pDefault;
544 		continue;
545 	    }
546 	    my_blob[k++] = (char) ch;
547 	    break;
548 	case pString:
549 	    switch (ch) {
550 	    case '\\':
551 		if (my_blob[j + 1] == '\0') {
552 		    state = pDefault;
553 		    continue;
554 		}
555 		break;
556 	    case '\n':
557 		state = pNewline;
558 		continue;
559 	    case ':':
560 		state = pDefault;
561 		break;
562 	    }
563 	    my_blob[k++] = (char) ch;
564 	    break;
565 	default:
566 	    /* not used */
567 	    break;
568 	}
569     }
570     my_blob[k] = '\0';
571 
572     /*
573      * Then, parse what's left, making indexes of the names and values.
574      */
575     state = pDefault;
576     for (j = 0; my_blob[j] != '\0'; ++j) {
577 	switch (state) {
578 	case pDefault:
579 	    switch (my_blob[j]) {
580 	    case '\\':
581 		state = pEscaped;
582 		break;
583 	    case ':':
584 		my_blob[j] = '\0';
585 		if (my_blob[j + 1] != '\0' && my_blob[j + 1] != ':')
586 		    state = pName;
587 		break;
588 	    case ' ':
589 		break;
590 	    default:
591 		break;
592 	    }
593 	case pEscaped:
594 	    break;
595 	case pName:
596 	    state = pDefault;
597 	    /*
598 	     * Commented-out capabilities might be accessible (they are in
599 	     * ncurses).
600 	     */
601 	    if (my_blob[j] == '.' && my_blob[j + 1] == '.') {
602 		j += 2;
603 	    }
604 	    if (my_blob[j + 1] != '\0') {
605 		switch (my_blob[j + 2]) {
606 		case '#':
607 		    my_numvalues[count_nums] = &my_blob[j + 3];
608 		    my_numcodes[count_nums++] = &my_blob[j];
609 		    my_blob[j + 2] = '\0';
610 		    state = pNumber;
611 		    j += 2;
612 		    break;
613 		case '=':
614 		    my_strvalues[count_strs] = &my_blob[j + 3];
615 		    my_strcodes[count_strs++] = &my_blob[j];
616 		    my_blob[j + 2] = '\0';
617 		    state = pString;
618 		    j += 2;
619 		    break;
620 		default:
621 		    if (my_blob[j + 2] == '@') {
622 			/*
623 			 * We cannot get the type for a cancelled item
624 			 * directly, but can infer it assuming the input
625 			 * came from infocmp, which puts the data in a
626 			 * known order.
627 			 */
628 			if (count_strs) {
629 			    my_strvalues[count_strs] = empty;
630 			    my_strcodes[count_strs++] = &my_blob[j];
631 			} else if (count_nums) {
632 			    my_numvalues[count_nums] = empty;
633 			    my_numcodes[count_nums++] = &my_blob[j];
634 			} else {
635 			    my_boolcodes[count_bools++] = &my_blob[j];
636 			}
637 		    } else {
638 			my_boolcodes[count_bools++] = &my_blob[j];
639 		    }
640 		    j++;
641 		    break;
642 		}
643 	    }
644 	    break;
645 	case pNumber:
646 	    if (!isdigit(UChar(my_blob[j]))) {
647 		--j;
648 		state = pDefault;
649 	    }
650 	    break;
651 	case pString:
652 	    switch (my_blob[j]) {
653 	    case '\\':
654 		if (my_blob[j + 1] == '\0') {
655 		    state = pDefault;
656 		    continue;
657 		} else {
658 		    ++j;
659 		}
660 		break;
661 	    case '\n':
662 		state = pNewline;
663 		continue;
664 	    case ':':
665 		--j;
666 		state = pDefault;
667 		break;
668 	    }
669 	    break;
670 	case pNewline:
671 	case pComment:
672 	case pDescription:
673 	default:
674 	    break;
675 	}
676     }
677     my_boolcodes[count_bools] = 0;
678     my_numcodes[count_nums] = 0;
679     my_numvalues[count_nums] = 0;
680     my_strcodes[count_strs] = 0;
681     my_strvalues[count_strs] = 0;
682 
683 #if 0
684     printf("bools:%d\n", (int) count_bools);
685     for (j = 0; my_boolcodes[j]; ++j)
686 	printf("%5d:%s\n", (int) j, my_boolcodes[j]);
687 
688     printf("numbers:%d\n", (int) count_nums);
689     for (j = 0; my_numcodes[j]; ++j)
690 	printf("%5d:%s(%s)\n", (int) j, my_numcodes[j], my_numvalues[j]);
691 
692     printf("strings:%d\n", (int) count_strs);
693     for (j = 0; my_strcodes[j]; ++j)
694 	printf("%5d:%s(%s)\n", (int) j, my_strcodes[j], my_strvalues[j]);
695 #endif
696 }
697 
698 #if USE_CODE_LISTS
699 static char **
copy_code_list(NCURSES_CONST char * const * list)700 copy_code_list(NCURSES_CONST char *const *list)
701 {
702     int pass;
703     size_t count;
704     size_t length = 1;
705     char **result = 0;
706     char *unused = 0;
707 
708     for (pass = 0; pass < 2; ++pass) {
709 	for (count = 0; list[count] != 0; ++count) {
710 	    size_t chunk = strlen(list[count]) + 1;
711 	    if (pass == 0) {
712 		length += chunk;
713 	    } else {
714 		result[count] = unused;
715 		_nc_STRCPY(unused, list[count], length);
716 		unused += chunk;
717 	    }
718 	}
719 	if (pass == 0) {
720 	    char *blob = malloc(length);
721 	    result = typeCalloc(char *, count + 1);
722 	    unused = blob;
723 	    if (blob == 0 || result == 0)
724 		failed("copy_code_list failed");
725 	}
726     }
727 
728     return result;
729 }
730 
731 #if NO_LEAKS
732 static void
free_code_list(char ** list)733 free_code_list(char **list)
734 {
735     if (list) {
736 	free(list[0]);
737 	free(list);
738     }
739 }
740 #endif /* NO_LEAKS */
741 #endif /* USE_CODE_LISTS */
742 
743 static void
usage(int ok)744 usage(int ok)
745 {
746     static const char *msg[] =
747     {
748 	"Usage: demo_termcap [options] [terminal]"
749 	,""
750 	,"If no options are given, print all (boolean, numeric, string)"
751 	,"capabilities for the given terminal, using short names."
752 	,""
753 	,USAGE_COMMON
754 	,"Options:"
755 	," -a       try all names, print capabilities found"
756 	," -b       print boolean-capabilities"
757 	," -d LIST  colon-separated list of databases to use"
758 	," -e NAME  environment variable to set with -d option"
759 	," -i NAME  terminal description to use as names for \"-a\" option, etc."
760 	," -n       print numeric-capabilities"
761 	," -q       quiet (prints only counts)"
762 	," -r COUNT repeat for given count"
763 	," -s       print string-capabilities"
764 	," -v       print termcap-variables"
765 #ifdef NCURSES_VERSION
766 	," -x       print extended capabilities"
767 #endif
768     };
769     unsigned n;
770     for (n = 0; n < SIZEOF(msg); ++n) {
771 	fprintf(stderr, "%s\n", msg[n]);
772     }
773     ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
774 }
775 /* *INDENT-OFF* */
VERSION_COMMON()776 VERSION_COMMON()
777 /* *INDENT-ON* */
778 
779 int
780 main(int argc, char *argv[])
781 {
782     int ch;
783     int n;
784     char *name;
785     bool a_opt = FALSE;
786 #if defined(NCURSES_VERSION) || defined(HAVE_CURSES_DATA_OSPEED)
787     bool v_opt = FALSE;
788 #endif
789     char *input_name = 0;
790 
791     int repeat;
792     int r_opt = 1;
793 
794     while ((ch = getopt(argc, argv, OPTS_COMMON "abd:e:i:nqr:svxy")) != -1) {
795 	switch (ch) {
796 	case 'a':
797 	    a_opt = TRUE;
798 	    break;
799 	case 'b':
800 	    b_opt = TRUE;
801 	    break;
802 	case 'd':
803 	    d_opt = optarg;
804 	    break;
805 	case 'e':
806 	    e_opt = optarg;
807 	    break;
808 	case 'i':
809 	    input_name = optarg;
810 	    break;
811 	case 'n':
812 	    n_opt = TRUE;
813 	    break;
814 	case 'q':
815 	    q_opt = TRUE;
816 	    break;
817 	case 'r':
818 	    if ((r_opt = atoi(optarg)) <= 0)
819 		usage(FALSE);
820 	    break;
821 	case 's':
822 	    s_opt = TRUE;
823 	    break;
824 #if defined(NCURSES_VERSION) || defined(HAVE_CURSES_DATA_OSPEED)
825 	case 'v':
826 	    v_opt = TRUE;
827 	    break;
828 #endif
829 #ifdef NCURSES_VERSION
830 #if NCURSES_XNAMES
831 	case 'x':
832 	    x_opt = TRUE;
833 	    break;
834 	case 'y':
835 	    y_opt = TRUE;
836 	    x_opt = TRUE;
837 	    break;
838 #endif
839 #endif
840 	case OPTS_VERSION:
841 	    show_version(argv);
842 	    ExitProgram(EXIT_SUCCESS);
843 	default:
844 	    usage(ch == OPTS_USAGE);
845 	    /* NOTREACHED */
846 	}
847     }
848 
849 #if HAVE_USE_EXTENDED_NAMES
850     use_extended_names(x_opt);
851 #endif
852 
853     if (!(b_opt || n_opt || s_opt)) {
854 	b_opt = TRUE;
855 	n_opt = TRUE;
856 	s_opt = TRUE;
857     }
858 
859     make_dblist();
860 
861     if (a_opt) {
862 	for (repeat = 0; repeat < r_opt; ++repeat) {
863 	    if (optind < argc) {
864 		for (n = optind; n < argc; ++n) {
865 		    brute_force(argv[n]);
866 		}
867 	    } else if ((name = getenv("TERM")) != 0) {
868 		brute_force(name);
869 	    } else {
870 		static char dumb[] = "dumb";
871 		brute_force(dumb);
872 	    }
873 	}
874     } else {
875 	if (input_name != 0) {
876 	    parse_description(input_name);
877 	}
878 #if USE_CODE_LISTS
879 	else {
880 	    my_boolcodes = copy_code_list(boolcodes);
881 	    my_numcodes = copy_code_list(numcodes);
882 	    my_strcodes = copy_code_list(strcodes);
883 	}
884 #else
885 	else {
886 	    failed("no capability-lists available (use -i option)");
887 	}
888 #endif /* USE_CODE_LISTS */
889 	for (repeat = 0; repeat < r_opt; ++repeat) {
890 	    if (optind < argc) {
891 		for (n = optind; n < argc; ++n) {
892 		    demo_termcap(argv[n]);
893 		}
894 	    } else if ((name = getenv("TERM")) != 0) {
895 		demo_termcap(name);
896 	    } else {
897 		static char dumb[] = "dumb";
898 		demo_termcap(dumb);
899 	    }
900 	}
901     }
902 
903     printf("%ld values (%ld booleans, %ld numbers, %ld strings)\n",
904 	   total_values, total_b_values, total_n_values, total_s_values);
905 
906 #if defined(NCURSES_VERSION) || defined(HAVE_CURSES_DATA_OSPEED)
907     if (v_opt) {
908 	show_number("PC", PC);
909 	show_string("UP", UP);
910 	show_string("BC", BC);
911 	show_number("ospeed", (int) ospeed);
912     }
913 #endif
914 
915 #if NO_LEAKS
916     free_dblist();
917 #if USE_CODE_LISTS
918     free_code_list(my_boolcodes);
919     free_code_list(my_numcodes);
920     free_code_list(my_strcodes);
921 #endif
922 #endif /* NO_LEAKS */
923 
924     ExitProgram(EXIT_SUCCESS);
925 }
926 
927 #else
928 int
main(void)929 main(void)
930 {
931     failed("This program requires termcap");
932 }
933 #endif
934