tput.c revision 1.21
1/*	$OpenBSD: tput.c,v 1.21 2015/01/16 06:40:13 deraadt Exp $	*/
2
3/*
4 * Copyright (c) 1999 Todd C. Miller <Todd.Miller@courtesan.com>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18/*-
19 * Copyright (c) 1980, 1988, 1993
20 *	The Regents of the University of California.  All rights reserved.
21 *
22 * Redistribution and use in source and binary forms, with or without
23 * modification, are permitted provided that the following conditions
24 * are met:
25 * 1. Redistributions of source code must retain the above copyright
26 *    notice, this list of conditions and the following disclaimer.
27 * 2. Redistributions in binary form must reproduce the above copyright
28 *    notice, this list of conditions and the following disclaimer in the
29 *    documentation and/or other materials provided with the distribution.
30 * 3. Neither the name of the University nor the names of its contributors
31 *    may be used to endorse or promote products derived from this software
32 *    without specific prior written permission.
33 *
34 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
35 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44 * SUCH DAMAGE.
45 */
46
47#include <ctype.h>
48#include <err.h>
49#include <curses.h>
50#include <term.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <termios.h>
54#include <unistd.h>
55#include <limits.h>
56#include <string.h>
57
58#define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
59
60#include <sys/wait.h>
61
62static void   init(void);
63static char **process(char *, char *, char **);
64static void   reset(void);
65static void   set_margins(void);
66static void   usage(void);
67
68extern char  *__progname;
69
70int
71main(int argc, char *argv[])
72{
73	int ch, exitval, n, Sflag;
74	size_t len;
75	char *p, *term, *str;
76	char **oargv;
77
78	oargv = argv;
79	term = NULL;
80	Sflag = exitval = 0;
81	while ((ch = getopt(argc, argv, "ST:")) != -1)
82		switch(ch) {
83		case 'T':
84			term = optarg;
85			break;
86		case 'S':
87			Sflag = 1;
88			break;
89		case '?':
90		default:
91			usage();
92		}
93	argc -= optind;
94	argv += optind;
95
96	if (Sflag && argc > 0)
97		usage();
98
99	if (!term && !(term = getenv("TERM")))
100		errx(2, "No value for $TERM and no -T specified");
101
102	/*
103	 * NOTE: tgetent() will call setupterm() and set ospeed for us
104	 * (this is ncurses-specific behavior)
105	 */
106	if (tgetent(NULL, term) != 1)
107		errx(3, "Unknown terminal type `%s'", term);
108
109	if (strcmp(__progname, "clear") == 0) {
110		if (Sflag)
111			usage();
112		argv = oargv;
113		*argv = __progname;
114		*(argv+1) = NULL;
115	}
116	if (Sflag) {
117		char **av;
118
119		/* Build new argv based on stdin */
120		argc = n = 0;
121		av = NULL;
122		while ((str = fgetln(stdin, &len)) != NULL) {
123			if (str[len-1] != '\n')
124				errx(1, "premature EOF");
125			str[len-1] = '\0';
126			while ((p = strsep(&str, " \t")) != NULL) {
127				/* grow av as needed */
128				if (argc + 1 >= n) {
129					n += 64;
130					av = reallocarray(av, n,
131					    sizeof(char *));
132					if (av == NULL)
133						errx(1, "out of memory");
134				}
135				if (*p != '\0' &&
136				    (av[argc++] = strdup(p)) == NULL)
137					errx(1, "out of memory");
138			}
139		}
140		if (argc > 0) {
141			av[argc] = NULL;
142			argv = av;
143		}
144	}
145	while ((p = *argv++)) {
146		switch (*p) {
147		case 'i':
148			if (!strcmp(p, "init")) {
149				init();
150				continue;
151			}
152			break;
153		case 'l':
154			if (!strcmp(p, "longname")) {
155				puts(longname());
156				continue;
157			}
158			break;
159		case 'r':
160			if (!strcmp(p, "reset")) {
161				reset();
162				continue;
163			}
164			break;
165		}
166
167		/* First try as terminfo */
168		if ((str = tigetstr(p)) && str != (char *)-1)
169			argv = process(p, str, argv);
170		else if ((n = tigetnum(p)) != -2)
171			(void)printf("%d\n", n);
172		else if ((n = tigetflag(p)) != -1)
173			exitval = !n;
174		/* Then fall back on termcap */
175		else if ((str = tgetstr(p, NULL)))
176			argv = process(p, str, argv);
177		else if ((n = tgetnum(p)) != -1)
178			(void)printf("%d\n", n);
179		else if ((exitval = tgetflag(p)) != 0)
180			exitval = !exitval;
181		else {
182			warnx("Unknown terminfo capability `%s'", p);
183			exitval = 4;
184		}
185	}
186	exit(exitval);
187}
188
189static char **
190process(char *cap, char *str, char **argv)
191{
192	char *cp, *s, *nargv[9];
193	int arg_need, popcount, i;
194
195	/* Count how many values we need for this capability. */
196	for (cp = str, arg_need = popcount = 0; *cp != '\0'; cp++) {
197		if (*cp == '%') {
198			switch (*++cp) {
199			case '%':
200			   	cp++;
201				break;
202			case 'i':
203				if (popcount < 2)
204					popcount = 2;
205				break;
206			case 'p':
207				cp++;
208				if (isdigit((unsigned char)cp[1]) &&
209				    popcount < cp[1] - '0')
210					popcount = cp[1] - '0';
211				break;
212			case 'd':
213			case 's':
214			case '0':
215			case '1':
216			case '2':
217			case '3':
218			case '4':
219			case '5':
220			case '6':
221			case '7':
222			case '8':
223			case '9':
224			case '.':
225			case '+':
226				arg_need++;
227				break;
228			default:
229				break;
230			}
231		}
232	}
233	arg_need = MAXIMUM(arg_need, popcount);
234	if (arg_need > 9)
235		errx(2, "too many arguments (%d) for capability `%s'",
236		    arg_need, cap);
237
238	for (i = 0; i < arg_need; i++) {
239		long l;
240
241		if (argv[i] == NULL)
242			errx(2, "not enough arguments (%d) for capability `%s'",
243			    arg_need, cap);
244
245		/* convert ascii representation of numbers to longs */
246		if (isdigit((unsigned char)argv[i][0])
247		    && (l = strtol(argv[i], &cp, 10)) >= 0
248		    && l < LONG_MAX && *cp == '\0')
249			nargv[i] = (char *)l;
250		else
251			nargv[i] = argv[i];
252	}
253
254	s = tparm(str, nargv[0], nargv[1], nargv[2], nargv[3],
255	    nargv[4], nargv[5], nargv[6], nargv[7], nargv[8]);
256	putp(s);
257	fflush(stdout);
258
259	return (argv + arg_need);
260}
261
262static void
263init(void)
264{
265	FILE *ifile;
266	size_t len;
267	char *buf;
268	int wstatus;
269
270	if (init_prog && !issetugid()) {
271		switch (vfork()) {
272		case -1:
273			err(4, "vfork");
274			break;
275		case 0:
276			/* child */
277			execl(init_prog, init_prog, (char *)NULL);
278			_exit(127);
279			break;
280		default:
281			wait(&wstatus);
282			/* parent */
283			break;
284		}
285	}
286	if (init_1string)
287		putp(init_1string);
288	if (init_2string)
289		putp(init_2string);
290	set_margins();
291	/* always use 8 space tabs */
292	if (init_tabs != 8 && clear_all_tabs && set_tab) {
293		int i;
294
295		putp(clear_all_tabs);
296		for (i = 0; i < (columns - 1) / 8; i++) {
297			if (parm_right_cursor)
298				putp(tparm(parm_right_cursor, 8));
299			else
300				fputs("        ", stdout);
301			putp(set_tab);
302		}
303	}
304	if (init_file && !issetugid() && (ifile = fopen(init_file, "r"))) {
305		while ((buf = fgetln(ifile, &len)) != NULL) {
306			if (buf[len-1] != '\n')
307				errx(1, "premature EOF reading %s", init_file);
308			buf[len-1] = '\0';
309			putp(buf);
310		}
311		fclose(ifile);
312	}
313	if (init_3string)
314		putp(init_3string);
315	fflush(stdout);
316}
317
318static void
319reset(void)
320{
321	FILE *rfile;
322	size_t len;
323	char *buf;
324
325	if (reset_1string)
326		putp(reset_1string);
327	if (reset_2string)
328		putp(reset_2string);
329	set_margins();
330	if (reset_file && !issetugid() && (rfile = fopen(reset_file, "r"))) {
331		while ((buf = fgetln(rfile, &len)) != NULL) {
332			if (buf[len-1] != '\n')
333				errx(1, "premature EOF reading %s", reset_file);
334			buf[len-1] = '\0';
335			putp(buf);
336		}
337		fclose(rfile);
338	}
339	if (reset_3string)
340		putp(reset_3string);
341	fflush(stdout);
342}
343
344static void
345set_margins(void)
346{
347
348	/*
349	 * Four possibilities:
350	 *	1) we have set_lr_margin and can set things with one call
351	 *	2) we have set_{left,right}_margin_parm, use two calls
352	 *	3) we have set_{left,right}_margin, set based on position
353	 *	4) none of the above, leave things the way they are
354	 */
355	if (set_lr_margin) {
356		putp(tparm(set_lr_margin, 0, columns - 1));
357	} else if (set_left_margin_parm && set_right_margin_parm) {
358		putp(tparm(set_left_margin_parm, 0));
359		putp(tparm(set_right_margin_parm, columns - 1));
360	} else if (set_left_margin && set_right_margin && clear_margins) {
361		putp(clear_margins);
362
363		/* go to column 0 and set the left margin */
364		putp(carriage_return ? carriage_return : "\r");
365		putp(set_left_margin);
366
367		/* go to last column and set the right margin */
368		if (parm_right_cursor)
369			putp(tparm(parm_right_cursor, columns - 1));
370		else
371			printf("%*s", columns - 1, " ");
372		putp(set_right_margin);
373		putp(carriage_return ? carriage_return : "\r");
374	}
375	fflush(stdout);
376}
377
378static void
379usage(void)
380{
381
382	if (strcmp(__progname, "clear") == 0)
383		(void)fprintf(stderr, "usage: %s [-T term]\n", __progname);
384	else
385		(void)fprintf(stderr,
386		    "usage: %s [-T term] attribute [attribute-args] ...\n"
387		    "       %s [-T term] -S\n", __progname, __progname);
388	exit(1);
389}
390