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