ncurses 4.1
[ncurses.git] / progs / tset.c
1
2 /***************************************************************************
3 *                            COPYRIGHT NOTICE                              *
4 ****************************************************************************
5 *                ncurses is copyright (C) 1992-1995                        *
6 *                          Zeyd M. Ben-Halim                               *
7 *                          zmbenhal@netcom.com                             *
8 *                          Eric S. Raymond                                 *
9 *                          esr@snark.thyrsus.com                           *
10 *                                                                          *
11 *        Permission is hereby granted to reproduce and distribute ncurses  *
12 *        by any means and for any fee, whether alone or as part of a       *
13 *        larger distribution, in source or in binary form, PROVIDED        *
14 *        this notice is included with any such distribution, and is not    *
15 *        removed from any of its header files. Mention of ncurses in any   *
16 *        applications linked with it is highly appreciated.                *
17 *                                                                          *
18 *        ncurses comes AS IS with no warranty, implied or expressed.       *
19 *                                                                          *
20 ***************************************************************************/
21
22
23 /*
24  * tset.c - terminal initialization utility
25  *
26  * This code was mostly swiped from 4.4BSD tset, with some obsolescent
27  * cruft removed and substantial portions rewritten.  A Regents of the
28  * University of California copyright applies to some portions of the
29  * code, and is reproduced below:
30  */
31 /*-
32  * Copyright (c) 1980, 1991, 1993
33  *      The Regents of the University of California.  All rights reserved.
34  *
35  * Redistribution and use in source and binary forms, with or without
36  * modification, are permitted provided that the following conditions
37  * are met:
38  * 1. Redistributions of source code must retain the above copyright
39  *    notice, this list of conditions and the following disclaimer.
40  * 2. Redistributions in binary form must reproduce the above copyright
41  *    notice, this list of conditions and the following disclaimer in the
42  *    documentation and/or other materials provided with the distribution.
43  * 3. All advertising materials mentioning features or use of this software
44  *    must display the following acknowledgement:
45  *      This product includes software developed by the University of
46  *      California, Berkeley and its contributors.
47  * 4. Neither the name of the University nor the names of its contributors
48  *    may be used to endorse or promote products derived from this software
49  *    without specific prior written permission.
50  *
51  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61  * SUCH DAMAGE.
62  */
63
64 #define __INTERNAL_CAPS_VISIBLE /* we need to see has_hardware_tabs */
65 #include <progs.priv.h>
66
67 #include <errno.h>
68 #include <stdio.h>
69 #include <ctype.h>
70 #include <termcap.h>
71 #include <fcntl.h>
72
73 #if HAVE_GETTTYNAM && HAVE_TTYENT_H
74 #include <ttyent.h>
75 #endif
76 #ifdef NeXT
77 char *ttyname(int fd);
78 #endif
79
80 /* this is just to stifle a missing-prototype warning */
81 #ifdef linux
82 # include <sys/ioctl.h>
83 #endif
84
85 #if SYSTEM_LOOKS_LIKE_SCO
86 /* they neglected to define struct winsize in termios.h -- it's only
87    in termio.h  */
88 #include        <sys/stream.h>
89 #include        <sys/ptem.h>
90 #endif
91
92 #include <curses.h>     /* for bool typedef */
93 #include <dump_entry.h>
94
95 MODULE_ID("$Id: tset.c,v 0.23 1997/05/10 17:44:47 tom Exp $")
96
97 extern char **environ;
98
99 #undef CTRL
100 #define CTRL(x) ((x) & 0x1f)
101
102 const char *_nc_progname = "tset";
103
104 static TTY mode, oldmode;
105
106 static int      terasechar;             /* new erase character */
107 static int      intrchar;               /* new interrupt character */
108 static int      isreset;                /* invoked as reset */
109 static int      tkillchar;              /* new kill character */
110 static int      tlines, tcolumns;               /* window size */
111
112 #define LOWERCASE(c) ((isalpha(c) && isupper(c)) ? tolower(c) : (c))
113
114 static int
115 CaselessCmp(const char *a, const char *b) /* strcasecmp isn't portable */
116 {
117         while (*a && *b) {
118                 int cmp = LOWERCASE(*a) - LOWERCASE(*b);
119                 if (cmp != 0)
120                         break;
121                 a++, b++;
122         }
123         return LOWERCASE(*a) - LOWERCASE(*b);
124 }
125
126 #if !HAVE_STRDUP
127 static char *strdup (char *s)
128 {
129   char *p;
130
131   p = malloc(strlen(s)+1);
132   if (p)
133     strcpy(p,s);
134   return(p);
135 }
136 #endif /* not HAVE_STRDUP */
137
138 static void
139 err(const char *fmt, ...)
140 {
141         va_list ap;
142         va_start(ap, fmt);
143         (void)fprintf(stderr, "tset: ");
144         (void)vfprintf(stderr, fmt, ap);
145         va_end(ap);
146         (void)fprintf(stderr, "\n");
147         exit(EXIT_FAILURE);
148         /* NOTREACHED */
149 }
150
151 static void
152 failed(const char *msg)
153 {
154         char    temp[BUFSIZ];
155         perror(strcat(strcpy(temp, "tset: "), msg));
156         exit(EXIT_FAILURE);
157         /* NOTREACHED */
158 }
159
160 static void
161 cat(char *file)
162 {
163         register int fd, nr, nw;
164         char buf[1024];
165
166         if ((fd = open(file, O_RDONLY, 0)) < 0)
167                 failed(file);
168
169         while ((nr = read(fd, buf, sizeof(buf))) > 0)
170                 if ((nw = write(STDERR_FILENO, buf, (size_t)nr)) == -1)
171                         failed("write to stderr");
172         if (nr != 0)
173                 failed(file);
174         (void)close(fd);
175 }
176
177 static int
178 outc(int c)
179 {
180         return putc(c, stderr);
181 }
182
183 /* Prompt the user for a terminal type. */
184 static const char *
185 askuser(const char *dflt)
186 {
187         static char answer[256];
188         char *p;
189
190         /* We can get recalled; if so, don't continue uselessly. */
191         if (feof(stdin) || ferror(stdin)) {
192                 (void)fprintf(stderr, "\n");
193                 exit(EXIT_FAILURE);
194         }
195         for (;;) {
196                 if (dflt)
197                         (void)fprintf(stderr, "Terminal type? [%s] ", dflt);
198                 else
199                         (void)fprintf(stderr, "Terminal type? ");
200                 (void)fflush(stderr);
201
202                 if (fgets(answer, sizeof(answer), stdin) == 0) {
203                         if (dflt == 0) {
204                                 (void)fprintf(stderr, "\n");
205                                 exit(EXIT_FAILURE);
206                         }
207                         return (dflt);
208                 }
209
210                 if ((p = strchr(answer, '\n')) != 0)
211                         *p = '\0';
212                 if (answer[0])
213                         return (answer);
214                 if (dflt != 0)
215                         return (dflt);
216         }
217 }
218
219 /**************************************************************************
220  *
221  * Mapping logic begins here
222  *
223  **************************************************************************/
224
225 /* Baud rate conditionals for mapping. */
226 #define GT              0x01
227 #define EQ              0x02
228 #define LT              0x04
229 #define NOT             0x08
230 #define GE              (GT | EQ)
231 #define LE              (LT | EQ)
232
233 typedef struct map {
234         struct map *next;       /* Linked list of maps. */
235         const char *porttype;   /* Port type, or "" for any. */
236         const char *type;       /* Terminal type to select. */
237         int conditional;        /* Baud rate conditionals bitmask. */
238         int speed;              /* Baud rate to compare against. */
239 } MAP;
240
241 static MAP *cur, *maplist;
242
243 typedef struct speeds {
244         const char *string;
245         int     speed;
246 } SPEEDS;
247
248 static const SPEEDS speeds[] = {
249         { "0",          B0 },
250         { "50",         B50 },
251         { "75",         B75 },
252         { "110",        B110 },
253         { "134",        B134 },
254         { "134.5",      B134 },
255         { "150",        B150 },
256         { "200",        B200 },
257         { "300",        B300 },
258         { "600",        B600 },
259         { "1200",       B1200 },
260         { "1800",       B1800 },
261         { "2400",       B2400 },
262         { "4800",       B4800 },
263         { "9600",       B9600 },
264         { "19200",      B19200 },
265         { "38400",      B38400 },
266         { "19200",      B19200 },
267         { "38400",      B38400 },
268         { (char *)0,    0 }
269 };
270
271 static int
272 tbaudrate(char *rate)
273 {
274         const SPEEDS *sp;
275         int found = FALSE;
276
277         /* The baudrate number can be preceded by a 'B', which is ignored. */
278         if (*rate == 'B')
279                 ++rate;
280
281         for (sp = speeds; sp->string; ++sp) {
282                 if (!CaselessCmp(rate, sp->string)) {
283                         found = TRUE;
284                         break;
285                 }
286         }
287         if (!found)
288                 err("unknown baud rate %s", rate);
289         return (sp->speed);
290 }
291
292 /*
293  * Syntax for -m:
294  * [port-type][test baudrate]:terminal-type
295  * The baud rate tests are: >, <, @, =, !
296  */
297 static void
298 add_mapping(const char *port, char *arg)
299 {
300         MAP *mapp;
301         char *copy, *p;
302         const char *termp;
303         char *base = 0;
304
305         copy = strdup(arg);
306         mapp = malloc((u_int)sizeof(MAP));
307         if (copy == 0 || mapp == 0)
308                 failed("malloc");
309         mapp->next = 0;
310         if (maplist == 0)
311                 cur = maplist = mapp;
312         else {
313                 cur->next = mapp;
314                 cur =  mapp;
315         }
316
317         mapp->porttype = arg;
318         mapp->conditional = 0;
319
320         arg = strpbrk(arg, "><@=!:");
321
322         if (arg == 0) {                 /* [?]term */
323                 mapp->type = mapp->porttype;
324                 mapp->porttype = 0;
325                 goto done;
326         }
327
328         if (arg == mapp->porttype)              /* [><@=! baud]:term */
329                 termp = mapp->porttype = 0;
330         else
331                 termp = base = arg;
332
333         for (;; ++arg)                          /* Optional conditionals. */
334                 switch(*arg) {
335                 case '<':
336                         if (mapp->conditional & GT)
337                                 goto badmopt;
338                         mapp->conditional |= LT;
339                         break;
340                 case '>':
341                         if (mapp->conditional & LT)
342                                 goto badmopt;
343                         mapp->conditional |= GT;
344                         break;
345                 case '@':
346                 case '=':                       /* Not documented. */
347                         mapp->conditional |= EQ;
348                         break;
349                 case '!':
350                         mapp->conditional |= NOT;
351                         break;
352                 default:
353                         goto next;
354                 }
355
356 next:   if (*arg == ':') {
357                 if (mapp->conditional)
358                         goto badmopt;
359                 ++arg;
360         } else {                                /* Optional baudrate. */
361                 arg = strchr(p = arg, ':');
362                 if (arg == 0)
363                         goto badmopt;
364                 *arg++ = '\0';
365                 mapp->speed = tbaudrate(p);
366         }
367
368         if (arg == (char *)0)           /* Non-optional type. */
369                 goto badmopt;
370
371         mapp->type = arg;
372
373         /* Terminate porttype, if specified. */
374         if (termp != 0)
375                 *base = '\0';
376
377         /* If a NOT conditional, reverse the test. */
378         if (mapp->conditional & NOT)
379                 mapp->conditional = ~mapp->conditional & (EQ | GT | LT);
380
381         /* If user specified a port with an option flag, set it. */
382 done:   if (port) {
383                 if (mapp->porttype)
384 badmopt:                err("illegal -m option format: %s", copy);
385                 mapp->porttype = port;
386         }
387
388 #ifdef MAPDEBUG
389         (void)printf("port: %s\n", mapp->porttype ? mapp->porttype : "ANY");
390         (void)printf("type: %s\n", mapp->type);
391         (void)printf("conditional: ");
392         p = "";
393         if (mapp->conditional & GT) {
394                 (void)printf("GT");
395                 p = "/";
396         }
397         if (mapp->conditional & EQ) {
398                 (void)printf("%sEQ", p);
399                 p = "/";
400         }
401         if (mapp->conditional & LT)
402                 (void)printf("%sLT", p);
403         (void)printf("\nspeed: %d\n", mapp->speed);
404 #endif
405 }
406
407 /*
408  * Return the type of terminal to use for a port of type 'type', as specified
409  * by the first applicable mapping in 'map'.  If no mappings apply, return
410  * 'type'.
411  */
412 static const char *
413 mapped(const char *type)
414 {
415         MAP *mapp;
416         int match;
417
418         for (mapp = maplist; mapp; mapp = mapp->next)
419                 if (mapp->porttype == 0 || !strcmp(mapp->porttype, type)) {
420                         switch (mapp->conditional) {
421                         case 0:                 /* No test specified. */
422                                 match = TRUE;
423                                 break;
424                         case EQ:
425                                 match = (ospeed == mapp->speed);
426                                 break;
427                         case GE:
428                                 match = (ospeed >= mapp->speed);
429                                 break;
430                         case GT:
431                                 match = (ospeed > mapp->speed);
432                                 break;
433                         case LE:
434                                 match = (ospeed <= mapp->speed);
435                                 break;
436                         case LT:
437                                 match = (ospeed < mapp->speed);
438                                 break;
439                         default:
440                                 match = FALSE;
441                         }
442                         if (match)
443                                 return (mapp->type);
444                 }
445         /* No match found; return given type. */
446         return (type);
447 }
448
449 /**************************************************************************
450  *
451  * Entry fetching
452  *
453  **************************************************************************/
454
455 /*
456  * Figure out what kind of terminal we're dealing with, and then read in
457  * its termcap entry.
458  */
459 static const char *
460 get_termcap_entry(char *userarg)
461 {
462         int rval, errret;
463         char *p;
464         const char *ttype;
465 #if HAVE_GETTTYNAM
466         struct ttyent *t;
467         char *ttypath;
468 #endif
469
470         if (userarg) {
471                 ttype = userarg;
472                 goto found;
473         }
474
475         /* Try the environment. */
476         if ((ttype = getenv("TERM")) != 0)
477                 goto map;
478
479 #if HAVE_GETTTYNAM
480         /*
481          * We have the 4.3BSD library call getttynam(3); that means
482          * there's an /etc/ttys to look up device-to-type mappings in.
483          * Try ttyname(3); check for dialup or other mapping.
484          */
485         if ((ttypath = ttyname(STDERR_FILENO)) != 0) {
486                 if ((p = strrchr(ttypath, '/')) != 0)
487                         ++p;
488                 else
489                         p = ttypath;
490                 if ((t = getttynam(p))) {
491                         ttype = t->ty_type;
492                         goto map;
493                 }
494         }
495 #endif /* BSD */
496
497         /* If still undefined, use "unknown". */
498         ttype = "unknown";
499
500 map:    ttype = mapped(ttype);
501
502         /*
503          * If not a path, remove TERMCAP from the environment so we get a
504          * real entry from /etc/termcap.  This prevents us from being fooled
505          * by out of date stuff in the environment.
506          */
507 found:  if ((p = getenv("TERMCAP")) != 0 && *p != '/') {
508                 /* 'unsetenv("TERMCAP")' is not portable.
509                  * The 'environ' array is better.
510                  */
511                 int n;
512                 for (n = 0; environ[n] != 0; n++) {
513                         if (!strncmp("TERMCAP=", environ[n], 8)) {
514                                 while ((environ[n] = environ[n+1]) != 0) {
515                                         n++;
516                                 }
517                                 break;
518                         }
519                 }
520         }
521
522         /*
523          * ttype now contains a pointer to the type of the terminal.
524          * If the first character is '?', ask the user.
525          */
526         if (ttype[0] == '?')
527                 if (ttype[1] != '\0')
528                         ttype = askuser(ttype + 1);
529                 else
530                         ttype = askuser(0);
531
532         /* Find the terminfo entry.  If it doesn't exist, ask the user. */
533         while ((rval = setupterm(ttype, STDOUT_FILENO, &errret)) != OK) {
534                 if (errret == 0) {
535                         (void)fprintf(stderr, "tset: unknown terminal type %s\n",
536                             ttype);
537                         ttype = 0;
538                 }
539                 else {
540                         (void)fprintf(stderr, "tset: can't initialize terminal\
541                             type %s (error %d)\n", ttype, errret);
542                         ttype = 0;
543                 }
544                 ttype = askuser(ttype);
545         }
546 #if BROKEN_LINKER
547         tgetflag("am"); /* force lib_termcap.o to be linked for 'ospeed' */
548 #endif
549         return (ttype);
550 }
551
552 /**************************************************************************
553  *
554  * Mode-setting logic
555  *
556  **************************************************************************/
557
558 /* some BSD systems have these built in, some systems are missing
559  * one or more definitions. The safest solution is to override.
560  */
561 #undef CEOF
562 #undef CERASE
563 #undef CINTR
564 #undef CKILL
565 #undef CQUIT
566 #undef CSTART
567 #undef CSTOP
568 #undef CSUSP
569
570 /* control-character defaults */
571 #define CEOF    CTRL('D')
572 #define CERASE  CTRL('H')
573 #define CINTR   127             /* ^? */
574 #define CKILL   CTRL('U')
575 #if defined(CLNEXT)
576 #undef CLNEXT
577 #define CLNEXT  CTRL('v')
578 #endif
579 #if defined(CRPRNT)
580 #undef CRPRNT
581 #define CRPRNT  CTRL('r')
582 #endif
583 #define CQUIT   CTRL('\\')
584 #define CSTART  CTRL('Q')
585 #define CSTOP   CTRL('S')
586 #define CSUSP   CTRL('Z')
587
588 #define CHK(val, dft)   ((int)val <= 0 ? dft : val)
589
590 static bool     set_tabs (void);
591
592 /*
593  * Reset the terminal mode bits to a sensible state.  Very useful after
594  * a child program dies in raw mode.
595  */
596 static void
597 reset_mode(void)
598 {
599 #ifdef TERMIOS
600         tcgetattr(STDERR_FILENO, &mode);
601 #else
602         stty(STDERR_FILENO,&mode);
603 #endif
604
605 #ifdef TERMIOS
606 #if defined(VDISCARD) && defined(CDISCARD)
607         mode.c_cc[VDISCARD] = CHK(mode.c_cc[VDISCARD], CDISCARD);
608 #endif
609         mode.c_cc[VEOF] = CHK(mode.c_cc[VEOF], CEOF);
610         mode.c_cc[VERASE] = CHK(mode.c_cc[VERASE], CERASE);
611 #if defined(VFLUSH) && defined(CFLUSH)
612         mode.c_cc[VFLUSH] = CHK(mode.c_cc[VFLUSH], CFLUSH);
613 #endif
614         mode.c_cc[VINTR] = CHK(mode.c_cc[VINTR], CINTR);
615         mode.c_cc[VKILL] = CHK(mode.c_cc[VKILL], CKILL);
616 #if defined(VLNEXT) && defined(CLNEXT)
617         mode.c_cc[VLNEXT] = CHK(mode.c_cc[VLNEXT], CLNEXT);
618 #endif
619         mode.c_cc[VQUIT] = CHK(mode.c_cc[VQUIT], CQUIT);
620 #if defined(VREPRINT) && defined(CRPRNT)
621         mode.c_cc[VREPRINT] = CHK(mode.c_cc[VREPRINT], CRPRNT);
622 #endif
623 #if defined(VSTART) && defined(CSTART)
624         mode.c_cc[VSTART] = CHK(mode.c_cc[VSTART], CSTART);
625 #endif
626 #if defined(VSTOP) && defined(CSTOP)
627         mode.c_cc[VSTOP] = CHK(mode.c_cc[VSTOP], CSTOP);
628 #endif
629 #if defined(VSUSP) && defined(CSUSP)
630         mode.c_cc[VSUSP] = CHK(mode.c_cc[VSUSP], CSUSP);
631 #endif
632 #if defined(VWERASE) && defined(CWERASE)
633         mode.c_cc[VWERASE] = CHK(mode.c_cc[VWERASE], CWERASE);
634 #endif
635
636         mode.c_iflag &= ~(IGNBRK | PARMRK | INPCK | ISTRIP | INLCR | IGNCR
637 #ifdef IUCLC
638                           | IUCLC
639 #endif
640 #ifdef IXANY
641                           | IXANY
642 #endif
643                           | IXOFF);
644
645         mode.c_iflag |= (BRKINT | IGNPAR | ICRNL | IXON
646 #ifdef IMAXBEL
647                          | IMAXBEL
648 #endif
649                          );
650
651         mode.c_oflag &= ~(0
652 #ifdef OLCUC
653                           | OLCUC
654 #endif
655 #ifdef OCRNL
656                           | OCRNL
657 #endif
658 #ifdef ONOCR
659                           | ONOCR
660 #endif
661 #ifdef ONLRET
662                           | ONLRET
663 #endif
664 #ifdef OFILL
665                           | OFILL
666 #endif
667 #ifdef OFDEL
668                           | OFDEL
669 #endif
670 #ifdef NLDLY
671                           | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY
672 #endif
673                           );
674
675         mode.c_oflag |= (OPOST
676 #ifdef ONLCR
677                          | ONLCR
678 #endif
679                          );
680
681         mode.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | CLOCAL);
682         mode.c_cflag |= (CS8 | CREAD);
683         mode.c_lflag &= ~(ECHONL | NOFLSH
684 #ifdef TOSTOP
685                           | TOSTOP
686 #endif
687 #ifdef ECHOPTR
688                           | ECHOPRT
689 #endif
690 #ifdef XCASE
691                           | XCASE
692 #endif
693                           );
694
695         mode.c_lflag |= (ISIG | ICANON | ECHO | ECHOE | ECHOK
696 #ifdef ECHOCTL
697                          | ECHOCTL
698 #endif
699 #ifdef ECHOKE
700                          | ECHOKE
701 #endif
702                          );
703 #endif
704
705 #ifdef TERMIOS
706         tcsetattr(STDERR_FILENO, TCSADRAIN, &mode);
707 #else
708         stty(STDERR_FILENO, &mode);
709 #endif
710 }
711
712 /*
713  * Determine the erase, interrupt, and kill characters from the termcap
714  * entry and command line and update their values in 'mode'.
715  */
716 static void
717 set_control_chars(void)
718 {
719 #ifdef __OBSOLETE__
720         /*
721          * 4.4BSD logic for setting erasechar, left here in case there is some
722          * necessary subtlety missed in the production code below that really
723          * needs to be added someday (in particular, I don't understand what
724          * the second if-statement involving the os flag is doing, and it makes
725          * my head hurt when I try and follow out all the combinations).
726          */
727         char *bp, *p, bs_char, buf[1024];
728
729         bp = buf;
730         p = tgetstr("kb", &bp);
731         if (p == 0 || p[1] != '\0')
732                 p = tgetstr("bc", &bp);
733         if (p != 0 && p[1] == '\0')
734                 bs_char = p[0];
735         else if (tgetflag("bs"))
736                 bs_char = CTRL('h');
737         else
738                 bs_char = 0;
739
740         if (terasechar==0 && !tgetflag("os") && mode.c_cc[VERASE] != CERASE) {
741                 if (tgetflag("bs") || bs_char != 0)
742                         terasechar = -1;
743         }
744         if (terasechar < 0)
745                 terasechar = (bs_char != 0) ? bs_char : CTRL('h');
746 #else
747         /* the real erasechar logic used now */
748         char bs_char = 0;
749
750         if (key_backspace != (char *)0)
751                 bs_char = key_backspace[0];
752
753         if (terasechar <= 0)
754             terasechar = (bs_char != 0) ? bs_char : CTRL('h');
755 #endif /* __OBSOLETE__ */
756
757 #ifdef TERMIOS
758         if (mode.c_cc[VERASE] == 0 || terasechar != 0)
759                 mode.c_cc[VERASE] = terasechar ? terasechar : CERASE;
760
761         if (mode.c_cc[VINTR] == 0 || intrchar != 0)
762                  mode.c_cc[VINTR] = intrchar ? intrchar : CINTR;
763
764         if (mode.c_cc[VKILL] == 0 || tkillchar != 0)
765                 mode.c_cc[VKILL] = tkillchar ? tkillchar : CKILL;
766 #endif
767 }
768
769 /*
770  * Set up various conversions in 'mode', including parity, tabs, returns,
771  * echo, and case, according to the termcap entry.  If the program we're
772  * running was named with a leading upper-case character, map external
773  * uppercase to internal lowercase.
774  */
775 static void
776 set_conversions(void)
777 {
778 #ifdef __OBSOLETE__
779         /*
780          * Conversion logic for some *really* ancient terminal glitches,
781          * not supported in terminfo.  Left here for succeeding generations
782          * to marvel at.
783          */
784         if (tgetflag("UC")) {
785 #ifdef IUCLC
786                 mode.c_iflag |= IUCLC;
787                 mode.c_oflag |= OLCUC;
788 #endif
789         } else if (tgetflag("LC")) {
790 #ifdef IUCLC
791                 mode.c_iflag &= ~IUCLC;
792                 mode.c_oflag &= ~OLCUC;
793 #endif
794         }
795         mode.c_iflag &= ~(PARMRK | INPCK);
796         mode.c_lflag |= ICANON;
797         if (tgetflag("EP")) {
798                 mode.c_cflag |= PARENB;
799                 mode.c_cflag &= ~PARODD;
800         }
801         if (tgetflag("OP")) {
802                 mode.c_cflag |= PARENB;
803                 mode.c_cflag |= PARODD;
804         }
805 #endif /* __OBSOLETE__ */
806
807 #ifdef TERMIOS
808 #ifdef ONLCR
809         mode.c_oflag |= ONLCR;
810 #endif
811         mode.c_iflag |= ICRNL;
812         mode.c_lflag |= ECHO;
813 #ifdef OXTABS
814         mode.c_oflag |= OXTABS;
815 #endif /* OXTABS */
816
817         /* test used to be tgetflag("NL") */
818         if (newline != (char *)0 && newline[0] == '\n' && !newline[1]) {
819                 /* Newline, not linefeed. */
820 #ifdef ONLCR
821                 mode.c_oflag &= ~ONLCR;
822 #endif
823                 mode.c_iflag &= ~ICRNL;
824         }
825 #ifdef __OBSOLETE__
826         if (tgetflag("HD"))                     /* Half duplex. */
827                 mode.c_lflag &= ~ECHO;
828 #endif /* __OBSOLETE__ */
829 #ifdef OXTABS
830         /* test used to be tgetflag("pt") */
831         if (has_hardware_tabs)                  /* Print tabs. */
832                 mode.c_oflag &= ~OXTABS;
833 #endif /* OXTABS */
834         mode.c_lflag |= (ECHOE | ECHOK);
835 #endif
836 }
837
838 /* Output startup string. */
839 static void
840 set_init(void)
841 {
842         char    *p;
843         bool settle;
844
845 #ifdef __OBSOLETE__
846         if (pad_char != (char *)0)              /* Get/set pad character. */
847                 PC = pad_char[0];
848 #endif /* OBSOLETE */
849
850 #ifdef TAB3
851         if (oldmode.c_oflag & (TAB3 | ONLCR | OCRNL | ONLRET)) {
852                 oldmode.c_oflag &= (TAB3 | ONLCR | OCRNL | ONLRET);
853                 tcsetattr(STDERR_FILENO, TCSADRAIN, &oldmode);
854         }
855 #endif
856         settle = set_tabs();
857
858         if (isreset) {
859                 if ((p = reset_1string) != 0) {
860                         tputs(p, 0, outc);
861                         settle = TRUE;
862                 }
863                 if ((p = reset_2string) != 0) {
864                         tputs(p, 0, outc);
865                         settle = TRUE;
866                 }
867                 /* What about rf, rs3, as per terminfo man page? */
868                 /* also might be nice to send rmacs, rmul, rmm */
869                 if ((p = reset_file) != 0
870                  || (p = init_file) != 0) {
871                         cat(p);
872                         settle = TRUE;
873                 }
874         }
875
876         if (settle) {
877                 (void)putc('\r', stderr);
878                 (void)fflush(stderr);
879                 (void)sleep(1);                 /* Settle the terminal. */
880         }
881 }
882
883 /*
884  * Set the hardware tabs on the terminal, using the ct (clear all tabs),
885  * st (set one tab) and ch (horizontal cursor addressing) capabilities.
886  * This is done before if and is, so they can patch in case we blow this.
887  * Return TRUE if we set any tab stops, FALSE if not.
888  */
889 static bool
890 set_tabs()
891 {
892         if (set_tab && clear_all_tabs) {
893                 int c;
894
895                 (void)putc('\r', stderr);       /* Force to left margin. */
896                 tputs(clear_all_tabs, 0, outc);
897
898                 for (c = 8; c < tcolumns; c += 8) {
899                         /* Get to the right column.  In BSD tset, this
900                          * used to try a bunch of half-clever things
901                          * with cup and hpa, for an average saving of
902                          * somewhat less than two character times per
903                          * tab stop, less that .01 sec at 2400cps. We
904                          * lost all this cruft because it seemed to be
905                          * introducing some odd bugs.
906                          * ----------12345678----------- */
907                         (void)fputs("        ", stderr);
908                         tputs(set_tab, 0, outc);
909                 }
910                 putc('\r', stderr);
911                 return (TRUE);
912         }
913         return (FALSE);
914 }
915
916 /**************************************************************************
917  *
918  * Main sequence
919  *
920  **************************************************************************/
921
922 /*
923  * Tell the user if a control key has been changed from the default value.
924  */
925 static void
926 report(const char *name, int which, u_int def)
927 {
928 #ifdef TERMIOS
929         u_int old, new;
930         char *p;
931
932         new = mode.c_cc[which];
933         old = oldmode.c_cc[which];
934
935         if (old == new && old == def)
936                 return;
937
938         (void)fprintf(stderr, "%s %s ", name, old == new ? "is" : "set to");
939
940         if ((p = key_backspace) != 0
941          && new == (u_int)p[0]
942          && p[1] == '\0')
943                 (void)fprintf(stderr, "backspace.\n");
944         else if (new == 0177)
945                 (void)fprintf(stderr, "delete.\n");
946         else if (new < 040) {
947                 new ^= 0100;
948                 (void)fprintf(stderr, "control-%c (^%c).\n", new, new);
949         } else
950                 (void)fprintf(stderr, "%c.\n", new);
951 #endif
952 }
953
954 /*
955  * Convert the obsolete argument forms into something that getopt can handle.
956  * This means that -e, -i and -k get default arguments supplied for them.
957  */
958 static void
959 obsolete(char **argv)
960 {
961         for (; *argv; ++argv) {
962                 char *parm = argv[0];
963
964                 if (parm[0] == '-' && parm[1] == '\0')
965                 {
966                     argv[0] = strdup("-q");
967                     continue;
968                 }
969
970                 if ((parm[0] != '-')
971                  || (argv[1] && argv[1][0] != '-')
972                  || (parm[1] != 'e' && parm[1] != 'i' && parm[1] != 'k')
973                  || (parm[2] != '\0'))
974                         continue;
975                 switch(argv[0][1]) {
976                 case 'e':
977                         argv[0] = strdup("-e^H");
978                         break;
979                 case 'i':
980                         argv[0] = strdup("-i^C");
981                         break;
982                 case 'k':
983                         argv[0] = strdup("-k^U");
984                         break;
985                 }
986         }
987 }
988
989 static void
990 usage(const char* pname)
991 {
992         (void)fprintf(stderr,
993 "usage: %s [-IQrs] [-] [-e ch] [-i ch] [-k ch] [-m mapping] [terminal]\n", pname);
994         exit(EXIT_FAILURE);
995 }
996
997 int
998 main(int argc, char **argv)
999 {
1000 #ifdef TIOCGWINSZ
1001         struct winsize win;
1002 #endif
1003         int ch, noinit, noset, quiet, Sflag, sflag, showterm;
1004         const char *p;
1005         const char *ttype;
1006
1007 #ifdef TERMIOS
1008         if (tcgetattr(STDERR_FILENO, &mode) < 0)
1009                 failed("standard error");
1010
1011         oldmode = mode;
1012         ospeed = cfgetospeed(&mode);
1013 #else
1014         if (gtty(STDERR_FILENO, &mode) < 0)
1015                 failed("standard error");
1016
1017         oldmode = mode;
1018         ospeed = mode.sg_ospeed;
1019 #endif
1020
1021         if ((p = strrchr(*argv, '/')) != 0)
1022                 ++p;
1023         else
1024                 p = *argv;
1025         if (!CaselessCmp(p, "reset")) {
1026                 isreset = 1;
1027                 reset_mode();
1028         }
1029
1030         obsolete(argv);
1031         noinit = noset = quiet = Sflag = sflag = showterm = 0;
1032         while ((ch = getopt(argc, argv, "a:d:e:Ii:k:m:np:qQSrs")) != EOF) {
1033                 switch (ch) {
1034                 case 'q':               /* display term only */
1035                         noset = 1;
1036                         break;
1037                 case 'a':               /* OBSOLETE: map identifier to type */
1038                         add_mapping("arpanet", optarg);
1039                         break;
1040                 case 'd':               /* OBSOLETE: map identifier to type */
1041                         add_mapping("dialup", optarg);
1042                         break;
1043                 case 'e':               /* erase character */
1044                         terasechar = optarg[0] == '^' && optarg[1] != '\0' ?
1045                             optarg[1] == '?' ? '\177' : CTRL(optarg[1]) :
1046                             optarg[0];
1047                         break;
1048                 case 'I':               /* no initialization strings */
1049                         noinit = 1;
1050                         break;
1051                 case 'i':               /* interrupt character */
1052                         intrchar = optarg[0] == '^' && optarg[1] != '\0' ?
1053                             optarg[1] == '?' ? '\177' : CTRL(optarg[1]) :
1054                             optarg[0];
1055                         break;
1056                 case 'k':               /* kill character */
1057                         tkillchar = optarg[0] == '^' && optarg[1] != '\0' ?
1058                             optarg[1] == '?' ? '\177' : CTRL(optarg[1]) :
1059                             optarg[0];
1060                         break;
1061                 case 'm':               /* map identifier to type */
1062                         add_mapping(0, optarg);
1063                         break;
1064                 case 'n':               /* OBSOLETE: set new tty driver */
1065                         break;
1066                 case 'p':               /* OBSOLETE: map identifier to type */
1067                         add_mapping("plugboard", optarg);
1068                         break;
1069                 case 'Q':               /* don't output control key settings */
1070                         quiet = 1;
1071                         break;
1072                 case 'S':               /* OBSOLETE: output TERM & TERMCAP */
1073                         Sflag = 1;
1074                         break;
1075                 case 'r':               /* display term on stderr */
1076                         showterm = 1;
1077                         break;
1078                 case 's':               /* output TERM set command */
1079                         sflag = 1;
1080                         break;
1081                 case '?':
1082                 default:
1083                         usage(*argv);
1084                 }
1085         }
1086         argc -= optind;
1087         argv += optind;
1088
1089         if (argc > 1)
1090                 usage(*argv);
1091
1092         ttype = get_termcap_entry(*argv);
1093
1094         if (!noset) {
1095                 tcolumns = columns;
1096                 tlines = lines;
1097
1098 #ifdef TIOCGWINSZ
1099                 /* Set window size */
1100                 (void)ioctl(STDERR_FILENO, TIOCGWINSZ, &win);
1101                 if (win.ws_row == 0 && win.ws_col == 0 &&
1102                     tlines > 0 && tcolumns > 0) {
1103                         win.ws_row = tlines;
1104                         win.ws_col = tcolumns;
1105                         (void)ioctl(STDERR_FILENO, TIOCSWINSZ, &win);
1106                 }
1107 #endif
1108                 set_control_chars();
1109                 set_conversions();
1110
1111                 if (!noinit)
1112                         set_init();
1113
1114                 /* Set the modes if they've changed. */
1115                 if (memcmp(&mode, &oldmode, sizeof(mode)))
1116 #ifdef TERMIOS
1117                         tcsetattr(STDERR_FILENO, TCSADRAIN, &mode);
1118 #else
1119                         stty(STDERR_FILENO, &mode);
1120 #endif
1121         }
1122
1123         /* Get the terminal name from the entry. */
1124         ttype = _nc_first_name(cur_term->type.term_names);
1125
1126         if (noset)
1127                 (void)printf("%s\n", ttype);
1128         else {
1129                 if (showterm)
1130                         (void)fprintf(stderr, "Terminal type is %s.\n", ttype);
1131                 /*
1132                  * If erase, kill and interrupt characters could have been
1133                  * modified and not -Q, display the changes.
1134                  */
1135                 if (!quiet) {
1136                         report("Erase", VERASE, CERASE);
1137                         report("Kill", VKILL, CINTR);
1138                         report("Interrupt", VINTR, CKILL);
1139                 }
1140         }
1141
1142         if (Sflag)
1143                 err("The -S option is not supported under terminfo.");
1144
1145         if (sflag) {
1146                 /*
1147                  * Figure out what shell we're using.  A hack, we look for an
1148                  * environmental variable SHELL ending in "csh".
1149                  */
1150                 if ((p = getenv("SHELL")) != 0
1151                  && !strcmp(p + strlen(p) - 3, "csh"))
1152                         p = "set noglob;\nsetenv TERM %s;\nunset noglob;\n";
1153                 else
1154                         p = "TERM=%s;\n";
1155                 (void) printf(p, ttype);
1156         }
1157
1158         return EXIT_SUCCESS;
1159 }
1160
1161 /* tset.c ends here */