tput.c revision 1.18
1/*	$OpenBSD: tput.c,v 1.18 2013/11/27 00:13:24 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 <sys/param.h>
48
49#include <ctype.h>
50#include <err.h>
51#include <curses.h>
52#include <term.h>
53#include <stdio.h>
54#include <stdlib.h>
55#include <termios.h>
56#include <unistd.h>
57#include <string.h>
58
59#include <sys/wait.h>
60
61static void   init(void);
62static char **process(char *, char *, char **);
63static void   reset(void);
64static void   set_margins(void);
65static void   usage(void);
66
67extern char  *__progname;
68
69int
70main(int argc, char *argv[])
71{
72	int ch, exitval, n, Sflag;
73	size_t len;
74	char *p, *term, *str;
75	char **oargv;
76
77	oargv = argv;
78	term = NULL;
79	Sflag = exitval = 0;
80	while ((ch = getopt(argc, argv, "ST:")) != -1)
81		switch(ch) {
82		case 'T':
83			term = optarg;
84			break;
85		case 'S':
86			Sflag = 1;
87			break;
88		case '?':
89		default:
90			usage();
91		}
92	argc -= optind;
93	argv += optind;
94
95	if (Sflag && argc > 0)
96		usage();
97
98	if (!term && !(term = getenv("TERM")))
99		errx(2, "No value for $TERM and no -T specified");
100
101	/*
102	 * NOTE: tgetent() will call setupterm() and set ospeed for us
103	 * (this is ncurses-specific behavior)
104	 */
105	if (tgetent(NULL, term) != 1)
106		errx(3, "Unknown terminal type `%s'", term);
107
108	if (strcmp(__progname, "clear") == 0) {
109		if (Sflag)
110			usage();
111		argv = oargv;
112		*argv = __progname;
113		*(argv+1) = NULL;
114	}
115	if (Sflag) {
116		char **av;
117
118		/* Build new argv based on stdin */
119		argc = n = 0;
120		av = NULL;
121		while ((str = fgetln(stdin, &len)) != NULL) {
122			if (str[len-1] != '\n')
123				errx(1, "premature EOF");
124			str[len-1] = '\0';
125			while ((p = strsep(&str, " \t")) != NULL) {
126				/* grow av as needed */
127				if (argc + 1 >= n) {
128					n += 64;
129					av = (char **)realloc(av, sizeof(char *) * n);
130					if (av == NULL)
131						errx(1, "out of memory");
132				}
133				if (*p != '\0' &&
134				    (av[argc++] = strdup(p)) == NULL)
135					errx(1, "out of memory");
136			}
137		}
138		if (argc > 0) {
139			av[argc] = NULL;
140			argv = av;
141		}
142	}
143	while ((p = *argv++)) {
144		switch (*p) {
145		case 'i':
146			if (!strcmp(p, "init")) {
147				init();
148				continue;
149			}
150			break;
151		case 'l':
152			if (!strcmp(p, "longname")) {
153				puts(longname());
154				continue;
155			}
156			break;
157		case 'r':
158			if (!strcmp(p, "reset")) {
159				reset();
160				continue;
161			}
162			break;
163		}
164
165		/* First try as terminfo */
166		if ((str = tigetstr(p)) && str != (char *)-1)
167			argv = process(p, str, argv);
168		else if ((n = tigetnum(p)) != -2)
169			(void)printf("%d\n", n);
170		else if ((n = tigetflag(p)) != -1)
171			exitval = !n;
172		/* Then fall back on termcap */
173		else if ((str = tgetstr(p, NULL)))
174			argv = process(p, str, argv);
175		else if ((n = tgetnum(p)) != -1)
176			(void)printf("%d\n", n);
177		else if ((exitval = tgetflag(p)) != 0)
178			exitval = !exitval;
179		else {
180			warnx("Unknown terminfo capability `%s'", p);
181			exitval = 4;
182		}
183	}
184	exit(exitval);
185}
186
187static char **
188process(char *cap, char *str, char **argv)
189{
190	char *cp, *s, *nargv[9];
191	int arg_need, popcount, i;
192
193	/* Count how many values we need for this capability. */
194	for (cp = str, arg_need = popcount = 0; *cp != '\0'; cp++) {
195		if (*cp == '%') {
196			switch (*++cp) {
197			case '%':
198			   	cp++;
199				break;
200			case 'i':
201				if (popcount < 2)
202					popcount = 2;
203				break;
204			case 'p':
205				cp++;
206				if (isdigit((unsigned char)cp[1]) &&
207				    popcount < cp[1] - '0')
208					popcount = cp[1] - '0';
209				break;
210			case 'd':
211			case 's':
212			case '0':
213			case '1':
214			case '2':
215			case '3':
216			case '4':
217			case '5':
218			case '6':
219			case '7':
220			case '8':
221			case '9':
222			case '.':
223			case '+':
224				arg_need++;
225				break;
226			default:
227				break;
228			}
229		}
230	}
231	arg_need = MAX(arg_need, popcount);
232	if (arg_need > 9)
233		errx(2, "too many arguments (%d) for capability `%s'",
234		    arg_need, cap);
235
236	for (i = 0; i < arg_need; i++) {
237		long l;
238
239		if (argv[i] == NULL)
240			errx(2, "not enough arguments (%d) for capability `%s'",
241			    arg_need, cap);
242
243		/* convert ascii representation of numbers to longs */
244		if ((unsigned char)isdigit(argv[i][0])
245		    && (l = strtol(argv[i], &cp, 10)) >= 0
246		    && l < LONG_MAX && *cp == '\0')
247			nargv[i] = (char *)l;
248		else
249			nargv[i] = argv[i];
250	}
251
252	s = tparm(str, nargv[0], nargv[1], nargv[2], nargv[3],
253	    nargv[4], nargv[5], nargv[6], nargv[7], nargv[8]);
254	putp(s);
255	fflush(stdout);
256
257	return (argv + arg_need);
258}
259
260static void
261init(void)
262{
263	FILE *ifile;
264	size_t len;
265	char *buf;
266	int wstatus;
267
268	if (init_prog && !issetugid()) {
269		switch (vfork()) {
270		case -1:
271			err(4, "vfork");
272			break;
273		case 0:
274			/* child */
275			execl(init_prog, init_prog, (char *)NULL);
276			_exit(127);
277			break;
278		default:
279			wait(&wstatus);
280			/* parent */
281			break;
282		}
283	}
284	if (init_1string)
285		putp(init_1string);
286	if (init_2string)
287		putp(init_2string);
288	set_margins();
289	/* always use 8 space tabs */
290	if (init_tabs != 8 && clear_all_tabs && set_tab) {
291		int i;
292
293		putp(clear_all_tabs);
294		for (i = 0; i < (columns - 1) / 8; i++) {
295			if (parm_right_cursor)
296				putp(tparm(parm_right_cursor, 8));
297			else
298				fputs("        ", stdout);
299			putp(set_tab);
300		}
301	}
302	if (init_file && !issetugid() && (ifile = fopen(init_file, "r"))) {
303		while ((buf = fgetln(ifile, &len)) != NULL) {
304			if (buf[len-1] != '\n')
305				errx(1, "premature EOF reading %s", init_file);
306			buf[len-1] = '\0';
307			putp(buf);
308		}
309		fclose(ifile);
310	}
311	if (init_3string)
312		putp(init_3string);
313	fflush(stdout);
314}
315
316static void
317reset(void)
318{
319	FILE *rfile;
320	size_t len;
321	char *buf;
322
323	if (reset_1string)
324		putp(reset_1string);
325	if (reset_2string)
326		putp(reset_2string);
327	set_margins();
328	if (reset_file && !issetugid() && (rfile = fopen(reset_file, "r"))) {
329		while ((buf = fgetln(rfile, &len)) != NULL) {
330			if (buf[len-1] != '\n')
331				errx(1, "premature EOF reading %s", reset_file);
332			buf[len-1] = '\0';
333			putp(buf);
334		}
335		fclose(rfile);
336	}
337	if (reset_3string)
338		putp(reset_3string);
339	fflush(stdout);
340}
341
342static void
343set_margins(void)
344{
345
346	/*
347	 * Four possibilities:
348	 *	1) we have set_lr_margin and can set things with one call
349	 *	2) we have set_{left,right}_margin_parm, use two calls
350	 *	3) we have set_{left,right}_margin, set based on position
351	 *	4) none of the above, leave things the way they are
352	 */
353	if (set_lr_margin) {
354		putp(tparm(set_lr_margin, 0, columns - 1));
355	} else if (set_left_margin_parm && set_right_margin_parm) {
356		putp(tparm(set_left_margin_parm, 0));
357		putp(tparm(set_right_margin_parm, columns - 1));
358	} else if (set_left_margin && set_right_margin && clear_margins) {
359		putp(clear_margins);
360
361		/* go to column 0 and set the left margin */
362		putp(carriage_return ? carriage_return : "\r");
363		putp(set_left_margin);
364
365		/* go to last column and set the right margin */
366		if (parm_right_cursor)
367			putp(tparm(parm_right_cursor, columns - 1));
368		else
369			printf("%*s", columns - 1, " ");
370		putp(set_right_margin);
371		putp(carriage_return ? carriage_return : "\r");
372	}
373	fflush(stdout);
374}
375
376static void
377usage(void)
378{
379
380	if (strcmp(__progname, "clear") == 0)
381		(void)fprintf(stderr, "usage: %s [-T term]\n", __progname);
382	else
383		(void)fprintf(stderr,
384		    "usage: %s [-T term] attribute [attribute-args] ...\n"
385		    "       %s [-T term] -S\n", __progname, __progname);
386	exit(1);
387}
388