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