]> git.ipfire.org Git - thirdparty/util-linux.git/blob - term-utils/setterm.c
lib/fileutils: add close_all_fds()
[thirdparty/util-linux.git] / term-utils / setterm.c
1 /* setterm.c, set terminal attributes.
2 *
3 * Copyright (C) 1990 Gordon Irlam (gordoni@cs.ua.oz.au). Conditions of use,
4 * modification, and redistribution are contained in the file COPYRIGHT that
5 * forms part of this distribution.
6 *
7 * Adaption to Linux by Peter MacDonald.
8 *
9 * Enhancements by Mika Liljeberg (liljeber@cs.Helsinki.FI)
10 *
11 * Beep modifications by Christophe Jolif (cjolif@storm.gatelink.fr.net)
12 *
13 * Sanity increases by Cafeine Addict [sic].
14 *
15 * Powersave features by todd j. derr <tjd@wordsmith.org>
16 *
17 * Converted to terminfo by Kars de Jong (jongk@cs.utwente.nl)
18 *
19 * 1999-02-22 Arkadiusz Miƛkiewicz <misiek@pld.ORG.PL>
20 * - added Native Language Support
21 *
22 * Semantics:
23 *
24 * Setterm writes to standard output a character string that will
25 * invoke the specified terminal capabilities. Where possible
26 * terminfo is consulted to find the string to use. Some options
27 * however do not correspond to a terminfo capability. In this case if
28 * the terminal type is "con*", or "linux*" the string that invokes
29 * the specified capabilities on the PC Linux virtual console driver
30 * is output. Options that are not implemented by the terminal are
31 * ignored.
32 *
33 * The following options are non-obvious.
34 *
35 * -term can be used to override the TERM environment variable.
36 *
37 * -reset displays the terminal reset string, which typically resets the
38 * terminal to its power on state.
39 *
40 * -initialize displays the terminal initialization string, which typically
41 * sets the terminal's rendering options, and other attributes to the
42 * default values.
43 *
44 * -default sets the terminal's rendering options to the default values.
45 *
46 * -store stores the terminal's current rendering options as the default
47 * values. */
48
49 #include <ctype.h>
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <getopt.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <sys/ioctl.h>
57 #include <sys/klog.h>
58 #include <sys/param.h> /* for MAXPATHLEN */
59 #include <sys/time.h>
60 #include <termios.h>
61 #include <unistd.h>
62
63 #if defined(HAVE_NCURSESW_TERM_H)
64 # include <ncursesw/term.h>
65 #elif defined(HAVE_NCURSES_TERM_H)
66 # include <ncurses/term.h>
67 #elif defined(HAVE_TERM_H)
68 # include <term.h>
69 #endif
70
71 #ifdef HAVE_LINUX_TIOCL_H
72 # include <linux/tiocl.h>
73 #endif
74
75 #include "all-io.h"
76 #include "c.h"
77 #include "closestream.h"
78 #include "nls.h"
79 #include "optutils.h"
80 #include "strutils.h"
81 #include "xalloc.h"
82
83 /* Constants. */
84
85 /* Non-standard return values. */
86 #define EXIT_DUMPFILE -1
87
88 /* Colors. */
89 enum {
90 BLACK = 0,
91 RED,
92 GREEN,
93 YELLOW,
94 BLUE,
95 MAGENTA,
96 CYAN,
97 WHITE,
98 GREY,
99 DEFAULT
100 };
101
102 static const char *colornames[] = {
103 [BLACK] = "black",
104 [RED] = "red",
105 [GREEN] = "green",
106 [YELLOW]= "yellow",
107 [BLUE] = "blue",
108 [MAGENTA]="magenta",
109 [CYAN] = "cyan",
110 [WHITE] = "white",
111 [GREY] = "grey",
112 [DEFAULT] = "default"
113 };
114
115 #define is_valid_color(x) (x >= 0 && (size_t) x < ARRAY_SIZE(colornames))
116
117 /* Blank commands */
118 enum {
119 BLANKSCREEN = -1,
120 UNBLANKSCREEN = -2,
121 BLANKEDSCREEN = -3
122 };
123
124 /* <linux/tiocl.h> fallback */
125 #ifndef TIOCL_BLANKSCREEN
126 enum {
127 TIOCL_UNBLANKSCREEN = 4, /* unblank screen */
128 TIOCL_SETVESABLANK = 10, /* set vesa blanking mode */
129 TIOCL_BLANKSCREEN = 14, /* keep screen blank even if a key is pressed */
130 TIOCL_BLANKEDSCREEN = 15 /* return which vt was blanked */
131 };
132 #endif
133
134 /* Powersave modes */
135 enum {
136 VESA_BLANK_MODE_OFF = 0,
137 VESA_BLANK_MODE_SUSPENDV,
138 VESA_BLANK_MODE_SUSPENDH,
139 VESA_BLANK_MODE_POWERDOWN
140 };
141
142 /* klogctl() actions */
143 enum {
144 SYSLOG_ACTION_CONSOLE_OFF = 6,
145 SYSLOG_ACTION_CONSOLE_ON = 7,
146 SYSLOG_ACTION_CONSOLE_LEVEL = 8
147 };
148
149 /* Console log levels */
150 enum {
151 CONSOLE_LEVEL_MIN = 0,
152 CONSOLE_LEVEL_MAX = 8
153 };
154
155 /* Various numbers */
156 #define DEFAULT_TAB_LEN 8
157 #define BLANK_MAX 60
158 #define TABS_MAX 160
159 #define BLENGTH_MAX 2000
160
161 /* Command controls. */
162 struct setterm_control {
163 char *opt_te_terminal_name; /* terminal name */
164 int opt_bl_min; /* blank screen */
165 int opt_blength_l; /* bell duration in milliseconds */
166 int opt_bfreq_f; /* bell frequency in Hz */
167 int opt_sn_num; /* console number to be snapshot */
168 char *opt_sn_name; /* path to write snap */
169 char *in_device; /* device to snapshot */
170 int opt_msglevel_num; /* printk() logging level */
171 int opt_ps_mode; /* powersave mode */
172 int opt_pd_min; /* powerdown time */
173 int opt_rt_len; /* regular tab length */
174 int opt_tb_array[TABS_MAX + 1]; /* array for tab list */
175 /* colors */
176 unsigned int opt_fo_color:4, opt_ba_color:4, opt_ul_color:4, opt_hb_color:4;
177 /* boolean options */
178 unsigned int opt_cu_on:1, opt_li_on:1, opt_bo_on:1, opt_hb_on:1,
179 opt_bl_on:1, opt_re_on:1, opt_un_on:1, opt_rep_on:1,
180 opt_appck_on:1, opt_invsc_on:1, opt_msg_on:1, opt_cl_all:1,
181 vcterm:1;
182 /* Option flags. Set when an option is invoked. */
183 uint64_t opt_term:1, opt_reset:1, opt_resize:1, opt_initialize:1, opt_cursor:1,
184 opt_linewrap:1, opt_default:1, opt_foreground:1,
185 opt_background:1, opt_bold:1, opt_blink:1, opt_reverse:1,
186 opt_underline:1, opt_store:1, opt_clear:1, opt_blank:1,
187 opt_snap:1, opt_snapfile:1, opt_append:1, opt_ulcolor:1,
188 opt_hbcolor:1, opt_halfbright:1, opt_repeat:1, opt_tabs:1,
189 opt_clrtabs:1, opt_regtabs:1, opt_appcursorkeys:1,
190 opt_inversescreen:1, opt_msg:1, opt_msglevel:1, opt_powersave:1,
191 opt_powerdown:1, opt_blength:1, opt_bfreq:1;
192 };
193
194 static int parse_color(const char *arg)
195 {
196 size_t i;
197
198 for (i = 0; i < ARRAY_SIZE(colornames); i++) {
199 if (strcmp(colornames[i], arg) == 0)
200 return i;
201 }
202
203 return -EINVAL;
204 }
205
206 static int parse_febg_color(const char *arg)
207 {
208 int color = parse_color(arg);
209
210 if (color < 0)
211 color = strtos32_or_err(arg, _("argument error"));
212
213 if (!is_valid_color(color) || color == GREY)
214 errx(EXIT_FAILURE, "%s: %s", _("argument error"), arg);
215 return color;
216 }
217
218 static int parse_ulhb_color(char **av, int *oi)
219 {
220 char *color_name;
221 int bright = 0;
222 int color = -1;
223
224 if (av[*oi] && strcmp(av[*oi - 1], "bright") == 0) {
225 bright = 1;
226 color_name = av[*oi];
227 (*oi)++;
228 } else
229 color_name = av[*oi - 1];
230
231 color = parse_color(color_name);
232 if (color < 0)
233 color = strtos32_or_err(color_name, _("argument error"));
234 if (!is_valid_color(color) || color == DEFAULT)
235 errx(EXIT_FAILURE, "%s: %s", _("argument error"), color_name);
236 if (bright && (color == BLACK || color == GREY))
237 errx(EXIT_FAILURE, _("argument error: bright %s is not supported"), color_name);
238
239 if (bright)
240 color |= 8;
241
242 return color;
243 }
244
245 static char *find_optional_arg(char **av, char *oa, int *oi)
246 {
247 char *arg;
248 if (oa)
249 return oa;
250 else {
251 arg = av[*oi];
252 if (!arg || arg[0] == '-')
253 return NULL;
254 }
255 (*oi)++;
256 return arg;
257 }
258
259 static int parse_blank(char **av, char *oa, int *oi)
260 {
261 char *arg;
262
263 arg = find_optional_arg(av, oa, oi);
264 if (!arg)
265 return BLANKEDSCREEN;
266 if (!strcmp(arg, "force"))
267 return BLANKSCREEN;
268 else if (!strcmp(arg, "poke"))
269 return UNBLANKSCREEN;
270 else {
271 int ret;
272
273 ret = strtos32_or_err(arg, _("argument error"));
274 if (ret < 0 || BLANK_MAX < ret)
275 errx(EXIT_FAILURE, "%s: %s", _("argument error"), arg);
276 return ret;
277 }
278 }
279
280 static int parse_powersave(const char *arg)
281 {
282 if (strcmp(arg, "on") == 0)
283 return VESA_BLANK_MODE_SUSPENDV;
284 else if (strcmp(arg, "vsync") == 0)
285 return VESA_BLANK_MODE_SUSPENDV;
286 else if (strcmp(arg, "hsync") == 0)
287 return VESA_BLANK_MODE_SUSPENDH;
288 else if (strcmp(arg, "powerdown") == 0)
289 return VESA_BLANK_MODE_POWERDOWN;
290 else if (strcmp(arg, "off") == 0)
291 return VESA_BLANK_MODE_OFF;
292 errx(EXIT_FAILURE, "%s: %s", _("argument error"), arg);
293 }
294
295 static int parse_msglevel(const char *arg)
296 {
297 int ret;
298
299 ret = strtos32_or_err(arg, _("argument error"));
300 if (ret < CONSOLE_LEVEL_MIN || CONSOLE_LEVEL_MAX < ret)
301 errx(EXIT_FAILURE, "%s: %s", _("argument error"), arg);
302 return ret;
303 }
304
305 static int parse_snap(char **av, char *oa, int *oi)
306 {
307 int ret;
308 char *arg;
309
310 arg = find_optional_arg(av, oa, oi);
311 if (!arg)
312 return 0;
313 ret = strtos32_or_err(arg, _("argument error"));
314 if (ret < 1)
315 errx(EXIT_FAILURE, "%s: %s", _("argument error"), arg);
316 return ret;
317 }
318
319 static void parse_tabs(char **av, char *oa, int *oi, int *tab_array)
320 {
321 int i = 0;
322
323 if (oa) {
324 tab_array[i] = strtos32_or_err(oa, _("argument error"));
325 i++;
326 }
327 while (av[*oi]) {
328 if (TABS_MAX < i)
329 errx(EXIT_FAILURE, _("too many tabs"));
330 if (av[*oi][0] == '-')
331 break;
332 tab_array[i] = strtos32_or_err(av[*oi], _("argument error"));
333 (*oi)++;
334 i++;
335 }
336 tab_array[i] = -1;
337 }
338
339 static int parse_regtabs(char **av, char *oa, int *oi)
340 {
341 int ret;
342 char *arg;
343
344 arg = find_optional_arg(av, oa, oi);
345 if (!arg)
346 return DEFAULT_TAB_LEN;
347 ret = strtos32_or_err(arg, _("argument error"));
348 if (ret < 1 || TABS_MAX < ret)
349 errx(EXIT_FAILURE, "%s: %s", _("argument error"), arg);
350 return ret;
351 }
352
353 static int parse_blength(char **av, char *oa, int *oi)
354 {
355 int ret = -1;
356 char *arg;
357
358 arg = find_optional_arg(av, oa, oi);
359 if (!arg)
360 return 0;
361 ret = strtos32_or_err(arg, _("argument error"));
362 if (ret < 0 || BLENGTH_MAX < ret)
363 errx(EXIT_FAILURE, "%s: %s", _("argument error"), arg);
364 return ret;
365 }
366
367 static int parse_bfreq(char **av, char *oa, int *oi)
368 {
369 char *arg;
370
371 arg = find_optional_arg(av, oa, oi);
372 if (!arg)
373 return 0;
374 return strtos32_or_err(arg, _("argument error"));
375 }
376
377 static void __attribute__((__noreturn__)) usage(void)
378 {
379 FILE *out = stdout;
380 fputs(USAGE_HEADER, out);
381 fprintf(out,
382 _(" %s [options]\n"), program_invocation_short_name);
383
384 fputs(USAGE_SEPARATOR, out);
385 fputs(_("Set the attributes of a terminal.\n"), out);
386
387 fputs(USAGE_OPTIONS, out);
388 fputs(_(" --term <terminal_name> override TERM environment variable\n"), out);
389 fputs(_(" --reset reset terminal to power-on state\n"), out);
390 fputs(_(" --resize reset terminal rows and columns\n"), out);
391 fputs(_(" --initialize display init string, and use default settings\n"), out);
392 fputs(_(" --default use default terminal settings\n"), out);
393 fputs(_(" --store save current terminal settings as default\n"), out);
394 fputs(USAGE_SEPARATOR, out);
395
396 fputs(_(" --cursor on|off display cursor\n"), out);
397 fputs(_(" --repeat on|off keyboard repeat\n"), out);
398 fputs(_(" --appcursorkeys on|off cursor key application mode\n"), out);
399 fputs(_(" --linewrap on|off continue on a new line when a line is full\n"), out);
400 fputs(_(" --inversescreen on|off swap colors for the whole screen\n"), out);
401 fputs(USAGE_SEPARATOR, out);
402
403 fputs(_(" --msg on|off send kernel messages to console\n"), out);
404 fputs(_(" --msglevel <0-8> kernel console log level\n"), out);
405 fputs(USAGE_SEPARATOR, out);
406
407 fputs(_(" --foreground default|<color> set foreground color\n"), out);
408 fputs(_(" --background default|<color> set background color\n"), out);
409 fputs(_(" --ulcolor [bright] <color> set underlined text color\n"), out);
410 fputs(_(" --hbcolor [bright] <color> set half-bright text color\n"), out);
411 fputs(_(" <color>: black blue cyan green grey magenta red white yellow\n"), out);
412 fputs(USAGE_SEPARATOR, out);
413
414 fputs(_(" --bold on|off bold\n"), out);
415 fputs(_(" --half-bright on|off dim\n"), out);
416 fputs(_(" --blink on|off blink\n"), out);
417 fputs(_(" --underline on|off underline\n"), out);
418 fputs(_(" --reverse on|off swap foreground and background colors\n"), out);
419 fputs(USAGE_SEPARATOR, out);
420
421 fputs(_(" --clear[=<all|rest>] clear screen and set cursor position\n"), out);
422 fputs(_(" --tabs[=<number>...] set these tab stop positions, or show them\n"), out);
423 fputs(_(" --clrtabs[=<number>...] clear these tab stop positions, or all\n"), out);
424 fputs(_(" --regtabs[=1-160] set a regular tab stop interval\n"), out);
425 fputs(_(" --blank[=0-60|force|poke] set time of inactivity before screen blanks\n"), out);
426 fputs(USAGE_SEPARATOR, out);
427
428 fputs(_(" --dump[=<number>] write vcsa<number> console dump to file\n"), out);
429 fputs(_(" --append <number> append vcsa<number> console dump to file\n"), out);
430 fputs(_(" --file <filename> name of the dump file\n"), out);
431 fputs(USAGE_SEPARATOR, out);
432
433 fputs(_(" --powersave on|vsync|hsync|powerdown|off\n"), out);
434 fputs(_(" set vesa powersaving features\n"), out);
435 fputs(_(" --powerdown[=<0-60>] set vesa powerdown interval in minutes\n"), out);
436 fputs(USAGE_SEPARATOR, out);
437
438 fputs(_(" --blength[=<0-2000>] duration of the bell in milliseconds\n"), out);
439 fputs(_(" --bfreq[=<number>] bell frequency in Hertz\n"), out);
440
441 fputs(USAGE_SEPARATOR, out);
442 printf( " --help %s\n", USAGE_OPTSTR_HELP);
443 printf( " --version %s\n", USAGE_OPTSTR_VERSION);
444
445 printf(USAGE_MAN_TAIL("setterm(1)"));
446 exit(EXIT_SUCCESS);
447 }
448
449 static int __attribute__((__pure__)) set_opt_flag(int opt)
450 {
451 if (opt)
452 errx(EXIT_FAILURE, _("duplicate use of an option"));
453 return 1;
454 }
455
456 static void parse_option(struct setterm_control *ctl, int ac, char **av)
457 {
458 int c;
459 enum {
460 OPT_TERM = CHAR_MAX + 1,
461 OPT_RESET,
462 OPT_RESIZE,
463 OPT_INITIALIZE,
464 OPT_CURSOR,
465 OPT_REPEAT,
466 OPT_APPCURSORKEYS,
467 OPT_LINEWRAP,
468 OPT_DEFAULT,
469 OPT_FOREGROUND,
470 OPT_BACKGROUND,
471 OPT_ULCOLOR,
472 OPT_HBCOLOR,
473 OPT_INVERSESCREEN,
474 OPT_BOLD,
475 OPT_HALF_BRIGHT,
476 OPT_BLINK,
477 OPT_REVERSE,
478 OPT_UNDERLINE,
479 OPT_STORE,
480 OPT_CLEAR,
481 OPT_TABS,
482 OPT_CLRTABS,
483 OPT_REGTABS,
484 OPT_BLANK,
485 OPT_DUMP,
486 OPT_APPEND,
487 OPT_FILE,
488 OPT_MSG,
489 OPT_MSGLEVEL,
490 OPT_POWERSAVE,
491 OPT_POWERDOWN,
492 OPT_BLENGTH,
493 OPT_BFREQ,
494 OPT_VERSION,
495 OPT_HELP
496 };
497 static const struct option longopts[] = {
498 {"term", required_argument, NULL, OPT_TERM},
499 {"reset", no_argument, NULL, OPT_RESET},
500 {"resize", no_argument, NULL, OPT_RESIZE},
501 {"initialize", no_argument, NULL, OPT_INITIALIZE},
502 {"cursor", required_argument, NULL, OPT_CURSOR},
503 {"repeat", required_argument, NULL, OPT_REPEAT},
504 {"appcursorkeys", required_argument, NULL, OPT_APPCURSORKEYS},
505 {"linewrap", required_argument, NULL, OPT_LINEWRAP},
506 {"default", no_argument, NULL, OPT_DEFAULT},
507 {"foreground", required_argument, NULL, OPT_FOREGROUND},
508 {"background", required_argument, NULL, OPT_BACKGROUND},
509 {"ulcolor", required_argument, NULL, OPT_ULCOLOR},
510 {"hbcolor", required_argument, NULL, OPT_HBCOLOR},
511 {"inversescreen", required_argument, NULL, OPT_INVERSESCREEN},
512 {"bold", required_argument, NULL, OPT_BOLD},
513 {"half-bright", required_argument, NULL, OPT_HALF_BRIGHT},
514 {"blink", required_argument, NULL, OPT_BLINK},
515 {"reverse", required_argument, NULL, OPT_REVERSE},
516 {"underline", required_argument, NULL, OPT_UNDERLINE},
517 {"store", no_argument, NULL, OPT_STORE},
518 {"clear", optional_argument, NULL, OPT_CLEAR},
519 {"tabs", optional_argument, NULL, OPT_TABS},
520 {"clrtabs", optional_argument, NULL, OPT_CLRTABS},
521 {"regtabs", optional_argument, NULL, OPT_REGTABS},
522 {"blank", optional_argument, NULL, OPT_BLANK},
523 {"dump", optional_argument, NULL, OPT_DUMP},
524 {"append", required_argument, NULL, OPT_APPEND},
525 {"file", required_argument, NULL, OPT_FILE},
526 {"msg", required_argument, NULL, OPT_MSG},
527 {"msglevel", required_argument, NULL, OPT_MSGLEVEL},
528 {"powersave", required_argument, NULL, OPT_POWERSAVE},
529 {"powerdown", optional_argument, NULL, OPT_POWERDOWN},
530 {"blength", optional_argument, NULL, OPT_BLENGTH},
531 {"bfreq", optional_argument, NULL, OPT_BFREQ},
532 {"version", no_argument, NULL, OPT_VERSION},
533 {"help", no_argument, NULL, OPT_HELP},
534 {NULL, 0, NULL, 0}
535 };
536 static const ul_excl_t excl[] = {
537 { OPT_DEFAULT, OPT_STORE },
538 { OPT_TABS, OPT_CLRTABS, OPT_REGTABS },
539 { OPT_MSG, OPT_MSGLEVEL },
540 { 0 }
541 };
542 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
543
544 while ((c = getopt_long_only(ac, av, "", longopts, NULL)) != -1) {
545 err_exclusive_options(c, longopts, excl, excl_st);
546 switch (c) {
547 case OPT_TERM:
548 ctl->opt_term = set_opt_flag(ctl->opt_term);
549 ctl->opt_te_terminal_name = optarg;
550 break;
551 case OPT_RESET:
552 ctl->opt_reset = set_opt_flag(ctl->opt_reset);
553 break;
554 case OPT_RESIZE:
555 ctl->opt_resize = set_opt_flag(ctl->opt_resize);
556 break;
557 case OPT_INITIALIZE:
558 ctl->opt_initialize = set_opt_flag(ctl->opt_initialize);
559 break;
560 case OPT_CURSOR:
561 ctl->opt_cursor = set_opt_flag(ctl->opt_cursor);
562 ctl->opt_cu_on = parse_switch(optarg, _("argument error"),
563 "on", "off", NULL);
564 break;
565 case OPT_REPEAT:
566 ctl->opt_repeat = set_opt_flag(ctl->opt_repeat);
567 ctl->opt_rep_on = parse_switch(optarg, _("argument error"),
568 "on", "off", NULL);
569 break;
570 case OPT_APPCURSORKEYS:
571 ctl->opt_appcursorkeys = set_opt_flag(ctl->opt_appcursorkeys);
572 ctl->opt_appck_on = parse_switch(optarg, _("argument error"),
573 "on", "off", NULL);
574 break;
575 case OPT_LINEWRAP:
576 ctl->opt_linewrap = set_opt_flag(ctl->opt_linewrap);
577 ctl->opt_li_on = parse_switch(optarg, _("argument error"),
578 "on", "off", NULL);
579 break;
580 case OPT_DEFAULT:
581 ctl->opt_default = set_opt_flag(ctl->opt_default);
582 break;
583 case OPT_FOREGROUND:
584 ctl->opt_foreground = set_opt_flag(ctl->opt_foreground);
585 ctl->opt_fo_color = parse_febg_color(optarg);
586 break;
587 case OPT_BACKGROUND:
588 ctl->opt_background = set_opt_flag(ctl->opt_background);
589 ctl->opt_ba_color = parse_febg_color(optarg);
590 break;
591 case OPT_ULCOLOR:
592 ctl->opt_ulcolor = set_opt_flag(ctl->opt_ulcolor);
593 ctl->opt_ul_color = parse_ulhb_color(av, &optind);
594 break;
595 case OPT_HBCOLOR:
596 ctl->opt_hbcolor = set_opt_flag(ctl->opt_hbcolor);
597 ctl->opt_hb_color = parse_ulhb_color(av, &optind);
598 break;
599 case OPT_INVERSESCREEN:
600 ctl->opt_inversescreen = set_opt_flag(ctl->opt_inversescreen);
601 ctl->opt_invsc_on = parse_switch(optarg, _("argument error"),
602 "on", "off", NULL);
603 break;
604 case OPT_BOLD:
605 ctl->opt_bold = set_opt_flag(ctl->opt_bold);
606 ctl->opt_bo_on = parse_switch(optarg, _("argument error"),
607 "on", "off", NULL);
608 break;
609 case OPT_HALF_BRIGHT:
610 ctl->opt_halfbright = set_opt_flag(ctl->opt_halfbright);
611 ctl->opt_hb_on = parse_switch(optarg, _("argument error"),
612 "on", "off", NULL);
613 break;
614 case OPT_BLINK:
615 ctl->opt_blink = set_opt_flag(ctl->opt_blink);
616 ctl->opt_bl_on = parse_switch(optarg, _("argument error"),
617 "on", "off", NULL);
618 break;
619 case OPT_REVERSE:
620 ctl->opt_reverse = set_opt_flag(ctl->opt_reverse);
621 ctl->opt_re_on = parse_switch(optarg, _("argument error"),
622 "on", "off", NULL);
623 break;
624 case OPT_UNDERLINE:
625 ctl->opt_underline = set_opt_flag(ctl->opt_underline);
626 ctl->opt_un_on = parse_switch(optarg, _("argument error"),
627 "on", "off", NULL);
628 break;
629 case OPT_STORE:
630 ctl->opt_store = set_opt_flag(ctl->opt_store);
631 break;
632 case OPT_CLEAR:
633 ctl->opt_clear = set_opt_flag(ctl->opt_clear);
634 if (optarg)
635 ctl->opt_cl_all = parse_switch(optarg, _("argument error"),
636 "all", "rest", NULL);
637 else
638 ctl->opt_cl_all = 1;
639 break;
640 case OPT_TABS:
641 ctl->opt_tabs = set_opt_flag(ctl->opt_tabs);
642 parse_tabs(av, optarg, &optind, ctl->opt_tb_array);
643 break;
644 case OPT_CLRTABS:
645 ctl->opt_clrtabs = set_opt_flag(ctl->opt_clrtabs);
646 parse_tabs(av, optarg, &optind, ctl->opt_tb_array);
647 break;
648 case OPT_REGTABS:
649 ctl->opt_regtabs = set_opt_flag(ctl->opt_regtabs);
650 ctl->opt_rt_len = parse_regtabs(av, optarg, &optind);
651 break;
652 case OPT_BLANK:
653 ctl->opt_blank = set_opt_flag(ctl->opt_blank);
654 ctl->opt_bl_min = parse_blank(av, optarg, &optind);
655 break;
656 case OPT_DUMP:
657 ctl->opt_snap = set_opt_flag(ctl->opt_snap);
658 ctl->opt_sn_num = parse_snap(av, optarg, &optind);
659 break;
660 case OPT_APPEND:
661 ctl->opt_append = set_opt_flag(ctl->opt_append);
662 ctl->opt_sn_num = parse_snap(av, optarg, &optind);
663 break;
664 case OPT_FILE:
665 ctl->opt_snapfile = set_opt_flag(ctl->opt_snapfile);
666 ctl->opt_sn_name = optarg;
667 break;
668 case OPT_MSG:
669 ctl->opt_msg = set_opt_flag(ctl->opt_msg);
670 ctl->opt_msg_on = parse_switch(optarg, _("argument error"),
671 "on", "off", NULL);
672 break;
673 case OPT_MSGLEVEL:
674 ctl->opt_msglevel = set_opt_flag(ctl->opt_msglevel);
675 ctl->opt_msglevel_num = parse_msglevel(optarg);
676 if (ctl->opt_msglevel_num == 0) {
677 ctl->opt_msg = set_opt_flag(ctl->opt_msg);
678 ctl->opt_msg_on |= 1;
679 }
680 break;
681 case OPT_POWERSAVE:
682 ctl->opt_powersave = set_opt_flag(ctl->opt_powersave);
683 ctl->opt_ps_mode = parse_powersave(optarg);
684 break;
685 case OPT_POWERDOWN:
686 ctl->opt_powerdown = set_opt_flag(ctl->opt_powerdown);
687 ctl->opt_pd_min = parse_blank(av, optarg, &optind);
688 break;
689 case OPT_BLENGTH:
690 ctl->opt_blength = set_opt_flag(ctl->opt_blength);
691 ctl->opt_blength_l = parse_blength(av, optarg, &optind);
692 break;
693 case OPT_BFREQ:
694 ctl->opt_bfreq = set_opt_flag(ctl->opt_bfreq);
695 ctl->opt_bfreq_f = parse_bfreq(av, optarg, &optind);
696 break;
697
698 case OPT_VERSION:
699 print_version(EXIT_SUCCESS);
700 case OPT_HELP:
701 usage();
702 default:
703 errtryhelp(EXIT_FAILURE);
704 }
705 }
706 }
707
708 /* Return the specified terminfo string, or an empty string if no such
709 * terminfo capability exists. */
710 static char *ti_entry(const char *name)
711 {
712 char *buf_ptr;
713
714 if ((buf_ptr = tigetstr(name)) == (char *)-1)
715 buf_ptr = NULL;
716 return buf_ptr;
717 }
718
719 static void show_tabs(void)
720 {
721 int i, co = tigetnum("cols");
722
723 if (co > 0) {
724 printf("\r ");
725 for (i = 10; i < co - 2; i += 10)
726 printf("%-10d", i);
727 putchar('\n');
728 for (i = 1; i <= co; i++)
729 putchar(i % 10 + '0');
730 putchar('\n');
731 for (i = 1; i < co; i++)
732 printf("\tT\b");
733 putchar('\n');
734 }
735 }
736
737 static int open_snapshot_device(struct setterm_control *ctl)
738 {
739 int fd;
740
741 if (ctl->opt_sn_num)
742 xasprintf(&ctl->in_device, "/dev/vcsa%d", ctl->opt_sn_num);
743 else
744 xasprintf(&ctl->in_device, "/dev/vcsa");
745 fd = open(ctl->in_device, O_RDONLY);
746 if (fd < 0)
747 err(EXIT_DUMPFILE, _("cannot read %s"), ctl->in_device);
748 return fd;
749 }
750
751 static void set_blanking(struct setterm_control *ctl)
752 {
753 char ioctlarg;
754 int ret;
755
756 if (0 <= ctl->opt_bl_min) {
757 printf("\033[9;%d]", ctl->opt_bl_min);
758 return;
759 }
760 switch (ctl->opt_bl_min) {
761 case BLANKSCREEN:
762 ioctlarg = TIOCL_BLANKSCREEN;
763 if (ioctl(STDIN_FILENO, TIOCLINUX, &ioctlarg))
764 warn(_("cannot force blank"));
765 break;
766 case UNBLANKSCREEN:
767 ioctlarg = TIOCL_UNBLANKSCREEN;
768 if (ioctl(STDIN_FILENO, TIOCLINUX, &ioctlarg))
769 warn(_("cannot force unblank"));
770 break;
771 case BLANKEDSCREEN:
772 ioctlarg = TIOCL_BLANKEDSCREEN;
773 ret = ioctl(STDIN_FILENO, TIOCLINUX, &ioctlarg);
774 if (ret < 0)
775 warn(_("cannot get blank status"));
776 else
777 printf("%d\n", ret);
778 break;
779 default: /* should be impossible to reach */
780 abort();
781 }
782 return;
783 }
784
785 static void screendump(struct setterm_control *ctl)
786 {
787 unsigned char header[4];
788 unsigned int rows, cols;
789 int fd;
790 FILE *out;
791 size_t i, j;
792 ssize_t rc;
793 char *inbuf, *outbuf, *p, *q;
794
795 /* open source and destination files */
796 fd = open_snapshot_device(ctl);
797 if (!ctl->opt_sn_name)
798 ctl->opt_sn_name = "screen.dump";
799 out = fopen(ctl->opt_sn_name, ctl->opt_snap ? "w" : "a");
800 if (!out)
801 err(EXIT_DUMPFILE, _("cannot open dump file %s for output"), ctl->opt_sn_name);
802 /* determine snapshot size */
803 if (read(fd, header, 4) != 4)
804 err(EXIT_DUMPFILE, _("cannot read %s"), ctl->in_device);
805 rows = header[0];
806 cols = header[1];
807 if (rows * cols == 0)
808 err(EXIT_DUMPFILE, _("cannot read %s"), ctl->in_device);
809 /* allocate buffers */
810 inbuf = xmalloc(rows * cols * 2);
811 outbuf = xmalloc(rows * (cols + 1));
812 /* read input */
813 rc = read(fd, inbuf, rows * cols * 2);
814 if (rc < 0 || (size_t)rc != rows * cols * 2)
815 err(EXIT_DUMPFILE, _("cannot read %s"), ctl->in_device);
816 p = inbuf;
817 q = outbuf;
818 /* copy inbuf to outbuf */
819 for (i = 0; i < rows; i++) {
820 for (j = 0; j < cols; j++) {
821 *q++ = *p;
822 p += 2;
823 }
824 while (j-- > 0 && q[-1] == ' ')
825 q--;
826 *q++ = '\n';
827 }
828 fwrite(outbuf, 1, q - outbuf, out);
829 /* clean up allocations */
830 close(fd);
831 free(inbuf);
832 free(outbuf);
833 free(ctl->in_device);
834 if (close_stream(out) != 0)
835 errx(EXIT_FAILURE, _("write error"));
836 return;
837 }
838
839 /* Some options are applicable when terminal is virtual console. */
840 static int vc_only(struct setterm_control *ctl, const char *err)
841 {
842 if (!ctl->vcterm && err)
843 warnx(_("terminal %s does not support %s"),
844 ctl->opt_te_terminal_name, err);
845 return ctl->vcterm;
846 }
847
848 static void tty_raw(struct termios *saved_attributes, int *saved_fl)
849 {
850 struct termios tattr;
851
852 fcntl(STDIN_FILENO, F_GETFL, saved_fl);
853 tcgetattr(STDIN_FILENO, saved_attributes);
854 fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
855 memcpy(&tattr, saved_attributes, sizeof(struct termios));
856 tattr.c_lflag &= ~(ICANON | ECHO);
857 tattr.c_cc[VMIN] = 1;
858 tattr.c_cc[VTIME] = 0;
859 tcsetattr(STDIN_FILENO, TCSAFLUSH, &tattr);
860 }
861
862 static void tty_restore(struct termios *saved_attributes, int *saved_fl)
863 {
864 fcntl(STDIN_FILENO, F_SETFL, *saved_fl);
865 tcsetattr(STDIN_FILENO, TCSANOW, saved_attributes);
866 }
867
868 static int select_wait(void)
869 {
870 struct timeval tv;
871 fd_set set;
872 int ret;
873
874 FD_ZERO(&set);
875 FD_SET(STDIN_FILENO, &set);
876 tv.tv_sec = 10;
877 tv.tv_usec = 0;
878 while ((ret = select(1, &set, NULL, NULL, &tv)) < 0) {
879 if (errno == EINTR)
880 continue;
881 err(EXIT_FAILURE, _("select failed"));
882 }
883 return ret;
884 }
885
886 static int resizetty(void)
887 {
888 /*
889 * \e7 Save current state (cursor coordinates, attributes,
890 * character sets pointed at by G0, G1).
891 * \e[r Set scrolling region; parameters are top and bottom row.
892 * \e[32766E Move cursor down 32766 (INT16_MAX - 1) rows.
893 * \e[32766C Move cursor right 32766 columns.
894 * \e[6n Report cursor position.
895 * \e8 Restore state most recently saved by \e7.
896 */
897 static const char *getpos = "\e7\e[r\e[32766E\e[32766C\e[6n\e8";
898 char retstr[32];
899 int row, col;
900 size_t pos;
901 ssize_t rc;
902 struct winsize ws;
903 struct termios saved_attributes;
904 int saved_fl;
905
906 if (!isatty(STDIN_FILENO))
907 errx(EXIT_FAILURE, _("stdin does not refer to a terminal"));
908
909 tty_raw(&saved_attributes, &saved_fl);
910 if (write_all(STDIN_FILENO, getpos, strlen(getpos)) < 0) {
911 warn(_("write failed"));
912 tty_restore(&saved_attributes, &saved_fl);
913 return 1;
914 }
915 for (pos = 0; pos < sizeof(retstr) - 1;) {
916 if (0 == select_wait())
917 break;
918 if ((rc =
919 read(STDIN_FILENO, retstr + pos,
920 sizeof(retstr) - 1 - pos)) < 0) {
921 if (errno == EINTR)
922 continue;
923 warn(_("read failed"));
924 tty_restore(&saved_attributes, &saved_fl);
925 return 1;
926 }
927 pos += rc;
928 if (retstr[pos - 1] == 'R')
929 break;
930 }
931 retstr[pos] = 0;
932 tty_restore(&saved_attributes, &saved_fl);
933 rc = sscanf(retstr, "\033[%d;%dR", &row, &col);
934 if (rc != 2) {
935 warnx(_("invalid cursor position: %s"), retstr);
936 return 1;
937 }
938 memset(&ws, 0, sizeof(struct winsize));
939 ioctl(STDIN_FILENO, TIOCGWINSZ, &ws);
940 ws.ws_row = row;
941 ws.ws_col = col;
942 ioctl(STDIN_FILENO, TIOCSWINSZ, &ws);
943 return 0;
944 }
945
946 static void perform_sequence(struct setterm_control *ctl)
947 {
948 int result;
949
950 /* -reset. */
951 if (ctl->opt_reset)
952 putp(ti_entry("rs1"));
953
954 /* -resize. */
955 if (ctl->opt_resize)
956 if (resizetty())
957 warnx(_("reset failed"));
958
959 /* -initialize. */
960 if (ctl->opt_initialize)
961 putp(ti_entry("is2"));
962
963 /* -cursor [on|off]. */
964 if (ctl->opt_cursor) {
965 if (ctl->opt_cu_on)
966 putp(ti_entry("cnorm"));
967 else
968 putp(ti_entry("civis"));
969 }
970
971 /* -linewrap [on|off]. */
972 if (ctl->opt_linewrap)
973 fputs(ctl->opt_li_on ? "\033[?7h" : "\033[?7l", stdout);
974
975 /* -repeat [on|off]. */
976 if (ctl->opt_repeat && vc_only(ctl, "--repeat"))
977 fputs(ctl->opt_rep_on ? "\033[?8h" : "\033[?8l", stdout);
978
979 /* -appcursorkeys [on|off]. */
980 if (ctl->opt_appcursorkeys && vc_only(ctl, "--appcursorkeys"))
981 fputs(ctl->opt_appck_on ? "\033[?1h" : "\033[?1l", stdout);
982
983 /* -default. Vc sets default rendition, otherwise clears all
984 * attributes. */
985 if (ctl->opt_default) {
986 if (vc_only(ctl, NULL))
987 printf("\033[0m");
988 else
989 putp(ti_entry("sgr0"));
990 }
991
992 /* -foreground black|red|green|yellow|blue|magenta|cyan|white|default. */
993 if (ctl->opt_foreground)
994 printf("\033[3%c%s", '0' + ctl->opt_fo_color, "m");
995
996 /* -background black|red|green|yellow|blue|magenta|cyan|white|default. */
997 if (ctl->opt_background)
998 printf("\033[4%c%s", '0' + ctl->opt_ba_color, "m");
999
1000 /* -ulcolor [bright] black|red|green|yellow|blue|magenta|cyan|white. */
1001 if (ctl->opt_ulcolor && vc_only(ctl, "--ulcolor"))
1002 printf("\033[1;%d]", ctl->opt_ul_color);
1003
1004 /* -hbcolor [bright] black|red|green|yellow|blue|magenta|cyan|white. */
1005 if (ctl->opt_hbcolor)
1006 printf("\033[2;%d]", ctl->opt_hb_color);
1007
1008 /* -inversescreen [on|off]. */
1009 if (ctl->opt_inversescreen)
1010 fputs(ctl->opt_invsc_on ? "\033[?5h" : "\033[?5l", stdout);
1011
1012 /* -bold [on|off]. Vc behaves as expected, otherwise off turns off
1013 * all attributes. */
1014 if (ctl->opt_bold) {
1015 if (ctl->opt_bo_on)
1016 putp(ti_entry("bold"));
1017 else {
1018 if (vc_only(ctl, NULL))
1019 fputs("\033[22m", stdout);
1020 else
1021 putp(ti_entry("sgr0"));
1022 }
1023 }
1024
1025 /* -half-bright [on|off]. Vc behaves as expected, otherwise off
1026 * turns off all attributes. */
1027 if (ctl->opt_halfbright) {
1028 if (ctl->opt_hb_on)
1029 putp(ti_entry("dim"));
1030 else {
1031 if (vc_only(ctl, NULL))
1032 fputs("\033[22m", stdout);
1033 else
1034 putp(ti_entry("sgr0"));
1035 }
1036 }
1037
1038 /* -blink [on|off]. Vc behaves as expected, otherwise off turns off
1039 * all attributes. */
1040 if (ctl->opt_blink) {
1041 if (ctl->opt_bl_on)
1042 putp(ti_entry("blink"));
1043 else {
1044 if (vc_only(ctl, NULL))
1045 fputs("\033[25m", stdout);
1046 else
1047 putp(ti_entry("sgr0"));
1048 }
1049 }
1050
1051 /* -reverse [on|off]. Vc behaves as expected, otherwise off turns
1052 * off all attributes. */
1053 if (ctl->opt_reverse) {
1054 if (ctl->opt_re_on)
1055 putp(ti_entry("rev"));
1056 else {
1057 if (vc_only(ctl, NULL))
1058 fputs("\033[27m", stdout);
1059 else
1060 putp(ti_entry("sgr0"));
1061 }
1062 }
1063
1064 /* -underline [on|off]. */
1065 if (ctl->opt_underline)
1066 putp(ti_entry(ctl->opt_un_on ? "smul" : "rmul"));
1067
1068 /* -store. */
1069 if (ctl->opt_store && vc_only(ctl, "--store"))
1070 fputs("\033[8]", stdout);
1071
1072 /* -clear [all|rest]. */
1073 if (ctl->opt_clear)
1074 putp(ti_entry(ctl->opt_cl_all ? "clear" : "ed"));
1075
1076 /* -tabs. */
1077 if (ctl->opt_tabs) {
1078 if (ctl->opt_tb_array[0] == -1)
1079 show_tabs();
1080 else {
1081 int i;
1082
1083 for (i = 0; ctl->opt_tb_array[i] > 0; i++)
1084 printf("\033[%dG\033H", ctl->opt_tb_array[i]);
1085 putchar('\r');
1086 }
1087 }
1088
1089 /* -clrtabs. */
1090 if (ctl->opt_clrtabs && vc_only(ctl, "--clrtabs")) {
1091 int i;
1092
1093 if (ctl->opt_tb_array[0] == -1)
1094 fputs("\033[3g", stdout);
1095 else
1096 for (i = 0; ctl->opt_tb_array[i] > 0; i++)
1097 printf("\033[%dG\033[g", ctl->opt_tb_array[i]);
1098 putchar('\r');
1099 }
1100
1101 /* -regtabs. */
1102 if (ctl->opt_regtabs && vc_only(ctl, "--regtabs")) {
1103 int i;
1104
1105 fputs("\033[3g\r", stdout);
1106 for (i = ctl->opt_rt_len + 1; i <= TABS_MAX; i += ctl->opt_rt_len)
1107 printf("\033[%dC\033H", ctl->opt_rt_len);
1108 putchar('\r');
1109 }
1110
1111 /* -blank [0-60]. */
1112 if (ctl->opt_blank && vc_only(ctl, "--blank"))
1113 set_blanking(ctl);
1114
1115 /* -powersave [on|vsync|hsync|powerdown|off] (console) */
1116 if (ctl->opt_powersave) {
1117 char ioctlarg[2];
1118 ioctlarg[0] = TIOCL_SETVESABLANK;
1119 ioctlarg[1] = ctl->opt_ps_mode;
1120 if (ioctl(STDIN_FILENO, TIOCLINUX, ioctlarg))
1121 warn(_("cannot (un)set powersave mode"));
1122 }
1123
1124 /* -powerdown [0-60]. */
1125 if (ctl->opt_powerdown)
1126 printf("\033[14;%d]", ctl->opt_pd_min);
1127
1128 /* -snap [1-NR_CONS]. */
1129 if (ctl->opt_snap || ctl->opt_append)
1130 screendump(ctl);
1131
1132 /* -msg [on|off]. Controls printk's to console. */
1133 if (ctl->opt_msg && vc_only(ctl, "--msg")) {
1134 if (ctl->opt_msg_on)
1135 result = klogctl(SYSLOG_ACTION_CONSOLE_ON, NULL, 0);
1136 else
1137 result = klogctl(SYSLOG_ACTION_CONSOLE_OFF, NULL, 0);
1138
1139 if (result != 0)
1140 warn(_("klogctl error"));
1141 }
1142
1143 /* -msglevel [0-8]. Console printk message level. */
1144 if (ctl->opt_msglevel_num && vc_only(ctl, "--msglevel")) {
1145 result =
1146 klogctl(SYSLOG_ACTION_CONSOLE_LEVEL, NULL,
1147 ctl->opt_msglevel_num);
1148 if (result != 0)
1149 warn(_("klogctl error"));
1150 }
1151
1152 /* -blength [0-2000] */
1153 if (ctl->opt_blength && vc_only(ctl, "--blength")) {
1154 printf("\033[11;%d]", ctl->opt_blength_l);
1155 }
1156
1157 /* -bfreq freqnumber */
1158 if (ctl->opt_bfreq && vc_only(ctl, "--bfreq")) {
1159 printf("\033[10;%d]", ctl->opt_bfreq_f);
1160 }
1161 }
1162
1163 static void init_terminal(struct setterm_control *ctl)
1164 {
1165 int term_errno;
1166
1167 if (!ctl->opt_te_terminal_name) {
1168 ctl->opt_te_terminal_name = getenv("TERM");
1169 if (ctl->opt_te_terminal_name == NULL)
1170 errx(EXIT_FAILURE, _("$TERM is not defined."));
1171 }
1172
1173 /* Find terminfo entry. */
1174 if (setupterm(ctl->opt_te_terminal_name, STDOUT_FILENO, &term_errno))
1175 switch (term_errno) {
1176 case -1:
1177 errx(EXIT_FAILURE, _("terminfo database cannot be found"));
1178 case 0:
1179 errx(EXIT_FAILURE, _("%s: unknown terminal type"), ctl->opt_te_terminal_name);
1180 case 1:
1181 errx(EXIT_FAILURE, _("terminal is hardcopy"));
1182 }
1183
1184 /* See if the terminal is a virtual console terminal. */
1185 ctl->vcterm = (!strncmp(ctl->opt_te_terminal_name, "con", 3) ||
1186 !strncmp(ctl->opt_te_terminal_name, "linux", 5));
1187 }
1188
1189
1190 int main(int argc, char **argv)
1191 {
1192 struct setterm_control ctl = { NULL };
1193
1194 setlocale(LC_ALL, "");
1195 bindtextdomain(PACKAGE, LOCALEDIR);
1196 textdomain(PACKAGE);
1197 close_stdout_atexit();
1198
1199 if (argc < 2) {
1200 warnx(_("bad usage"));
1201 errtryhelp(EXIT_FAILURE);
1202 }
1203 parse_option(&ctl, argc, argv);
1204 init_terminal(&ctl);
1205 perform_sequence(&ctl);
1206
1207 return EXIT_SUCCESS;
1208 }