ncurses 5.6 - patch 20080804
[ncurses.git] / progs / tset.c
1 /****************************************************************************
2  * Copyright (c) 1998-2007,2008 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.73 2008/08/04 12:47:05 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 ((int) len < (int) 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:
444     if (port) {
445         if (mapp->porttype) {
446           badmopt:
447             err("illegal -m option format: %s", copy);
448         }
449         mapp->porttype = port;
450     }
451     free(copy);
452 #ifdef MAPDEBUG
453     (void) printf("port: %s\n", mapp->porttype ? mapp->porttype : "ANY");
454     (void) printf("type: %s\n", mapp->type);
455     (void) printf("conditional: ");
456     p = "";
457     if (mapp->conditional & GT) {
458         (void) printf("GT");
459         p = "/";
460     }
461     if (mapp->conditional & EQ) {
462         (void) printf("%sEQ", p);
463         p = "/";
464     }
465     if (mapp->conditional & LT)
466         (void) printf("%sLT", p);
467     (void) printf("\nspeed: %d\n", mapp->speed);
468 #endif
469 }
470
471 /*
472  * Return the type of terminal to use for a port of type 'type', as specified
473  * by the first applicable mapping in 'map'.  If no mappings apply, return
474  * 'type'.
475  */
476 static const char *
477 mapped(const char *type)
478 {
479     MAP *mapp;
480     int match;
481
482     for (mapp = maplist; mapp; mapp = mapp->next)
483         if (mapp->porttype == 0 || !strcmp(mapp->porttype, type)) {
484             switch (mapp->conditional) {
485             case 0:             /* No test specified. */
486                 match = TRUE;
487                 break;
488             case EQ:
489                 match = (ospeed == mapp->speed);
490                 break;
491             case GE:
492                 match = (ospeed >= mapp->speed);
493                 break;
494             case GT:
495                 match = (ospeed > mapp->speed);
496                 break;
497             case LE:
498                 match = (ospeed <= mapp->speed);
499                 break;
500             case LT:
501                 match = (ospeed < mapp->speed);
502                 break;
503             default:
504                 match = FALSE;
505             }
506             if (match)
507                 return (mapp->type);
508         }
509     /* No match found; return given type. */
510     return (type);
511 }
512
513 /**************************************************************************
514  *
515  * Entry fetching
516  *
517  **************************************************************************/
518
519 /*
520  * Figure out what kind of terminal we're dealing with, and then read in
521  * its termcap entry.
522  */
523 static const char *
524 get_termcap_entry(char *userarg)
525 {
526     int errret;
527     char *p;
528     const char *ttype;
529 #if HAVE_GETTTYNAM
530     struct ttyent *t;
531 #else
532     FILE *fp;
533 #endif
534     char *ttypath;
535
536     if (userarg) {
537         ttype = userarg;
538         goto found;
539     }
540
541     /* Try the environment. */
542     if ((ttype = getenv("TERM")) != 0)
543         goto map;
544
545     if ((ttypath = ttyname(STDERR_FILENO)) != 0) {
546         p = _nc_basename(ttypath);
547 #if HAVE_GETTTYNAM
548         /*
549          * We have the 4.3BSD library call getttynam(3); that means
550          * there's an /etc/ttys to look up device-to-type mappings in.
551          * Try ttyname(3); check for dialup or other mapping.
552          */
553         if ((t = getttynam(p))) {
554             ttype = t->ty_type;
555             goto map;
556         }
557 #else
558         if ((fp = fopen("/etc/ttytype", "r")) != 0
559             || (fp = fopen("/etc/ttys", "r")) != 0) {
560             char buffer[BUFSIZ];
561             char *s, *t, *d;
562
563             while (fgets(buffer, sizeof(buffer) - 1, fp) != 0) {
564                 for (s = buffer, t = d = 0; *s; s++) {
565                     if (isspace(UChar(*s)))
566                         *s = '\0';
567                     else if (t == 0)
568                         t = s;
569                     else if (d == 0 && s != buffer && s[-1] == '\0')
570                         d = s;
571                 }
572                 if (t != 0 && d != 0 && !strcmp(d, p)) {
573                     ttype = strdup(t);
574                     fclose(fp);
575                     goto map;
576                 }
577             }
578             fclose(fp);
579         }
580 #endif /* HAVE_GETTTYNAM */
581     }
582
583     /* If still undefined, use "unknown". */
584     ttype = "unknown";
585
586   map:ttype = mapped(ttype);
587
588     /*
589      * If not a path, remove TERMCAP from the environment so we get a
590      * real entry from /etc/termcap.  This prevents us from being fooled
591      * by out of date stuff in the environment.
592      */
593   found:if ((p = getenv("TERMCAP")) != 0 && !_nc_is_abs_path(p)) {
594         /* 'unsetenv("TERMCAP")' is not portable.
595          * The 'environ' array is better.
596          */
597         int n;
598         for (n = 0; environ[n] != 0; n++) {
599             if (!strncmp("TERMCAP=", environ[n], 8)) {
600                 while ((environ[n] = environ[n + 1]) != 0) {
601                     n++;
602                 }
603                 break;
604             }
605         }
606     }
607
608     /*
609      * ttype now contains a pointer to the type of the terminal.
610      * If the first character is '?', ask the user.
611      */
612     if (ttype[0] == '?') {
613         if (ttype[1] != '\0')
614             ttype = askuser(ttype + 1);
615         else
616             ttype = askuser(0);
617     }
618     /* Find the terminfo entry.  If it doesn't exist, ask the user. */
619     while (setupterm((NCURSES_CONST char *) ttype, STDOUT_FILENO, &errret)
620            != OK) {
621         if (errret == 0) {
622             (void) fprintf(stderr, "%s: unknown terminal type %s\n",
623                            _nc_progname, ttype);
624             ttype = 0;
625         } else {
626             (void) fprintf(stderr,
627                            "%s: can't initialize terminal type %s (error %d)\n",
628                            _nc_progname, ttype, errret);
629             ttype = 0;
630         }
631         ttype = askuser(ttype);
632     }
633 #if BROKEN_LINKER
634     tgetflag("am");             /* force lib_termcap.o to be linked for 'ospeed' */
635 #endif
636     return (ttype);
637 }
638
639 /**************************************************************************
640  *
641  * Mode-setting logic
642  *
643  **************************************************************************/
644
645 /* some BSD systems have these built in, some systems are missing
646  * one or more definitions. The safest solution is to override unless the
647  * commonly-altered ones are defined.
648  */
649 #if !(defined(CERASE) && defined(CINTR) && defined(CKILL) && defined(CQUIT))
650 #undef CEOF
651 #undef CERASE
652 #undef CINTR
653 #undef CKILL
654 #undef CLNEXT
655 #undef CRPRNT
656 #undef CQUIT
657 #undef CSTART
658 #undef CSTOP
659 #undef CSUSP
660 #endif
661
662 /* control-character defaults */
663 #ifndef CEOF
664 #define CEOF    CTRL('D')
665 #endif
666 #ifndef CERASE
667 #define CERASE  CTRL('H')
668 #endif
669 #ifndef CINTR
670 #define CINTR   127             /* ^? */
671 #endif
672 #ifndef CKILL
673 #define CKILL   CTRL('U')
674 #endif
675 #ifndef CLNEXT
676 #define CLNEXT  CTRL('v')
677 #endif
678 #ifndef CRPRNT
679 #define CRPRNT  CTRL('r')
680 #endif
681 #ifndef CQUIT
682 #define CQUIT   CTRL('\\')
683 #endif
684 #ifndef CSTART
685 #define CSTART  CTRL('Q')
686 #endif
687 #ifndef CSTOP
688 #define CSTOP   CTRL('S')
689 #endif
690 #ifndef CSUSP
691 #define CSUSP   CTRL('Z')
692 #endif
693
694 #if defined(_POSIX_VDISABLE)
695 #define DISABLED(val)   (((_POSIX_VDISABLE != -1) \
696                        && ((val) == _POSIX_VDISABLE)) \
697                       || ((val) <= 0))
698 #else
699 #define DISABLED(val)   ((int)(val) <= 0)
700 #endif
701
702 #define CHK(val, dft)   (DISABLED(val) ? dft : val)
703
704 static bool set_tabs(void);
705
706 /*
707  * Reset the terminal mode bits to a sensible state.  Very useful after
708  * a child program dies in raw mode.
709  */
710 static void
711 reset_mode(void)
712 {
713 #ifdef TERMIOS
714     tcgetattr(STDERR_FILENO, &mode);
715 #else
716     stty(STDERR_FILENO, &mode);
717 #endif
718
719 #ifdef TERMIOS
720 #if defined(VDISCARD) && defined(CDISCARD)
721     mode.c_cc[VDISCARD] = CHK(mode.c_cc[VDISCARD], CDISCARD);
722 #endif
723     mode.c_cc[VEOF] = CHK(mode.c_cc[VEOF], CEOF);
724     mode.c_cc[VERASE] = CHK(mode.c_cc[VERASE], CERASE);
725 #if defined(VFLUSH) && defined(CFLUSH)
726     mode.c_cc[VFLUSH] = CHK(mode.c_cc[VFLUSH], CFLUSH);
727 #endif
728     mode.c_cc[VINTR] = CHK(mode.c_cc[VINTR], CINTR);
729     mode.c_cc[VKILL] = CHK(mode.c_cc[VKILL], CKILL);
730 #if defined(VLNEXT) && defined(CLNEXT)
731     mode.c_cc[VLNEXT] = CHK(mode.c_cc[VLNEXT], CLNEXT);
732 #endif
733     mode.c_cc[VQUIT] = CHK(mode.c_cc[VQUIT], CQUIT);
734 #if defined(VREPRINT) && defined(CRPRNT)
735     mode.c_cc[VREPRINT] = CHK(mode.c_cc[VREPRINT], CRPRNT);
736 #endif
737 #if defined(VSTART) && defined(CSTART)
738     mode.c_cc[VSTART] = CHK(mode.c_cc[VSTART], CSTART);
739 #endif
740 #if defined(VSTOP) && defined(CSTOP)
741     mode.c_cc[VSTOP] = CHK(mode.c_cc[VSTOP], CSTOP);
742 #endif
743 #if defined(VSUSP) && defined(CSUSP)
744     mode.c_cc[VSUSP] = CHK(mode.c_cc[VSUSP], CSUSP);
745 #endif
746 #if defined(VWERASE) && defined(CWERASE)
747     mode.c_cc[VWERASE] = CHK(mode.c_cc[VWERASE], CWERASE);
748 #endif
749
750     mode.c_iflag &= ~(IGNBRK | PARMRK | INPCK | ISTRIP | INLCR | IGNCR
751 #ifdef IUCLC
752                       | IUCLC
753 #endif
754 #ifdef IXANY
755                       | IXANY
756 #endif
757                       | IXOFF);
758
759     mode.c_iflag |= (BRKINT | IGNPAR | ICRNL | IXON
760 #ifdef IMAXBEL
761                      | IMAXBEL
762 #endif
763         );
764
765     mode.c_oflag &= ~(0
766 #ifdef OLCUC
767                       | OLCUC
768 #endif
769 #ifdef OCRNL
770                       | OCRNL
771 #endif
772 #ifdef ONOCR
773                       | ONOCR
774 #endif
775 #ifdef ONLRET
776                       | ONLRET
777 #endif
778 #ifdef OFILL
779                       | OFILL
780 #endif
781 #ifdef OFDEL
782                       | OFDEL
783 #endif
784 #ifdef NLDLY
785                       | NLDLY
786 #endif
787 #ifdef CRDLY
788                       | CRDLY
789 #endif
790 #ifdef TABDLY
791                       | TABDLY
792 #endif
793 #ifdef BSDLY
794                       | BSDLY
795 #endif
796 #ifdef VTDLY
797                       | VTDLY
798 #endif
799 #ifdef FFDLY
800                       | FFDLY
801 #endif
802         );
803
804     mode.c_oflag |= (OPOST
805 #ifdef ONLCR
806                      | ONLCR
807 #endif
808         );
809
810     mode.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | CLOCAL);
811     mode.c_cflag |= (CS8 | CREAD);
812     mode.c_lflag &= ~(ECHONL | NOFLSH
813 #ifdef TOSTOP
814                       | TOSTOP
815 #endif
816 #ifdef ECHOPTR
817                       | ECHOPRT
818 #endif
819 #ifdef XCASE
820                       | XCASE
821 #endif
822         );
823
824     mode.c_lflag |= (ISIG | ICANON | ECHO | ECHOE | ECHOK
825 #ifdef ECHOCTL
826                      | ECHOCTL
827 #endif
828 #ifdef ECHOKE
829                      | ECHOKE
830 #endif
831         );
832 #endif
833
834     SET_TTY(STDERR_FILENO, &mode);
835 }
836
837 /*
838  * Returns a "good" value for the erase character.  This is loosely based on
839  * the BSD4.4 logic.
840  */
841 #ifdef TERMIOS
842 static int
843 default_erase(void)
844 {
845     int result;
846
847     if (over_strike
848         && key_backspace != 0
849         && strlen(key_backspace) == 1)
850         result = key_backspace[0];
851     else
852         result = CERASE;
853
854     return result;
855 }
856 #endif
857
858 /*
859  * Update the values of the erase, interrupt, and kill characters in 'mode'.
860  *
861  * SVr4 tset (e.g., Solaris 2.5) only modifies the intr, quit or erase
862  * characters if they're unset, or if we specify them as options.  This differs
863  * from BSD 4.4 tset, which always sets erase.
864  */
865 static void
866 set_control_chars(void)
867 {
868 #ifdef TERMIOS
869     if (DISABLED(mode.c_cc[VERASE]) || terasechar >= 0)
870         mode.c_cc[VERASE] = terasechar >= 0 ? terasechar : default_erase();
871
872     if (DISABLED(mode.c_cc[VINTR]) || intrchar >= 0)
873         mode.c_cc[VINTR] = intrchar >= 0 ? intrchar : CINTR;
874
875     if (DISABLED(mode.c_cc[VKILL]) || tkillchar >= 0)
876         mode.c_cc[VKILL] = tkillchar >= 0 ? tkillchar : CKILL;
877 #endif
878 }
879
880 /*
881  * Set up various conversions in 'mode', including parity, tabs, returns,
882  * echo, and case, according to the termcap entry.  If the program we're
883  * running was named with a leading upper-case character, map external
884  * uppercase to internal lowercase.
885  */
886 static void
887 set_conversions(void)
888 {
889 #ifdef __OBSOLETE__
890     /*
891      * Conversion logic for some *really* ancient terminal glitches,
892      * not supported in terminfo.  Left here for succeeding generations
893      * to marvel at.
894      */
895     if (tgetflag("UC")) {
896 #ifdef IUCLC
897         mode.c_iflag |= IUCLC;
898         mode.c_oflag |= OLCUC;
899 #endif
900     } else if (tgetflag("LC")) {
901 #ifdef IUCLC
902         mode.c_iflag &= ~IUCLC;
903         mode.c_oflag &= ~OLCUC;
904 #endif
905     }
906     mode.c_iflag &= ~(PARMRK | INPCK);
907     mode.c_lflag |= ICANON;
908     if (tgetflag("EP")) {
909         mode.c_cflag |= PARENB;
910         mode.c_cflag &= ~PARODD;
911     }
912     if (tgetflag("OP")) {
913         mode.c_cflag |= PARENB;
914         mode.c_cflag |= PARODD;
915     }
916 #endif /* __OBSOLETE__ */
917
918 #ifdef TERMIOS
919 #ifdef ONLCR
920     mode.c_oflag |= ONLCR;
921 #endif
922     mode.c_iflag |= ICRNL;
923     mode.c_lflag |= ECHO;
924 #ifdef OXTABS
925     mode.c_oflag |= OXTABS;
926 #endif /* OXTABS */
927
928     /* test used to be tgetflag("NL") */
929     if (newline != (char *) 0 && newline[0] == '\n' && !newline[1]) {
930         /* Newline, not linefeed. */
931 #ifdef ONLCR
932         mode.c_oflag &= ~ONLCR;
933 #endif
934         mode.c_iflag &= ~ICRNL;
935     }
936 #ifdef __OBSOLETE__
937     if (tgetflag("HD"))         /* Half duplex. */
938         mode.c_lflag &= ~ECHO;
939 #endif /* __OBSOLETE__ */
940 #ifdef OXTABS
941     /* test used to be tgetflag("pt") */
942     if (has_hardware_tabs)      /* Print tabs. */
943         mode.c_oflag &= ~OXTABS;
944 #endif /* OXTABS */
945     mode.c_lflag |= (ECHOE | ECHOK);
946 #endif
947 }
948
949 /* Output startup string. */
950 static void
951 set_init(void)
952 {
953     char *p;
954     bool settle;
955
956 #ifdef __OBSOLETE__
957     if (pad_char != (char *) 0) /* Get/set pad character. */
958         PC = pad_char[0];
959 #endif /* OBSOLETE */
960
961 #ifdef TAB3
962     if (oldmode.c_oflag & (TAB3 | ONLCR | OCRNL | ONLRET)) {
963         oldmode.c_oflag &= (TAB3 | ONLCR | OCRNL | ONLRET);
964         SET_TTY(STDERR_FILENO, &oldmode);
965     }
966 #endif
967     settle = set_tabs();
968
969     if (isreset) {
970         if ((p = reset_1string) != 0) {
971             tputs(p, 0, outc);
972             settle = TRUE;
973         }
974         if ((p = reset_2string) != 0) {
975             tputs(p, 0, outc);
976             settle = TRUE;
977         }
978         /* What about rf, rs3, as per terminfo man page? */
979         /* also might be nice to send rmacs, rmul, rmm */
980         if ((p = reset_file) != 0
981             || (p = init_file) != 0) {
982             cat(p);
983             settle = TRUE;
984         }
985     }
986
987     if (settle) {
988         (void) putc('\r', stderr);
989         (void) fflush(stderr);
990         (void) napms(1000);     /* Settle the terminal. */
991     }
992 }
993
994 /*
995  * Set the hardware tabs on the terminal, using the ct (clear all tabs),
996  * st (set one tab) and ch (horizontal cursor addressing) capabilities.
997  * This is done before if and is, so they can patch in case we blow this.
998  * Return TRUE if we set any tab stops, FALSE if not.
999  */
1000 static bool
1001 set_tabs(void)
1002 {
1003     if (set_tab && clear_all_tabs) {
1004         int c;
1005
1006         (void) putc('\r', stderr);      /* Force to left margin. */
1007         tputs(clear_all_tabs, 0, outc);
1008
1009         for (c = 8; c < tcolumns; c += 8) {
1010             /* Get to the right column.  In BSD tset, this
1011              * used to try a bunch of half-clever things
1012              * with cup and hpa, for an average saving of
1013              * somewhat less than two character times per
1014              * tab stop, less than .01 sec at 2400cps. We
1015              * lost all this cruft because it seemed to be
1016              * introducing some odd bugs.
1017              * -----------12345678----------- */
1018             (void) fputs("        ", stderr);
1019             tputs(set_tab, 0, outc);
1020         }
1021         putc('\r', stderr);
1022         return (TRUE);
1023     }
1024     return (FALSE);
1025 }
1026
1027 /**************************************************************************
1028  *
1029  * Main sequence
1030  *
1031  **************************************************************************/
1032
1033 /*
1034  * Tell the user if a control key has been changed from the default value.
1035  */
1036 #ifdef TERMIOS
1037 static void
1038 report(const char *name, int which, unsigned def)
1039 {
1040     unsigned older, newer;
1041     char *p;
1042
1043     newer = mode.c_cc[which];
1044     older = oldmode.c_cc[which];
1045
1046     if (older == newer && older == def)
1047         return;
1048
1049     (void) fprintf(stderr, "%s %s ", name, older == newer ? "is" : "set to");
1050
1051     if (DISABLED(newer))
1052         (void) fprintf(stderr, "undef.\n");
1053     /*
1054      * Check 'delete' before 'backspace', since the key_backspace value
1055      * is ambiguous.
1056      */
1057     else if (newer == 0177)
1058         (void) fprintf(stderr, "delete.\n");
1059     else if ((p = key_backspace) != 0
1060              && newer == (unsigned char) p[0]
1061              && p[1] == '\0')
1062         (void) fprintf(stderr, "backspace.\n");
1063     else if (newer < 040) {
1064         newer ^= 0100;
1065         (void) fprintf(stderr, "control-%c (^%c).\n", UChar(newer), UChar(newer));
1066     } else
1067         (void) fprintf(stderr, "%c.\n", UChar(newer));
1068 }
1069 #endif
1070
1071 /*
1072  * Convert the obsolete argument forms into something that getopt can handle.
1073  * This means that -e, -i and -k get default arguments supplied for them.
1074  */
1075 static void
1076 obsolete(char **argv)
1077 {
1078     for (; *argv; ++argv) {
1079         char *parm = argv[0];
1080
1081         if (parm[0] == '-' && parm[1] == '\0') {
1082             argv[0] = strdup("-q");
1083             continue;
1084         }
1085
1086         if ((parm[0] != '-')
1087             || (argv[1] && argv[1][0] != '-')
1088             || (parm[1] != 'e' && parm[1] != 'i' && parm[1] != 'k')
1089             || (parm[2] != '\0'))
1090             continue;
1091         switch (argv[0][1]) {
1092         case 'e':
1093             argv[0] = strdup("-e^H");
1094             break;
1095         case 'i':
1096             argv[0] = strdup("-i^C");
1097             break;
1098         case 'k':
1099             argv[0] = strdup("-k^U");
1100             break;
1101         }
1102     }
1103 }
1104
1105 static void
1106 usage(void)
1107 {
1108     static const char *tbl[] =
1109     {
1110         ""
1111         ,"Options:"
1112         ,"  -c          set control characters"
1113         ,"  -e ch       erase character"
1114         ,"  -I          no initialization strings"
1115         ,"  -i ch       interrupt character"
1116         ,"  -k ch       kill character"
1117         ,"  -m mapping  map identifier to type"
1118         ,"  -Q          do not output control key settings"
1119         ,"  -r          display term on stderr"
1120         ,"  -s          output TERM set command"
1121         ,"  -V          print curses-version"
1122         ,"  -w          set window-size"
1123     };
1124     unsigned n;
1125     (void) fprintf(stderr, "Usage: %s [options] [terminal]\n", _nc_progname);
1126     for (n = 0; n < sizeof(tbl) / sizeof(tbl[0]); ++n)
1127         fprintf(stderr, "%s\n", tbl[n]);
1128     exit_error();
1129     /* NOTREACHED */
1130 }
1131
1132 static char
1133 arg_to_char(void)
1134 {
1135     return (char) ((optarg[0] == '^' && optarg[1] != '\0')
1136                    ? ((optarg[1] == '?') ? '\177' : CTRL(optarg[1]))
1137                    : optarg[0]);
1138 }
1139
1140 int
1141 main(int argc, char **argv)
1142 {
1143 #if defined(TIOCGWINSZ) && defined(TIOCSWINSZ)
1144     struct winsize win;
1145 #endif
1146     int ch, noinit, noset, quiet, Sflag, sflag, showterm;
1147     const char *p;
1148     const char *ttype;
1149
1150     obsolete(argv);
1151     noinit = noset = quiet = Sflag = sflag = showterm = 0;
1152     while ((ch = getopt(argc, argv, "a:cd:e:Ii:k:m:np:qQSrsVw")) != -1) {
1153         switch (ch) {
1154         case 'c':               /* set control-chars */
1155             opt_c = TRUE;
1156             break;
1157         case 'a':               /* OBSOLETE: map identifier to type */
1158             add_mapping("arpanet", optarg);
1159             break;
1160         case 'd':               /* OBSOLETE: map identifier to type */
1161             add_mapping("dialup", optarg);
1162             break;
1163         case 'e':               /* erase character */
1164             terasechar = arg_to_char();
1165             break;
1166         case 'I':               /* no initialization strings */
1167             noinit = 1;
1168             break;
1169         case 'i':               /* interrupt character */
1170             intrchar = arg_to_char();
1171             break;
1172         case 'k':               /* kill character */
1173             tkillchar = arg_to_char();
1174             break;
1175         case 'm':               /* map identifier to type */
1176             add_mapping(0, optarg);
1177             break;
1178         case 'n':               /* OBSOLETE: set new tty driver */
1179             break;
1180         case 'p':               /* OBSOLETE: map identifier to type */
1181             add_mapping("plugboard", optarg);
1182             break;
1183         case 'Q':               /* don't output control key settings */
1184             quiet = 1;
1185             break;
1186         case 'q':               /* display term only */
1187             noset = 1;
1188             break;
1189         case 'r':               /* display term on stderr */
1190             showterm = 1;
1191             break;
1192         case 'S':               /* OBSOLETE: output TERM & TERMCAP */
1193             Sflag = 1;
1194             break;
1195         case 's':               /* output TERM set command */
1196             sflag = 1;
1197             break;
1198         case 'V':               /* print curses-version */
1199             puts(curses_version());
1200             ExitProgram(EXIT_SUCCESS);
1201         case 'w':               /* set window-size */
1202             opt_w = TRUE;
1203             break;
1204         case '?':
1205         default:
1206             usage();
1207         }
1208     }
1209
1210     _nc_progname = _nc_rootname(*argv);
1211     argc -= optind;
1212     argv += optind;
1213
1214     if (argc > 1)
1215         usage();
1216
1217     if (!opt_c && !opt_w)
1218         opt_c = opt_w = TRUE;
1219
1220     if (GET_TTY(STDERR_FILENO, &mode) < 0)
1221         failed("standard error");
1222     can_restore = TRUE;
1223     original = oldmode = mode;
1224 #ifdef TERMIOS
1225     ospeed = (NCURSES_OSPEED) cfgetospeed(&mode);
1226 #else
1227     ospeed = (NCURSES_OSPEED) mode.sg_ospeed;
1228 #endif
1229
1230     if (!strcmp(_nc_progname, PROG_RESET)) {
1231         isreset = TRUE;
1232         reset_mode();
1233     }
1234
1235     ttype = get_termcap_entry(*argv);
1236
1237     if (!noset) {
1238         tcolumns = columns;
1239         tlines = lines;
1240
1241 #if defined(TIOCGWINSZ) && defined(TIOCSWINSZ)
1242         if (opt_w) {
1243             /* Set window size */
1244             (void) ioctl(STDERR_FILENO, TIOCGWINSZ, &win);
1245             if (win.ws_row == 0 && win.ws_col == 0 &&
1246                 tlines > 0 && tcolumns > 0) {
1247                 win.ws_row = tlines;
1248                 win.ws_col = tcolumns;
1249                 (void) ioctl(STDERR_FILENO, TIOCSWINSZ, &win);
1250             }
1251         }
1252 #endif
1253         if (opt_c) {
1254             set_control_chars();
1255             set_conversions();
1256
1257             if (!noinit)
1258                 set_init();
1259
1260             /* Set the modes if they've changed. */
1261             if (memcmp(&mode, &oldmode, sizeof(mode))) {
1262                 SET_TTY(STDERR_FILENO, &mode);
1263             }
1264         }
1265     }
1266
1267     /* Get the terminal name from the entry. */
1268     ttype = _nc_first_name(cur_term->type.term_names);
1269
1270     if (noset)
1271         (void) printf("%s\n", ttype);
1272     else {
1273         if (showterm)
1274             (void) fprintf(stderr, "Terminal type is %s.\n", ttype);
1275         /*
1276          * If erase, kill and interrupt characters could have been
1277          * modified and not -Q, display the changes.
1278          */
1279 #ifdef TERMIOS
1280         if (!quiet) {
1281             report("Erase", VERASE, CERASE);
1282             report("Kill", VKILL, CKILL);
1283             report("Interrupt", VINTR, CINTR);
1284         }
1285 #endif
1286     }
1287
1288     if (Sflag)
1289         err("The -S option is not supported under terminfo.");
1290
1291     if (sflag) {
1292         int len;
1293         char *var;
1294         char *leaf;
1295         /*
1296          * Figure out what shell we're using.  A hack, we look for an
1297          * environmental variable SHELL ending in "csh".
1298          */
1299         if ((var = getenv("SHELL")) != 0
1300             && ((len = (int) strlen(leaf = _nc_basename(var))) >= 3)
1301             && !strcmp(leaf + len - 3, "csh"))
1302             p = "set noglob;\nsetenv TERM %s;\nunset noglob;\n";
1303         else
1304             p = "TERM=%s;\n";
1305         (void) printf(p, ttype);
1306     }
1307
1308     ExitProgram(EXIT_SUCCESS);
1309 }