[BACK]Return to tput.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tput

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: }