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