Annotation of src/usr.bin/tput/tput.c, Revision 1.30
1.30 ! nicm 1: /* $OpenBSD: tput.c,v 1.29 2023/09/06 05:04:07 jsg Exp $ */
! 2:
! 3: /****************************************************************************
! 4: * Copyright 2018-2022,2023 Thomas E. Dickey *
! 5: * Copyright 1998-2016,2017 Free Software Foundation, Inc. *
! 6: * *
! 7: * Permission is hereby granted, free of charge, to any person obtaining a *
! 8: * copy of this software and associated documentation files (the *
! 9: * "Software"), to deal in the Software without restriction, including *
! 10: * without limitation the rights to use, copy, modify, merge, publish, *
! 11: * distribute, distribute with modifications, sublicense, and/or sell *
! 12: * copies of the Software, and to permit persons to whom the Software is *
! 13: * furnished to do so, subject to the following conditions: *
! 14: * *
! 15: * The above copyright notice and this permission notice shall be included *
! 16: * in all copies or substantial portions of the Software. *
! 17: * *
! 18: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
! 19: * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
! 20: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
! 21: * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
! 22: * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
! 23: * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
! 24: * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
! 25: * *
! 26: * Except as contained in this notice, the name(s) of the above copyright *
! 27: * holders shall not be used in advertising or otherwise to promote the *
! 28: * sale, use or other dealings in this Software without prior written *
! 29: * authorization. *
! 30: ****************************************************************************/
! 31:
! 32: /****************************************************************************
! 33: * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
! 34: * and: Eric S. Raymond <esr@snark.thyrsus.com> *
! 35: * and: Thomas E. Dickey 1996-on *
! 36: ****************************************************************************/
1.1 deraadt 37:
1.8 millert 38: /*
1.30 ! nicm 39: * tput.c -- shellscript access to terminal capabilities
1.8 millert 40: *
1.30 ! nicm 41: * by Eric S. Raymond <esr@snark.thyrsus.com>, portions based on code from
! 42: * Ross Ridge's mytinfo package.
1.1 deraadt 43: */
44:
1.30 ! nicm 45: #include <tparm_type.h>
! 46: #include <clear_cmd.h>
! 47: #include <reset_cmd.h>
! 48:
! 49: #include <transform.h>
! 50: #include <tty_settings.h>
! 51:
! 52:
1.8 millert 53:
1.30 ! nicm 54: #define PUTS(s) fputs(s, stdout)
1.1 deraadt 55:
1.30 ! nicm 56: const char *_nc_progname = "tput";
1.1 deraadt 57:
1.30 ! nicm 58: static bool opt_v = FALSE; /* quiet, do not show warnings */
! 59: static bool opt_x = FALSE; /* clear scrollback if possible */
1.4 gene 60:
1.30 ! nicm 61: static bool is_init = FALSE;
! 62: static bool is_reset = FALSE;
! 63: static bool is_clear = FALSE;
1.8 millert 64:
1.30 ! nicm 65: static GCC_NORETURN void
! 66: quit(int status, const char *fmt, ...)
! 67: {
! 68: va_list argp;
! 69:
! 70: va_start(argp, fmt);
! 71: fprintf(stderr, "%s: ", _nc_progname);
! 72: vfprintf(stderr, fmt, argp);
! 73: fprintf(stderr, "\n");
! 74: va_end(argp);
! 75: ExitProgram(status);
! 76: }
1.8 millert 77:
1.30 ! nicm 78: static GCC_NORETURN void
! 79: usage(const char *optstring)
! 80: {
! 81: #define KEEP(s) s "\n"
! 82: static const char msg[] =
! 83: {
! 84: KEEP("")
! 85: KEEP("Options:")
! 86: KEEP(" -S << read commands from standard input")
! 87: KEEP(" -T TERM use this instead of $TERM")
! 88: KEEP(" -V print curses-version")
! 89: KEEP(" -v verbose, show warnings")
! 90: KEEP(" -x do not try to clear scrollback")
! 91: KEEP("")
! 92: KEEP("Commands:")
! 93: KEEP(" clear clear the screen")
! 94: KEEP(" init initialize the terminal")
! 95: KEEP(" reset reinitialize the terminal")
! 96: KEEP(" capname unlike clear/init/reset, print value for capability \"capname\"")
! 97: };
! 98: #undef KEEP
! 99: (void) fprintf(stderr, "Usage: %s [options] [command]\n", _nc_progname);
! 100: if (optstring != NULL) {
! 101: const char *s = msg;
! 102: while (*s != '\0') {
! 103: fputc(UChar(*s), stderr);
! 104: if (!strncmp(s, " -", 3)) {
! 105: if (strchr(optstring, s[3]) == NULL)
! 106: s = strchr(s, '\n') + 1;
! 107: } else if (!strncmp(s, "\n\nC", 3))
! 108: break;
! 109: ++s;
1.1 deraadt 110: }
1.30 ! nicm 111: } else {
! 112: fputs(msg, stderr);
! 113: }
! 114: ExitProgram(ErrUsage);
1.1 deraadt 115: }
116:
1.30 ! nicm 117: static char *
! 118: check_aliases(char *name, bool program)
1.1 deraadt 119: {
1.30 ! nicm 120: static char my_init[] = "init";
! 121: static char my_reset[] = "reset";
! 122: static char my_clear[] = "clear";
! 123:
! 124: char *result = name;
! 125: if ((is_init = same_program(name, program ? PROG_INIT : my_init)))
! 126: result = my_init;
! 127: if ((is_reset = same_program(name, program ? PROG_RESET : my_reset)))
! 128: result = my_reset;
! 129: if ((is_clear = same_program(name, program ? PROG_CLEAR : my_clear)))
! 130: result = my_clear;
! 131: return result;
! 132: }
1.1 deraadt 133:
1.30 ! nicm 134: static int
! 135: exit_code(int token, int value)
! 136: {
! 137: int result = 99;
1.1 deraadt 138:
1.30 ! nicm 139: switch (token) {
! 140: case BOOLEAN:
! 141: result = !value; /* TRUE=0, FALSE=1 */
! 142: break;
! 143: case NUMBER:
! 144: result = 0; /* always zero */
! 145: break;
! 146: case STRING:
! 147: result = value; /* 0=normal, 1=missing */
! 148: break;
! 149: }
! 150: return result;
1.1 deraadt 151: }
152:
1.30 ! nicm 153: /*
! 154: * Returns nonzero on error.
! 155: */
! 156: static int
! 157: tput_cmd(int fd, TTY * settings, int argc, char **argv, int *used)
1.1 deraadt 158: {
1.30 ! nicm 159: NCURSES_CONST char *name;
! 160: char *s;
! 161: int status;
! 162: #if !PURE_TERMINFO
! 163: bool termcap = FALSE;
! 164: #endif
! 165:
! 166: name = check_aliases(argv[0], FALSE);
! 167: *used = 1;
! 168: if (is_reset || is_init) {
! 169: TTY oldmode = *settings;
! 170:
! 171: int terasechar = -1; /* new erase character */
! 172: int intrchar = -1; /* new interrupt character */
! 173: int tkillchar = -1; /* new kill character */
! 174:
! 175: if (is_reset) {
! 176: reset_start(stdout, TRUE, FALSE);
! 177: reset_tty_settings(fd, settings, FALSE);
! 178: } else {
! 179: reset_start(stdout, FALSE, TRUE);
! 180: }
! 181:
! 182: #if HAVE_SIZECHANGE
! 183: set_window_size(fd, &lines, &columns);
! 184: #else
! 185: (void) fd;
! 186: #endif
! 187: set_control_chars(settings, terasechar, intrchar, tkillchar);
! 188: set_conversions(settings);
! 189:
! 190: if (send_init_strings(fd, &oldmode)) {
! 191: reset_flush();
1.8 millert 192: }
1.30 ! nicm 193:
! 194: update_tty_settings(&oldmode, settings);
! 195: return 0;
! 196: }
! 197:
! 198: if (strcmp(name, "longname") == 0) {
! 199: PUTS(longname());
! 200: return 0;
! 201: }
! 202: #if !PURE_TERMINFO
! 203: retry:
! 204: #endif
! 205: if (strcmp(name, "clear") == 0) {
! 206: return (clear_cmd(opt_x) == ERR) ? ErrUsage : 0;
! 207: } else if ((status = tigetflag(name)) != -1) {
! 208: return exit_code(BOOLEAN, status);
! 209: } else if ((status = tigetnum(name)) != CANCELLED_NUMERIC) {
! 210: (void) printf("%d\n", status);
! 211: return exit_code(NUMBER, 0);
! 212: } else if ((s = tigetstr(name)) == CANCELLED_STRING) {
! 213: #if !PURE_TERMINFO
! 214: if (!termcap) {
! 215: const struct name_table_entry *np;
! 216:
! 217: termcap = TRUE;
! 218: if ((np = _nc_find_entry(name, _nc_get_hash_table(termcap))) != 0) {
! 219: switch (np->nte_type) {
! 220: case BOOLEAN:
! 221: name = boolnames[np->nte_index];
! 222: break;
! 223:
! 224: case NUMBER:
! 225: name = numnames[np->nte_index];
! 226: break;
! 227:
! 228: case STRING:
! 229: name = strnames[np->nte_index];
! 230: break;
1.9 millert 231: }
1.30 ! nicm 232: goto retry;
! 233: }
1.9 millert 234: }
1.30 ! nicm 235: #endif
! 236: quit(ErrCapName, "unknown terminfo capability '%s'", name);
! 237: } else if (VALID_STRING(s)) {
! 238: if (argc > 1) {
! 239: int k;
! 240: int narg;
! 241: int analyzed;
! 242: int provided;
! 243: int popcount;
! 244: long numbers[1 + NUM_PARM];
! 245: char *strings[1 + NUM_PARM];
! 246: char *p_is_s[NUM_PARM];
! 247: TParams paramType;
! 248:
! 249: /* Nasty hack time. The tparm function needs to see numeric
! 250: * parameters as numbers, not as pointers to their string
! 251: * representations
! 252: */
! 253:
! 254: for (k = 1; (k < argc) && (k <= NUM_PARM); k++) {
! 255: char *tmp = 0;
! 256: strings[k] = argv[k];
! 257: numbers[k] = strtol(argv[k], &tmp, 0);
! 258: if (tmp == 0 || *tmp != 0)
! 259: numbers[k] = 0;
! 260: }
! 261: for (k = argc; k <= NUM_PARM; k++) {
! 262: numbers[k] = 0;
! 263: strings[k] = 0;
! 264: }
! 265:
! 266: paramType = tparm_type(name);
! 267: #if NCURSES_XNAMES
! 268: /*
! 269: * If the capability is an extended one, analyze the string.
! 270: */
! 271: if (paramType == Numbers) {
! 272: struct name_table_entry const *entry_ptr;
! 273: entry_ptr = _nc_find_type_entry(name, STRING, FALSE);
! 274: if (entry_ptr == NULL) {
! 275: paramType = Other;
1.8 millert 276: }
1.30 ! nicm 277: }
! 278: #endif
! 279:
! 280: popcount = 0;
! 281: _nc_reset_tparm(NULL);
! 282: /*
! 283: * Count the number of numeric parameters which are provided.
! 284: */
! 285: provided = 0;
! 286: for (narg = 1; narg < argc; ++narg) {
! 287: char *ending = NULL;
! 288: long check = strtol(argv[narg], &ending, 10);
! 289: if (check < 0 || ending == argv[narg] || *ending != '\0')
! 290: break;
! 291: provided = narg;
! 292: }
! 293: switch (paramType) {
! 294: case Str:
! 295: s = TPARM_1(s, strings[1]);
! 296: analyzed = 1;
! 297: if (provided == 0 && argc >= 1)
! 298: provided++;
! 299: break;
! 300: case Str_Str:
! 301: s = TPARM_2(s, strings[1], strings[2]);
! 302: analyzed = 2;
! 303: if (provided == 0 && argc >= 1)
! 304: provided++;
! 305: if (provided == 1 && argc >= 2)
! 306: provided++;
! 307: break;
! 308: case Num_Str:
! 309: s = TPARM_2(s, numbers[1], strings[2]);
! 310: analyzed = 2;
! 311: if (provided == 1 && argc >= 2)
! 312: provided++;
! 313: break;
! 314: case Num_Str_Str:
! 315: s = TPARM_3(s, numbers[1], strings[2], strings[3]);
! 316: analyzed = 3;
! 317: if (provided == 1 && argc >= 2)
! 318: provided++;
! 319: if (provided == 2 && argc >= 3)
! 320: provided++;
! 321: break;
! 322: case Numbers:
! 323: analyzed = _nc_tparm_analyze(NULL, s, p_is_s, &popcount);
! 324: #define myParam(n) numbers[n]
! 325: s = TIPARM_9(s,
! 326: myParam(1),
! 327: myParam(2),
! 328: myParam(3),
! 329: myParam(4),
! 330: myParam(5),
! 331: myParam(6),
! 332: myParam(7),
! 333: myParam(8),
! 334: myParam(9));
! 335: #undef myParam
! 336: break;
! 337: case Other:
! 338: /* FALLTHRU */
! 339: default:
! 340: analyzed = _nc_tparm_analyze(NULL, s, p_is_s, &popcount);
! 341: #define myParam(n) (p_is_s[n - 1] != 0 ? ((TPARM_ARG) strings[n]) : numbers[n])
! 342: s = TPARM_9(s,
! 343: myParam(1),
! 344: myParam(2),
! 345: myParam(3),
! 346: myParam(4),
! 347: myParam(5),
! 348: myParam(6),
! 349: myParam(7),
! 350: myParam(8),
! 351: myParam(9));
! 352: #undef myParam
! 353: break;
! 354: }
! 355: if (analyzed < popcount) {
! 356: analyzed = popcount;
! 357: }
! 358: if (opt_v && (analyzed != provided)) {
! 359: fprintf(stderr, "%s: %s parameters for \"%s\"\n",
! 360: _nc_progname,
! 361: (analyzed < provided ? "extra" : "missing"),
! 362: argv[0]);
! 363: }
! 364: *used += provided;
1.8 millert 365: }
1.30 ! nicm 366:
! 367: /* use putp() in order to perform padding */
! 368: putp(s);
! 369: return exit_code(STRING, 0);
! 370: }
! 371: return exit_code(STRING, 1);
1.1 deraadt 372: }
373:
1.30 ! nicm 374: int
! 375: main(int argc, char **argv)
1.7 millert 376: {
1.30 ! nicm 377: char *term;
! 378: int errret;
! 379: bool cmdline = TRUE;
! 380: int c;
! 381: char buf[BUFSIZ];
! 382: int result = 0;
! 383: int fd;
! 384: int used;
! 385: TTY old_settings;
! 386: TTY tty_settings;
! 387: bool is_alias;
! 388: bool need_tty;
! 389:
! 390: if (pledge("stdio rpath wpath tty", NULL) == -1) {
! 391: perror("pledge");
! 392: exit(1);
! 393: }
! 394:
! 395: _nc_progname = check_aliases(_nc_rootname(argv[0]), TRUE);
! 396: is_alias = (is_clear || is_reset || is_init);
! 397:
! 398: term = getenv("TERM");
! 399:
! 400: while ((c = getopt(argc, argv, is_alias ? "T:Vvx" : "ST:Vvx")) != -1) {
! 401: switch (c) {
! 402: case 'S':
! 403: cmdline = FALSE;
! 404: break;
! 405: case 'T':
! 406: use_env(FALSE);
! 407: use_tioctl(TRUE);
! 408: term = optarg;
! 409: break;
! 410: case 'V':
! 411: puts(curses_version());
! 412: ExitProgram(EXIT_SUCCESS);
! 413: case 'v': /* verbose */
! 414: opt_v = TRUE;
! 415: break;
! 416: case 'x': /* do not try to clear scrollback */
! 417: opt_x = TRUE;
! 418: break;
! 419: default:
! 420: usage(is_alias ? "TVx" : NULL);
! 421: /* NOTREACHED */
! 422: }
! 423: }
! 424:
! 425: need_tty = ((is_reset || is_init) ||
! 426: (optind < argc &&
! 427: (!strcmp(argv[optind], "reset") ||
! 428: !strcmp(argv[optind], "init"))));
! 429:
! 430: /*
! 431: * Modify the argument list to omit the options we processed.
! 432: */
! 433: if (is_alias) {
! 434: if (optind-- < argc) {
! 435: argc -= optind;
! 436: argv += optind;
! 437: }
! 438: argv[0] = strdup(_nc_progname);
! 439: } else {
! 440: argc -= optind;
! 441: argv += optind;
! 442: }
! 443:
! 444: if (term == 0 || *term == '\0')
! 445: quit(ErrUsage, "No value for $TERM and no -T specified");
! 446:
! 447: fd = save_tty_settings(&tty_settings, need_tty);
! 448: old_settings = tty_settings;
! 449:
! 450: if (setupterm(term, fd, &errret) != OK && errret <= 0)
! 451: quit(ErrTermType, "unknown terminal \"%s\"", term);
! 452:
! 453: if (cmdline) {
! 454: int code = 0;
! 455: if ((argc <= 0) && !is_alias)
! 456: usage(NULL);
! 457: while (argc > 0) {
! 458: tty_settings = old_settings;
! 459: code = tput_cmd(fd, &tty_settings, argc, argv, &used);
! 460: if (code != 0)
! 461: break;
! 462: argc -= used;
! 463: argv += used;
1.8 millert 464: }
1.30 ! nicm 465: ExitProgram(code);
! 466: }
! 467:
! 468: while (fgets(buf, sizeof(buf), stdin) != 0) {
! 469: size_t need = strlen(buf);
! 470: char **argvec = typeCalloc(char *, need + 1);
! 471: char **argnow;
! 472: int argnum = 0;
! 473: char *cp;
1.9 millert 474:
1.30 ! nicm 475: if (argvec == NULL) {
! 476: quit(ErrSystem(1), strerror(errno));
! 477: }
1.9 millert 478:
1.30 ! nicm 479: /* split the buffer into tokens */
! 480: for (cp = buf; *cp; cp++) {
! 481: if (isspace(UChar(*cp))) {
! 482: *cp = '\0';
! 483: } else if (cp == buf || cp[-1] == '\0') {
! 484: argvec[argnum++] = cp;
! 485: if (argnum >= (int) need)
! 486: break;
! 487: }
1.9 millert 488: }
1.7 millert 489:
1.30 ! nicm 490: argnow = argvec;
! 491: while (argnum > 0) {
! 492: int code;
! 493: tty_settings = old_settings;
! 494: code = tput_cmd(fd, &tty_settings, argnum, argnow, &used);
! 495: if (code != 0) {
! 496: if (result == 0)
! 497: result = ErrSystem(0); /* will return value >4 */
! 498: ++result;
! 499: }
! 500: argnum -= used;
! 501: argnow += used;
! 502: }
! 503: free(argvec);
! 504: }
1.8 millert 505:
1.30 ! nicm 506: ExitProgram(result);
1.1 deraadt 507: }