]>
Commit | Line | Data |
---|---|---|
6dbe3af9 KZ |
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. | |
2bb3aa36 | 6 | * |
6dbe3af9 KZ |
7 | * Adaption to Linux by Peter MacDonald. |
8 | * | |
9 | * Enhancements by Mika Liljeberg (liljeber@cs.Helsinki.FI) | |
10 | * | |
fd6b7a7f KZ |
11 | * Beep modifications by Christophe Jolif (cjolif@storm.gatelink.fr.net) |
12 | * | |
13 | * Sanity increases by Cafeine Addict [sic]. | |
14 | * | |
eb63b9b8 | 15 | * Powersave features by todd j. derr <tjd@wordsmith.org> |
fd6b7a7f KZ |
16 | * |
17 | * Converted to terminfo by Kars de Jong (jongk@cs.utwente.nl) | |
6dbe3af9 | 18 | * |
b50945d4 | 19 | * 1999-02-22 Arkadiusz MiĆkiewicz <misiek@pld.ORG.PL> |
7eda085c KZ |
20 | * - added Native Language Support |
21 | * | |
6dbe3af9 KZ |
22 | * Semantics: |
23 | * | |
fd6b7a7f | 24 | * Setterm writes to standard output a character string that will |
871ffd09 | 25 | * invoke the specified terminal capabilities. Where possible |
fd6b7a7f KZ |
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. | |
6dbe3af9 KZ |
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 | |
fd6b7a7f | 47 | * values. */ |
6dbe3af9 | 48 | |
104ecc7e SK |
49 | #include <ctype.h> |
50 | #include <errno.h> | |
51 | #include <fcntl.h> | |
45c90b77 | 52 | #include <getopt.h> |
6dbe3af9 | 53 | #include <stdio.h> |
22853e4a | 54 | #include <stdlib.h> |
6dbe3af9 | 55 | #include <string.h> |
104ecc7e SK |
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> | |
bfc4331b | 62 | |
3947ca4c KZ |
63 | #if defined(HAVE_NCURSESW_TERM_H) |
64 | # include <ncursesw/term.h> | |
2ac1bc84 KZ |
65 | #elif defined(HAVE_NCURSES_TERM_H) |
66 | # include <ncurses/term.h> | |
3947ca4c KZ |
67 | #elif defined(HAVE_TERM_H) |
68 | # include <term.h> | |
5c36a0eb | 69 | #endif |
48d7b13a | 70 | |
5c55d9bf | 71 | #ifdef HAVE_LINUX_TIOCL_H |
104ecc7e | 72 | # include <linux/tiocl.h> |
5c55d9bf | 73 | #endif |
b6e899d9 | 74 | |
5d795999 | 75 | #include "all-io.h" |
b6e899d9 | 76 | #include "c.h" |
cdd2a8c3 | 77 | #include "closestream.h" |
104ecc7e | 78 | #include "nls.h" |
11f69359 | 79 | #include "optutils.h" |
35b578a0 | 80 | #include "strutils.h" |
104ecc7e | 81 | #include "xalloc.h" |
6dbe3af9 | 82 | |
fd6b7a7f | 83 | /* Constants. */ |
6dbe3af9 | 84 | |
261f1423 SK |
85 | /* Non-standard return values. */ |
86 | #define EXIT_DUMPFILE -1 | |
87 | ||
6dbe3af9 | 88 | /* Colors. */ |
010f219d SK |
89 | enum { |
90 | BLACK = 0, | |
91 | RED, | |
92 | GREEN, | |
93 | YELLOW, | |
94 | BLUE, | |
95 | MAGENTA, | |
96 | CYAN, | |
97 | WHITE, | |
98 | GREY, | |
99 | DEFAULT | |
100 | }; | |
6dbe3af9 | 101 | |
98297e65 KZ |
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 | ||
5c55d9bf | 117 | /* Blank commands */ |
010f219d SK |
118 | enum { |
119 | BLANKSCREEN = -1, | |
120 | UNBLANKSCREEN = -2, | |
121 | BLANKEDSCREEN = -3 | |
122 | }; | |
5c55d9bf ST |
123 | |
124 | /* <linux/tiocl.h> fallback */ | |
125 | #ifndef TIOCL_BLANKSCREEN | |
010f219d SK |
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 | }; | |
5c55d9bf ST |
132 | #endif |
133 | ||
3393c136 SK |
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 { | |
91746583 | 151 | CONSOLE_LEVEL_MIN = 0, |
3393c136 SK |
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 | ||
341566ff | 161 | /* Command controls. */ |
d87d20b0 SK |
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 */ | |
9e930041 | 167 | int opt_sn_num; /* console number to be snapshot */ |
d87d20b0 | 168 | char *opt_sn_name; /* path to write snap */ |
341566ff | 169 | char *in_device; /* device to snapshot */ |
9e930041 | 170 | int opt_msglevel_num; /* printk() logging level */ |
d87d20b0 SK |
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 */ | |
e9f62f38 | 176 | unsigned int opt_fo_color:4, opt_ba_color:4, opt_ul_color:4, opt_hb_color:4; |
d87d20b0 | 177 | /* boolean options */ |
d8c1fc7a SK |
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; | |
d87d20b0 | 182 | /* Option flags. Set when an option is invoked. */ |
5d795999 | 183 | uint64_t opt_term:1, opt_reset:1, opt_resize:1, opt_initialize:1, opt_cursor:1, |
d87d20b0 SK |
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 | }; | |
6dbe3af9 | 193 | |
98297e65 KZ |
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 | ||
45c90b77 SK |
206 | static int parse_febg_color(const char *arg) |
207 | { | |
98297e65 | 208 | int color = parse_color(arg); |
35b578a0 | 209 | |
98297e65 | 210 | if (color < 0) |
35b578a0 | 211 | color = strtos32_or_err(arg, _("argument error")); |
98297e65 KZ |
212 | |
213 | if (!is_valid_color(color) || color == GREY) | |
c8a6f83e | 214 | errx(EXIT_FAILURE, "%s: %s", _("argument error"), arg); |
35b578a0 | 215 | return color; |
6dbe3af9 KZ |
216 | } |
217 | ||
ef4d11d5 | 218 | static int parse_ulhb_color(char **av, int *oi) |
45c90b77 SK |
219 | { |
220 | char *color_name; | |
221 | int bright = 0; | |
222 | int color = -1; | |
223 | ||
ef4d11d5 | 224 | if (av[*oi] && strcmp(av[*oi - 1], "bright") == 0) { |
45c90b77 | 225 | bright = 1; |
ef4d11d5 SK |
226 | color_name = av[*oi]; |
227 | (*oi)++; | |
45c90b77 | 228 | } else |
ef4d11d5 | 229 | color_name = av[*oi - 1]; |
45c90b77 | 230 | |
98297e65 KZ |
231 | color = parse_color(color_name); |
232 | if (color < 0) | |
35b578a0 | 233 | color = strtos32_or_err(color_name, _("argument error")); |
c1b4cf10 | 234 | if (!is_valid_color(color) || color == DEFAULT) |
c8a6f83e | 235 | errx(EXIT_FAILURE, "%s: %s", _("argument error"), color_name); |
45c90b77 SK |
236 | if (bright && (color == BLACK || color == GREY)) |
237 | errx(EXIT_FAILURE, _("argument error: bright %s is not supported"), color_name); | |
238 | ||
3d886e86 JW |
239 | if (bright) |
240 | color |= 8; | |
241 | ||
45c90b77 | 242 | return color; |
6dbe3af9 KZ |
243 | } |
244 | ||
ef4d11d5 | 245 | static char *find_optional_arg(char **av, char *oa, int *oi) |
45c90b77 SK |
246 | { |
247 | char *arg; | |
ef4d11d5 SK |
248 | if (oa) |
249 | return oa; | |
042f62df RP |
250 | |
251 | arg = av[*oi]; | |
252 | if (!arg || arg[0] == '-') | |
253 | return NULL; | |
254 | ||
ef4d11d5 | 255 | (*oi)++; |
45c90b77 | 256 | return arg; |
6dbe3af9 KZ |
257 | } |
258 | ||
ef4d11d5 | 259 | static int parse_blank(char **av, char *oa, int *oi) |
45c90b77 SK |
260 | { |
261 | char *arg; | |
262 | ||
ef4d11d5 | 263 | arg = find_optional_arg(av, oa, oi); |
45c90b77 SK |
264 | if (!arg) |
265 | return BLANKEDSCREEN; | |
266 | if (!strcmp(arg, "force")) | |
267 | return BLANKSCREEN; | |
042f62df | 268 | if (!strcmp(arg, "poke")) |
45c90b77 | 269 | return UNBLANKSCREEN; |
45c90b77 | 270 | |
042f62df RP |
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; | |
6dbe3af9 KZ |
277 | } |
278 | ||
45c90b77 SK |
279 | static int parse_powersave(const char *arg) |
280 | { | |
281 | if (strcmp(arg, "on") == 0) | |
282 | return VESA_BLANK_MODE_SUSPENDV; | |
042f62df | 283 | if (strcmp(arg, "vsync") == 0) |
45c90b77 | 284 | return VESA_BLANK_MODE_SUSPENDV; |
042f62df | 285 | if (strcmp(arg, "hsync") == 0) |
45c90b77 | 286 | return VESA_BLANK_MODE_SUSPENDH; |
042f62df | 287 | if (strcmp(arg, "powerdown") == 0) |
45c90b77 | 288 | return VESA_BLANK_MODE_POWERDOWN; |
042f62df | 289 | if (strcmp(arg, "off") == 0) |
45c90b77 | 290 | return VESA_BLANK_MODE_OFF; |
c8a6f83e | 291 | errx(EXIT_FAILURE, "%s: %s", _("argument error"), arg); |
fd6b7a7f KZ |
292 | } |
293 | ||
45c90b77 SK |
294 | static int parse_msglevel(const char *arg) |
295 | { | |
35b578a0 | 296 | int ret; |
6dbe3af9 | 297 | |
35b578a0 | 298 | ret = strtos32_or_err(arg, _("argument error")); |
45c90b77 | 299 | if (ret < CONSOLE_LEVEL_MIN || CONSOLE_LEVEL_MAX < ret) |
c8a6f83e | 300 | errx(EXIT_FAILURE, "%s: %s", _("argument error"), arg); |
45c90b77 | 301 | return ret; |
6dbe3af9 KZ |
302 | } |
303 | ||
ef4d11d5 | 304 | static int parse_snap(char **av, char *oa, int *oi) |
45c90b77 | 305 | { |
35b578a0 | 306 | int ret; |
45c90b77 SK |
307 | char *arg; |
308 | ||
ef4d11d5 | 309 | arg = find_optional_arg(av, oa, oi); |
45c90b77 SK |
310 | if (!arg) |
311 | return 0; | |
35b578a0 | 312 | ret = strtos32_or_err(arg, _("argument error")); |
45c90b77 | 313 | if (ret < 1) |
c8a6f83e | 314 | errx(EXIT_FAILURE, "%s: %s", _("argument error"), arg); |
45c90b77 | 315 | return ret; |
6dbe3af9 KZ |
316 | } |
317 | ||
ef4d11d5 | 318 | static void parse_tabs(char **av, char *oa, int *oi, int *tab_array) |
45c90b77 SK |
319 | { |
320 | int i = 0; | |
6dbe3af9 | 321 | |
ef4d11d5 SK |
322 | if (oa) { |
323 | tab_array[i] = strtos32_or_err(oa, _("argument error")); | |
45c90b77 | 324 | i++; |
22853e4a | 325 | } |
ef4d11d5 | 326 | while (av[*oi]) { |
45c90b77 SK |
327 | if (TABS_MAX < i) |
328 | errx(EXIT_FAILURE, _("too many tabs")); | |
ef4d11d5 | 329 | if (av[*oi][0] == '-') |
45c90b77 | 330 | break; |
ef4d11d5 SK |
331 | tab_array[i] = strtos32_or_err(av[*oi], _("argument error")); |
332 | (*oi)++; | |
45c90b77 | 333 | i++; |
22853e4a | 334 | } |
45c90b77 | 335 | tab_array[i] = -1; |
6dbe3af9 KZ |
336 | } |
337 | ||
ef4d11d5 | 338 | static int parse_regtabs(char **av, char *oa, int *oi) |
45c90b77 SK |
339 | { |
340 | int ret; | |
341 | char *arg; | |
342 | ||
ef4d11d5 | 343 | arg = find_optional_arg(av, oa, oi); |
45c90b77 SK |
344 | if (!arg) |
345 | return DEFAULT_TAB_LEN; | |
35b578a0 | 346 | ret = strtos32_or_err(arg, _("argument error")); |
45c90b77 | 347 | if (ret < 1 || TABS_MAX < ret) |
c8a6f83e | 348 | errx(EXIT_FAILURE, "%s: %s", _("argument error"), arg); |
45c90b77 | 349 | return ret; |
6dbe3af9 KZ |
350 | } |
351 | ||
ef4d11d5 | 352 | static int parse_blength(char **av, char *oa, int *oi) |
45c90b77 SK |
353 | { |
354 | int ret = -1; | |
355 | char *arg; | |
356 | ||
ef4d11d5 | 357 | arg = find_optional_arg(av, oa, oi); |
45c90b77 SK |
358 | if (!arg) |
359 | return 0; | |
35b578a0 | 360 | ret = strtos32_or_err(arg, _("argument error")); |
45c90b77 | 361 | if (ret < 0 || BLENGTH_MAX < ret) |
c8a6f83e | 362 | errx(EXIT_FAILURE, "%s: %s", _("argument error"), arg); |
45c90b77 | 363 | return ret; |
fd6b7a7f KZ |
364 | } |
365 | ||
ef4d11d5 | 366 | static int parse_bfreq(char **av, char *oa, int *oi) |
45c90b77 SK |
367 | { |
368 | char *arg; | |
369 | ||
ef4d11d5 | 370 | arg = find_optional_arg(av, oa, oi); |
45c90b77 SK |
371 | if (!arg) |
372 | return 0; | |
35b578a0 | 373 | return strtos32_or_err(arg, _("argument error")); |
fd6b7a7f KZ |
374 | } |
375 | ||
6e1eda6f | 376 | static void __attribute__((__noreturn__)) usage(void) |
253e5e71 | 377 | { |
6e1eda6f | 378 | FILE *out = stdout; |
253e5e71 | 379 | fputs(USAGE_HEADER, out); |
a1e04e8f KZ |
380 | fprintf(out, |
381 | _(" %s [options]\n"), program_invocation_short_name); | |
451dbcfa BS |
382 | |
383 | fputs(USAGE_SEPARATOR, out); | |
384 | fputs(_("Set the attributes of a terminal.\n"), out); | |
385 | ||
253e5e71 | 386 | fputs(USAGE_OPTIONS, out); |
bc6439ba KZ |
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); | |
b3054454 | 443 | |
f45f3ec3 | 444 | printf(USAGE_MAN_TAIL("setterm(1)")); |
6e1eda6f | 445 | exit(EXIT_SUCCESS); |
faa0548d | 446 | } |
6dbe3af9 | 447 | |
d87d20b0 | 448 | static int __attribute__((__pure__)) set_opt_flag(int opt) |
45c90b77 | 449 | { |
d87d20b0 | 450 | if (opt) |
45c90b77 | 451 | errx(EXIT_FAILURE, _("duplicate use of an option")); |
d87d20b0 | 452 | return 1; |
45c90b77 | 453 | } |
6dbe3af9 | 454 | |
ef4d11d5 | 455 | static void parse_option(struct setterm_control *ctl, int ac, char **av) |
45c90b77 SK |
456 | { |
457 | int c; | |
458 | enum { | |
459 | OPT_TERM = CHAR_MAX + 1, | |
460 | OPT_RESET, | |
5d795999 | 461 | OPT_RESIZE, |
45c90b77 SK |
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}, | |
5d795999 | 499 | {"resize", no_argument, NULL, OPT_RESIZE}, |
45c90b77 SK |
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}, | |
45c90b77 SK |
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}, | |
36ce5eba | 517 | {"clear", optional_argument, NULL, OPT_CLEAR}, |
45c90b77 SK |
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 | }; | |
11f69359 SK |
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; | |
45c90b77 | 542 | |
ef4d11d5 | 543 | while ((c = getopt_long_only(ac, av, "", longopts, NULL)) != -1) { |
11f69359 | 544 | err_exclusive_options(c, longopts, excl, excl_st); |
45c90b77 SK |
545 | switch (c) { |
546 | case OPT_TERM: | |
d87d20b0 SK |
547 | ctl->opt_term = set_opt_flag(ctl->opt_term); |
548 | ctl->opt_te_terminal_name = optarg; | |
45c90b77 SK |
549 | break; |
550 | case OPT_RESET: | |
d87d20b0 | 551 | ctl->opt_reset = set_opt_flag(ctl->opt_reset); |
45c90b77 | 552 | break; |
5d795999 SK |
553 | case OPT_RESIZE: |
554 | ctl->opt_resize = set_opt_flag(ctl->opt_resize); | |
555 | break; | |
45c90b77 | 556 | case OPT_INITIALIZE: |
d87d20b0 | 557 | ctl->opt_initialize = set_opt_flag(ctl->opt_initialize); |
45c90b77 SK |
558 | break; |
559 | case OPT_CURSOR: | |
d87d20b0 | 560 | ctl->opt_cursor = set_opt_flag(ctl->opt_cursor); |
30b294c4 KZ |
561 | ctl->opt_cu_on = parse_switch(optarg, _("argument error"), |
562 | "on", "off", NULL); | |
45c90b77 SK |
563 | break; |
564 | case OPT_REPEAT: | |
d87d20b0 | 565 | ctl->opt_repeat = set_opt_flag(ctl->opt_repeat); |
30b294c4 KZ |
566 | ctl->opt_rep_on = parse_switch(optarg, _("argument error"), |
567 | "on", "off", NULL); | |
45c90b77 SK |
568 | break; |
569 | case OPT_APPCURSORKEYS: | |
d87d20b0 | 570 | ctl->opt_appcursorkeys = set_opt_flag(ctl->opt_appcursorkeys); |
30b294c4 KZ |
571 | ctl->opt_appck_on = parse_switch(optarg, _("argument error"), |
572 | "on", "off", NULL); | |
45c90b77 SK |
573 | break; |
574 | case OPT_LINEWRAP: | |
d87d20b0 | 575 | ctl->opt_linewrap = set_opt_flag(ctl->opt_linewrap); |
30b294c4 KZ |
576 | ctl->opt_li_on = parse_switch(optarg, _("argument error"), |
577 | "on", "off", NULL); | |
45c90b77 SK |
578 | break; |
579 | case OPT_DEFAULT: | |
d87d20b0 | 580 | ctl->opt_default = set_opt_flag(ctl->opt_default); |
45c90b77 SK |
581 | break; |
582 | case OPT_FOREGROUND: | |
d87d20b0 SK |
583 | ctl->opt_foreground = set_opt_flag(ctl->opt_foreground); |
584 | ctl->opt_fo_color = parse_febg_color(optarg); | |
45c90b77 SK |
585 | break; |
586 | case OPT_BACKGROUND: | |
d87d20b0 SK |
587 | ctl->opt_background = set_opt_flag(ctl->opt_background); |
588 | ctl->opt_ba_color = parse_febg_color(optarg); | |
45c90b77 SK |
589 | break; |
590 | case OPT_ULCOLOR: | |
d87d20b0 | 591 | ctl->opt_ulcolor = set_opt_flag(ctl->opt_ulcolor); |
ef4d11d5 | 592 | ctl->opt_ul_color = parse_ulhb_color(av, &optind); |
45c90b77 SK |
593 | break; |
594 | case OPT_HBCOLOR: | |
d87d20b0 | 595 | ctl->opt_hbcolor = set_opt_flag(ctl->opt_hbcolor); |
ef4d11d5 | 596 | ctl->opt_hb_color = parse_ulhb_color(av, &optind); |
45c90b77 SK |
597 | break; |
598 | case OPT_INVERSESCREEN: | |
d87d20b0 | 599 | ctl->opt_inversescreen = set_opt_flag(ctl->opt_inversescreen); |
30b294c4 KZ |
600 | ctl->opt_invsc_on = parse_switch(optarg, _("argument error"), |
601 | "on", "off", NULL); | |
45c90b77 SK |
602 | break; |
603 | case OPT_BOLD: | |
d87d20b0 | 604 | ctl->opt_bold = set_opt_flag(ctl->opt_bold); |
30b294c4 KZ |
605 | ctl->opt_bo_on = parse_switch(optarg, _("argument error"), |
606 | "on", "off", NULL); | |
45c90b77 SK |
607 | break; |
608 | case OPT_HALF_BRIGHT: | |
d87d20b0 | 609 | ctl->opt_halfbright = set_opt_flag(ctl->opt_halfbright); |
30b294c4 KZ |
610 | ctl->opt_hb_on = parse_switch(optarg, _("argument error"), |
611 | "on", "off", NULL); | |
45c90b77 SK |
612 | break; |
613 | case OPT_BLINK: | |
d87d20b0 | 614 | ctl->opt_blink = set_opt_flag(ctl->opt_blink); |
30b294c4 KZ |
615 | ctl->opt_bl_on = parse_switch(optarg, _("argument error"), |
616 | "on", "off", NULL); | |
45c90b77 SK |
617 | break; |
618 | case OPT_REVERSE: | |
d87d20b0 | 619 | ctl->opt_reverse = set_opt_flag(ctl->opt_reverse); |
30b294c4 KZ |
620 | ctl->opt_re_on = parse_switch(optarg, _("argument error"), |
621 | "on", "off", NULL); | |
45c90b77 SK |
622 | break; |
623 | case OPT_UNDERLINE: | |
d87d20b0 | 624 | ctl->opt_underline = set_opt_flag(ctl->opt_underline); |
30b294c4 KZ |
625 | ctl->opt_un_on = parse_switch(optarg, _("argument error"), |
626 | "on", "off", NULL); | |
45c90b77 SK |
627 | break; |
628 | case OPT_STORE: | |
d87d20b0 | 629 | ctl->opt_store = set_opt_flag(ctl->opt_store); |
45c90b77 SK |
630 | break; |
631 | case OPT_CLEAR: | |
d87d20b0 | 632 | ctl->opt_clear = set_opt_flag(ctl->opt_clear); |
36ce5eba KZ |
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; | |
45c90b77 SK |
638 | break; |
639 | case OPT_TABS: | |
d87d20b0 | 640 | ctl->opt_tabs = set_opt_flag(ctl->opt_tabs); |
ef4d11d5 | 641 | parse_tabs(av, optarg, &optind, ctl->opt_tb_array); |
45c90b77 SK |
642 | break; |
643 | case OPT_CLRTABS: | |
d87d20b0 | 644 | ctl->opt_clrtabs = set_opt_flag(ctl->opt_clrtabs); |
ef4d11d5 | 645 | parse_tabs(av, optarg, &optind, ctl->opt_tb_array); |
45c90b77 SK |
646 | break; |
647 | case OPT_REGTABS: | |
d87d20b0 | 648 | ctl->opt_regtabs = set_opt_flag(ctl->opt_regtabs); |
ef4d11d5 | 649 | ctl->opt_rt_len = parse_regtabs(av, optarg, &optind); |
45c90b77 SK |
650 | break; |
651 | case OPT_BLANK: | |
d87d20b0 | 652 | ctl->opt_blank = set_opt_flag(ctl->opt_blank); |
ef4d11d5 | 653 | ctl->opt_bl_min = parse_blank(av, optarg, &optind); |
45c90b77 SK |
654 | break; |
655 | case OPT_DUMP: | |
d87d20b0 | 656 | ctl->opt_snap = set_opt_flag(ctl->opt_snap); |
ef4d11d5 | 657 | ctl->opt_sn_num = parse_snap(av, optarg, &optind); |
45c90b77 SK |
658 | break; |
659 | case OPT_APPEND: | |
d87d20b0 | 660 | ctl->opt_append = set_opt_flag(ctl->opt_append); |
ef4d11d5 | 661 | ctl->opt_sn_num = parse_snap(av, optarg, &optind); |
45c90b77 SK |
662 | break; |
663 | case OPT_FILE: | |
d87d20b0 SK |
664 | ctl->opt_snapfile = set_opt_flag(ctl->opt_snapfile); |
665 | ctl->opt_sn_name = optarg; | |
45c90b77 SK |
666 | break; |
667 | case OPT_MSG: | |
d87d20b0 | 668 | ctl->opt_msg = set_opt_flag(ctl->opt_msg); |
30b294c4 KZ |
669 | ctl->opt_msg_on = parse_switch(optarg, _("argument error"), |
670 | "on", "off", NULL); | |
45c90b77 SK |
671 | break; |
672 | case OPT_MSGLEVEL: | |
d87d20b0 SK |
673 | ctl->opt_msglevel = set_opt_flag(ctl->opt_msglevel); |
674 | ctl->opt_msglevel_num = parse_msglevel(optarg); | |
91746583 SK |
675 | if (ctl->opt_msglevel_num == 0) { |
676 | ctl->opt_msg = set_opt_flag(ctl->opt_msg); | |
677 | ctl->opt_msg_on |= 1; | |
678 | } | |
45c90b77 SK |
679 | break; |
680 | case OPT_POWERSAVE: | |
d87d20b0 SK |
681 | ctl->opt_powersave = set_opt_flag(ctl->opt_powersave); |
682 | ctl->opt_ps_mode = parse_powersave(optarg); | |
45c90b77 SK |
683 | break; |
684 | case OPT_POWERDOWN: | |
d87d20b0 | 685 | ctl->opt_powerdown = set_opt_flag(ctl->opt_powerdown); |
ef4d11d5 | 686 | ctl->opt_pd_min = parse_blank(av, optarg, &optind); |
45c90b77 SK |
687 | break; |
688 | case OPT_BLENGTH: | |
d87d20b0 | 689 | ctl->opt_blength = set_opt_flag(ctl->opt_blength); |
ef4d11d5 | 690 | ctl->opt_blength_l = parse_blength(av, optarg, &optind); |
45c90b77 SK |
691 | break; |
692 | case OPT_BFREQ: | |
d87d20b0 | 693 | ctl->opt_bfreq = set_opt_flag(ctl->opt_bfreq); |
ef4d11d5 | 694 | ctl->opt_bfreq_f = parse_bfreq(av, optarg, &optind); |
45c90b77 | 695 | break; |
2c308875 | 696 | |
45c90b77 | 697 | case OPT_VERSION: |
2c308875 | 698 | print_version(EXIT_SUCCESS); |
45c90b77 | 699 | case OPT_HELP: |
6e1eda6f | 700 | usage(); |
45c90b77 | 701 | default: |
677ec86c | 702 | errtryhelp(EXIT_FAILURE); |
45c90b77 SK |
703 | } |
704 | } | |
6dbe3af9 KZ |
705 | } |
706 | ||
16279cc2 SK |
707 | /* Return the specified terminfo string, or an empty string if no such |
708 | * terminfo capability exists. */ | |
709 | static char *ti_entry(const char *name) | |
710 | { | |
22853e4a | 711 | char *buf_ptr; |
6dbe3af9 | 712 | |
df3bc05a | 713 | if ((buf_ptr = tigetstr(name)) == (char *)-1) |
ffc43748 | 714 | buf_ptr = NULL; |
22853e4a | 715 | return buf_ptr; |
6dbe3af9 KZ |
716 | } |
717 | ||
c591be87 SK |
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 | ||
341566ff | 736 | static int open_snapshot_device(struct setterm_control *ctl) |
c591be87 | 737 | { |
c591be87 | 738 | int fd; |
c591be87 | 739 | |
609d4853 SK |
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); | |
341566ff | 745 | if (fd < 0) |
cda2b5b9 | 746 | err(EXIT_DUMPFILE, _("cannot read %s"), ctl->in_device); |
341566ff SK |
747 | return fd; |
748 | } | |
749 | ||
7a20c8a1 SK |
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 | } | |
7a20c8a1 SK |
781 | } |
782 | ||
341566ff SK |
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; | |
341566ff SK |
792 | |
793 | /* open source and destination files */ | |
794 | fd = open_snapshot_device(ctl); | |
795 | if (!ctl->opt_sn_name) | |
4636f061 | 796 | ctl->opt_sn_name = "screen.dump"; |
341566ff SK |
797 | out = fopen(ctl->opt_sn_name, ctl->opt_snap ? "w" : "a"); |
798 | if (!out) | |
223939d9 | 799 | err(EXIT_DUMPFILE, _("cannot open dump file %s for output"), ctl->opt_sn_name); |
341566ff | 800 | /* determine snapshot size */ |
c591be87 | 801 | if (read(fd, header, 4) != 4) |
cda2b5b9 | 802 | err(EXIT_DUMPFILE, _("cannot read %s"), ctl->in_device); |
c591be87 SK |
803 | rows = header[0]; |
804 | cols = header[1]; | |
805 | if (rows * cols == 0) | |
cda2b5b9 | 806 | err(EXIT_DUMPFILE, _("cannot read %s"), ctl->in_device); |
341566ff | 807 | /* allocate buffers */ |
c591be87 SK |
808 | inbuf = xmalloc(rows * cols * 2); |
809 | outbuf = xmalloc(rows * (cols + 1)); | |
341566ff | 810 | /* read input */ |
c591be87 SK |
811 | rc = read(fd, inbuf, rows * cols * 2); |
812 | if (rc < 0 || (size_t)rc != rows * cols * 2) | |
cda2b5b9 | 813 | err(EXIT_DUMPFILE, _("cannot read %s"), ctl->in_device); |
c591be87 SK |
814 | p = inbuf; |
815 | q = outbuf; | |
341566ff | 816 | /* copy inbuf to outbuf */ |
c591be87 SK |
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 | } | |
341566ff SK |
826 | fwrite(outbuf, 1, q - outbuf, out); |
827 | /* clean up allocations */ | |
c591be87 SK |
828 | close(fd); |
829 | free(inbuf); | |
830 | free(outbuf); | |
609d4853 | 831 | free(ctl->in_device); |
341566ff SK |
832 | if (close_stream(out) != 0) |
833 | errx(EXIT_FAILURE, _("write error")); | |
c591be87 SK |
834 | } |
835 | ||
da27df25 SK |
836 | /* Some options are applicable when terminal is virtual console. */ |
837 | static int vc_only(struct setterm_control *ctl, const char *err) | |
838 | { | |
74ce680a SK |
839 | if (!ctl->vcterm && err) |
840 | warnx(_("terminal %s does not support %s"), | |
841 | ctl->opt_te_terminal_name, err); | |
da27df25 SK |
842 | return ctl->vcterm; |
843 | } | |
844 | ||
5d795999 SK |
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 | ||
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 | ||
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 | ||
883 | static int resizetty(void) | |
884 | { | |
2bb3aa36 | 885 | /* |
5d795999 SK |
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 | ||
d87d20b0 SK |
943 | static void perform_sequence(struct setterm_control *ctl) |
944 | { | |
22853e4a | 945 | int result; |
6dbe3af9 | 946 | |
22853e4a | 947 | /* -reset. */ |
16279cc2 | 948 | if (ctl->opt_reset) |
22853e4a | 949 | putp(ti_entry("rs1")); |
6dbe3af9 | 950 | |
5d795999 SK |
951 | /* -resize. */ |
952 | if (ctl->opt_resize) | |
953 | if (resizetty()) | |
954 | warnx(_("reset failed")); | |
955 | ||
22853e4a | 956 | /* -initialize. */ |
16279cc2 | 957 | if (ctl->opt_initialize) |
22853e4a | 958 | putp(ti_entry("is2")); |
6dbe3af9 | 959 | |
22853e4a | 960 | /* -cursor [on|off]. */ |
d87d20b0 SK |
961 | if (ctl->opt_cursor) { |
962 | if (ctl->opt_cu_on) | |
22853e4a KZ |
963 | putp(ti_entry("cnorm")); |
964 | else | |
965 | putp(ti_entry("civis")); | |
966 | } | |
6dbe3af9 | 967 | |
da27df25 | 968 | /* -linewrap [on|off]. */ |
78dd23cb | 969 | if (ctl->opt_linewrap) |
d87d20b0 | 970 | fputs(ctl->opt_li_on ? "\033[?7h" : "\033[?7l", stdout); |
6dbe3af9 | 971 | |
da27df25 SK |
972 | /* -repeat [on|off]. */ |
973 | if (ctl->opt_repeat && vc_only(ctl, "--repeat")) | |
d87d20b0 | 974 | fputs(ctl->opt_rep_on ? "\033[?8h" : "\033[?8l", stdout); |
6dbe3af9 | 975 | |
da27df25 SK |
976 | /* -appcursorkeys [on|off]. */ |
977 | if (ctl->opt_appcursorkeys && vc_only(ctl, "--appcursorkeys")) | |
d87d20b0 | 978 | fputs(ctl->opt_appck_on ? "\033[?1h" : "\033[?1l", stdout); |
6dbe3af9 | 979 | |
22853e4a | 980 | /* -default. Vc sets default rendition, otherwise clears all |
16279cc2 | 981 | * attributes. */ |
d87d20b0 | 982 | if (ctl->opt_default) { |
da27df25 | 983 | if (vc_only(ctl, NULL)) |
22853e4a | 984 | printf("\033[0m"); |
6dbe3af9 | 985 | else |
fd6b7a7f | 986 | putp(ti_entry("sgr0")); |
6dbe3af9 | 987 | } |
22853e4a | 988 | |
da27df25 | 989 | /* -foreground black|red|green|yellow|blue|magenta|cyan|white|default. */ |
78dd23cb | 990 | if (ctl->opt_foreground) |
d87d20b0 | 991 | printf("\033[3%c%s", '0' + ctl->opt_fo_color, "m"); |
22853e4a | 992 | |
da27df25 | 993 | /* -background black|red|green|yellow|blue|magenta|cyan|white|default. */ |
78dd23cb | 994 | if (ctl->opt_background) |
d87d20b0 | 995 | printf("\033[4%c%s", '0' + ctl->opt_ba_color, "m"); |
22853e4a | 996 | |
bec23f0b | 997 | /* -ulcolor [bright] black|red|green|yellow|blue|magenta|cyan|white. */ |
da27df25 | 998 | if (ctl->opt_ulcolor && vc_only(ctl, "--ulcolor")) |
d87d20b0 | 999 | printf("\033[1;%d]", ctl->opt_ul_color); |
22853e4a | 1000 | |
bec23f0b | 1001 | /* -hbcolor [bright] black|red|green|yellow|blue|magenta|cyan|white. */ |
78dd23cb | 1002 | if (ctl->opt_hbcolor) |
d87d20b0 | 1003 | printf("\033[2;%d]", ctl->opt_hb_color); |
22853e4a | 1004 | |
da27df25 | 1005 | /* -inversescreen [on|off]. */ |
78dd23cb | 1006 | if (ctl->opt_inversescreen) |
d87d20b0 | 1007 | fputs(ctl->opt_invsc_on ? "\033[?5h" : "\033[?5l", stdout); |
22853e4a KZ |
1008 | |
1009 | /* -bold [on|off]. Vc behaves as expected, otherwise off turns off | |
16279cc2 | 1010 | * all attributes. */ |
d87d20b0 SK |
1011 | if (ctl->opt_bold) { |
1012 | if (ctl->opt_bo_on) | |
22853e4a KZ |
1013 | putp(ti_entry("bold")); |
1014 | else { | |
da27df25 | 1015 | if (vc_only(ctl, NULL)) |
6ed37604 | 1016 | fputs("\033[22m", stdout); |
22853e4a KZ |
1017 | else |
1018 | putp(ti_entry("sgr0")); | |
1019 | } | |
1020 | } | |
1021 | ||
16279cc2 SK |
1022 | /* -half-bright [on|off]. Vc behaves as expected, otherwise off |
1023 | * turns off all attributes. */ | |
d87d20b0 SK |
1024 | if (ctl->opt_halfbright) { |
1025 | if (ctl->opt_hb_on) | |
22853e4a KZ |
1026 | putp(ti_entry("dim")); |
1027 | else { | |
da27df25 | 1028 | if (vc_only(ctl, NULL)) |
6ed37604 | 1029 | fputs("\033[22m", stdout); |
22853e4a KZ |
1030 | else |
1031 | putp(ti_entry("sgr0")); | |
1032 | } | |
1033 | } | |
1034 | ||
1035 | /* -blink [on|off]. Vc behaves as expected, otherwise off turns off | |
16279cc2 | 1036 | * all attributes. */ |
d87d20b0 SK |
1037 | if (ctl->opt_blink) { |
1038 | if (ctl->opt_bl_on) | |
22853e4a KZ |
1039 | putp(ti_entry("blink")); |
1040 | else { | |
da27df25 | 1041 | if (vc_only(ctl, NULL)) |
6ed37604 | 1042 | fputs("\033[25m", stdout); |
22853e4a KZ |
1043 | else |
1044 | putp(ti_entry("sgr0")); | |
1045 | } | |
1046 | } | |
1047 | ||
1048 | /* -reverse [on|off]. Vc behaves as expected, otherwise off turns | |
16279cc2 | 1049 | * off all attributes. */ |
d87d20b0 SK |
1050 | if (ctl->opt_reverse) { |
1051 | if (ctl->opt_re_on) | |
22853e4a KZ |
1052 | putp(ti_entry("rev")); |
1053 | else { | |
da27df25 | 1054 | if (vc_only(ctl, NULL)) |
6ed37604 | 1055 | fputs("\033[27m", stdout); |
22853e4a KZ |
1056 | else |
1057 | putp(ti_entry("sgr0")); | |
1058 | } | |
1059 | } | |
1060 | ||
1061 | /* -underline [on|off]. */ | |
d87d20b0 SK |
1062 | if (ctl->opt_underline) |
1063 | putp(ti_entry(ctl->opt_un_on ? "smul" : "rmul")); | |
22853e4a | 1064 | |
da27df25 SK |
1065 | /* -store. */ |
1066 | if (ctl->opt_store && vc_only(ctl, "--store")) | |
6ed37604 | 1067 | fputs("\033[8]", stdout); |
22853e4a KZ |
1068 | |
1069 | /* -clear [all|rest]. */ | |
d87d20b0 SK |
1070 | if (ctl->opt_clear) |
1071 | putp(ti_entry(ctl->opt_cl_all ? "clear" : "ed")); | |
22853e4a | 1072 | |
da27df25 | 1073 | /* -tabs. */ |
78dd23cb | 1074 | if (ctl->opt_tabs) { |
d87d20b0 | 1075 | if (ctl->opt_tb_array[0] == -1) |
22853e4a KZ |
1076 | show_tabs(); |
1077 | else { | |
78dd23cb SK |
1078 | int i; |
1079 | ||
16279cc2 | 1080 | for (i = 0; ctl->opt_tb_array[i] > 0; i++) |
d87d20b0 | 1081 | printf("\033[%dG\033H", ctl->opt_tb_array[i]); |
22853e4a KZ |
1082 | putchar('\r'); |
1083 | } | |
1084 | } | |
1085 | ||
da27df25 SK |
1086 | /* -clrtabs. */ |
1087 | if (ctl->opt_clrtabs && vc_only(ctl, "--clrtabs")) { | |
22853e4a KZ |
1088 | int i; |
1089 | ||
d87d20b0 | 1090 | if (ctl->opt_tb_array[0] == -1) |
6ed37604 | 1091 | fputs("\033[3g", stdout); |
6dbe3af9 | 1092 | else |
16279cc2 | 1093 | for (i = 0; ctl->opt_tb_array[i] > 0; i++) |
d87d20b0 | 1094 | printf("\033[%dG\033[g", ctl->opt_tb_array[i]); |
22853e4a | 1095 | putchar('\r'); |
6dbe3af9 | 1096 | } |
6dbe3af9 | 1097 | |
da27df25 SK |
1098 | /* -regtabs. */ |
1099 | if (ctl->opt_regtabs && vc_only(ctl, "--regtabs")) { | |
22853e4a KZ |
1100 | int i; |
1101 | ||
6ed37604 | 1102 | fputs("\033[3g\r", stdout); |
16279cc2 SK |
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); | |
22853e4a KZ |
1105 | putchar('\r'); |
1106 | } | |
1107 | ||
1108 | /* -blank [0-60]. */ | |
7a20c8a1 SK |
1109 | if (ctl->opt_blank && vc_only(ctl, "--blank")) |
1110 | set_blanking(ctl); | |
5c55d9bf | 1111 | |
22853e4a | 1112 | /* -powersave [on|vsync|hsync|powerdown|off] (console) */ |
d87d20b0 | 1113 | if (ctl->opt_powersave) { |
22853e4a | 1114 | char ioctlarg[2]; |
5c55d9bf | 1115 | ioctlarg[0] = TIOCL_SETVESABLANK; |
d87d20b0 | 1116 | ioctlarg[1] = ctl->opt_ps_mode; |
16279cc2 | 1117 | if (ioctl(STDIN_FILENO, TIOCLINUX, ioctlarg)) |
261f1423 | 1118 | warn(_("cannot (un)set powersave mode")); |
22853e4a KZ |
1119 | } |
1120 | ||
1121 | /* -powerdown [0-60]. */ | |
16279cc2 | 1122 | if (ctl->opt_powerdown) |
d87d20b0 | 1123 | printf("\033[14;%d]", ctl->opt_pd_min); |
fd6b7a7f | 1124 | |
22853e4a | 1125 | /* -snap [1-NR_CONS]. */ |
16279cc2 | 1126 | if (ctl->opt_snap || ctl->opt_append) |
341566ff | 1127 | screendump(ctl); |
22853e4a | 1128 | |
16279cc2 | 1129 | /* -msg [on|off]. Controls printk's to console. */ |
da27df25 | 1130 | if (ctl->opt_msg && vc_only(ctl, "--msg")) { |
d87d20b0 | 1131 | if (ctl->opt_msg_on) |
3393c136 | 1132 | result = klogctl(SYSLOG_ACTION_CONSOLE_ON, NULL, 0); |
22853e4a | 1133 | else |
3393c136 | 1134 | result = klogctl(SYSLOG_ACTION_CONSOLE_OFF, NULL, 0); |
22853e4a KZ |
1135 | |
1136 | if (result != 0) | |
261f1423 | 1137 | warn(_("klogctl error")); |
22853e4a KZ |
1138 | } |
1139 | ||
16279cc2 | 1140 | /* -msglevel [0-8]. Console printk message level. */ |
da27df25 | 1141 | if (ctl->opt_msglevel_num && vc_only(ctl, "--msglevel")) { |
16279cc2 SK |
1142 | result = |
1143 | klogctl(SYSLOG_ACTION_CONSOLE_LEVEL, NULL, | |
1144 | ctl->opt_msglevel_num); | |
22853e4a | 1145 | if (result != 0) |
261f1423 | 1146 | warn(_("klogctl error")); |
22853e4a KZ |
1147 | } |
1148 | ||
1149 | /* -blength [0-2000] */ | |
da27df25 | 1150 | if (ctl->opt_blength && vc_only(ctl, "--blength")) { |
d87d20b0 | 1151 | printf("\033[11;%d]", ctl->opt_blength_l); |
22853e4a | 1152 | } |
b6e899d9 | 1153 | |
22853e4a | 1154 | /* -bfreq freqnumber */ |
da27df25 | 1155 | if (ctl->opt_bfreq && vc_only(ctl, "--bfreq")) { |
d87d20b0 | 1156 | printf("\033[10;%d]", ctl->opt_bfreq_f); |
22853e4a | 1157 | } |
6dbe3af9 KZ |
1158 | } |
1159 | ||
dcc2fe29 | 1160 | static void init_terminal(struct setterm_control *ctl) |
d87d20b0 | 1161 | { |
d87d20b0 | 1162 | int term_errno; |
6dbe3af9 | 1163 | |
dcc2fe29 SK |
1164 | if (!ctl->opt_te_terminal_name) { |
1165 | ctl->opt_te_terminal_name = getenv("TERM"); | |
1166 | if (ctl->opt_te_terminal_name == NULL) | |
261f1423 | 1167 | errx(EXIT_FAILURE, _("$TERM is not defined.")); |
6dbe3af9 | 1168 | } |
6dbe3af9 | 1169 | |
22853e4a | 1170 | /* Find terminfo entry. */ |
dcc2fe29 SK |
1171 | if (setupterm(ctl->opt_te_terminal_name, STDOUT_FILENO, &term_errno)) |
1172 | switch (term_errno) { | |
261f1423 SK |
1173 | case -1: |
1174 | errx(EXIT_FAILURE, _("terminfo database cannot be found")); | |
1175 | case 0: | |
dcc2fe29 | 1176 | errx(EXIT_FAILURE, _("%s: unknown terminal type"), ctl->opt_te_terminal_name); |
261f1423 SK |
1177 | case 1: |
1178 | errx(EXIT_FAILURE, _("terminal is hardcopy")); | |
1179 | } | |
6dbe3af9 | 1180 | |
22853e4a | 1181 | /* See if the terminal is a virtual console terminal. */ |
dcc2fe29 SK |
1182 | ctl->vcterm = (!strncmp(ctl->opt_te_terminal_name, "con", 3) || |
1183 | !strncmp(ctl->opt_te_terminal_name, "linux", 5)); | |
1184 | } | |
1185 | ||
6dbe3af9 | 1186 | |
dcc2fe29 SK |
1187 | int main(int argc, char **argv) |
1188 | { | |
87918040 | 1189 | struct setterm_control ctl = { NULL }; |
dcc2fe29 SK |
1190 | |
1191 | setlocale(LC_ALL, ""); | |
1192 | bindtextdomain(PACKAGE, LOCALEDIR); | |
1193 | textdomain(PACKAGE); | |
2c308875 | 1194 | close_stdout_atexit(); |
dcc2fe29 | 1195 | |
6e1eda6f RM |
1196 | if (argc < 2) { |
1197 | warnx(_("bad usage")); | |
1198 | errtryhelp(EXIT_FAILURE); | |
1199 | } | |
dcc2fe29 SK |
1200 | parse_option(&ctl, argc, argv); |
1201 | init_terminal(&ctl); | |
d87d20b0 | 1202 | perform_sequence(&ctl); |
6dbe3af9 | 1203 | |
261f1423 | 1204 | return EXIT_SUCCESS; |
6dbe3af9 | 1205 | } |