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