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