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