tput.c revision 1.29
1/* $OpenBSD: tput.c,v 1.29 2023/09/06 05:04:07 jsg 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 <sys/wait.h> 48#include <ctype.h> 49#include <err.h> 50#include <curses.h> 51#include <term.h> 52#include <stdio.h> 53#include <stdlib.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#define NUM_PARM 9 /* must match tic.h */ 62 63static void init(void); 64static char **process(char *, char *, char **); 65static void reset(void); 66static void set_margins(void); 67static void usage(void); 68 69extern char *__progname; 70extern int _nc_tparm_analyze(const char *string, char *p_is_s[NUM_PARM], int *popcount); 71 72int 73main(int argc, char *argv[]) 74{ 75 int ch, exitval, n, Sflag; 76 size_t len; 77 char *p, *term, *str; 78 char **oargv; 79 80 if (pledge("stdio rpath wpath tty", NULL) == -1) 81 err(1, "pledge"); 82 83 oargv = argv; 84 term = NULL; 85 Sflag = exitval = 0; 86 while ((ch = getopt(argc, argv, "ST:")) != -1) 87 switch(ch) { 88 case 'T': 89 term = optarg; 90 break; 91 case 'S': 92 Sflag = 1; 93 break; 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 193static char ** 194process(char *cap, char *str, char **argv) 195{ 196 char *s, *nargv[NUM_PARM] = {0}; 197 char *p_is_s[NUM_PARM]; 198 int arg_need, i; 199 200 /* Count how many values we need for this capability. */ 201 i = _nc_tparm_analyze(str, p_is_s, &arg_need); 202 if (arg_need == 0) 203 arg_need = i; 204 if (arg_need > NUM_PARM) 205 errx(2, "too many arguments (%d) for capability `%s'", 206 arg_need, cap); 207 208 for (i = 0; i < arg_need; i++) { 209 const char *errstr; 210 long l; 211 212 if (argv[i] == NULL) 213 errx(2, "not enough arguments (%d) for capability `%s'", 214 arg_need, cap); 215 216 if (p_is_s[i] != 0) { 217 nargv[i] = argv[i]; 218 } else { 219 /* convert ascii representation of numbers to longs */ 220 l = strtonum(argv[i], LONG_MIN, LONG_MAX, &errstr); 221 if (errstr != NULL) 222 errx(2, "capability `%s' is %s", cap, errstr); 223 nargv[i] = (char *)l; 224 } 225 } 226 227 s = tparm(str, nargv[0], nargv[1], nargv[2], nargv[3], 228 nargv[4], nargv[5], nargv[6], nargv[7], nargv[8]); 229 putp(s); 230 fflush(stdout); 231 232 return (argv + arg_need); 233} 234 235static void 236init(void) 237{ 238 FILE *ifile; 239 size_t len; 240 char *buf; 241 int wstatus; 242 pid_t pid; 243 244 if (init_prog && !issetugid()) { 245 switch (pid = vfork()) { 246 case -1: 247 err(4, "vfork"); 248 break; 249 case 0: 250 /* child */ 251 execl(init_prog, init_prog, (char *)NULL); 252 _exit(127); 253 break; 254 default: 255 while (waitpid(pid, &wstatus, 0) == -1) { 256 if (errno != EINTR) 257 break; 258 } 259 /* parent */ 260 break; 261 } 262 } 263 if (init_1string) 264 putp(init_1string); 265 if (init_2string) 266 putp(init_2string); 267 set_margins(); 268 /* always use 8 space tabs */ 269 if (init_tabs != 8 && clear_all_tabs && set_tab) { 270 int i; 271 272 putp(clear_all_tabs); 273 for (i = 0; i < (columns - 1) / 8; i++) { 274 if (parm_right_cursor) 275 putp(tparm(parm_right_cursor, 8)); 276 else 277 fputs(" ", stdout); 278 putp(set_tab); 279 } 280 } 281 if (init_file && !issetugid() && (ifile = fopen(init_file, "r"))) { 282 while ((buf = fgetln(ifile, &len)) != NULL) { 283 if (buf[len-1] != '\n') 284 errx(1, "premature EOF reading %s", init_file); 285 buf[len-1] = '\0'; 286 putp(buf); 287 } 288 fclose(ifile); 289 } 290 if (init_3string) 291 putp(init_3string); 292 fflush(stdout); 293} 294 295static void 296reset(void) 297{ 298 FILE *rfile; 299 size_t len; 300 char *buf; 301 302 if (reset_1string) 303 putp(reset_1string); 304 if (reset_2string) 305 putp(reset_2string); 306 set_margins(); 307 if (reset_file && !issetugid() && (rfile = fopen(reset_file, "r"))) { 308 while ((buf = fgetln(rfile, &len)) != NULL) { 309 if (buf[len-1] != '\n') 310 errx(1, "premature EOF reading %s", reset_file); 311 buf[len-1] = '\0'; 312 putp(buf); 313 } 314 fclose(rfile); 315 } 316 if (reset_3string) 317 putp(reset_3string); 318 fflush(stdout); 319} 320 321static void 322set_margins(void) 323{ 324 325 /* 326 * Four possibilities: 327 * 1) we have set_lr_margin and can set things with one call 328 * 2) we have set_{left,right}_margin_parm, use two calls 329 * 3) we have set_{left,right}_margin, set based on position 330 * 4) none of the above, leave things the way they are 331 */ 332 if (set_lr_margin) { 333 putp(tparm(set_lr_margin, 0, columns - 1)); 334 } else if (set_left_margin_parm && set_right_margin_parm) { 335 putp(tparm(set_left_margin_parm, 0)); 336 putp(tparm(set_right_margin_parm, columns - 1)); 337 } else if (set_left_margin && set_right_margin && clear_margins) { 338 putp(clear_margins); 339 340 /* go to column 0 and set the left margin */ 341 putp(carriage_return ? carriage_return : "\r"); 342 putp(set_left_margin); 343 344 /* go to last column and set the right margin */ 345 if (parm_right_cursor) 346 putp(tparm(parm_right_cursor, columns - 1)); 347 else 348 printf("%*s", columns - 1, " "); 349 putp(set_right_margin); 350 putp(carriage_return ? carriage_return : "\r"); 351 } 352 fflush(stdout); 353} 354 355static void 356usage(void) 357{ 358 359 if (strcmp(__progname, "clear") == 0) 360 (void)fprintf(stderr, "usage: %s [-T term]\n", __progname); 361 else 362 (void)fprintf(stderr, 363 "usage: %s [-T term] attribute [attribute-arg ...] ...\n" 364 " %s [-T term] -S\n", __progname, __progname); 365 exit(1); 366} 367