1 /* $OpenBSD: tset.c,v 1.45 2023/10/17 09:52:11 nicm Exp $ */
2
3 /****************************************************************************
4 * Copyright 2020,2021 Thomas E. Dickey *
5 * Copyright 1998-2016,2017 Free Software Foundation, Inc. *
6 * *
7 * Permission is hereby granted, free of charge, to any person obtaining a *
8 * copy of this software and associated documentation files (the *
9 * "Software"), to deal in the Software without restriction, including *
10 * without limitation the rights to use, copy, modify, merge, publish, *
11 * distribute, distribute with modifications, sublicense, and/or sell *
12 * copies of the Software, and to permit persons to whom the Software is *
13 * furnished to do so, subject to the following conditions: *
14 * *
15 * The above copyright notice and this permission notice shall be included *
16 * in all copies or substantial portions of the Software. *
17 * *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
21 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
24 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
25 * *
26 * Except as contained in this notice, the name(s) of the above copyright *
27 * holders shall not be used in advertising or otherwise to promote the *
28 * sale, use or other dealings in this Software without prior written *
29 * authorization. *
30 ****************************************************************************/
31
32 /****************************************************************************
33 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
34 * and: Eric S. Raymond <esr@snark.thyrsus.com> *
35 * and: Thomas E. Dickey 1996-on *
36 ****************************************************************************/
37
38 /*
39 * Notes:
40 * The initial adaptation from 4.4BSD Lite sources in September 1995 used 686
41 * lines from that version, and made changes/additions for 150 lines. There
42 * was no reformatting, so with/without ignoring whitespace, the amount of
43 * change is the same.
44 *
45 * Comparing with current (2009) source, excluding this comment:
46 * a) 209 lines match identically to the 4.4BSD Lite sources, with 771 lines
47 * changed/added.
48 * a) Ignoring whitespace, the current version still uses 516 lines from the
49 * 4.4BSD Lite sources, with 402 lines changed/added.
50 *
51 * Raymond's original comment on this follows...
52 */
53
54 /*
55 * tset.c - terminal initialization utility
56 *
57 * This code was mostly swiped from 4.4BSD tset, with some obsolescent
58 * cruft removed and substantial portions rewritten. A Regents of the
59 * University of California copyright applies to some portions of the
60 * code, and is reproduced below:
61 */
62 /*-
63 * Copyright (c) 1980, 1991, 1993
64 * The Regents of the University of California. All rights reserved.
65 *
66 * Redistribution and use in source and binary forms, with or without
67 * modification, are permitted provided that the following conditions
68 * are met:
69 * 1. Redistributions of source code must retain the above copyright
70 * notice, this list of conditions and the following disclaimer.
71 * 2. Redistributions in binary form must reproduce the above copyright
72 * notice, this list of conditions and the following disclaimer in the
73 * documentation and/or other materials provided with the distribution.
74 * 3. Neither the name of the University nor the names of its contributors
75 * may be used to endorse or promote products derived from this software
76 * without specific prior written permission.
77 *
78 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
79 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
80 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
81 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
82 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
83 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
84 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
85 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
86 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
87 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
88 * SUCH DAMAGE.
89 */
90
91 #include <reset_cmd.h>
92 #include <termcap.h>
93 #include <transform.h>
94 #include <tty_settings.h>
95
96 #if HAVE_GETTTYNAM
97 #include <ttyent.h>
98 #endif
99 #ifdef NeXT
100 char *ttyname(int fd);
101 #endif
102
103
104
105 #ifndef environ
106 extern char **environ;
107 #endif
108
109 const char *_nc_progname = "tset";
110
111 #define LOWERCASE(c) ((isalpha(UChar(c)) && isupper(UChar(c))) ? tolower(UChar(c)) : (c))
112
113 static GCC_NORETURN void exit_error(void);
114
115 static int
CaselessCmp(const char * a,const char * b)116 CaselessCmp(const char *a, const char *b)
117 { /* strcasecmp isn't portable */
118 while (*a && *b) {
119 int cmp = LOWERCASE(*a) - LOWERCASE(*b);
120 if (cmp != 0)
121 break;
122 a++, b++;
123 }
124 return LOWERCASE(*a) - LOWERCASE(*b);
125 }
126
127 static GCC_NORETURN void
exit_error(void)128 exit_error(void)
129 {
130 restore_tty_settings();
131 (void) fprintf(stderr, "\n");
132 fflush(stderr);
133 ExitProgram(EXIT_FAILURE);
134 /* NOTREACHED */
135 }
136
137 static GCC_NORETURN void
err(const char * fmt,...)138 err(const char *fmt, ...)
139 {
140 va_list ap;
141 va_start(ap, fmt);
142 (void) fprintf(stderr, "%s: ", _nc_progname);
143 (void) vfprintf(stderr, fmt, ap);
144 va_end(ap);
145 exit_error();
146 /* NOTREACHED */
147 }
148
149 static GCC_NORETURN void
failed(const char * msg)150 failed(const char *msg)
151 {
152 char temp[BUFSIZ];
153 size_t len = strlen(_nc_progname) + 2;
154
155 if ((int) len < (int) sizeof(temp) - 12) {
156 _nc_STRCPY(temp, _nc_progname, sizeof(temp));
157 _nc_STRCAT(temp, ": ", sizeof(temp));
158 } else {
159 _nc_STRCPY(temp, "tset: ", sizeof(temp));
160 }
161 _nc_STRNCAT(temp, msg, sizeof(temp), sizeof(temp) - strlen(temp) - 2);
162 perror(temp);
163 exit_error();
164 /* NOTREACHED */
165 }
166
167 /* Prompt the user for a terminal type. */
168 static const char *
askuser(const char * dflt)169 askuser(const char *dflt)
170 {
171 static char answer[256];
172
173 /* We can get recalled; if so, don't continue uselessly. */
174 clearerr(stdin);
175 if (feof(stdin) || ferror(stdin)) {
176 (void) fprintf(stderr, "\n");
177 exit_error();
178 /* NOTREACHED */
179 }
180
181 for (;;) {
182 char *p;
183
184 if (dflt)
185 (void) fprintf(stderr, "Terminal type? [%s] ", dflt);
186 else
187 (void) fprintf(stderr, "Terminal type? ");
188 (void) fflush(stderr);
189
190 if (fgets(answer, sizeof(answer), stdin) == 0) {
191 if (dflt == 0) {
192 exit_error();
193 /* NOTREACHED */
194 }
195 return (dflt);
196 }
197
198 if ((p = strchr(answer, '\n')) != 0)
199 *p = '\0';
200 if (answer[0])
201 return (answer);
202 if (dflt != 0)
203 return (dflt);
204 }
205 }
206
207 /**************************************************************************
208 *
209 * Mapping logic begins here
210 *
211 **************************************************************************/
212
213 /* Baud rate conditionals for mapping. */
214 #define GT 0x01
215 #define EQ 0x02
216 #define LT 0x04
217 #define NOT 0x08
218 #define GE (GT | EQ)
219 #define LE (LT | EQ)
220
221 typedef struct map {
222 struct map *next; /* Linked list of maps. */
223 const char *porttype; /* Port type, or "" for any. */
224 const char *type; /* Terminal type to select. */
225 int conditional; /* Baud rate conditionals bitmask. */
226 int speed; /* Baud rate to compare against. */
227 } MAP;
228
229 static MAP *cur, *maplist;
230
231 #define DATA(name,value) { { name }, value }
232
233 typedef struct speeds {
234 const char string[8];
235 int speed;
236 } SPEEDS;
237
238 #if defined(EXP_WIN32_DRIVER)
239 static const SPEEDS speeds[] =
240 {
241 {"0", 0}
242 };
243 #else
244 static const SPEEDS speeds[] =
245 {
246 DATA("0", B0),
247 DATA("50", B50),
248 DATA("75", B75),
249 DATA("110", B110),
250 DATA("134", B134),
251 DATA("134.5", B134),
252 DATA("150", B150),
253 DATA("200", B200),
254 DATA("300", B300),
255 DATA("600", B600),
256 DATA("1200", B1200),
257 DATA("1800", B1800),
258 DATA("2400", B2400),
259 DATA("4800", B4800),
260 DATA("9600", B9600),
261 /* sgttyb may define up to this point */
262 #ifdef B19200
263 DATA("19200", B19200),
264 #endif
265 #ifdef B38400
266 DATA("38400", B38400),
267 #endif
268 #ifdef B19200
269 DATA("19200", B19200),
270 #endif
271 #ifdef B38400
272 DATA("38400", B38400),
273 #endif
274 #ifdef B19200
275 DATA("19200", B19200),
276 #else
277 #ifdef EXTA
278 DATA("19200", EXTA),
279 #endif
280 #endif
281 #ifdef B38400
282 DATA("38400", B38400),
283 #else
284 #ifdef EXTB
285 DATA("38400", EXTB),
286 #endif
287 #endif
288 #ifdef B57600
289 DATA("57600", B57600),
290 #endif
291 #ifdef B76800
292 DATA("76800", B57600),
293 #endif
294 #ifdef B115200
295 DATA("115200", B115200),
296 #endif
297 #ifdef B153600
298 DATA("153600", B153600),
299 #endif
300 #ifdef B230400
301 DATA("230400", B230400),
302 #endif
303 #ifdef B307200
304 DATA("307200", B307200),
305 #endif
306 #ifdef B460800
307 DATA("460800", B460800),
308 #endif
309 #ifdef B500000
310 DATA("500000", B500000),
311 #endif
312 #ifdef B576000
313 DATA("576000", B576000),
314 #endif
315 #ifdef B921600
316 DATA("921600", B921600),
317 #endif
318 #ifdef B1000000
319 DATA("1000000", B1000000),
320 #endif
321 #ifdef B1152000
322 DATA("1152000", B1152000),
323 #endif
324 #ifdef B1500000
325 DATA("1500000", B1500000),
326 #endif
327 #ifdef B2000000
328 DATA("2000000", B2000000),
329 #endif
330 #ifdef B2500000
331 DATA("2500000", B2500000),
332 #endif
333 #ifdef B3000000
334 DATA("3000000", B3000000),
335 #endif
336 #ifdef B3500000
337 DATA("3500000", B3500000),
338 #endif
339 #ifdef B4000000
340 DATA("4000000", B4000000),
341 #endif
342 };
343 #undef DATA
344 #endif
345
346 static int
tbaudrate(char * rate)347 tbaudrate(char *rate)
348 {
349 const SPEEDS *sp = 0;
350 size_t n;
351
352 /* The baudrate number can be preceded by a 'B', which is ignored. */
353 if (*rate == 'B')
354 ++rate;
355
356 for (n = 0; n < SIZEOF(speeds); ++n) {
357 if (n > 0 && (speeds[n].speed <= speeds[n - 1].speed)) {
358 /* if the speeds are not increasing, likely a numeric overflow */
359 break;
360 }
361 if (!CaselessCmp(rate, speeds[n].string)) {
362 sp = speeds + n;
363 break;
364 }
365 }
366 if (sp == 0)
367 err("unknown baud rate %s", rate);
368 return (sp->speed);
369 }
370
371 /*
372 * Syntax for -m:
373 * [port-type][test baudrate]:terminal-type
374 * The baud rate tests are: >, <, @, =, !
375 */
376 static void
add_mapping(const char * port,char * arg)377 add_mapping(const char *port, char *arg)
378 {
379 MAP *mapp;
380 char *copy, *p;
381 const char *termp;
382 char *base = 0;
383
384 copy = strdup(arg);
385 mapp = typeMalloc(MAP, 1);
386 if (copy == 0 || mapp == 0)
387 failed("malloc");
388
389 assert(copy != 0);
390 assert(mapp != 0);
391
392 mapp->next = 0;
393 if (maplist == 0)
394 cur = maplist = mapp;
395 else {
396 cur->next = mapp;
397 cur = mapp;
398 }
399
400 mapp->porttype = arg;
401 mapp->conditional = 0;
402
403 arg = strpbrk(arg, "><@=!:");
404
405 if (arg == 0) { /* [?]term */
406 mapp->type = mapp->porttype;
407 mapp->porttype = 0;
408 goto done;
409 }
410
411 if (arg == mapp->porttype) /* [><@=! baud]:term */
412 termp = mapp->porttype = 0;
413 else
414 termp = base = arg;
415
416 for (;; ++arg) { /* Optional conditionals. */
417 switch (*arg) {
418 case '<':
419 if (mapp->conditional & GT)
420 goto badmopt;
421 mapp->conditional |= LT;
422 break;
423 case '>':
424 if (mapp->conditional & LT)
425 goto badmopt;
426 mapp->conditional |= GT;
427 break;
428 case '@':
429 case '=': /* Not documented. */
430 mapp->conditional |= EQ;
431 break;
432 case '!':
433 mapp->conditional |= NOT;
434 break;
435 default:
436 goto next;
437 }
438 }
439
440 next:
441 if (*arg == ':') {
442 if (mapp->conditional)
443 goto badmopt;
444 ++arg;
445 } else { /* Optional baudrate. */
446 arg = strchr(p = arg, ':');
447 if (arg == 0)
448 goto badmopt;
449 *arg++ = '\0';
450 mapp->speed = tbaudrate(p);
451 }
452
453 mapp->type = arg;
454
455 /* Terminate porttype, if specified. */
456 if (termp != 0)
457 *base = '\0';
458
459 /* If a NOT conditional, reverse the test. */
460 if (mapp->conditional & NOT)
461 mapp->conditional = ~mapp->conditional & (EQ | GT | LT);
462
463 /* If user specified a port with an option flag, set it. */
464 done:
465 if (port) {
466 if (mapp->porttype) {
467 badmopt:
468 err("illegal -m option format: %s", copy);
469 }
470 mapp->porttype = port;
471 }
472 free(copy);
473 #ifdef MAPDEBUG
474 (void) printf("port: %s\n", mapp->porttype ? mapp->porttype : "ANY");
475 (void) printf("type: %s\n", mapp->type);
476 (void) printf("conditional: ");
477 p = "";
478 if (mapp->conditional & GT) {
479 (void) printf("GT");
480 p = "/";
481 }
482 if (mapp->conditional & EQ) {
483 (void) printf("%sEQ", p);
484 p = "/";
485 }
486 if (mapp->conditional & LT)
487 (void) printf("%sLT", p);
488 (void) printf("\nspeed: %d\n", mapp->speed);
489 #endif
490 }
491
492 /*
493 * Return the type of terminal to use for a port of type 'type', as specified
494 * by the first applicable mapping in 'map'. If no mappings apply, return
495 * 'type'.
496 */
497 static const char *
mapped(const char * type)498 mapped(const char *type)
499 {
500 MAP *mapp;
501 int match;
502
503 for (mapp = maplist; mapp; mapp = mapp->next)
504 if (mapp->porttype == 0 || !strcmp(mapp->porttype, type)) {
505 switch (mapp->conditional) {
506 case 0: /* No test specified. */
507 match = TRUE;
508 break;
509 case EQ:
510 match = ((int) ospeed == mapp->speed);
511 break;
512 case GE:
513 match = ((int) ospeed >= mapp->speed);
514 break;
515 case GT:
516 match = ((int) ospeed > mapp->speed);
517 break;
518 case LE:
519 match = ((int) ospeed <= mapp->speed);
520 break;
521 case LT:
522 match = ((int) ospeed < mapp->speed);
523 break;
524 default:
525 match = FALSE;
526 }
527 if (match)
528 return (mapp->type);
529 }
530 /* No match found; return given type. */
531 return (type);
532 }
533
534 /**************************************************************************
535 *
536 * Entry fetching
537 *
538 **************************************************************************/
539
540 /*
541 * Figure out what kind of terminal we're dealing with, and then read in
542 * its termcap entry.
543 */
544 static const char *
get_termcap_entry(int fd,char * userarg)545 get_termcap_entry(int fd, char *userarg)
546 {
547 int errret;
548 char *p;
549 const char *ttype;
550 #if HAVE_PATH_TTYS
551 #if HAVE_GETTTYNAM
552 struct ttyent *t;
553 #else
554 FILE *fp;
555 #endif
556 char *ttypath;
557 #endif /* HAVE_PATH_TTYS */
558
559 (void) fd;
560
561 if (userarg) {
562 ttype = userarg;
563 goto found;
564 }
565
566 /* Try the environment. */
567 if ((ttype = getenv("TERM")) != 0)
568 goto map;
569
570 #if HAVE_PATH_TTYS
571 if ((ttypath = ttyname(fd)) != 0) {
572 p = _nc_basename(ttypath);
573 #if HAVE_GETTTYNAM
574 /*
575 * We have the 4.3BSD library call getttynam(3); that means
576 * there's an /etc/ttys to look up device-to-type mappings in.
577 * Try ttyname(3); check for dialup or other mapping.
578 */
579 if ((t = getttynam(p))) {
580 ttype = t->ty_type;
581 goto map;
582 }
583 #else
584 if ((fp = fopen("/etc/ttytype", "r")) != 0
585 || (fp = fopen("/etc/ttys", "r")) != 0) {
586 char buffer[BUFSIZ];
587 char *s, *t, *d;
588
589 while (fgets(buffer, sizeof(buffer) - 1, fp) != 0) {
590 for (s = buffer, t = d = 0; *s; s++) {
591 if (isspace(UChar(*s)))
592 *s = '\0';
593 else if (t == 0)
594 t = s;
595 else if (d == 0 && s != buffer && s[-1] == '\0')
596 d = s;
597 }
598 if (t != 0 && d != 0 && !strcmp(d, p)) {
599 ttype = strdup(t);
600 fclose(fp);
601 goto map;
602 }
603 }
604 fclose(fp);
605 }
606 #endif /* HAVE_GETTTYNAM */
607 }
608 #endif /* HAVE_PATH_TTYS */
609
610 /* If still undefined, use "unknown". */
611 ttype = "unknown";
612
613 map:ttype = mapped(ttype);
614
615 /*
616 * If not a path, remove TERMCAP from the environment so we get a
617 * real entry from /etc/termcap. This prevents us from being fooled
618 * by out of date stuff in the environment.
619 */
620 found:
621 if ((p = getenv("TERMCAP")) != 0 && !_nc_is_abs_path(p)) {
622 /* 'unsetenv("TERMCAP")' is not portable.
623 * The 'environ' array is better.
624 */
625 int n;
626 for (n = 0; environ[n] != 0; n++) {
627 if (!strncmp("TERMCAP=", environ[n], (size_t) 8)) {
628 while ((environ[n] = environ[n + 1]) != 0) {
629 n++;
630 }
631 break;
632 }
633 }
634 }
635
636 /*
637 * ttype now contains a pointer to the type of the terminal.
638 * If the first character is '?', ask the user.
639 */
640 if (ttype[0] == '?') {
641 if (ttype[1] != '\0')
642 ttype = askuser(ttype + 1);
643 else
644 ttype = askuser(0);
645 }
646 /* Find the terminfo entry. If it doesn't exist, ask the user. */
647 while (setupterm((NCURSES_CONST char *) ttype, fd, &errret)
648 != OK) {
649 if (errret == 0) {
650 (void) fprintf(stderr, "%s: unknown terminal type %s\n",
651 _nc_progname, ttype);
652 ttype = 0;
653 } else {
654 (void) fprintf(stderr,
655 "%s: can't initialize terminal type %s (error %d)\n",
656 _nc_progname, ttype, errret);
657 ttype = 0;
658 }
659 ttype = askuser(ttype);
660 }
661 #if BROKEN_LINKER
662 tgetflag("am"); /* force lib_termcap.o to be linked for 'ospeed' */
663 #endif
664 return (ttype);
665 }
666
667 /**************************************************************************
668 *
669 * Main sequence
670 *
671 **************************************************************************/
672
673 /*
674 * Convert the obsolete argument forms into something that getopt can handle.
675 * This means that -e, -i and -k get default arguments supplied for them.
676 */
677 static void
obsolete(char ** argv)678 obsolete(char **argv)
679 {
680 for (; *argv; ++argv) {
681 char *parm = argv[0];
682
683 if (parm[0] == '-' && parm[1] == '\0') {
684 argv[0] = strdup("-q");
685 continue;
686 }
687
688 if ((parm[0] != '-')
689 || (argv[1] && argv[1][0] != '-')
690 || (parm[1] != 'e' && parm[1] != 'i' && parm[1] != 'k')
691 || (parm[2] != '\0'))
692 continue;
693 switch (argv[0][1]) {
694 case 'e':
695 argv[0] = strdup("-e^H");
696 break;
697 case 'i':
698 argv[0] = strdup("-i^C");
699 break;
700 case 'k':
701 argv[0] = strdup("-k^U");
702 break;
703 }
704 }
705 }
706
707 static void
print_shell_commands(const char * ttype)708 print_shell_commands(const char *ttype)
709 {
710 const char *p;
711 int len;
712 char *var;
713 char *leaf;
714 /*
715 * Figure out what shell we're using. A hack, we look for an
716 * environmental variable SHELL ending in "csh".
717 */
718 if ((var = getenv("SHELL")) != 0
719 && ((len = (int) strlen(leaf = _nc_basename(var))) >= 3)
720 && !strcmp(leaf + len - 3, "csh"))
721 p = "set noglob;\nsetenv TERM %s;\nunset noglob;\n";
722 else
723 p = "TERM=%s;\n";
724 (void) printf(p, ttype);
725 }
726
727 static void
usage(void)728 usage(void)
729 {
730 #define SKIP(s) /* nothing */
731 #define KEEP(s) s "\n"
732 static const char msg[] =
733 {
734 KEEP("")
735 KEEP("Options:")
736 SKIP(" -a arpanet (obsolete)")
737 KEEP(" -c set control characters")
738 SKIP(" -d dialup (obsolete)")
739 KEEP(" -e ch erase character")
740 KEEP(" -I no initialization strings")
741 KEEP(" -i ch interrupt character")
742 KEEP(" -k ch kill character")
743 KEEP(" -m mapping map identifier to type")
744 SKIP(" -p plugboard (obsolete)")
745 KEEP(" -Q do not output control key settings")
746 KEEP(" -q display term only, do no changes")
747 KEEP(" -r display term on stderr")
748 SKIP(" -S (obsolete)")
749 KEEP(" -s output TERM set command")
750 KEEP(" -V print curses-version")
751 KEEP(" -w set window-size")
752 KEEP("")
753 KEEP("If neither -c/-w are given, both are assumed.")
754 };
755 #undef KEEP
756 #undef SKIP
757 (void) fprintf(stderr, "Usage: %s [options] [terminal]\n", _nc_progname);
758 fputs(msg, stderr);
759 ExitProgram(EXIT_FAILURE);
760 /* NOTREACHED */
761 }
762
763 static char
arg_to_char(void)764 arg_to_char(void)
765 {
766 return (char) ((optarg[0] == '^' && optarg[1] != '\0')
767 ? ((optarg[1] == '?') ? '\177' : CTRL(optarg[1]))
768 : optarg[0]);
769 }
770
771 int
main(int argc,char ** argv)772 main(int argc, char **argv)
773 {
774 int ch, noinit, noset, quiet, Sflag, sflag, showterm;
775 const char *ttype;
776 int terasechar = -1; /* new erase character */
777 int intrchar = -1; /* new interrupt character */
778 int tkillchar = -1; /* new kill character */
779 int my_fd;
780 bool opt_c = FALSE; /* set control-chars */
781 bool opt_w = FALSE; /* set window-size */
782 TTY mode, oldmode;
783
784 _nc_progname = _nc_rootname(*argv);
785
786 if (pledge("stdio rpath wpath tty", NULL) == -1)
787 err("pledge: %s", strerror(errno));
788
789 obsolete(argv);
790 noinit = noset = quiet = Sflag = sflag = showterm = 0;
791 while ((ch = getopt(argc, argv, "a:cd:e:Ii:k:m:np:qQrSsVw")) != -1) {
792 switch (ch) {
793 case 'c': /* set control-chars */
794 opt_c = TRUE;
795 break;
796 case 'a': /* OBSOLETE: map identifier to type */
797 add_mapping("arpanet", optarg);
798 break;
799 case 'd': /* OBSOLETE: map identifier to type */
800 add_mapping("dialup", optarg);
801 break;
802 case 'e': /* erase character */
803 terasechar = arg_to_char();
804 break;
805 case 'I': /* no initialization strings */
806 noinit = 1;
807 break;
808 case 'i': /* interrupt character */
809 intrchar = arg_to_char();
810 break;
811 case 'k': /* kill character */
812 tkillchar = arg_to_char();
813 break;
814 case 'm': /* map identifier to type */
815 add_mapping(0, optarg);
816 break;
817 case 'n': /* OBSOLETE: set new tty driver */
818 break;
819 case 'p': /* OBSOLETE: map identifier to type */
820 add_mapping("plugboard", optarg);
821 break;
822 case 'Q': /* don't output control key settings */
823 quiet = 1;
824 break;
825 case 'q': /* display term only */
826 noset = 1;
827 break;
828 case 'r': /* display term on stderr */
829 showterm = 1;
830 break;
831 case 'S': /* OBSOLETE: output TERM & TERMCAP */
832 Sflag = 1;
833 break;
834 case 's': /* output TERM set command */
835 sflag = 1;
836 break;
837 case 'V': /* print curses-version */
838 puts(curses_version());
839 ExitProgram(EXIT_SUCCESS);
840 case 'w': /* set window-size */
841 opt_w = TRUE;
842 break;
843 case '?':
844 default:
845 usage();
846 }
847 }
848
849 argc -= optind;
850 argv += optind;
851
852 if (argc > 1)
853 usage();
854
855 if (!opt_c && !opt_w)
856 opt_c = opt_w = TRUE;
857
858 my_fd = save_tty_settings(&mode, TRUE);
859 oldmode = mode;
860 #ifdef TERMIOS
861 ospeed = (NCURSES_OSPEED) cfgetospeed(&mode);
862 #elif defined(EXP_WIN32_DRIVER)
863 ospeed = 0;
864 #else
865 ospeed = (NCURSES_OSPEED) mode.sg_ospeed;
866 #endif
867
868 if (same_program(_nc_progname, PROG_RESET)) {
869 reset_start(stderr, TRUE, FALSE);
870 reset_tty_settings(my_fd, &mode, noset);
871 } else {
872 reset_start(stderr, FALSE, TRUE);
873 }
874
875 ttype = get_termcap_entry(my_fd, *argv);
876
877 if (!noset) {
878 #if HAVE_SIZECHANGE
879 if (opt_w) {
880 set_window_size(my_fd, &lines, &columns);
881 }
882 #endif
883 if (opt_c) {
884 set_control_chars(&mode, terasechar, intrchar, tkillchar);
885 set_conversions(&mode);
886
887 if (!noinit) {
888 if (send_init_strings(my_fd, &oldmode)) {
889 (void) putc('\r', stderr);
890 (void) fflush(stderr);
891 (void) napms(1000); /* Settle the terminal. */
892 }
893 }
894
895 update_tty_settings(&oldmode, &mode);
896 }
897 }
898
899 if (noset) {
900 (void) printf("%s\n", ttype);
901 } else {
902 if (showterm)
903 (void) fprintf(stderr, "Terminal type is %s.\n", ttype);
904 /*
905 * If erase, kill and interrupt characters could have been
906 * modified and not -Q, display the changes.
907 */
908 if (!quiet) {
909 print_tty_chars(&oldmode, &mode);
910 }
911 }
912
913 if (Sflag)
914 err("The -S option is not supported under terminfo.");
915
916 if (sflag) {
917 print_shell_commands(ttype);
918 }
919
920 ExitProgram(EXIT_SUCCESS);
921 }
922