xref: /openbsd/usr.bin/tput/tput.c (revision 133306f0)
1 /*	$OpenBSD: tput.c,v 1.10 1999/09/12 10:29:01 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
63 static 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
70 static char sccsid[] = "@(#)tput.c	8.3 (Berkeley) 4/28/95";
71 #endif
72 static char rcsid[] = "$OpenBSD: tput.c,v 1.10 1999/09/12 10:29:01 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 
89 static void   init __P((void));
90 static char **process __P((char *, char *, char **));
91 static void   reset __P((void));
92 static void   set_margins __P((void));
93 static void   usage __P((void));
94 
95 extern char  *__progname;
96 
97 int
98 main(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 	exit(exitval);
215 }
216 
217 static char **
218 process(cap, str, argv)
219 	char *cap, *str, **argv;
220 {
221 	char *cp, *s, *nargv[9];
222 	int arg_need, popcount, i;
223 
224 	/* Count how many values we need for this capability. */
225 	for (cp = str, arg_need = popcount = 0; *cp != '\0'; cp++) {
226 		if (*cp == '%') {
227 			switch (*++cp) {
228 			case '%':
229 			   	cp++;
230 				break;
231 			case 'i':
232 				if (popcount < 2)
233 					popcount = 2;
234 				break;
235 			case 'p':
236 				cp++;
237 				if (isdigit(cp[1]) && popcount < cp[1] - '0')
238 					popcount = cp[1] - '0';
239 				break;
240 			case 'd':
241 			case 's':
242 			case '0':
243 			case '1':
244 			case '2':
245 			case '3':
246 			case '4':
247 			case '5':
248 			case '6':
249 			case '7':
250 			case '8':
251 			case '9':
252 			case '.':
253 			case '+':
254 				arg_need++;
255 				break;
256 			default:
257 				break;
258 			}
259 		}
260 	}
261 	arg_need = MAX(arg_need, popcount);
262 	if (arg_need > 9)
263 		errx(2, "too many arguments (%d) for capability `%s'",
264 		    arg_need, cap);
265 
266 	for (i = 0; i < arg_need; i++) {
267 		long l;
268 
269 		if (argv[i] == NULL)
270 			errx(2, "not enough arguments (%d) for capability `%s'",
271 			    arg_need, cap);
272 
273 		/* convert ascii representation of numbers to longs */
274 		if (isdigit(argv[i][0]) && (l = strtol(argv[i], &cp, 10)) >= 0
275 		    && l < LONG_MAX && *cp == '\0')
276 			nargv[i] = (char *)l;
277 		else
278 			nargv[i] = argv[i];
279 	}
280 
281 	s = tparm(str, nargv[0], nargv[1], nargv[2], nargv[3],
282 	    nargv[4], nargv[5], nargv[6], nargv[7], nargv[8]);
283 	putp(s);
284 	fflush(stdout);
285 
286 	return (argv + arg_need);
287 }
288 
289 static void
290 init()
291 {
292 	FILE *ifile;
293 	size_t len;
294 	char *buf;
295 	int wstatus;
296 
297 	if (init_prog && !issetugid()) {
298 		switch (vfork()) {
299 		case -1:
300 			err(4, "vfork");
301 			break;
302 		case 0:
303 			/* child */
304 			execl(init_prog, init_prog, NULL);
305 			_exit(127);
306 			break;
307 		default:
308 			wait(&wstatus);
309 			/* parent */
310 			break;
311 		}
312 	}
313 	if (init_1string)
314 		putp(init_1string);
315 	if (init_2string)
316 		putp(init_2string);
317 	set_margins();
318 	/* always use 8 space tabs */
319 	if (init_tabs != 8 && clear_all_tabs && set_tab) {
320 		int i;
321 
322 		putp(clear_all_tabs);
323 		for (i = 0; i < (columns - 1) / 8; i++) {
324 			if (parm_right_cursor)
325 				putp(tparm(parm_right_cursor, 8));
326 			else
327 				fputs("        ", stdout);
328 			putp(set_tab);
329 		}
330 	}
331 	if (init_file && !issetugid() && (ifile = fopen(init_file, "r"))) {
332 		while ((buf = fgetln(ifile, &len)) != NULL) {
333 			if (buf[len-1] != '\n')
334 				errx(1, "premature EOF reading %s", init_file);
335 			buf[len-1] = '\0';
336 			putp(buf);
337 		}
338 		fclose(ifile);
339 	}
340 	if (init_3string)
341 		putp(init_3string);
342 	fflush(stdout);
343 }
344 
345 static void
346 reset()
347 {
348 	FILE *rfile;
349 	size_t len;
350 	char *buf;
351 
352 	if (reset_1string)
353 		putp(reset_1string);
354 	if (reset_2string)
355 		putp(reset_2string);
356 	set_margins();
357 	if (reset_file && !issetugid() && (rfile = fopen(reset_file, "r"))) {
358 		while ((buf = fgetln(rfile, &len)) != NULL) {
359 			if (buf[len-1] != '\n')
360 				errx(1, "premature EOF reading %s", reset_file);
361 			buf[len-1] = '\0';
362 			putp(buf);
363 		}
364 		fclose(rfile);
365 	}
366 	if (reset_3string)
367 		putp(reset_3string);
368 	fflush(stdout);
369 }
370 
371 static void
372 set_margins()
373 {
374 
375 	/*
376 	 * Four possibilities:
377 	 *	1) we have set_lr_margin and can set things with one call
378 	 *	2) we have set_{left,right}_margin_parm, use two calls
379 	 *	3) we have set_{left,right}_margin, set based on position
380 	 *	4) none of the above, leave things the way they are
381 	 */
382 	if (set_lr_margin) {
383 		putp(tparm(set_lr_margin, 0, columns - 1));
384 	} else if (set_left_margin_parm && set_right_margin_parm) {
385 		putp(tparm(set_left_margin_parm, 0));
386 		putp(tparm(set_right_margin_parm, columns - 1));
387 	} else if (set_left_margin && set_right_margin && clear_margins) {
388 		putp(clear_margins);
389 
390 		/* go to column 0 and set the left margin */
391 		putp(carriage_return ? carriage_return : "\r");
392 		putp(set_left_margin);
393 
394 		/* go to last column and set the right margin */
395 		if (parm_right_cursor)
396 			putp(tparm(parm_right_cursor, columns - 1));
397 		else
398 			printf("%*s", columns - 1, " ");
399 		putp(set_right_margin);
400 		putp(carriage_return ? carriage_return : "\r");
401 	}
402 	fflush(stdout);
403 }
404 
405 static void
406 usage()
407 {
408 
409 	if (strcmp(__progname, "clear") == 0)
410 		(void)fprintf(stderr, "usage: %s [-T term]\n", __progname);
411 	else
412 		(void)fprintf(stderr,
413 		    "usage: %s [-T term] attribute [attribute-args] ...\n"
414 		    "       %s [-T term] -S\n", __progname, __progname);
415 	exit(1);
416 }
417