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