ncurses 6.2 - patch 20210418
[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.90 2021/04/18 17:18:19 tom Exp $")
51
52 #define PUTS(s)         fputs(s, stdout)
53
54 const char *_nc_progname = "tput";
55
56 static char *prg_name;
57 static bool is_init = FALSE;
58 static bool is_reset = FALSE;
59 static bool is_clear = FALSE;
60
61 static GCC_NORETURN void
62 quit(int status, const char *fmt, ...)
63 {
64     va_list argp;
65
66     va_start(argp, fmt);
67     fprintf(stderr, "%s: ", prg_name);
68     vfprintf(stderr, fmt, argp);
69     fprintf(stderr, "\n");
70     va_end(argp);
71     ExitProgram(status);
72 }
73
74 static GCC_NORETURN void
75 usage(void)
76 {
77 #define KEEP(s) s "\n"
78     static const char msg[] =
79     {
80         KEEP("")
81         KEEP("Options:")
82         KEEP("  -S <<       read commands from standard input")
83         KEEP("  -T TERM     use this instead of $TERM")
84         KEEP("  -V          print curses-version")
85         KEEP("  -x          do not try to clear scrollback")
86         KEEP("")
87         KEEP("Commands:")
88         KEEP("  clear       clear the screen")
89         KEEP("  init        initialize the terminal")
90         KEEP("  reset       reinitialize the terminal")
91         KEEP("  capname     unlike clear/init/reset, print value for capability \"capname\"")
92     };
93 #undef KEEP
94     (void) fprintf(stderr, "Usage: %s [options] [command]\n", prg_name);
95     fputs(msg, stderr);
96     ExitProgram(ErrUsage);
97 }
98
99 static char *
100 check_aliases(char *name, bool program)
101 {
102     static char my_init[] = "init";
103     static char my_reset[] = "reset";
104     static char my_clear[] = "clear";
105
106     char *result = name;
107     if ((is_init = same_program(name, program ? PROG_INIT : my_init)))
108         result = my_init;
109     if ((is_reset = same_program(name, program ? PROG_RESET : my_reset)))
110         result = my_reset;
111     if ((is_clear = same_program(name, program ? PROG_CLEAR : my_clear)))
112         result = my_clear;
113     return result;
114 }
115
116 static int
117 exit_code(int token, int value)
118 {
119     int result = 99;
120
121     switch (token) {
122     case BOOLEAN:
123         result = !value;        /* TRUE=0, FALSE=1 */
124         break;
125     case NUMBER:
126         result = 0;             /* always zero */
127         break;
128     case STRING:
129         result = value;         /* 0=normal, 1=missing */
130         break;
131     }
132     return result;
133 }
134
135 /*
136  * Returns nonzero on error.
137  */
138 static int
139 tput_cmd(int fd, TTY * saved_settings, bool opt_x, int argc, char *argv[])
140 {
141     NCURSES_CONST char *name;
142     char *s;
143     int status;
144 #if !PURE_TERMINFO
145     bool termcap = FALSE;
146 #endif
147
148     name = check_aliases(argv[0], FALSE);
149     if (is_reset || is_init) {
150         TTY oldmode;
151
152         int terasechar = -1;    /* new erase character */
153         int intrchar = -1;      /* new interrupt character */
154         int tkillchar = -1;     /* new kill character */
155
156         if (is_reset) {
157             reset_start(stdout, TRUE, FALSE);
158             reset_tty_settings(fd, saved_settings);
159         } else {
160             reset_start(stdout, FALSE, TRUE);
161         }
162
163 #if HAVE_SIZECHANGE
164         set_window_size(fd, &lines, &columns);
165 #else
166         (void) fd;
167 #endif
168         set_control_chars(saved_settings, terasechar, intrchar, tkillchar);
169         set_conversions(saved_settings);
170         if (send_init_strings(fd, &oldmode)) {
171             reset_flush();
172         }
173
174         update_tty_settings(&oldmode, saved_settings);
175         return 0;
176     }
177
178     if (strcmp(name, "longname") == 0) {
179         PUTS(longname());
180         return 0;
181     }
182 #if !PURE_TERMINFO
183   retry:
184 #endif
185     if (strcmp(name, "clear") == 0) {
186         return (clear_cmd(opt_x) == ERR) ? ErrUsage : 0;
187     } else if ((status = tigetflag(name)) != -1) {
188         return exit_code(BOOLEAN, status);
189     } else if ((status = tigetnum(name)) != CANCELLED_NUMERIC) {
190         (void) printf("%d\n", status);
191         return exit_code(NUMBER, 0);
192     } else if ((s = tigetstr(name)) == CANCELLED_STRING) {
193 #if !PURE_TERMINFO
194         if (!termcap) {
195             const struct name_table_entry *np;
196
197             termcap = TRUE;
198             if ((np = _nc_find_entry(name, _nc_get_hash_table(termcap))) != 0) {
199                 switch (np->nte_type) {
200                 case BOOLEAN:
201                     name = boolnames[np->nte_index];
202                     break;
203
204                 case NUMBER:
205                     name = numnames[np->nte_index];
206                     break;
207
208                 case STRING:
209                     name = strnames[np->nte_index];
210                     break;
211                 }
212                 goto retry;
213             }
214         }
215 #endif
216         quit(ErrCapName, "unknown terminfo capability '%s'", name);
217     } else if (VALID_STRING(s)) {
218         if (argc > 1) {
219             int k;
220             int ignored;
221             long numbers[1 + NUM_PARM];
222             char *strings[1 + NUM_PARM];
223             char *p_is_s[NUM_PARM];
224             TParams paramType;
225
226             /* Nasty hack time. The tparm function needs to see numeric
227              * parameters as numbers, not as pointers to their string
228              * representations
229              */
230
231             for (k = 1; (k < argc) && (k <= NUM_PARM); k++) {
232                 char *tmp = 0;
233                 strings[k] = argv[k];
234                 numbers[k] = strtol(argv[k], &tmp, 0);
235                 if (tmp == 0 || *tmp != 0)
236                     numbers[k] = 0;
237             }
238             for (k = argc; k <= NUM_PARM; k++) {
239                 numbers[k] = 0;
240                 strings[k] = 0;
241             }
242
243             paramType = tparm_type(name);
244 #if NCURSES_XNAMES
245             /*
246              * If the capability is an extended one, analyze the string.
247              */
248             if (paramType == Numbers) {
249                 struct name_table_entry const *entry_ptr;
250                 entry_ptr = _nc_find_type_entry(name, STRING, FALSE);
251                 if (entry_ptr == NULL) {
252                     paramType = Other;
253                 }
254             }
255 #endif
256
257             switch (paramType) {
258             case Num_Str:
259                 s = TPARM_2(s, numbers[1], strings[2]);
260                 break;
261             case Num_Str_Str:
262                 s = TPARM_3(s, numbers[1], strings[2], strings[3]);
263                 break;
264             case Numbers:
265 #define myParam(n) numbers[n]
266                 s = TIPARM_9(s,
267                              myParam(1),
268                              myParam(2),
269                              myParam(3),
270                              myParam(4),
271                              myParam(5),
272                              myParam(6),
273                              myParam(7),
274                              myParam(8),
275                              myParam(9));
276 #undef myParam
277                 break;
278             case Other:
279                 /* FALLTHRU */
280             default:
281                 (void) _nc_tparm_analyze(s, p_is_s, &ignored);
282 #define myParam(n) (p_is_s[n - 1] != 0 ? ((TPARM_ARG) strings[n]) : numbers[n])
283                 s = TPARM_9(s,
284                             myParam(1),
285                             myParam(2),
286                             myParam(3),
287                             myParam(4),
288                             myParam(5),
289                             myParam(6),
290                             myParam(7),
291                             myParam(8),
292                             myParam(9));
293 #undef myParam
294                 break;
295             }
296         }
297
298         /* use putp() in order to perform padding */
299         putp(s);
300         return exit_code(STRING, 0);
301     }
302     return exit_code(STRING, 1);
303 }
304
305 int
306 main(int argc, char **argv)
307 {
308     char *term;
309     int errret;
310     bool cmdline = TRUE;
311     int c;
312     char buf[BUFSIZ];
313     int result = 0;
314     int fd;
315     TTY tty_settings;
316     bool opt_x = FALSE;         /* clear scrollback if possible */
317     bool is_alias;
318     bool need_tty;
319
320     prg_name = check_aliases(_nc_rootname(argv[0]), TRUE);
321
322     term = getenv("TERM");
323
324     while ((c = getopt(argc, argv, "ST:Vx")) != -1) {
325         switch (c) {
326         case 'S':
327             cmdline = FALSE;
328             break;
329         case 'T':
330             use_env(FALSE);
331             use_tioctl(TRUE);
332             term = optarg;
333             break;
334         case 'V':
335             puts(curses_version());
336             ExitProgram(EXIT_SUCCESS);
337         case 'x':               /* do not try to clear scrollback */
338             opt_x = TRUE;
339             break;
340         default:
341             usage();
342             /* NOTREACHED */
343         }
344     }
345
346     is_alias = (is_clear || is_reset || is_init);
347     need_tty = ((is_reset || is_init) ||
348                 (optind < argc &&
349                  (!strcmp(argv[optind], "reset") ||
350                   !strcmp(argv[optind], "init"))));
351
352     /*
353      * Modify the argument list to omit the options we processed.
354      */
355     if (is_alias) {
356         if (optind-- < argc) {
357             argc -= optind;
358             argv += optind;
359         }
360         argv[0] = prg_name;
361     } else {
362         argc -= optind;
363         argv += optind;
364     }
365
366     if (term == 0 || *term == '\0')
367         quit(ErrUsage, "No value for $TERM and no -T specified");
368
369     fd = save_tty_settings(&tty_settings, need_tty);
370
371     if (setupterm(term, fd, &errret) != OK && errret <= 0)
372         quit(ErrTermType, "unknown terminal \"%s\"", term);
373
374     if (cmdline) {
375         if ((argc <= 0) && !is_alias)
376             usage();
377         ExitProgram(tput_cmd(fd, &tty_settings, opt_x, argc, argv));
378     }
379
380     while (fgets(buf, sizeof(buf), stdin) != 0) {
381         char *argvec[16];       /* command, 9 parms, null, & slop */
382         int argnum = 0;
383         char *cp;
384
385         /* crack the argument list into a dope vector */
386         for (cp = buf; *cp; cp++) {
387             if (isspace(UChar(*cp))) {
388                 *cp = '\0';
389             } else if (cp == buf || cp[-1] == 0) {
390                 argvec[argnum++] = cp;
391                 if (argnum >= (int) SIZEOF(argvec) - 1)
392                     break;
393             }
394         }
395         argvec[argnum] = 0;
396
397         if (argnum != 0
398             && tput_cmd(fd, &tty_settings, opt_x, argnum, argvec) != 0) {
399             if (result == 0)
400                 result = ErrSystem(0);  /* will return value >4 */
401             ++result;
402         }
403     }
404
405     ExitProgram(result);
406 }