]> ncurses.scripts.mit.edu Git - ncurses.git/blob - progs/tput.c
ncurses 6.3 - patch 20220813
[ncurses.git] / progs / tput.c
1 /****************************************************************************
2  * Copyright 2018-2021,2022 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.99 2022/02/26 23:19:31 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 = *settings;
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
184         if (send_init_strings(fd, &oldmode)) {
185             reset_flush();
186         }
187
188         update_tty_settings(&oldmode, settings);
189         return 0;
190     }
191
192     if (strcmp(name, "longname") == 0) {
193         PUTS(longname());
194         return 0;
195     }
196 #if !PURE_TERMINFO
197   retry:
198 #endif
199     if (strcmp(name, "clear") == 0) {
200         return (clear_cmd(opt_x) == ERR) ? ErrUsage : 0;
201     } else if ((status = tigetflag(name)) != -1) {
202         return exit_code(BOOLEAN, status);
203     } else if ((status = tigetnum(name)) != CANCELLED_NUMERIC) {
204         (void) printf("%d\n", status);
205         return exit_code(NUMBER, 0);
206     } else if ((s = tigetstr(name)) == CANCELLED_STRING) {
207 #if !PURE_TERMINFO
208         if (!termcap) {
209             const struct name_table_entry *np;
210
211             termcap = TRUE;
212             if ((np = _nc_find_entry(name, _nc_get_hash_table(termcap))) != 0) {
213                 switch (np->nte_type) {
214                 case BOOLEAN:
215                     name = boolnames[np->nte_index];
216                     break;
217
218                 case NUMBER:
219                     name = numnames[np->nte_index];
220                     break;
221
222                 case STRING:
223                     name = strnames[np->nte_index];
224                     break;
225                 }
226                 goto retry;
227             }
228         }
229 #endif
230         quit(ErrCapName, "unknown terminfo capability '%s'", name);
231     } else if (VALID_STRING(s)) {
232         if (argc > 1) {
233             int k;
234             int analyzed;
235             int popcount;
236             long numbers[1 + NUM_PARM];
237             char *strings[1 + NUM_PARM];
238             char *p_is_s[NUM_PARM];
239             TParams paramType;
240
241             /* Nasty hack time. The tparm function needs to see numeric
242              * parameters as numbers, not as pointers to their string
243              * representations
244              */
245
246             for (k = 1; (k < argc) && (k <= NUM_PARM); k++) {
247                 char *tmp = 0;
248                 strings[k] = argv[k];
249                 numbers[k] = strtol(argv[k], &tmp, 0);
250                 if (tmp == 0 || *tmp != 0)
251                     numbers[k] = 0;
252             }
253             for (k = argc; k <= NUM_PARM; k++) {
254                 numbers[k] = 0;
255                 strings[k] = 0;
256             }
257
258             paramType = tparm_type(name);
259 #if NCURSES_XNAMES
260             /*
261              * If the capability is an extended one, analyze the string.
262              */
263             if (paramType == Numbers) {
264                 struct name_table_entry const *entry_ptr;
265                 entry_ptr = _nc_find_type_entry(name, STRING, FALSE);
266                 if (entry_ptr == NULL) {
267                     paramType = Other;
268                 }
269             }
270 #endif
271
272             popcount = 0;
273             _nc_reset_tparm(NULL);
274             switch (paramType) {
275             case Num_Str:
276                 s = TPARM_2(s, numbers[1], strings[2]);
277                 analyzed = 2;
278                 break;
279             case Num_Str_Str:
280                 s = TPARM_3(s, numbers[1], strings[2], strings[3]);
281                 analyzed = 3;
282                 break;
283             case Numbers:
284                 analyzed = _nc_tparm_analyze(NULL, s, p_is_s, &popcount);
285 #define myParam(n) numbers[n]
286                 s = TIPARM_9(s,
287                              myParam(1),
288                              myParam(2),
289                              myParam(3),
290                              myParam(4),
291                              myParam(5),
292                              myParam(6),
293                              myParam(7),
294                              myParam(8),
295                              myParam(9));
296 #undef myParam
297                 break;
298             case Other:
299                 /* FALLTHRU */
300             default:
301                 analyzed = _nc_tparm_analyze(NULL, s, p_is_s, &popcount);
302 #define myParam(n) (p_is_s[n - 1] != 0 ? ((TPARM_ARG) strings[n]) : numbers[n])
303                 s = TPARM_9(s,
304                             myParam(1),
305                             myParam(2),
306                             myParam(3),
307                             myParam(4),
308                             myParam(5),
309                             myParam(6),
310                             myParam(7),
311                             myParam(8),
312                             myParam(9));
313 #undef myParam
314                 break;
315             }
316             if (analyzed < popcount) {
317                 analyzed = popcount;
318             }
319             *used += analyzed;
320         }
321
322         /* use putp() in order to perform padding */
323         putp(s);
324         return exit_code(STRING, 0);
325     }
326     return exit_code(STRING, 1);
327 }
328
329 int
330 main(int argc, char **argv)
331 {
332     char *term;
333     int errret;
334     bool cmdline = TRUE;
335     int c;
336     char buf[BUFSIZ];
337     int result = 0;
338     int fd;
339     int used;
340     TTY old_settings;
341     TTY tty_settings;
342     bool opt_x = FALSE;         /* clear scrollback if possible */
343     bool is_alias;
344     bool need_tty;
345
346     _nc_progname = check_aliases(_nc_rootname(argv[0]), TRUE);
347     is_alias = (is_clear || is_reset || is_init);
348
349     term = getenv("TERM");
350
351     while ((c = getopt(argc, argv, is_alias ? "T:Vx" : "ST:Vx")) != -1) {
352         switch (c) {
353         case 'S':
354             cmdline = FALSE;
355             break;
356         case 'T':
357             use_env(FALSE);
358             use_tioctl(TRUE);
359             term = optarg;
360             break;
361         case 'V':
362             puts(curses_version());
363             ExitProgram(EXIT_SUCCESS);
364         case 'x':               /* do not try to clear scrollback */
365             opt_x = TRUE;
366             break;
367         default:
368             usage(is_alias ? "TVx" : NULL);
369             /* NOTREACHED */
370         }
371     }
372
373     need_tty = ((is_reset || is_init) ||
374                 (optind < argc &&
375                  (!strcmp(argv[optind], "reset") ||
376                   !strcmp(argv[optind], "init"))));
377
378     /*
379      * Modify the argument list to omit the options we processed.
380      */
381     if (is_alias) {
382         if (optind-- < argc) {
383             argc -= optind;
384             argv += optind;
385         }
386         argv[0] = strdup(_nc_progname);
387     } else {
388         argc -= optind;
389         argv += optind;
390     }
391
392     if (term == 0 || *term == '\0')
393         quit(ErrUsage, "No value for $TERM and no -T specified");
394
395     fd = save_tty_settings(&tty_settings, need_tty);
396     old_settings = tty_settings;
397
398     if (setupterm(term, fd, &errret) != OK && errret <= 0)
399         quit(ErrTermType, "unknown terminal \"%s\"", term);
400
401     if (cmdline) {
402         int code = 0;
403         if ((argc <= 0) && !is_alias)
404             usage(NULL);
405         while (argc > 0) {
406             tty_settings = old_settings;
407             code = tput_cmd(fd, &tty_settings, opt_x, argc, argv, &used);
408             if (code != 0)
409                 break;
410             argc -= used;
411             argv += used;
412         }
413         ExitProgram(code);
414     }
415
416     while (fgets(buf, sizeof(buf), stdin) != 0) {
417         size_t need = strlen(buf);
418         char **argvec = typeCalloc(char *, need + 1);
419         char **argnow;
420         int argnum = 0;
421         char *cp;
422
423         if (argvec == NULL) {
424             quit(ErrSystem(1), strerror(errno));
425         }
426
427         /* split the buffer into tokens */
428         for (cp = buf; *cp; cp++) {
429             if (isspace(UChar(*cp))) {
430                 *cp = '\0';
431             } else if (cp == buf || cp[-1] == '\0') {
432                 argvec[argnum++] = cp;
433                 if (argnum >= (int) need)
434                     break;
435             }
436         }
437
438         argnow = argvec;
439         while (argnum > 0) {
440             int code;
441             tty_settings = old_settings;
442             code = tput_cmd(fd, &tty_settings, opt_x, argnum, argnow, &used);
443             if (code != 0) {
444                 if (result == 0)
445                     result = ErrSystem(0);      /* will return value >4 */
446                 ++result;
447             }
448             argnum -= used;
449             argnow += used;
450         }
451         free(argvec);
452     }
453
454     ExitProgram(result);
455 }