]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/test_tparm.c
ncurses 6.4 - patch 20230415
[ncurses.git] / test / test_tparm.c
1 /****************************************************************************
2  * Copyright 2020-2022,2023 Thomas E. Dickey                                *
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: Thomas E. Dickey
31  *
32  * $Id: test_tparm.c,v 1.30 2023/04/15 22:31:47 tom Exp $
33  *
34  * Exercise tparm, either for all possible capabilities with fixed parameters,
35  * or one capability with all possible parameters.
36  *
37  * TODO: optionally test tiparm
38  * TODO: add checks/logic to handle "%s" in tparm
39  */
40 #define USE_TINFO
41 #include <test.priv.h>
42
43 #if NCURSES_XNAMES
44 #if HAVE_TERM_ENTRY_H
45 #include <term_entry.h>
46 #else
47 #undef NCURSES_XNAMES
48 #define NCURSES_XNAMES 0
49 #endif
50 #endif
51
52 #define GrowArray(array,limit,length) \
53             if (length + 2 >= limit) { \
54                 limit *= 2; \
55                 array = typeRealloc(char *, limit, array); \
56                 if (array == 0) { \
57                     failed("no memory: " #array); \
58                 } \
59             }
60
61 static GCC_NORETURN void failed(const char *);
62
63 static void
64 failed(const char *msg)
65 {
66     fprintf(stderr, "%s\n", msg);
67     ExitProgram(EXIT_FAILURE);
68 }
69
70 #if HAVE_TIGETSTR
71
72 static int a_opt;
73 static int p_opt;
74 static int v_opt;
75
76 /*
77  * Total tests (and failures):
78  */
79 static long total_tests;
80 static long total_fails;
81
82 /*
83  * Total characters formatted for tputs:
84  */
85 static long total_nulls;
86 static long total_ctrls;
87 static long total_print;
88
89 static int
90 output_func(int ch)
91 {
92     if (ch == 0) {
93         total_nulls++;
94     } else if (ch < 32 || (ch >= 127 && ch < 160)) {
95         total_ctrls++;
96     } else {
97         total_print++;
98     }
99     return ch;
100 }
101
102 static int
103 isNumeric(char *source)
104 {
105     char *next = 0;
106     long value = strtol(source, &next, 0);
107     int result = (next == 0 || next == source || *next != '\0') ? 0 : 1;
108     (void) value;
109     return result;
110 }
111
112 static int
113 relevant(const char *name, const char *value)
114 {
115     int code = 1;
116     if (VALID_STRING(value)) {
117         if (strstr(value, "%p") == 0
118             && strstr(value, "%d") == 0
119             && strstr(value, "%s") == 0
120             && (!p_opt || strstr(value, "$<") == 0)) {
121             if (v_opt > 2)
122                 printf("? %s noparams\n", name);
123             code = 0;
124         }
125     } else {
126         if (v_opt > 2) {
127             printf("? %s %s\n",
128                    (value == ABSENT_STRING)
129                    ? "absent"
130                    : "cancel",
131                    name);
132         }
133         code = 0;
134     }
135     return code;
136 }
137
138 static int
139 increment(long *all_parms, int *num_parms, int len_parms, int end_parms)
140 {
141     int rc = 0;
142     int n;
143
144     if (len_parms > 9)
145         len_parms = 9;
146
147     if (end_parms < len_parms) {
148         if (all_parms[end_parms]++ >= num_parms[end_parms]) {
149             all_parms[end_parms] = 0;
150             increment(all_parms, num_parms, len_parms, end_parms + 1);
151         }
152     }
153     for (n = 0; n < len_parms; ++n) {
154         if (all_parms[n] != 0) {
155             rc = 1;
156             break;
157         }
158     }
159     /* return 1 until the vector resets to all 0's */
160     return rc;
161 }
162
163 /* parse the format string to determine which positional parameters
164  * are assumed to be strings.
165  */
166 #if HAVE__NC_TPARM_ANALYZE
167 extern int _nc_tparm_analyze(TERMINAL *, const char *, char **, int *);
168
169 static int
170 analyze_format(const char *format, char **p_is_s)
171 {
172     int popcount = 0;
173     int analyzed = _nc_tparm_analyze(cur_term, format, p_is_s, &popcount);
174     if (analyzed < popcount) {
175         analyzed = popcount;
176     }
177     return analyzed;
178 }
179 #else
180 /* TODO: make this work without direct use of ncurses internals. */
181 static int
182 analyze_format(const char *format, char **p_is_s)
183 {
184     int n;
185     char *filler = strstr(format, "%s");
186     for (n = 0; n < 9; ++n) {
187         p_is_s[n] = filler;
188     }
189     return n;
190 }
191 #endif
192
193 #define NumStr(n) use_strings[n] \
194                   ? (long) (number[n] \
195                      ? string[n] \
196                      : NULL) \
197                   : number[n]
198
199 static void
200 test_tparm(const char *name, const char *format, long *number, char **string)
201 {
202     char *use_strings[9];
203     char *result;
204     int nparam;
205
206     nparam = analyze_format(format, use_strings);
207     result = tparm(format,
208                    NumStr(0),
209                    NumStr(1),
210                    NumStr(2),
211                    NumStr(3),
212                    NumStr(4),
213                    NumStr(5),
214                    NumStr(6),
215                    NumStr(7),
216                    NumStr(8));
217     total_tests++;
218     if (result != NULL) {
219         tputs(result, 1, output_func);
220     } else {
221         total_fails++;
222     }
223     if (v_opt > 1) {
224         int n;
225         printf(".. %3d =", result != 0 ? (int) strlen(result) : -1);
226         for (n = 0; n < nparam; ++n) {
227             if (use_strings[n]) {
228                 if (number[n]) {
229                     printf(" \"%s\"", string[n]);
230                 } else {
231                     printf("  ?");
232                 }
233             } else {
234                 printf(" %2ld", number[n]);
235             }
236         }
237         printf(" %s\n", name);
238     }
239 }
240
241 static void
242 usage(int ok)
243 {
244     static const char *msg[] =
245     {
246         "Usage: test_tparm [options] [capability] [value1 [value2 [...]]]"
247         ,""
248         ,"Use tparm/tputs for all distinct combinations of given capability."
249         ,""
250         ,USAGE_COMMON
251         ,"Options:"
252         ," -T TERM  override $TERM; this may be a comma-separated list or \"-\""
253         ,"          to read a list from standard-input"
254         ," -a       test all combinations of parameters"
255         ,"          [value1...] forms a vector of maximum parameter-values."
256         ," -p       test capabilities with no parameters but having padding"
257         ," -r NUM   repeat tests NUM times"
258         ," -v       show values and results"
259     };
260     unsigned n;
261     for (n = 0; n < SIZEOF(msg); ++n) {
262         fprintf(stderr, "%s\n", msg[n]);
263     }
264     ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
265 }
266
267 #define PLURAL(n) n, (n != 1) ? "s" : ""
268 #define COLONS(n) (n >= 1) ? ":" : ""
269
270 #define NUMFORM "%10ld"
271 /* *INDENT-OFF* */
272 VERSION_COMMON()
273 /* *INDENT-ON* */
274
275 int
276 main(int argc, char *argv[])
277 {
278     int ch;
279     int n;
280     int r_run, t_run, n_run;
281     char *old_term = getenv("TERM");
282     int r_opt = 1;
283     char *t_opt = 0;
284
285     int std_caps = 0;           /* predefine items in all_caps[] */
286     int len_caps = 0;           /* cur # of items in all_caps[] */
287     int max_caps = 10;          /* max # of items in all_caps[] */
288     char **all_caps = typeCalloc(char *, max_caps);
289
290     long all_parms[10];         /* workspace for "-a" option */
291
292     int len_terms = 0;          /* cur # of items in all_terms[] */
293     int max_terms = 10;         /* max # of items in all_terms[] */
294     char **all_terms = typeCalloc(char *, max_terms);
295
296     int use_caps;
297     char **cap_name;
298     char **cap_data;
299
300     int len_parms = 0;          /* cur # of items in num_parms[], str_parms[] */
301     int max_parms = argc + 10;  /* max # of items in num_parms[], str_parms[] */
302     int *num_parms = typeCalloc(int, max_parms);
303     char **str_parms = typeCalloc(char *, max_parms);
304     long use_parms = 1;
305
306     if (all_caps == 0 || all_terms == 0 || num_parms == 0 || str_parms == 0)
307         failed("no memory");
308
309     while ((ch = getopt(argc, argv, OPTS_COMMON "T:apr:v")) != -1) {
310         switch (ch) {
311         case 'T':
312             t_opt = optarg;
313             break;
314         case 'a':
315             ++a_opt;
316             break;
317         case 'p':
318             ++p_opt;
319             break;
320         case 'r':
321             r_opt = atoi(optarg);
322             break;
323         case 'v':
324             ++v_opt;
325             break;
326         case OPTS_VERSION:
327             show_version(argv);
328             ExitProgram(EXIT_SUCCESS);
329         default:
330             usage(ch == OPTS_USAGE);
331             /* NOTREACHED */
332         }
333     }
334
335     /*
336      * If there is a nonnumeric parameter after the options, use that as the
337      * capability name.
338      */
339     if (optind < argc) {
340         if (!isNumeric(argv[optind])) {
341             all_caps[len_caps++] = strdup(argv[optind++]);
342         }
343     }
344
345     /*
346      * Any remaining arguments must be possible parameter values.  If numeric,
347      * and "-a" is not set, use those as the actual values for which the
348      * capabilities are tested.
349      */
350     while (optind < argc) {
351         if (isNumeric(argv[optind])) {
352             char *dummy = 0;
353             long value = strtol(argv[optind], &dummy, 0);
354             num_parms[len_parms] = (int) value;
355         }
356         str_parms[len_parms] = argv[optind];
357         ++optind;
358         ++len_parms;
359     }
360     for (n = len_parms; n < max_parms; ++n) {
361         static char dummy[1];
362         str_parms[n] = dummy;
363     }
364     if (v_opt) {
365         printf("%d parameter%s%s\n", PLURAL(len_parms), COLONS(len_parms));
366         if (v_opt > 3) {
367             for (n = 0; n < len_parms; ++n) {
368                 printf(" %d: %d (%s)\n", n + 1, num_parms[n], str_parms[n]);
369             }
370         }
371     }
372
373     /*
374      * Make a list of values for $TERM.  Accept "-" for standard input to
375      * simplify scripting a check of the whole database.
376      */
377     old_term = strdup((old_term == 0) ? "unknown" : old_term);
378     if (t_opt != 0) {
379         if (!strcmp(t_opt, "-")) {
380             char buffer[BUFSIZ];
381             while (fgets(buffer, sizeof(buffer) - 1, stdin) != 0) {
382                 char *s = buffer;
383                 char *t;
384                 while (isspace(UChar(s[0])))
385                     ++s;
386                 t = s + strlen(s);
387                 while (t != s && isspace(UChar(t[-1])))
388                     *--t = '\0';
389                 s = strdup(s);
390                 if (len_terms + 2 >= max_terms) {
391                     max_terms *= 2;
392                     all_terms = typeRealloc(char *, max_terms, all_terms);
393                     if (all_terms == 0)
394                         failed("no memory: all_terms");
395                 }
396                 all_terms[len_terms++] = s;
397             }
398         } else {
399             char *s = t_opt;
400             char *t;
401             while ((t = strtok(s, ",")) != 0) {
402                 s = 0;
403                 if (len_terms + 2 >= max_terms) {
404                     max_terms *= 2;
405                     all_terms = typeRealloc(char *, max_terms, all_terms);
406                     if (all_terms == 0)
407                         failed("no memory: all_terms");
408                 }
409                 all_terms[len_terms++] = strdup(t);
410             }
411         }
412     } else {
413         all_terms[len_terms++] = strdup(old_term);
414     }
415     all_terms[len_terms] = 0;
416     if (v_opt) {
417         printf("%d term%s:\n", PLURAL(len_terms));
418         if (v_opt > 3) {
419             for (n = 0; n < len_terms; ++n) {
420                 printf(" %d: %s\n", n + 1, all_terms[n]);
421             }
422         }
423     }
424
425     /*
426      * If no capability name was selected, use the predefined list of string
427      * capabilities.
428      *
429      * TODO: To address the "other" systems which do not follow SVr4,
430      * just use the output from infocmp on $TERM.
431      */
432     if (len_caps == 0) {
433 #if defined(HAVE_CURSES_DATA_BOOLNAMES) || defined(DECL_CURSES_DATA_BOOLNAMES)
434         for (n = 0; strnames[n] != 0; ++n) {
435             GrowArray(all_caps, max_caps, len_caps);
436             all_caps[len_caps++] = strdup(strnames[n]);
437         }
438 #else
439         all_caps[len_caps++] = strdup("cup");
440         all_caps[len_caps++] = strdup("sgr");
441 #endif
442     }
443     std_caps = len_caps;
444     all_caps[len_caps] = 0;
445     if (v_opt) {
446         printf("%d name%s%s\n", PLURAL(len_caps), COLONS(len_caps));
447         if (v_opt > 3) {
448             for (n = 0; n < len_caps; ++n) {
449                 printf(" %d: %s\n", n + 1, all_caps[n]);
450             }
451         }
452     }
453
454     cap_name = typeMalloc(char *, 1 + len_caps);
455     cap_data = typeMalloc(char *, 1 + len_caps);
456
457     if (r_opt <= 0)
458         r_opt = 1;
459
460     if (a_opt) {
461         for (n = 0; n < max_parms; ++n)
462             if (num_parms[n])
463                 use_parms *= (num_parms[n] + 1);
464     }
465
466     for (r_run = 0; r_run < r_opt; ++r_run) {
467         for (t_run = 0; t_run < len_terms; ++t_run) {
468             int errs;
469
470             if (setupterm(all_terms[t_run], fileno(stdout), &errs) != OK) {
471                 printf("** skipping %s (errs:%d)\n", all_terms[t_run], errs);
472             }
473 #if NCURSES_XNAMES
474             len_caps = std_caps;
475             if (cur_term) {
476                 TERMTYPE *term = (TERMTYPE *) cur_term;
477                 for (n = STRCOUNT; n < NUM_STRINGS(term); ++n) {
478                     GrowArray(all_caps, max_caps, len_caps);
479                     all_caps[len_caps++] = strdup(ExtStrname(term, (int) n, strnames));
480                 }
481             }
482 #endif
483
484             /*
485              * Most of the capabilities have no parameters, e.g., they are
486              * function-keys or simple operations such as clear-display.
487              * Ignore those, since they do not really exercise tparm.
488              */
489             use_caps = 0;
490             for (n = 0; n < len_caps; ++n) {
491                 char *value = tigetstr(all_caps[n]);
492                 if (relevant(all_caps[n], value)) {
493                     cap_name[use_caps] = all_caps[n];
494                     cap_data[use_caps] = value;
495                     use_caps++;
496                 }
497             }
498
499             if (v_opt) {
500                 printf("[%d:%d] %d paramerized cap%s * %ld test-case%s \"%s\"\n",
501                        r_run + 1, r_opt,
502                        PLURAL(use_caps),
503                        PLURAL(use_parms),
504                        all_terms[t_run]);
505             }
506
507             memset(all_parms, 0, sizeof(all_parms));
508             if (a_opt) {
509                 /* for each combination of values */
510                 do {
511                     for (n_run = 0; n_run < use_caps; ++n_run) {
512                         test_tparm(cap_name[n_run],
513                                    cap_data[n_run],
514                                    all_parms,
515                                    str_parms);
516                     }
517                 }
518                 while (increment(all_parms, num_parms, len_parms, 0));
519             } else {
520                 /* for the given values */
521                 for (n_run = 0; n_run < use_caps; ++n_run) {
522                     test_tparm(cap_name[n_run],
523                                cap_data[n_run],
524                                all_parms,
525                                str_parms);
526                 }
527             }
528 #if NCURSES_XNAMES
529             for (n = std_caps; n < len_caps; ++n) {
530                 free(all_caps[n]);
531             }
532 #endif
533             if (cur_term != 0) {
534                 del_curterm(cur_term);
535             } else {
536                 printf("? no cur_term\n");
537             }
538         }
539     }
540
541     printf("Tests:\n");
542     printf(NUMFORM " total\n", total_tests);
543     if (total_fails)
544         printf(NUMFORM " failed\n", total_fails);
545     printf("Characters:\n");
546     printf(NUMFORM " nulls\n", total_nulls);
547     printf(NUMFORM " controls\n", total_ctrls);
548     printf(NUMFORM " printable\n", total_print);
549     printf(NUMFORM " total\n", total_nulls + total_ctrls + total_print);
550 #if NO_LEAKS
551     for (n = 0; n < std_caps; ++n) {
552         free(all_caps[n]);
553     }
554     free(all_caps);
555     free(old_term);
556     for (n = 0; n < len_terms; ++n) {
557         free(all_terms[n]);
558     }
559     free(all_terms);
560     free(num_parms);
561     free(str_parms);
562     free(cap_name);
563     free(cap_data);
564 #endif
565
566     ExitProgram(EXIT_SUCCESS);
567 }
568
569 #else /* !HAVE_TIGETSTR */
570 int
571 main(void)
572 {
573     failed("This program requires the terminfo functions such as tigetstr");
574 }
575 #endif /* HAVE_TIGETSTR */