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