• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2  * Copyright 2019-2022,2023 Thomas E. Dickey                                *
3  * Copyright 2015-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: test_sgr.c,v 1.22 2023/05/27 20:13:10 tom Exp $
34  *
35  * A simple demo of the sgr/sgr0 terminal capabilities.
36  */
37 #define USE_TINFO
38 #include <test.priv.h>
39 
40 #if !HAVE_TIGETSTR
41 static GCC_NORETURN void failed(const char *);
42 
43 static void
failed(const char * msg)44 failed(const char *msg)
45 {
46     fprintf(stderr, "%s\n", msg);
47     ExitProgram(EXIT_FAILURE);
48 }
49 #endif
50 
51 #if HAVE_TIGETSTR
52 
53 static bool no_init = FALSE;
54 static bool q_opt = FALSE;
55 
56 static char *d_opt;
57 static char *e_opt;
58 static char **db_list;
59 static int db_item;
60 
61 static long total_values;
62 
63 static char *
make_dbitem(const char * const p,const char * const q)64 make_dbitem(const char *const p, const char *const q)
65 {
66     size_t need = strlen(e_opt) + 2 + (size_t) (p - q);
67     char *result = malloc(need);
68     _nc_SPRINTF(result, _nc_SLIMIT(need) "%s=%.*s", e_opt, (int) (p - q), q);
69     return result;
70 }
71 
72 static void
make_dblist(void)73 make_dblist(void)
74 {
75     if (d_opt && e_opt) {
76 	int pass;
77 
78 	for (pass = 0; pass < 2; ++pass) {
79 	    char *p, *q;
80 	    size_t count = 0;
81 
82 	    for (p = q = d_opt; *p != '\0'; ++p) {
83 		if (*p == ':') {
84 		    if (p != q + 1) {
85 			if (pass) {
86 			    db_list[count] = make_dbitem(p, q);
87 			}
88 			count++;
89 		    }
90 		    q = p + 1;
91 		}
92 	    }
93 	    if (p != q + 1) {
94 		if (pass) {
95 		    db_list[count] = make_dbitem(p, q);
96 		}
97 		count++;
98 	    }
99 	    if (!pass) {
100 		db_list = typeCalloc(char *, count + 1);
101 	    }
102 	}
103     }
104 }
105 
106 static char *
next_dbitem(void)107 next_dbitem(void)
108 {
109     char *result = 0;
110 
111     if (db_list) {
112 	if ((result = db_list[db_item]) == 0) {
113 	    db_item = 0;
114 	    result = db_list[0];
115 	} else {
116 	    db_item++;
117 	}
118     }
119     printf("** %s\n", result ? result : "<null>");
120     return result;
121 }
122 
123 #if NO_LEAKS
124 static void
free_dblist(void)125 free_dblist(void)
126 {
127     if (db_list) {
128 	int n;
129 	for (n = 0; db_list[n]; ++n)
130 	    free(db_list[n]);
131 	free(db_list);
132 	db_list = 0;
133     }
134 }
135 #endif
136 
137 #define MAXPAR    9
138 #define MAXSGR    (1 << MAXPAR)
139 #define BITS2P(n) (count & (1 << (n - 1)))
140 #define MASK_SMSO (1 << 0)
141 #define MASK_BOLD (1 << 5)
142 #define MASK_REV  (1 << 2)
143 
144 static void
dumpit(unsigned bits,unsigned ignore,const char * sgr,const char * sgr0)145 dumpit(unsigned bits, unsigned ignore, const char *sgr, const char *sgr0)
146 {
147     static const char sample[] = "abcdefghijklm";
148     static char params[] = "SURBDBIPA";
149     unsigned n;
150 
151     printf("%4u ", bits);
152     bits &= ~ignore;
153     for (n = 0; n < MAXPAR; ++n) {
154 	putchar((int) ((bits & (unsigned) (1 << n)) ? params[n] : '-'));
155     }
156     putchar(' ');
157     putp(sgr);
158     putp(sample);
159     putp(sgr0);
160     putchar('\n');
161 }
162 
163 static bool
one_bit(unsigned a,unsigned b)164 one_bit(unsigned a, unsigned b)
165 {
166     unsigned c = (a ^ b);
167     bool result = FALSE;
168     if (c) {
169 	while (!(c & 1)) {
170 	    c >>= 1;
171 	}
172 	result = (c == 1);
173     }
174     return result;
175 }
176 
177 static void
brute_force(const char * name)178 brute_force(const char *name)
179 {
180     unsigned count;
181     char *my_sgr;
182     char *my_sgr0;
183     char *my_bold;
184     char *my_revs;
185     char *my_smso;
186     char *my_name = strdup(name);
187 
188     if (db_list) {
189 	putenv(next_dbitem());
190     }
191 
192     if (!q_opt)
193 	printf("Terminal type \"%s\"\n", my_name);
194 
195     if (no_init) {
196 	START_TRACE();
197     } else {
198 	setupterm((NCURSES_CONST char *) my_name, 1, (int *) 0);
199     }
200 
201     if (!q_opt) {
202 	if (strcmp(my_name, ttytype))
203 	    printf("... actual \"%s\"\n", ttytype);
204     }
205 
206     my_sgr = tigetstr("sgr");
207     my_sgr0 = tigetstr("sgr0");
208     my_bold = tigetstr("bold");
209     my_revs = tigetstr("rev");
210     my_smso = tigetstr("smso");
211 
212     if (!VALID_STRING(my_sgr)) {
213 	fprintf(stderr, "no \"sgr\" capability found\n");
214     } else if (!VALID_STRING(my_sgr0)) {
215 	fprintf(stderr, "no \"sgr0\" capability found\n");
216     } else {
217 	char *values[MAXSGR + MAXPAR];
218 	unsigned j;
219 	unsigned ignore = 0;
220 	unsigned reason = 0;
221 	unsigned repeat = 0;
222 	for (count = 0; count < MAXSGR; ++count) {
223 	    values[count] = tparm(my_sgr,
224 				  BITS2P(1),
225 				  BITS2P(2),
226 				  BITS2P(3),
227 				  BITS2P(4),
228 				  BITS2P(5),
229 				  BITS2P(6),
230 				  BITS2P(7),
231 				  BITS2P(8),
232 				  BITS2P(9));
233 	    if (values[count] != 0) {
234 		values[count] = strdup(values[count]);
235 	    }
236 	}
237 	for (count = 0; count < MAXSGR; ++count) {
238 	    if (values[count] != 0) {
239 		for (j = count + 1; j < MAXSGR; ++j) {
240 		    if (values[j] == 0)
241 			continue;
242 		    if (strcmp(values[count], values[j]))
243 			continue;
244 		    if (one_bit(count, j)) {
245 			free(values[j]);
246 			values[j] = 0;
247 		    }
248 		}
249 	    }
250 	}
251 	for (j = 0; j < MAXPAR; ++j) {
252 	    unsigned mask = (unsigned) (1 << j);
253 	    for (count = 0; count < MAXSGR; ++count) {
254 		if ((count & mask) != 0)
255 		    continue;
256 		if (values[count] != 0 && values[count + mask] != 0) {
257 		    mask = 0;
258 		    break;
259 		}
260 	    }
261 	    ignore |= mask;
262 	}
263 	/* smso is tested first, but often duplicates bold or reverse. */
264 	if (VALID_STRING(my_smso)) {
265 	    if (VALID_STRING(my_bold) && !strcmp(my_bold, my_smso)) {
266 		repeat |= MASK_SMSO;
267 		reason = MASK_BOLD;
268 	    }
269 	    if (VALID_STRING(my_revs) && !strcmp(my_revs, my_smso)) {
270 		repeat |= MASK_SMSO;
271 		reason = MASK_REV;
272 	    }
273 	}
274 	for (count = 0; count < MAXSGR; ++count) {
275 	    if (values[count] != 0) {
276 		bool found = FALSE;
277 		if ((repeat & MASK_SMSO) != 0
278 		    && (count & MASK_SMSO) != 0) {
279 		    found = TRUE;
280 		} else {
281 		    for (j = 0; j < count; ++j) {
282 			if (values[j] != 0 && !strcmp(values[j], values[count])) {
283 			    if ((repeat & MASK_SMSO) != 0
284 				&& (j & MASK_SMSO) != 0
285 				&& (count & reason) != 0) {
286 				continue;
287 			    }
288 			    found = TRUE;
289 			    break;
290 			}
291 		    }
292 		}
293 		if (!found) {
294 		    dumpit(count, ignore, values[count], my_sgr0);
295 		    ++total_values;
296 		}
297 	    }
298 	}
299 	for (count = 0; count < MAXSGR; ++count) {
300 	    free(values[count]);
301 	}
302     }
303     free(my_name);
304     del_curterm(cur_term);
305 }
306 
307 static void
usage(int ok)308 usage(int ok)
309 {
310     static const char *msg[] =
311     {
312 	"Usage: test_sgr [options] [terminal]"
313 	,""
314 	,"Print all distinct combinations of sgr capability."
315 	,""
316 	,USAGE_COMMON
317 	,"Options:"
318 	," -d LIST  colon-separated list of databases to use"
319 	," -e NAME  environment variable to set with -d option"
320 	," -n       do not initialize terminal, to test error-checking"
321 	," -q       quiet (prints only counts)"
322     };
323     unsigned n;
324     for (n = 0; n < SIZEOF(msg); ++n) {
325 	fprintf(stderr, "%s\n", msg[n]);
326     }
327     ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
328 }
329 /* *INDENT-OFF* */
VERSION_COMMON()330 VERSION_COMMON()
331 /* *INDENT-ON* */
332 
333 int
334 main(int argc, char *argv[])
335 {
336     int ch;
337     char *name;
338 
339     while ((ch = getopt(argc, argv, OPTS_COMMON "d:e:nq")) != -1) {
340 	switch (ch) {
341 	case 'd':
342 	    d_opt = optarg;
343 	    break;
344 	case 'e':
345 	    e_opt = optarg;
346 	    break;
347 	case 'n':
348 	    no_init = TRUE;
349 	    break;
350 	case 'q':
351 	    q_opt = TRUE;
352 	    break;
353 	case OPTS_VERSION:
354 	    show_version(argv);
355 	    ExitProgram(EXIT_SUCCESS);
356 	default:
357 	    usage(ch == OPTS_USAGE);
358 	    /* NOTREACHED */
359 	}
360     }
361 
362     make_dblist();
363 
364     if (optind < argc) {
365 	int n;
366 	for (n = optind; n < argc; ++n) {
367 	    brute_force(argv[n]);
368 	}
369     } else if ((name = getenv("TERM")) != 0) {
370 	brute_force(name);
371     } else {
372 	static char dumb[] = "dumb";
373 	brute_force(dumb);
374     }
375 
376     printf("%ld distinct values\n", total_values);
377 
378 #if NO_LEAKS
379     free_dblist();
380 #endif
381 
382     ExitProgram(EXIT_SUCCESS);
383 }
384 
385 #else /* !HAVE_TIGETSTR */
386 int
main(void)387 main(void)
388 {
389     failed("This program requires the terminfo functions such as tigetstr");
390     ExitProgram(EXIT_FAILURE);
391 }
392 #endif /* HAVE_TIGETSTR */
393