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