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 
parse_color(const char * arg)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 
parse_febg_color(const char * arg)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 
parse_ulhb_color(char ** av,int * oi)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 
find_optional_arg(char ** av,char * oa,int * oi)245 static char *find_optional_arg(char **av, char *oa, int *oi)
246 {
247 	char *arg;
248 	if (oa)
249 		return oa;
250 
251 	arg = av[*oi];
252 	if (!arg || arg[0] == '-')
253 		return NULL;
254 
255 	(*oi)++;
256 	return arg;
257 }
258 
parse_blank(char ** av,char * oa,int * oi)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 	if (!strcmp(arg, "poke"))
269 		return UNBLANKSCREEN;
270 
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 
parse_powersave(const char * arg)279 static int parse_powersave(const char *arg)
280 {
281 	if (strcmp(arg, "on") == 0)
282 		return VESA_BLANK_MODE_SUSPENDV;
283 	if (strcmp(arg, "vsync") == 0)
284 		return VESA_BLANK_MODE_SUSPENDV;
285 	if (strcmp(arg, "hsync") == 0)
286 		return VESA_BLANK_MODE_SUSPENDH;
287 	if (strcmp(arg, "powerdown") == 0)
288 		return VESA_BLANK_MODE_POWERDOWN;
289 	if (strcmp(arg, "off") == 0)
290 		return VESA_BLANK_MODE_OFF;
291 	errx(EXIT_FAILURE, "%s: %s", _("argument error"), arg);
292 }
293 
parse_msglevel(const char * arg)294 static int parse_msglevel(const char *arg)
295 {
296 	int ret;
297 
298 	ret = strtos32_or_err(arg, _("argument error"));
299 	if (ret < CONSOLE_LEVEL_MIN || CONSOLE_LEVEL_MAX < ret)
300 		errx(EXIT_FAILURE, "%s: %s", _("argument error"), arg);
301 	return ret;
302 }
303 
parse_snap(char ** av,char * oa,int * oi)304 static int parse_snap(char **av, char *oa, int *oi)
305 {
306 	int ret;
307 	char *arg;
308 
309 	arg = find_optional_arg(av, oa, oi);
310 	if (!arg)
311 		return 0;
312 	ret = strtos32_or_err(arg, _("argument error"));
313 	if (ret < 1)
314 		errx(EXIT_FAILURE, "%s: %s", _("argument error"), arg);
315 	return ret;
316 }
317 
parse_tabs(char ** av,char * oa,int * oi,int * tab_array)318 static void parse_tabs(char **av, char *oa, int *oi, int *tab_array)
319 {
320 	int i = 0;
321 
322 	if (oa) {
323 		tab_array[i] = strtos32_or_err(oa, _("argument error"));
324 		i++;
325 	}
326 	while (av[*oi]) {
327 		if (TABS_MAX < i)
328 			errx(EXIT_FAILURE, _("too many tabs"));
329 		if (av[*oi][0] == '-')
330 			break;
331 		tab_array[i] = strtos32_or_err(av[*oi], _("argument error"));
332 		(*oi)++;
333 		i++;
334 	}
335 	tab_array[i] = -1;
336 }
337 
parse_regtabs(char ** av,char * oa,int * oi)338 static int parse_regtabs(char **av, char *oa, int *oi)
339 {
340 	int ret;
341 	char *arg;
342 
343 	arg = find_optional_arg(av, oa, oi);
344 	if (!arg)
345 		return DEFAULT_TAB_LEN;
346 	ret = strtos32_or_err(arg, _("argument error"));
347 	if (ret < 1 || TABS_MAX < ret)
348 		errx(EXIT_FAILURE, "%s: %s", _("argument error"), arg);
349 	return ret;
350 }
351 
parse_blength(char ** av,char * oa,int * oi)352 static int parse_blength(char **av, char *oa, int *oi)
353 {
354 	int ret = -1;
355 	char *arg;
356 
357 	arg = find_optional_arg(av, oa, oi);
358 	if (!arg)
359 		return 0;
360 	ret = strtos32_or_err(arg, _("argument error"));
361 	if (ret < 0 || BLENGTH_MAX < ret)
362 		errx(EXIT_FAILURE, "%s: %s", _("argument error"), arg);
363 	return ret;
364 }
365 
parse_bfreq(char ** av,char * oa,int * oi)366 static int parse_bfreq(char **av, char *oa, int *oi)
367 {
368 	char *arg;
369 
370 	arg = find_optional_arg(av, oa, oi);
371 	if (!arg)
372 		return 0;
373 	return strtos32_or_err(arg, _("argument error"));
374 }
375 
usage(void)376 static void __attribute__((__noreturn__)) usage(void)
377 {
378 	FILE *out = stdout;
379 	fputs(USAGE_HEADER, out);
380 	fprintf(out,
381 	      _(" %s [options]\n"), program_invocation_short_name);
382 
383 	fputs(USAGE_SEPARATOR, out);
384 	fputs(_("Set the attributes of a terminal.\n"), out);
385 
386 	fputs(USAGE_OPTIONS, out);
387 	fputs(_(" --term <terminal_name>        override TERM environment variable\n"), out);
388 	fputs(_(" --reset                       reset terminal to power-on state\n"), out);
389 	fputs(_(" --resize                      reset terminal rows and columns\n"), out);
390 	fputs(_(" --initialize                  display init string, and use default settings\n"), out);
391 	fputs(_(" --default                     use default terminal settings\n"), out);
392 	fputs(_(" --store                       save current terminal settings as default\n"), out);
393 	fputs(USAGE_SEPARATOR, out);
394 
395 	fputs(_(" --cursor on|off               display cursor\n"), out);
396 	fputs(_(" --repeat on|off               keyboard repeat\n"), out);
397 	fputs(_(" --appcursorkeys on|off        cursor key application mode\n"), out);
398 	fputs(_(" --linewrap on|off             continue on a new line when a line is full\n"), out);
399 	fputs(_(" --inversescreen on|off        swap colors for the whole screen\n"), out);
400 	fputs(USAGE_SEPARATOR, out);
401 
402 	fputs(_(" --msg on|off                  send kernel messages to console\n"), out);
403 	fputs(_(" --msglevel <0-8>              kernel console log level\n"), out);
404 	fputs(USAGE_SEPARATOR, out);
405 
406 	fputs(_(" --foreground default|<color>  set foreground color\n"), out);
407 	fputs(_(" --background default|<color>  set background color\n"), out);
408 	fputs(_(" --ulcolor [bright] <color>    set underlined text color\n"), out);
409 	fputs(_(" --hbcolor [bright] <color>    set half-bright text color\n"), out);
410 	fputs(_("        <color>: black blue cyan green grey magenta red white yellow\n"), out);
411 	fputs(USAGE_SEPARATOR, out);
412 
413 	fputs(_(" --bold on|off                 bold\n"), out);
414 	fputs(_(" --half-bright on|off          dim\n"), out);
415 	fputs(_(" --blink on|off                blink\n"), out);
416 	fputs(_(" --underline on|off            underline\n"), out);
417 	fputs(_(" --reverse  on|off             swap foreground and background colors\n"), out);
418 	fputs(USAGE_SEPARATOR, out);
419 
420 	fputs(_(" --clear[=<all|rest>]          clear screen and set cursor position\n"), out);
421 	fputs(_(" --tabs[=<number>...]          set these tab stop positions, or show them\n"), out);
422 	fputs(_(" --clrtabs[=<number>...]       clear these tab stop positions, or all\n"), out);
423 	fputs(_(" --regtabs[=1-160]             set a regular tab stop interval\n"), out);
424 	fputs(_(" --blank[=0-60|force|poke]     set time of inactivity before screen blanks\n"), out);
425 	fputs(USAGE_SEPARATOR, out);
426 
427 	fputs(_(" --dump[=<number>]             write vcsa<number> console dump to file\n"), out);
428 	fputs(_(" --append <number>             append vcsa<number> console dump to file\n"), out);
429 	fputs(_(" --file <filename>             name of the dump file\n"), out);
430 	fputs(USAGE_SEPARATOR, out);
431 
432 	fputs(_(" --powersave on|vsync|hsync|powerdown|off\n"), out);
433 	fputs(_("                               set vesa powersaving features\n"), out);
434 	fputs(_(" --powerdown[=<0-60>]          set vesa powerdown interval in minutes\n"), out);
435 	fputs(USAGE_SEPARATOR, out);
436 
437 	fputs(_(" --blength[=<0-2000>]          duration of the bell in milliseconds\n"), out);
438 	fputs(_(" --bfreq[=<number>]            bell frequency in Hertz\n"), out);
439 
440 	fputs(USAGE_SEPARATOR, out);
441 	printf( " --help                        %s\n", USAGE_OPTSTR_HELP);
442 	printf( " --version                     %s\n", USAGE_OPTSTR_VERSION);
443 
444 	printf(USAGE_MAN_TAIL("setterm(1)"));
445 	exit(EXIT_SUCCESS);
446 }
447 
set_opt_flag(int opt)448 static int __attribute__((__pure__)) set_opt_flag(int opt)
449 {
450 	if (opt)
451 		errx(EXIT_FAILURE, _("duplicate use of an option"));
452 	return 1;
453 }
454 
parse_option(struct setterm_control * ctl,int ac,char ** av)455 static void parse_option(struct setterm_control *ctl, int ac, char **av)
456 {
457 	int c;
458 	enum {
459 		OPT_TERM = CHAR_MAX + 1,
460 		OPT_RESET,
461 		OPT_RESIZE,
462 		OPT_INITIALIZE,
463 		OPT_CURSOR,
464 		OPT_REPEAT,
465 		OPT_APPCURSORKEYS,
466 		OPT_LINEWRAP,
467 		OPT_DEFAULT,
468 		OPT_FOREGROUND,
469 		OPT_BACKGROUND,
470 		OPT_ULCOLOR,
471 		OPT_HBCOLOR,
472 		OPT_INVERSESCREEN,
473 		OPT_BOLD,
474 		OPT_HALF_BRIGHT,
475 		OPT_BLINK,
476 		OPT_REVERSE,
477 		OPT_UNDERLINE,
478 		OPT_STORE,
479 		OPT_CLEAR,
480 		OPT_TABS,
481 		OPT_CLRTABS,
482 		OPT_REGTABS,
483 		OPT_BLANK,
484 		OPT_DUMP,
485 		OPT_APPEND,
486 		OPT_FILE,
487 		OPT_MSG,
488 		OPT_MSGLEVEL,
489 		OPT_POWERSAVE,
490 		OPT_POWERDOWN,
491 		OPT_BLENGTH,
492 		OPT_BFREQ,
493 		OPT_VERSION,
494 		OPT_HELP
495 	};
496 	static const struct option longopts[] = {
497 		{"term", required_argument, NULL, OPT_TERM},
498 		{"reset", no_argument, NULL, OPT_RESET},
499 		{"resize", no_argument, NULL, OPT_RESIZE},
500 		{"initialize", no_argument, NULL, OPT_INITIALIZE},
501 		{"cursor", required_argument, NULL, OPT_CURSOR},
502 		{"repeat", required_argument, NULL, OPT_REPEAT},
503 		{"appcursorkeys", required_argument, NULL, OPT_APPCURSORKEYS},
504 		{"linewrap", required_argument, NULL, OPT_LINEWRAP},
505 		{"default", no_argument, NULL, OPT_DEFAULT},
506 		{"foreground", required_argument, NULL, OPT_FOREGROUND},
507 		{"background", required_argument, NULL, OPT_BACKGROUND},
508 		{"ulcolor", required_argument, NULL, OPT_ULCOLOR},
509 		{"hbcolor", required_argument, NULL, OPT_HBCOLOR},
510 		{"inversescreen", required_argument, NULL, OPT_INVERSESCREEN},
511 		{"bold", required_argument, NULL, OPT_BOLD},
512 		{"half-bright", required_argument, NULL, OPT_HALF_BRIGHT},
513 		{"blink", required_argument, NULL, OPT_BLINK},
514 		{"reverse", required_argument, NULL, OPT_REVERSE},
515 		{"underline", required_argument, NULL, OPT_UNDERLINE},
516 		{"store", no_argument, NULL, OPT_STORE},
517 		{"clear", optional_argument, NULL, OPT_CLEAR},
518 		{"tabs", optional_argument, NULL, OPT_TABS},
519 		{"clrtabs", optional_argument, NULL, OPT_CLRTABS},
520 		{"regtabs", optional_argument, NULL, OPT_REGTABS},
521 		{"blank", optional_argument, NULL, OPT_BLANK},
522 		{"dump", optional_argument, NULL, OPT_DUMP},
523 		{"append", required_argument, NULL, OPT_APPEND},
524 		{"file", required_argument, NULL, OPT_FILE},
525 		{"msg", required_argument, NULL, OPT_MSG},
526 		{"msglevel", required_argument, NULL, OPT_MSGLEVEL},
527 		{"powersave", required_argument, NULL, OPT_POWERSAVE},
528 		{"powerdown", optional_argument, NULL, OPT_POWERDOWN},
529 		{"blength", optional_argument, NULL, OPT_BLENGTH},
530 		{"bfreq", optional_argument, NULL, OPT_BFREQ},
531 		{"version", no_argument, NULL, OPT_VERSION},
532 		{"help", no_argument, NULL, OPT_HELP},
533 		{NULL, 0, NULL, 0}
534 	};
535 	static const ul_excl_t excl[] = {
536 		{ OPT_DEFAULT, OPT_STORE },
537 		{ OPT_TABS, OPT_CLRTABS, OPT_REGTABS },
538 		{ OPT_MSG, OPT_MSGLEVEL },
539 		{ 0 }
540 	};
541 	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
542 
543 	while ((c = getopt_long_only(ac, av, "", longopts, NULL)) != -1) {
544 		err_exclusive_options(c, longopts, excl, excl_st);
545 		switch (c) {
546 		case OPT_TERM:
547 			ctl->opt_term = set_opt_flag(ctl->opt_term);
548 			ctl->opt_te_terminal_name = optarg;
549 			break;
550 		case OPT_RESET:
551 			ctl->opt_reset = set_opt_flag(ctl->opt_reset);
552 			break;
553 		case OPT_RESIZE:
554 			ctl->opt_resize = set_opt_flag(ctl->opt_resize);
555 			break;
556 		case OPT_INITIALIZE:
557 			ctl->opt_initialize = set_opt_flag(ctl->opt_initialize);
558 			break;
559 		case OPT_CURSOR:
560 			ctl->opt_cursor = set_opt_flag(ctl->opt_cursor);
561 			ctl->opt_cu_on = parse_switch(optarg, _("argument error"),
562 						"on", "off", NULL);
563 			break;
564 		case OPT_REPEAT:
565 			ctl->opt_repeat = set_opt_flag(ctl->opt_repeat);
566 			ctl->opt_rep_on = parse_switch(optarg, _("argument error"),
567 						"on", "off", NULL);
568 			break;
569 		case OPT_APPCURSORKEYS:
570 			ctl->opt_appcursorkeys = set_opt_flag(ctl->opt_appcursorkeys);
571 			ctl->opt_appck_on = parse_switch(optarg, _("argument error"),
572 						"on", "off", NULL);
573 			break;
574 		case OPT_LINEWRAP:
575 			ctl->opt_linewrap = set_opt_flag(ctl->opt_linewrap);
576 			ctl->opt_li_on = parse_switch(optarg, _("argument error"),
577 						"on", "off", NULL);
578 			break;
579 		case OPT_DEFAULT:
580 			ctl->opt_default = set_opt_flag(ctl->opt_default);
581 			break;
582 		case OPT_FOREGROUND:
583 			ctl->opt_foreground = set_opt_flag(ctl->opt_foreground);
584 			ctl->opt_fo_color = parse_febg_color(optarg);
585 			break;
586 		case OPT_BACKGROUND:
587 			ctl->opt_background = set_opt_flag(ctl->opt_background);
588 			ctl->opt_ba_color = parse_febg_color(optarg);
589 			break;
590 		case OPT_ULCOLOR:
591 			ctl->opt_ulcolor = set_opt_flag(ctl->opt_ulcolor);
592 			ctl->opt_ul_color = parse_ulhb_color(av, &optind);
593 			break;
594 		case OPT_HBCOLOR:
595 			ctl->opt_hbcolor = set_opt_flag(ctl->opt_hbcolor);
596 			ctl->opt_hb_color = parse_ulhb_color(av, &optind);
597 			break;
598 		case OPT_INVERSESCREEN:
599 			ctl->opt_inversescreen = set_opt_flag(ctl->opt_inversescreen);
600 			ctl->opt_invsc_on = parse_switch(optarg, _("argument error"),
601 						"on", "off", NULL);
602 			break;
603 		case OPT_BOLD:
604 			ctl->opt_bold = set_opt_flag(ctl->opt_bold);
605 			ctl->opt_bo_on = parse_switch(optarg, _("argument error"),
606 						"on", "off", NULL);
607 			break;
608 		case OPT_HALF_BRIGHT:
609 			ctl->opt_halfbright = set_opt_flag(ctl->opt_halfbright);
610 			ctl->opt_hb_on = parse_switch(optarg, _("argument error"),
611 						"on", "off", NULL);
612 			break;
613 		case OPT_BLINK:
614 			ctl->opt_blink = set_opt_flag(ctl->opt_blink);
615 			ctl->opt_bl_on = parse_switch(optarg, _("argument error"),
616 						"on", "off", NULL);
617 			break;
618 		case OPT_REVERSE:
619 			ctl->opt_reverse = set_opt_flag(ctl->opt_reverse);
620 			ctl->opt_re_on = parse_switch(optarg, _("argument error"),
621 						"on", "off", NULL);
622 			break;
623 		case OPT_UNDERLINE:
624 			ctl->opt_underline = set_opt_flag(ctl->opt_underline);
625 			ctl->opt_un_on = parse_switch(optarg, _("argument error"),
626 						"on", "off", NULL);
627 			break;
628 		case OPT_STORE:
629 			ctl->opt_store = set_opt_flag(ctl->opt_store);
630 			break;
631 		case OPT_CLEAR:
632 			ctl->opt_clear = set_opt_flag(ctl->opt_clear);
633 			if (optarg)
634 				ctl->opt_cl_all = parse_switch(optarg, _("argument error"),
635 						"all", "rest", NULL);
636 			else
637 				ctl->opt_cl_all = 1;
638 			break;
639 		case OPT_TABS:
640 			ctl->opt_tabs = set_opt_flag(ctl->opt_tabs);
641 			parse_tabs(av, optarg, &optind, ctl->opt_tb_array);
642 			break;
643 		case OPT_CLRTABS:
644 			ctl->opt_clrtabs = set_opt_flag(ctl->opt_clrtabs);
645 			parse_tabs(av, optarg, &optind, ctl->opt_tb_array);
646 			break;
647 		case OPT_REGTABS:
648 			ctl->opt_regtabs = set_opt_flag(ctl->opt_regtabs);
649 			ctl->opt_rt_len = parse_regtabs(av, optarg, &optind);
650 			break;
651 		case OPT_BLANK:
652 			ctl->opt_blank = set_opt_flag(ctl->opt_blank);
653 			ctl->opt_bl_min = parse_blank(av, optarg, &optind);
654 			break;
655 		case OPT_DUMP:
656 			ctl->opt_snap = set_opt_flag(ctl->opt_snap);
657 			ctl->opt_sn_num = parse_snap(av, optarg, &optind);
658 			break;
659 		case OPT_APPEND:
660 			ctl->opt_append = set_opt_flag(ctl->opt_append);
661 			ctl->opt_sn_num = parse_snap(av, optarg, &optind);
662 			break;
663 		case OPT_FILE:
664 			ctl->opt_snapfile = set_opt_flag(ctl->opt_snapfile);
665 			ctl->opt_sn_name = optarg;
666 			break;
667 		case OPT_MSG:
668 			ctl->opt_msg = set_opt_flag(ctl->opt_msg);
669 			ctl->opt_msg_on = parse_switch(optarg, _("argument error"),
670 						"on", "off", NULL);
671 			break;
672 		case OPT_MSGLEVEL:
673 			ctl->opt_msglevel = set_opt_flag(ctl->opt_msglevel);
674 			ctl->opt_msglevel_num = parse_msglevel(optarg);
675 			if (ctl->opt_msglevel_num == 0) {
676 				ctl->opt_msg = set_opt_flag(ctl->opt_msg);
677 				ctl->opt_msg_on |= 1;
678 			}
679 			break;
680 		case OPT_POWERSAVE:
681 			ctl->opt_powersave = set_opt_flag(ctl->opt_powersave);
682 			ctl->opt_ps_mode = parse_powersave(optarg);
683 			break;
684 		case OPT_POWERDOWN:
685 			ctl->opt_powerdown = set_opt_flag(ctl->opt_powerdown);
686 			ctl->opt_pd_min = parse_blank(av, optarg, &optind);
687 			break;
688 		case OPT_BLENGTH:
689 			ctl->opt_blength = set_opt_flag(ctl->opt_blength);
690 			ctl->opt_blength_l = parse_blength(av, optarg, &optind);
691 			break;
692 		case OPT_BFREQ:
693 			ctl->opt_bfreq = set_opt_flag(ctl->opt_bfreq);
694 			ctl->opt_bfreq_f = parse_bfreq(av, optarg, &optind);
695 			break;
696 
697 		case OPT_VERSION:
698 			print_version(EXIT_SUCCESS);
699 		case OPT_HELP:
700 			usage();
701 		default:
702 			errtryhelp(EXIT_FAILURE);
703 		}
704 	}
705 }
706 
707 /* Return the specified terminfo string, or an empty string if no such
708  * terminfo capability exists.  */
ti_entry(const char * name)709 static char *ti_entry(const char *name)
710 {
711 	char *buf_ptr;
712 
713 	if ((buf_ptr = tigetstr(name)) == (char *)-1)
714 		buf_ptr = NULL;
715 	return buf_ptr;
716 }
717 
show_tabs(void)718 static void show_tabs(void)
719 {
720 	int i, co = tigetnum("cols");
721 
722 	if (co > 0) {
723 		printf("\r         ");
724 		for (i = 10; i < co - 2; i += 10)
725 			printf("%-10d", i);
726 		putchar('\n');
727 		for (i = 1; i <= co; i++)
728 			putchar(i % 10 + '0');
729 		putchar('\n');
730 		for (i = 1; i < co; i++)
731 			printf("\tT\b");
732 		putchar('\n');
733 	}
734 }
735 
open_snapshot_device(struct setterm_control * ctl)736 static int open_snapshot_device(struct setterm_control *ctl)
737 {
738 	int fd;
739 
740 	if (ctl->opt_sn_num)
741 		xasprintf(&ctl->in_device, "/dev/vcsa%d", ctl->opt_sn_num);
742 	else
743 		xasprintf(&ctl->in_device, "/dev/vcsa");
744 	fd = open(ctl->in_device, O_RDONLY);
745 	if (fd < 0)
746 		err(EXIT_DUMPFILE, _("cannot read %s"), ctl->in_device);
747 	return fd;
748 }
749 
set_blanking(struct setterm_control * ctl)750 static void set_blanking(struct setterm_control *ctl)
751 {
752 	char ioctlarg;
753 	int ret;
754 
755 	if (0 <= ctl->opt_bl_min) {
756 		printf("\033[9;%d]", ctl->opt_bl_min);
757 		return;
758 	}
759 	switch (ctl->opt_bl_min) {
760 	case BLANKSCREEN:
761 		ioctlarg = TIOCL_BLANKSCREEN;
762 		if (ioctl(STDIN_FILENO, TIOCLINUX, &ioctlarg))
763 			warn(_("cannot force blank"));
764 		break;
765 	case UNBLANKSCREEN:
766 		ioctlarg = TIOCL_UNBLANKSCREEN;
767 		if (ioctl(STDIN_FILENO, TIOCLINUX, &ioctlarg))
768 			warn(_("cannot force unblank"));
769 		break;
770 	case BLANKEDSCREEN:
771 		ioctlarg = TIOCL_BLANKEDSCREEN;
772 		ret = ioctl(STDIN_FILENO, TIOCLINUX, &ioctlarg);
773 		if (ret < 0)
774 			warn(_("cannot get blank status"));
775 		else
776 			printf("%d\n", ret);
777 		break;
778 	default:		/* should be impossible to reach */
779 		abort();
780 	}
781 }
782 
screendump(struct setterm_control * ctl)783 static void screendump(struct setterm_control *ctl)
784 {
785 	unsigned char header[4];
786 	unsigned int rows, cols;
787 	int fd;
788 	FILE *out;
789 	size_t i, j;
790 	ssize_t rc;
791 	char *inbuf, *outbuf, *p, *q;
792 
793 	/* open source and destination files */
794 	fd = open_snapshot_device(ctl);
795 	if (!ctl->opt_sn_name)
796 		ctl->opt_sn_name = "screen.dump";
797 	out = fopen(ctl->opt_sn_name, ctl->opt_snap ? "w" : "a");
798 	if (!out)
799 		err(EXIT_DUMPFILE, _("cannot open dump file %s for output"), ctl->opt_sn_name);
800 	/* determine snapshot size */
801 	if (read(fd, header, 4) != 4)
802 		err(EXIT_DUMPFILE, _("cannot read %s"), ctl->in_device);
803 	rows = header[0];
804 	cols = header[1];
805 	if (rows * cols == 0)
806 		err(EXIT_DUMPFILE, _("cannot read %s"), ctl->in_device);
807 	/* allocate buffers */
808 	inbuf = xmalloc(rows * cols * 2);
809 	outbuf = xmalloc(rows * (cols + 1));
810 	/* read input */
811 	rc = read(fd, inbuf, rows * cols * 2);
812 	if (rc < 0 || (size_t)rc != rows * cols * 2)
813 		err(EXIT_DUMPFILE, _("cannot read %s"), ctl->in_device);
814 	p = inbuf;
815 	q = outbuf;
816 	/* copy inbuf to outbuf */
817 	for (i = 0; i < rows; i++) {
818 		for (j = 0; j < cols; j++) {
819 			*q++ = *p;
820 			p += 2;
821 		}
822 		while (j-- > 0 && q[-1] == ' ')
823 			q--;
824 		*q++ = '\n';
825 	}
826 	fwrite(outbuf, 1, q - outbuf, out);
827 	/* clean up allocations */
828 	close(fd);
829 	free(inbuf);
830 	free(outbuf);
831 	free(ctl->in_device);
832 	if (close_stream(out) != 0)
833 		errx(EXIT_FAILURE, _("write error"));
834 }
835 
836 /* Some options are applicable when terminal is virtual console. */
vc_only(struct setterm_control * ctl,const char * err)837 static int vc_only(struct setterm_control *ctl, const char *err)
838 {
839 	if (!ctl->vcterm && err)
840 		warnx(_("terminal %s does not support %s"),
841 		      ctl->opt_te_terminal_name, err);
842 	return ctl->vcterm;
843 }
844 
tty_raw(struct termios * saved_attributes,int * saved_fl)845 static void tty_raw(struct termios *saved_attributes, int *saved_fl)
846 {
847 	struct termios tattr;
848 
849 	fcntl(STDIN_FILENO, F_GETFL, saved_fl);
850 	tcgetattr(STDIN_FILENO, saved_attributes);
851 	fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
852 	memcpy(&tattr, saved_attributes, sizeof(struct termios));
853 	tattr.c_lflag &= ~(ICANON | ECHO);
854 	tattr.c_cc[VMIN] = 1;
855 	tattr.c_cc[VTIME] = 0;
856 	tcsetattr(STDIN_FILENO, TCSAFLUSH, &tattr);
857 }
858 
tty_restore(struct termios * saved_attributes,int * saved_fl)859 static void tty_restore(struct termios *saved_attributes, int *saved_fl)
860 {
861 	fcntl(STDIN_FILENO, F_SETFL, *saved_fl);
862 	tcsetattr(STDIN_FILENO, TCSANOW, saved_attributes);
863 }
864 
select_wait(void)865 static int select_wait(void)
866 {
867 	struct timeval tv;
868 	fd_set set;
869 	int ret;
870 
871 	FD_ZERO(&set);
872 	FD_SET(STDIN_FILENO, &set);
873 	tv.tv_sec = 10;
874 	tv.tv_usec = 0;
875 	while ((ret = select(1, &set, NULL, NULL, &tv)) < 0) {
876 		if (errno == EINTR)
877 			continue;
878 		err(EXIT_FAILURE, _("select failed"));
879 	}
880 	return ret;
881 }
882 
resizetty(void)883 static int resizetty(void)
884 {
885 	/*
886 	 * \e7        Save current state (cursor coordinates, attributes,
887 	 *                character sets pointed at by G0, G1).
888 	 * \e[r       Set scrolling region; parameters are top and bottom row.
889 	 * \e[32766E  Move cursor down 32766 (INT16_MAX - 1) rows.
890 	 * \e[32766C  Move cursor right 32766 columns.
891 	 * \e[6n      Report cursor position.
892 	 * \e8        Restore state most recently saved by \e7.
893 	 */
894 	static const char *getpos = "\e7\e[r\e[32766E\e[32766C\e[6n\e8";
895 	char retstr[32];
896 	int row, col;
897 	size_t pos;
898 	ssize_t rc;
899 	struct winsize ws;
900 	struct termios saved_attributes;
901 	int saved_fl;
902 
903 	if (!isatty(STDIN_FILENO))
904 		errx(EXIT_FAILURE, _("stdin does not refer to a terminal"));
905 
906 	tty_raw(&saved_attributes, &saved_fl);
907 	if (write_all(STDIN_FILENO, getpos, strlen(getpos)) < 0) {
908 		warn(_("write failed"));
909 		tty_restore(&saved_attributes, &saved_fl);
910 		return 1;
911 	}
912 	for (pos = 0; pos < sizeof(retstr) - 1;) {
913 		if (0 == select_wait())
914 			break;
915 		if ((rc =
916 		     read(STDIN_FILENO, retstr + pos,
917 			  sizeof(retstr) - 1 - pos)) < 0) {
918 			if (errno == EINTR)
919 				continue;
920 			warn(_("read failed"));
921 			tty_restore(&saved_attributes, &saved_fl);
922 			return 1;
923 		}
924 		pos += rc;
925 		if (retstr[pos - 1] == 'R')
926 			break;
927 	}
928 	retstr[pos] = 0;
929 	tty_restore(&saved_attributes, &saved_fl);
930 	rc = sscanf(retstr, "\033[%d;%dR", &row, &col);
931 	if (rc != 2) {
932 		warnx(_("invalid cursor position: %s"), retstr);
933 		return 1;
934 	}
935 	memset(&ws, 0, sizeof(struct winsize));
936 	ioctl(STDIN_FILENO, TIOCGWINSZ, &ws);
937 	ws.ws_row = row;
938 	ws.ws_col = col;
939 	ioctl(STDIN_FILENO, TIOCSWINSZ, &ws);
940 	return 0;
941 }
942 
perform_sequence(struct setterm_control * ctl)943 static void perform_sequence(struct setterm_control *ctl)
944 {
945 	int result;
946 
947 	/* -reset. */
948 	if (ctl->opt_reset)
949 		putp(ti_entry("rs1"));
950 
951 	/* -resize. */
952 	if (ctl->opt_resize)
953 		if (resizetty())
954 			warnx(_("reset failed"));
955 
956 	/* -initialize. */
957 	if (ctl->opt_initialize)
958 		putp(ti_entry("is2"));
959 
960 	/* -cursor [on|off]. */
961 	if (ctl->opt_cursor) {
962 		if (ctl->opt_cu_on)
963 			putp(ti_entry("cnorm"));
964 		else
965 			putp(ti_entry("civis"));
966 	}
967 
968 	/* -linewrap [on|off]. */
969 	if (ctl->opt_linewrap)
970 		fputs(ctl->opt_li_on ? "\033[?7h" : "\033[?7l", stdout);
971 
972 	/* -repeat [on|off]. */
973 	if (ctl->opt_repeat && vc_only(ctl, "--repeat"))
974 		fputs(ctl->opt_rep_on ? "\033[?8h" : "\033[?8l", stdout);
975 
976 	/* -appcursorkeys [on|off]. */
977 	if (ctl->opt_appcursorkeys && vc_only(ctl, "--appcursorkeys"))
978 		fputs(ctl->opt_appck_on ? "\033[?1h" : "\033[?1l", stdout);
979 
980 	/* -default.  Vc sets default rendition, otherwise clears all
981 	 * attributes. */
982 	if (ctl->opt_default) {
983 		if (vc_only(ctl, NULL))
984 			printf("\033[0m");
985 		else
986 			putp(ti_entry("sgr0"));
987 	}
988 
989 	/* -foreground black|red|green|yellow|blue|magenta|cyan|white|default. */
990 	if (ctl->opt_foreground)
991 		printf("\033[3%c%s", '0' + ctl->opt_fo_color, "m");
992 
993 	/* -background black|red|green|yellow|blue|magenta|cyan|white|default. */
994 	if (ctl->opt_background)
995 		printf("\033[4%c%s", '0' + ctl->opt_ba_color, "m");
996 
997 	/* -ulcolor [bright] black|red|green|yellow|blue|magenta|cyan|white. */
998 	if (ctl->opt_ulcolor && vc_only(ctl, "--ulcolor"))
999 		printf("\033[1;%d]", ctl->opt_ul_color);
1000 
1001 	/* -hbcolor [bright] black|red|green|yellow|blue|magenta|cyan|white. */
1002 	if (ctl->opt_hbcolor)
1003 		printf("\033[2;%d]", ctl->opt_hb_color);
1004 
1005 	/* -inversescreen [on|off]. */
1006 	if (ctl->opt_inversescreen)
1007 		fputs(ctl->opt_invsc_on ? "\033[?5h" : "\033[?5l", stdout);
1008 
1009 	/* -bold [on|off].  Vc behaves as expected, otherwise off turns off
1010 	 * all attributes. */
1011 	if (ctl->opt_bold) {
1012 		if (ctl->opt_bo_on)
1013 			putp(ti_entry("bold"));
1014 		else {
1015 			if (vc_only(ctl, NULL))
1016 				fputs("\033[22m", stdout);
1017 			else
1018 				putp(ti_entry("sgr0"));
1019 		}
1020 	}
1021 
1022 	/* -half-bright [on|off].  Vc behaves as expected, otherwise off
1023 	 * turns off all attributes.  */
1024 	if (ctl->opt_halfbright) {
1025 		if (ctl->opt_hb_on)
1026 			putp(ti_entry("dim"));
1027 		else {
1028 			if (vc_only(ctl, NULL))
1029 				fputs("\033[22m", stdout);
1030 			else
1031 				putp(ti_entry("sgr0"));
1032 		}
1033 	}
1034 
1035 	/* -blink [on|off].  Vc behaves as expected, otherwise off turns off
1036 	 * all attributes. */
1037 	if (ctl->opt_blink) {
1038 		if (ctl->opt_bl_on)
1039 			putp(ti_entry("blink"));
1040 		else {
1041 			if (vc_only(ctl, NULL))
1042 				fputs("\033[25m", stdout);
1043 			else
1044 				putp(ti_entry("sgr0"));
1045 		}
1046 	}
1047 
1048 	/* -reverse [on|off].  Vc behaves as expected, otherwise off turns
1049 	 * off all attributes. */
1050 	if (ctl->opt_reverse) {
1051 		if (ctl->opt_re_on)
1052 			putp(ti_entry("rev"));
1053 		else {
1054 			if (vc_only(ctl, NULL))
1055 				fputs("\033[27m", stdout);
1056 			else
1057 				putp(ti_entry("sgr0"));
1058 		}
1059 	}
1060 
1061 	/* -underline [on|off]. */
1062 	if (ctl->opt_underline)
1063 		putp(ti_entry(ctl->opt_un_on ? "smul" : "rmul"));
1064 
1065 	/* -store. */
1066 	if (ctl->opt_store && vc_only(ctl, "--store"))
1067 		fputs("\033[8]", stdout);
1068 
1069 	/* -clear [all|rest]. */
1070 	if (ctl->opt_clear)
1071 		putp(ti_entry(ctl->opt_cl_all ? "clear" : "ed"));
1072 
1073 	/* -tabs. */
1074 	if (ctl->opt_tabs) {
1075 		if (ctl->opt_tb_array[0] == -1)
1076 			show_tabs();
1077 		else {
1078 			int i;
1079 
1080 			for (i = 0; ctl->opt_tb_array[i] > 0; i++)
1081 				printf("\033[%dG\033H", ctl->opt_tb_array[i]);
1082 			putchar('\r');
1083 		}
1084 	}
1085 
1086 	/* -clrtabs. */
1087 	if (ctl->opt_clrtabs && vc_only(ctl, "--clrtabs")) {
1088 		int i;
1089 
1090 		if (ctl->opt_tb_array[0] == -1)
1091 			fputs("\033[3g", stdout);
1092 		else
1093 			for (i = 0; ctl->opt_tb_array[i] > 0; i++)
1094 				printf("\033[%dG\033[g", ctl->opt_tb_array[i]);
1095 		putchar('\r');
1096 	}
1097 
1098 	/* -regtabs. */
1099 	if (ctl->opt_regtabs && vc_only(ctl, "--regtabs")) {
1100 		int i;
1101 
1102 		fputs("\033[3g\r", stdout);
1103 		for (i = ctl->opt_rt_len + 1; i <= TABS_MAX; i += ctl->opt_rt_len)
1104 			printf("\033[%dC\033H", ctl->opt_rt_len);
1105 		putchar('\r');
1106 	}
1107 
1108 	/* -blank [0-60]. */
1109 	if (ctl->opt_blank && vc_only(ctl, "--blank"))
1110 		set_blanking(ctl);
1111 
1112 	/* -powersave [on|vsync|hsync|powerdown|off] (console) */
1113 	if (ctl->opt_powersave) {
1114 		char ioctlarg[2];
1115 		ioctlarg[0] = TIOCL_SETVESABLANK;
1116 		ioctlarg[1] = ctl->opt_ps_mode;
1117 		if (ioctl(STDIN_FILENO, TIOCLINUX, ioctlarg))
1118 			warn(_("cannot (un)set powersave mode"));
1119 	}
1120 
1121 	/* -powerdown [0-60]. */
1122 	if (ctl->opt_powerdown)
1123 		printf("\033[14;%d]", ctl->opt_pd_min);
1124 
1125 	/* -snap [1-NR_CONS]. */
1126 	if (ctl->opt_snap || ctl->opt_append)
1127 		screendump(ctl);
1128 
1129 	/* -msg [on|off].  Controls printk's to console. */
1130 	if (ctl->opt_msg && vc_only(ctl, "--msg")) {
1131 		if (ctl->opt_msg_on)
1132 			result = klogctl(SYSLOG_ACTION_CONSOLE_ON, NULL, 0);
1133 		else
1134 			result = klogctl(SYSLOG_ACTION_CONSOLE_OFF, NULL, 0);
1135 
1136 		if (result != 0)
1137 			warn(_("klogctl error"));
1138 	}
1139 
1140 	/* -msglevel [0-8].  Console printk message level. */
1141 	if (ctl->opt_msglevel_num && vc_only(ctl, "--msglevel")) {
1142 		result =
1143 		    klogctl(SYSLOG_ACTION_CONSOLE_LEVEL, NULL,
1144 			    ctl->opt_msglevel_num);
1145 		if (result != 0)
1146 			warn(_("klogctl error"));
1147 	}
1148 
1149 	/* -blength [0-2000] */
1150 	if (ctl->opt_blength && vc_only(ctl, "--blength")) {
1151 		printf("\033[11;%d]", ctl->opt_blength_l);
1152 	}
1153 
1154 	/* -bfreq freqnumber */
1155 	if (ctl->opt_bfreq && vc_only(ctl, "--bfreq")) {
1156 		printf("\033[10;%d]", ctl->opt_bfreq_f);
1157 	}
1158 }
1159 
init_terminal(struct setterm_control * ctl)1160 static void init_terminal(struct setterm_control *ctl)
1161 {
1162 	int term_errno;
1163 
1164 	if (!ctl->opt_te_terminal_name) {
1165 		ctl->opt_te_terminal_name = getenv("TERM");
1166 		if (ctl->opt_te_terminal_name == NULL)
1167 			errx(EXIT_FAILURE, _("$TERM is not defined."));
1168 	}
1169 
1170 	/* Find terminfo entry. */
1171 	if (setupterm(ctl->opt_te_terminal_name, STDOUT_FILENO, &term_errno))
1172 		switch (term_errno) {
1173 		case -1:
1174 			errx(EXIT_FAILURE, _("terminfo database cannot be found"));
1175 		case 0:
1176 			errx(EXIT_FAILURE, _("%s: unknown terminal type"), ctl->opt_te_terminal_name);
1177 		case 1:
1178 			errx(EXIT_FAILURE, _("terminal is hardcopy"));
1179 		}
1180 
1181 	/* See if the terminal is a virtual console terminal. */
1182 	ctl->vcterm = (!strncmp(ctl->opt_te_terminal_name, "con", 3) ||
1183 		       !strncmp(ctl->opt_te_terminal_name, "linux", 5));
1184 }
1185 
1186 
main(int argc,char ** argv)1187 int main(int argc, char **argv)
1188 {
1189 	struct setterm_control ctl = { NULL };
1190 
1191 	setlocale(LC_ALL, "");
1192 	bindtextdomain(PACKAGE, LOCALEDIR);
1193 	textdomain(PACKAGE);
1194 	close_stdout_atexit();
1195 
1196 	if (argc < 2) {
1197 		warnx(_("bad usage"));
1198 		errtryhelp(EXIT_FAILURE);
1199 	}
1200 	parse_option(&ctl, argc, argv);
1201 	init_terminal(&ctl);
1202 	perform_sequence(&ctl);
1203 
1204 	return EXIT_SUCCESS;
1205 }
1206