GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tset/tset.c Lines: 118 343 34.4 %
Date: 2017-11-07 Branches: 73 351 20.8 %

Line Branch Exec Source
1
/*	$OpenBSD: tset.c,v 1.39 2015/11/16 03:02:40 deraadt Exp $	*/
2
3
/****************************************************************************
4
 * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc.              *
5
 *                                                                          *
6
 * Permission is hereby granted, free of charge, to any person obtaining a  *
7
 * copy of this software and associated documentation files (the            *
8
 * "Software"), to deal in the Software without restriction, including      *
9
 * without limitation the rights to use, copy, modify, merge, publish,      *
10
 * distribute, distribute with modifications, sublicense, and/or sell       *
11
 * copies of the Software, and to permit persons to whom the Software is    *
12
 * furnished to do so, subject to the following conditions:                 *
13
 *                                                                          *
14
 * The above copyright notice and this permission notice shall be included  *
15
 * in all copies or substantial portions of the Software.                   *
16
 *                                                                          *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
18
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
19
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
20
 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
21
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
22
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
23
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
24
 *                                                                          *
25
 * Except as contained in this notice, the name(s) of the above copyright   *
26
 * holders shall not be used in advertising or otherwise to promote the     *
27
 * sale, use or other dealings in this Software without prior written       *
28
 * authorization.                                                           *
29
 ****************************************************************************/
30
31
/****************************************************************************
32
 *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
33
 *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
34
 *     and: Thomas E. Dickey                        1996-on                 *
35
 ****************************************************************************/
36
37
/*
38
 * tset.c - terminal initialization utility
39
 *
40
 * This code was mostly swiped from 4.4BSD tset, with some obsolescent
41
 * cruft removed and substantial portions rewritten.  A Regents of the
42
 * University of California copyright applies to some portions of the
43
 * code, and is reproduced below:
44
 */
45
/*-
46
 * Copyright (c) 1980, 1991, 1993
47
 *	The Regents of the University of California.  All rights reserved.
48
 *
49
 * Redistribution and use in source and binary forms, with or without
50
 * modification, are permitted provided that the following conditions
51
 * are met:
52
 * 1. Redistributions of source code must retain the above copyright
53
 *    notice, this list of conditions and the following disclaimer.
54
 * 2. Redistributions in binary form must reproduce the above copyright
55
 *    notice, this list of conditions and the following disclaimer in the
56
 *    documentation and/or other materials provided with the distribution.
57
 * 3. Neither the name of the University nor the names of its contributors
58
 *    may be used to endorse or promote products derived from this software
59
 *    without specific prior written permission.
60
 *
61
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
62
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
63
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
64
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
65
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
66
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
67
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
68
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
69
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
70
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
71
 * SUCH DAMAGE.
72
 */
73
74
#define USE_LIBTINFO
75
#define __INTERNAL_CAPS_VISIBLE	/* we need to see has_hardware_tabs */
76
#include <progs.priv.h>
77
78
#include <errno.h>
79
#include <stdio.h>
80
#include <termcap.h>
81
#include <fcntl.h>
82
83
#if HAVE_GETTTYNAM && HAVE_TTYENT_H
84
#include <ttyent.h>
85
#endif
86
#ifdef NeXT
87
char *ttyname(int fd);
88
#endif
89
90
#if HAVE_SIZECHANGE
91
# if !defined(sun) || !TERMIOS
92
#  if HAVE_SYS_IOCTL_H
93
#   include <sys/ioctl.h>
94
#  endif
95
# endif
96
#endif
97
98
#if NEED_PTEM_H
99
/* they neglected to define struct winsize in termios.h -- it's only
100
   in termio.h	*/
101
#include <sys/stream.h>
102
#include <sys/ptem.h>
103
#endif
104
105
#include <dump_entry.h>
106
#include <transform.h>
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
    fprintf(stderr, "%s: ", _nc_progname);
168
    perror(msg);
169
    exit_error();
170
    /* NOTREACHED */
171
}
172
173
static void
174
cat(char *file)
175
{
176
    FILE *fp;
177
    size_t nr;
178
    char buf[BUFSIZ];
179
180
    if ((fp = fopen(file, "r")) == 0)
181
	failed(file);
182
183
    while ((nr = fread(buf, sizeof(char), sizeof(buf), fp)) != 0)
184
	if (fwrite(buf, sizeof(char), nr, stderr) != nr)
185
	      failed("write to stderr");
186
    fclose(fp);
187
}
188
189
static int
190
outc(int c)
191
{
192
2736
    return putc(c, stderr);
193
}
194
195
/* Prompt the user for a terminal type. */
196
static const char *
197
askuser(const char *dflt)
198
{
199
    static char answer[256];
200
201
    /* We can get recalled; if so, don't continue uselessly. */
202
    clearerr(stdin);
203
    if (feof(stdin) || ferror(stdin)) {
204
	(void) fprintf(stderr, "\n");
205
	exit_error();
206
	/* NOTREACHED */
207
    }
208
    for (;;) {
209
	if (dflt)
210
	    (void) fprintf(stderr, "Terminal type? [%s] ", dflt);
211
	else
212
	    (void) fprintf(stderr, "Terminal type? ");
213
	(void) fflush(stderr);
214
215
	if (fgets(answer, sizeof(answer), stdin) == NULL) {
216
	    if (dflt == 0) {
217
		exit_error();
218
		/* NOTREACHED */
219
	    }
220
	    return (dflt);
221
	}
222
223
	answer[strcspn(answer, "\n")] = '\0';
224
	if (answer[0])
225
	    return (answer);
226
	if (dflt != 0)
227
	    return (dflt);
228
    }
229
}
230
231
/**************************************************************************
232
 *
233
 * Mapping logic begins here
234
 *
235
 **************************************************************************/
236
237
/* Baud rate conditionals for mapping. */
238
#define	GT		0x01
239
#define	EQ		0x02
240
#define	LT		0x04
241
#define	NOT		0x08
242
#define	GE		(GT | EQ)
243
#define	LE		(LT | EQ)
244
245
typedef struct map {
246
    struct map *next;		/* Linked list of maps. */
247
    const char *porttype;	/* Port type, or "" for any. */
248
    const char *type;		/* Terminal type to select. */
249
    int conditional;		/* Baud rate conditionals bitmask. */
250
    int speed;			/* Baud rate to compare against. */
251
} MAP;
252
253
static MAP *cur, *maplist;
254
255
typedef struct speeds {
256
    const char *string;
257
    int speed;
258
} SPEEDS;
259
260
static const SPEEDS speeds[] =
261
{
262
    {"0", B0},
263
    {"50", B50},
264
    {"75", B75},
265
    {"110", B110},
266
    {"134", B134},
267
    {"134.5", B134},
268
    {"150", B150},
269
    {"200", B200},
270
    {"300", B300},
271
    {"600", B600},
272
    {"1200", B1200},
273
    {"1800", B1800},
274
    {"2400", B2400},
275
    {"4800", B4800},
276
    {"9600", B9600},
277
    /* sgttyb may define up to this point */
278
#ifdef B19200
279
    {"19200", B19200},
280
#endif
281
#ifdef B38400
282
    {"38400", B38400},
283
#endif
284
#ifdef B19200
285
    {"19200", B19200},
286
#endif
287
#ifdef B38400
288
    {"38400", B38400},
289
#endif
290
#ifdef B19200
291
    {"19200", B19200},
292
#else
293
#ifdef EXTA
294
    {"19200", EXTA},
295
#endif
296
#endif
297
#ifdef B38400
298
    {"38400", B38400},
299
#else
300
#ifdef EXTB
301
    {"38400", EXTB},
302
#endif
303
#endif
304
#ifdef B57600
305
    {"57600", B57600},
306
#endif
307
#ifdef B115200
308
    {"115200", B115200},
309
#endif
310
#ifdef B230400
311
    {"230400", B230400},
312
#endif
313
#ifdef B460800
314
    {"460800", B460800},
315
#endif
316
    {(char *) 0, 0}
317
};
318
319
static int
320
tbaudrate(char *rate)
321
{
322
    const SPEEDS *sp;
323
    int found = FALSE;
324
325
    /* The baudrate number can be preceded by a 'B', which is ignored. */
326
    if (*rate == 'B')
327
	++rate;
328
329
    for (sp = speeds; sp->string; ++sp) {
330
	if (!CaselessCmp(rate, sp->string)) {
331
	    found = TRUE;
332
	    break;
333
	}
334
    }
335
    if (!found)
336
	err("unknown baud rate %s", rate);
337
    return (sp->speed);
338
}
339
340
/*
341
 * Syntax for -m:
342
 * [port-type][test baudrate]:terminal-type
343
 * The baud rate tests are: >, <, @, =, !
344
 */
345
static void
346
add_mapping(const char *port, char *arg)
347
{
348
    MAP *mapp;
349
    char *copy, *p;
350
    const char *termp;
351
    char *base = 0;
352
353
38
    copy = strdup(arg);
354
19
    mapp = malloc(sizeof(MAP));
355
19
    if (copy == 0 || mapp == 0)
356
	failed("malloc");
357
19
    mapp->next = 0;
358
19
    if (maplist == 0)
359
19
	cur = maplist = mapp;
360
    else {
361
	cur->next = mapp;
362
	cur = mapp;
363
    }
364
365
19
    mapp->porttype = arg;
366
19
    mapp->conditional = 0;
367
368
19
    arg = strpbrk(arg, "><@=!:");
369
370
19
    if (arg == 0) {		/* [?]term */
371
	mapp->type = mapp->porttype;
372
	mapp->porttype = 0;
373
	goto done;
374
    }
375
376
19
    if (arg == mapp->porttype)	/* [><@=! baud]:term */
377
	termp = mapp->porttype = 0;
378
    else
379
	termp = base = arg;
380
381
    for (;; ++arg) {		/* Optional conditionals. */
382

19
	switch (*arg) {
383
	case '<':
384
	    if (mapp->conditional & GT)
385
		goto badmopt;
386
	    mapp->conditional |= LT;
387
	    break;
388
	case '>':
389
	    if (mapp->conditional & LT)
390
		goto badmopt;
391
	    mapp->conditional |= GT;
392
	    break;
393
	case '@':
394
	case '=':		/* Not documented. */
395
	    mapp->conditional |= EQ;
396
	    break;
397
	case '!':
398
	    mapp->conditional |= NOT;
399
	    break;
400
	default:
401
	    goto next;
402
	}
403
    }
404
405
  next:
406
19
    if (*arg == ':') {
407
19
	if (mapp->conditional)
408
	    goto badmopt;
409
19
	++arg;
410
19
    } else {			/* Optional baudrate. */
411
	arg = strchr(p = arg, ':');
412
	if (arg == 0)
413
	    goto badmopt;
414
	*arg++ = '\0';
415
	mapp->speed = tbaudrate(p);
416
    }
417
418
19
    if (arg == (char *) 0)	/* Non-optional type. */
419
	goto badmopt;
420
421
19
    mapp->type = arg;
422
423
    /* Terminate porttype, if specified. */
424
19
    if (termp != 0)
425
19
	*base = '\0';
426
427
    /* If a NOT conditional, reverse the test. */
428
19
    if (mapp->conditional & NOT)
429
	mapp->conditional = ~mapp->conditional & (EQ | GT | LT);
430
431
    /* If user specified a port with an option flag, set it. */
432
  done:
433
19
    if (port) {
434
	if (mapp->porttype) {
435
	  badmopt:
436
	    err("illegal -m option format: %s", copy);
437
	}
438
	mapp->porttype = port;
439
    }
440
19
    free(copy);
441
#ifdef MAPDEBUG
442
    (void) printf("port: %s\n", mapp->porttype ? mapp->porttype : "ANY");
443
    (void) printf("type: %s\n", mapp->type);
444
    (void) printf("conditional: ");
445
    p = "";
446
    if (mapp->conditional & GT) {
447
	(void) printf("GT");
448
	p = "/";
449
    }
450
    if (mapp->conditional & EQ) {
451
	(void) printf("%sEQ", p);
452
	p = "/";
453
    }
454
    if (mapp->conditional & LT)
455
	(void) printf("%sLT", p);
456
    (void) printf("\nspeed: %d\n", mapp->speed);
457
#endif
458
19
}
459
460
/*
461
 * Return the type of terminal to use for a port of type 'type', as specified
462
 * by the first applicable mapping in 'map'.  If no mappings apply, return
463
 * 'type'.
464
 */
465
static const char *
466
mapped(const char *type)
467
{
468
    MAP *mapp;
469
    int match;
470
471
    for (mapp = maplist; mapp; mapp = mapp->next)
472
	if (mapp->porttype == 0 || !strcmp(mapp->porttype, type)) {
473
	    switch (mapp->conditional) {
474
	    case 0:		/* No test specified. */
475
		match = TRUE;
476
		break;
477
	    case EQ:
478
		match = (ospeed == mapp->speed);
479
		break;
480
	    case GE:
481
		match = (ospeed >= mapp->speed);
482
		break;
483
	    case GT:
484
		match = (ospeed > mapp->speed);
485
		break;
486
	    case LE:
487
		match = (ospeed <= mapp->speed);
488
		break;
489
	    case LT:
490
		match = (ospeed < mapp->speed);
491
		break;
492
	    default:
493
		match = FALSE;
494
	    }
495
	    if (match)
496
		return (mapp->type);
497
	}
498
    /* No match found; return given type. */
499
    return (type);
500
}
501
502
/**************************************************************************
503
 *
504
 * Entry fetching
505
 *
506
 **************************************************************************/
507
508
/*
509
 * Figure out what kind of terminal we're dealing with, and then read in
510
 * its termcap entry.
511
 */
512
static const char *
513
get_termcap_entry(char *userarg)
514
{
515
38
    int errret;
516
    char *p;
517
    const char *ttype;
518
#if HAVE_GETTTYNAM
519
    struct ttyent *t;
520
#else
521
    FILE *fp;
522
#endif
523
    char *ttypath;
524
525
19
    if (userarg) {
526
	ttype = userarg;
527
19
	goto found;
528
    }
529
530
    /* Try the environment. */
531
    if ((ttype = getenv("TERM")) != 0)
532
	goto map;
533
534
    if ((ttypath = ttyname(STDERR_FILENO)) != 0) {
535
	p = _nc_basename(ttypath);
536
#if HAVE_GETTTYNAM
537
	/*
538
	 * We have the 4.3BSD library call getttynam(3); that means
539
	 * there's an /etc/ttys to look up device-to-type mappings in.
540
	 * Try ttyname(3); check for dialup or other mapping.
541
	 */
542
	if ((t = getttynam(p))) {
543
	    ttype = t->ty_type;
544
	    goto map;
545
	}
546
#else
547
	if ((fp = fopen("/etc/ttytype", "r")) != 0
548
	    || (fp = fopen("/etc/ttys", "r")) != 0) {
549
	    char buffer[BUFSIZ];
550
	    char *s, *t, *d;
551
552
	    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
553
		for (s = buffer, t = d = 0; *s; s++) {
554
		    if (isspace(UChar(*s)))
555
			*s = '\0';
556
		    else if (t == 0)
557
			t = s;
558
		    else if (d == 0 && s != buffer && s[-1] == '\0')
559
			d = s;
560
		}
561
		if (t != 0 && d != 0 && !strcmp(d, p)) {
562
		    ttype = strdup(t);
563
		    fclose(fp);
564
		    goto map;
565
		}
566
	    }
567
	    fclose(fp);
568
	}
569
#endif /* HAVE_GETTTYNAM */
570
    }
571
572
    /* If still undefined, use "unknown". */
573
    ttype = "unknown";
574
575
  map:ttype = mapped(ttype);
576
577
    /*
578
     * If not a path, remove TERMCAP from the environment so we get a
579
     * real entry from /etc/termcap.  This prevents us from being fooled
580
     * by out of date stuff in the environment.
581
     */
582

19
  found:if ((p = getenv("TERMCAP")) != 0 && !_nc_is_abs_path(p)) {
583
	/* 'unsetenv("TERMCAP")' is not portable.
584
	 * The 'environ' array is better.
585
	 */
586
	int n;
587
	for (n = 0; environ[n] != 0; n++) {
588
	    if (!strncmp("TERMCAP=", environ[n], 8)) {
589
		while ((environ[n] = environ[n + 1]) != 0) {
590
		    n++;
591
		}
592
		break;
593
	    }
594
	}
595
    }
596
597
    /*
598
     * ttype now contains a pointer to the type of the terminal.
599
     * If the first character is '?', ask the user.
600
     */
601
19
    if (ttype[0] == '?') {
602
	if (ttype[1] != '\0')
603
	    ttype = askuser(ttype + 1);
604
	else
605
	    ttype = askuser(0);
606
    }
607
    /* Find the terminfo entry.  If it doesn't exist, ask the user. */
608
38
    while (setupterm((NCURSES_CONST char *) ttype, STDOUT_FILENO, &errret)
609
19
	   != OK) {
610
	if (errret == 0) {
611
	    (void) fprintf(stderr, "%s: unknown terminal type %s\n",
612
			   _nc_progname, ttype);
613
	    ttype = 0;
614
	} else {
615
	    (void) fprintf(stderr,
616
			   "%s: can't initialize terminal type %s (error %d)\n",
617
			   _nc_progname, ttype, errret);
618
	    ttype = 0;
619
	}
620
	ttype = askuser(ttype);
621
    }
622
#if BROKEN_LINKER
623
    tgetflag("am");		/* force lib_termcap.o to be linked for 'ospeed' */
624
#endif
625
19
    return (ttype);
626
19
}
627
628
/**************************************************************************
629
 *
630
 * Mode-setting logic
631
 *
632
 **************************************************************************/
633
634
/* some BSD systems have these built in, some systems are missing
635
 * one or more definitions. The safest solution is to override unless the
636
 * commonly-altered ones are defined.
637
 */
638
#if !(defined(CERASE) && defined(CINTR) && defined(CKILL) && defined(CQUIT))
639
#undef CEOF
640
#undef CERASE
641
#undef CINTR
642
#undef CKILL
643
#undef CLNEXT
644
#undef CRPRNT
645
#undef CQUIT
646
#undef CSTART
647
#undef CSTOP
648
#undef CSUSP
649
#endif
650
651
/* control-character defaults */
652
#ifndef CEOF
653
#define CEOF	CTRL('D')
654
#endif
655
#ifndef CERASE
656
#define CERASE	CTRL('H')
657
#endif
658
#ifndef CINTR
659
#define CINTR	127		/* ^? */
660
#endif
661
#ifndef CKILL
662
#define CKILL	CTRL('U')
663
#endif
664
#ifndef CLNEXT
665
#define CLNEXT  CTRL('v')
666
#endif
667
#ifndef CRPRNT
668
#define CRPRNT  CTRL('r')
669
#endif
670
#ifndef CQUIT
671
#define CQUIT	CTRL('\\')
672
#endif
673
#ifndef CSTART
674
#define CSTART	CTRL('Q')
675
#endif
676
#ifndef CSTOP
677
#define CSTOP	CTRL('S')
678
#endif
679
#ifndef CSUSP
680
#define CSUSP	CTRL('Z')
681
#endif
682
683
#if defined(_POSIX_VDISABLE)
684
#define DISABLED(val)   (((_POSIX_VDISABLE != -1) \
685
		       && ((val) == _POSIX_VDISABLE)) \
686
		      || ((val) <= 0))
687
#else
688
#define DISABLED(val)   ((int)(val) <= 0)
689
#endif
690
691
#define CHK(val, dft)   (DISABLED(val) ? dft : val)
692
693
static bool set_tabs(void);
694
695
/*
696
 * Reset the terminal mode bits to a sensible state.  Very useful after
697
 * a child program dies in raw mode.
698
 */
699
static void
700
reset_mode(void)
701
{
702
#ifdef TERMIOS
703
    tcgetattr(STDERR_FILENO, &mode);
704
#else
705
    stty(STDERR_FILENO, &mode);
706
#endif
707
708
#ifdef TERMIOS
709
#if defined(VDISCARD) && defined(CDISCARD)
710
    mode.c_cc[VDISCARD] = CHK(mode.c_cc[VDISCARD], CDISCARD);
711
#endif
712
    mode.c_cc[VEOF] = CHK(mode.c_cc[VEOF], CEOF);
713
    mode.c_cc[VERASE] = CHK(mode.c_cc[VERASE], CERASE);
714
#if defined(VFLUSH) && defined(CFLUSH)
715
    mode.c_cc[VFLUSH] = CHK(mode.c_cc[VFLUSH], CFLUSH);
716
#endif
717
    mode.c_cc[VINTR] = CHK(mode.c_cc[VINTR], CINTR);
718
    mode.c_cc[VKILL] = CHK(mode.c_cc[VKILL], CKILL);
719
#if defined(VLNEXT) && defined(CLNEXT)
720
    mode.c_cc[VLNEXT] = CHK(mode.c_cc[VLNEXT], CLNEXT);
721
#endif
722
    mode.c_cc[VQUIT] = CHK(mode.c_cc[VQUIT], CQUIT);
723
#if defined(VREPRINT) && defined(CRPRNT)
724
    mode.c_cc[VREPRINT] = CHK(mode.c_cc[VREPRINT], CRPRNT);
725
#endif
726
#if defined(VSTART) && defined(CSTART)
727
    mode.c_cc[VSTART] = CHK(mode.c_cc[VSTART], CSTART);
728
#endif
729
#if defined(VSTOP) && defined(CSTOP)
730
    mode.c_cc[VSTOP] = CHK(mode.c_cc[VSTOP], CSTOP);
731
#endif
732
#if defined(VSUSP) && defined(CSUSP)
733
    mode.c_cc[VSUSP] = CHK(mode.c_cc[VSUSP], CSUSP);
734
#endif
735
#if defined(VWERASE) && defined(CWERASE)
736
    mode.c_cc[VWERASE] = CHK(mode.c_cc[VWERASE], CWERASE);
737
#endif
738
739
    mode.c_iflag &= ~(IGNBRK | PARMRK | INPCK | ISTRIP | INLCR | IGNCR
740
#ifdef IUCLC
741
		      | IUCLC
742
#endif
743
#ifdef IXANY
744
		      | IXANY
745
#endif
746
		      | IXOFF);
747
748
    mode.c_iflag |= (BRKINT | IGNPAR | ICRNL | IXON
749
#ifdef IMAXBEL
750
		     | IMAXBEL
751
#endif
752
	);
753
754
    mode.c_oflag &= ~(0
755
#ifdef OLCUC
756
		      | OLCUC
757
#endif
758
#ifdef OCRNL
759
		      | OCRNL
760
#endif
761
#ifdef ONOCR
762
		      | ONOCR
763
#endif
764
#ifdef ONLRET
765
		      | ONLRET
766
#endif
767
#ifdef OFILL
768
		      | OFILL
769
#endif
770
#ifdef OFDEL
771
		      | OFDEL
772
#endif
773
#ifdef NLDLY
774
		      | NLDLY
775
#endif
776
#ifdef CRDLY
777
		      | CRDLY
778
#endif
779
#ifdef TABDLY
780
		      | TABDLY
781
#endif
782
#ifdef BSDLY
783
		      | BSDLY
784
#endif
785
#ifdef VTDLY
786
		      | VTDLY
787
#endif
788
#ifdef FFDLY
789
		      | FFDLY
790
#endif
791
	);
792
793
    mode.c_oflag |= (OPOST
794
#ifdef ONLCR
795
		     | ONLCR
796
#endif
797
	);
798
799
    mode.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | CLOCAL);
800
    mode.c_cflag |= (CS8 | CREAD);
801
    mode.c_lflag &= ~(ECHONL | NOFLSH
802
#ifdef TOSTOP
803
		      | TOSTOP
804
#endif
805
#ifdef ECHOPTR
806
		      | ECHOPRT
807
#endif
808
#ifdef XCASE
809
		      | XCASE
810
#endif
811
	);
812
813
    mode.c_lflag |= (ISIG | ICANON | ECHO | ECHOE | ECHOK
814
#ifdef ECHOCTL
815
		     | ECHOCTL
816
#endif
817
#ifdef ECHOKE
818
		     | ECHOKE
819
#endif
820
	);
821
#endif
822
823
    SET_TTY(STDERR_FILENO, &mode);
824
}
825
826
/*
827
 * Returns a "good" value for the erase character.  This is loosely based on
828
 * the BSD4.4 logic.
829
 */
830
#ifdef TERMIOS
831
static int
832
default_erase(void)
833
{
834
    int result;
835
836
    if (over_strike
837
	&& key_backspace != 0
838
	&& strlen(key_backspace) == 1)
839
	result = key_backspace[0];
840
    else
841
	result = CERASE;
842
843
    return result;
844
}
845
#endif
846
847
/*
848
 * Update the values of the erase, interrupt, and kill characters in 'mode'.
849
 *
850
 * SVr4 tset (e.g., Solaris 2.5) only modifies the intr, quit or erase
851
 * characters if they're unset, or if we specify them as options.  This differs
852
 * from BSD 4.4 tset, which always sets erase.
853
 */
854
static void
855
set_control_chars(void)
856
{
857
#ifdef TERMIOS
858

57
    if (DISABLED(mode.c_cc[VERASE]) || terasechar >= 0)
859
	mode.c_cc[VERASE] = (terasechar >= 0) ? terasechar : default_erase();
860
861

38
    if (DISABLED(mode.c_cc[VINTR]) || intrchar >= 0)
862
	mode.c_cc[VINTR] = (intrchar >= 0) ? intrchar : CINTR;
863
864

38
    if (DISABLED(mode.c_cc[VKILL]) || tkillchar >= 0)
865
	mode.c_cc[VKILL] = (tkillchar >= 0) ? tkillchar : CKILL;
866
#endif
867
19
}
868
869
/*
870
 * Set up various conversions in 'mode', including parity, tabs, returns,
871
 * echo, and case, according to the termcap entry.  If the program we're
872
 * running was named with a leading upper-case character, map external
873
 * uppercase to internal lowercase.
874
 */
875
static void
876
set_conversions(void)
877
{
878
#ifdef __OBSOLETE__
879
    /*
880
     * Conversion logic for some *really* ancient terminal glitches,
881
     * not supported in terminfo.  Left here for succeeding generations
882
     * to marvel at.
883
     */
884
    if (tgetflag("UC")) {
885
#ifdef IUCLC
886
	mode.c_iflag |= IUCLC;
887
	mode.c_oflag |= OLCUC;
888
#endif
889
    } else if (tgetflag("LC")) {
890
#ifdef IUCLC
891
	mode.c_iflag &= ~IUCLC;
892
	mode.c_oflag &= ~OLCUC;
893
#endif
894
    }
895
    mode.c_iflag &= ~(PARMRK | INPCK);
896
    mode.c_lflag |= ICANON;
897
    if (tgetflag("EP")) {
898
	mode.c_cflag |= PARENB;
899
	mode.c_cflag &= ~PARODD;
900
    }
901
    if (tgetflag("OP")) {
902
	mode.c_cflag |= PARENB;
903
	mode.c_cflag |= PARODD;
904
    }
905
#endif /* __OBSOLETE__ */
906
907
#ifdef TERMIOS
908
#ifdef ONLCR
909
38
    mode.c_oflag |= ONLCR;
910
#endif
911
19
    mode.c_iflag |= ICRNL;
912
19
    mode.c_lflag |= ECHO;
913
#ifdef OXTABS
914
19
    mode.c_oflag |= OXTABS;
915
#endif /* OXTABS */
916
917
    /* test used to be tgetflag("NL") */
918

38
    if (newline != (char *) 0 && newline[0] == '\n' && !newline[1]) {
919
	/* Newline, not linefeed. */
920
#ifdef ONLCR
921
	mode.c_oflag &= ~ONLCR;
922
#endif
923
	mode.c_iflag &= ~ICRNL;
924
    }
925
#ifdef __OBSOLETE__
926
    if (tgetflag("HD"))		/* Half duplex. */
927
	mode.c_lflag &= ~ECHO;
928
#endif /* __OBSOLETE__ */
929
#ifdef OXTABS
930
    /* test used to be tgetflag("pt") */
931
19
    if (has_hardware_tabs)	/* Print tabs. */
932
19
	mode.c_oflag &= ~OXTABS;
933
#endif /* OXTABS */
934
19
    mode.c_lflag |= (ECHOE | ECHOK);
935
#endif
936
19
}
937
938
/* Output startup string. */
939
static void
940
set_init(void)
941
{
942
    char *p;
943
    bool settle;
944
945
#ifdef __OBSOLETE__
946
    if (pad_char != (char *) 0)	/* Get/set pad character. */
947
	PC = pad_char[0];
948
#endif /* OBSOLETE */
949
950
#ifdef TAB3
951
    if (oldmode.c_oflag & (TAB3 | ONLCR | OCRNL | ONLRET)) {
952
	oldmode.c_oflag &= (TAB3 | ONLCR | OCRNL | ONLRET);
953
	SET_TTY(STDERR_FILENO, &oldmode);
954
    }
955
#endif
956
38
    settle = set_tabs();
957
958
19
    if (isreset) {
959
	if ((p = reset_1string) != 0) {
960
	    tputs(p, 0, outc);
961
	    settle = TRUE;
962
	}
963
	if ((p = reset_2string) != 0) {
964
	    tputs(p, 0, outc);
965
	    settle = TRUE;
966
	}
967
	/* What about rf, rs3, as per terminfo man page? */
968
	/* also might be nice to send rmacs, rmul, rmm */
969
	if ((p = reset_file) != 0
970
	    || (p = init_file) != 0) {
971
	    cat(p);
972
	    settle = TRUE;
973
	}
974
    }
975
976
19
    if (settle) {
977
38
	(void) putc('\r', stderr);
978
19
	(void) fflush(stderr);
979
19
	(void) napms(1000);	/* Settle the terminal. */
980
19
    }
981
19
}
982
983
/*
984
 * Set the hardware tabs on the terminal, using the ct (clear all tabs),
985
 * st (set one tab) and ch (horizontal cursor addressing) capabilities.
986
 * This is done before if and is, so they can patch in case we blow this.
987
 * Return TRUE if we set any tab stops, FALSE if not.
988
 */
989
static bool
990
set_tabs(void)
991
{
992

57
    if (set_tab && clear_all_tabs) {
993
	int c;
994
995
38
	(void) putc('\r', stderr);	/* Force to left margin. */
996
19
	tputs(clear_all_tabs, 0, outc);
997
998
646
	for (c = 8; c < tcolumns; c += 8) {
999
	    /* Get to the right column.  In BSD tset, this
1000
	     * used to try a bunch of half-clever things
1001
	     * with cup and hpa, for an average saving of
1002
	     * somewhat less than two character times per
1003
	     * tab stop, less than .01 sec at 2400cps. We
1004
	     * lost all this cruft because it seemed to be
1005
	     * introducing some odd bugs.
1006
	     * -----------12345678----------- */
1007
304
	    (void) fputs("        ", stderr);
1008
304
	    tputs(set_tab, 0, outc);
1009
	}
1010
38
	putc('\r', stderr);
1011
	return (TRUE);
1012
    }
1013
    return (FALSE);
1014
19
}
1015
1016
/**************************************************************************
1017
 *
1018
 * Main sequence
1019
 *
1020
 **************************************************************************/
1021
1022
/*
1023
 * Tell the user if a control key has been changed from the default value.
1024
 */
1025
#ifdef TERMIOS
1026
static void
1027
report(const char *name, int which, unsigned def)
1028
{
1029
    unsigned older, newer;
1030
    char *p;
1031
1032
    newer = mode.c_cc[which];
1033
    older = oldmode.c_cc[which];
1034
1035
    if (older == newer && older == def)
1036
	return;
1037
1038
    (void) fprintf(stderr, "%s %s ", name, older == newer ? "is" : "set to");
1039
1040
    if (DISABLED(newer))
1041
	(void) fprintf(stderr, "undef.\n");
1042
    /*
1043
     * Check 'delete' before 'backspace', since the key_backspace value
1044
     * is ambiguous.
1045
     */
1046
    else if (newer == 0177)
1047
	(void) fprintf(stderr, "delete.\n");
1048
    else if ((p = key_backspace) != 0
1049
	     && newer == (unsigned char) p[0]
1050
	     && p[1] == '\0')
1051
	(void) fprintf(stderr, "backspace.\n");
1052
    else if (newer < 040) {
1053
	newer ^= 0100;
1054
	(void) fprintf(stderr, "control-%c (^%c).\n", UChar(newer), UChar(newer));
1055
    } else
1056
	(void) fprintf(stderr, "%c.\n", UChar(newer));
1057
}
1058
#endif
1059
1060
/*
1061
 * Convert the obsolete argument forms into something that getopt can handle.
1062
 * This means that -e, -i and -k get default arguments supplied for them.
1063
 */
1064
static void
1065
obsolete(char **argv)
1066
{
1067
209
    for (; *argv; ++argv) {
1068
	char *parm = argv[0];
1069
1070

114
	if (parm[0] == '-' && parm[1] == '\0') {
1071
	    argv[0] = strdup("-q");
1072
	    continue;
1073
	}
1074
1075
76
	if ((parm[0] != '-')
1076

152
	    || (argv[1] && argv[1][0] != '-')
1077

95
	    || (parm[1] != 'e' && parm[1] != 'i' && parm[1] != 'k')
1078
19
	    || (parm[2] != '\0'))
1079
76
	    continue;
1080
	switch (argv[0][1]) {
1081
	case 'e':
1082
	    argv[0] = strdup("-e^H");
1083
	    break;
1084
	case 'i':
1085
	    argv[0] = strdup("-i^C");
1086
	    break;
1087
	case 'k':
1088
	    argv[0] = strdup("-k^U");
1089
	    break;
1090
	}
1091
    }
1092
19
}
1093
1094
static void
1095
usage(void)
1096
{
1097
    static const char *tbl[] =
1098
    {
1099
	""
1100
	,"Options:"
1101
	,"  -c          set control characters"
1102
	,"  -e ch       erase character"
1103
	,"  -I          no initialization strings"
1104
	,"  -i ch       interrupt character"
1105
	,"  -k ch       kill character"
1106
	,"  -m mapping  map identifier to type"
1107
	,"  -Q          do not output control key settings"
1108
	,"  -r          display term on stderr"
1109
	,"  -s          output TERM set command"
1110
	,"  -V          print curses-version"
1111
	,"  -w          set window-size"
1112
    };
1113
    unsigned n;
1114
    (void) fprintf(stderr, "Usage: %s [-cIQqrsVw] [-] "
1115
	"[-e ch] [-i ch] [-k ch] [-m mapping] [terminal]\n",
1116
	_nc_progname);
1117
    for (n = 0; n < sizeof(tbl) / sizeof(tbl[0]); ++n)
1118
	fprintf(stderr, "%s\n", tbl[n]);
1119
1120
    exit_error();
1121
    /* NOTREACHED */
1122
}
1123
1124
static char
1125
arg_to_char(void)
1126
{
1127
    return (char) ((optarg[0] == '^' && optarg[1] != '\0')
1128
		   ? ((optarg[1] == '?') ? '\177' : CTRL(optarg[1]))
1129
		   : optarg[0]);
1130
}
1131
1132
int
1133
main(int argc, char **argv)
1134
{
1135
    int ch, noinit, noset, quiet, Sflag, sflag, showterm;
1136
    const char *p;
1137
    const char *ttype;
1138
1139
38
    if (pledge("stdio rpath wpath tty flock cpath", NULL) == -1)
1140
	err("pledge: %s", strerror(errno));
1141
1142
19
    obsolete(argv);
1143
    noinit = noset = quiet = Sflag = sflag = showterm = 0;
1144
95
    while ((ch = getopt(argc, argv, "a:cd:e:Ii:k:m:np:qQSrsVw")) != -1) {
1145




114
	switch (ch) {
1146
	case 'c':		/* set control-chars */
1147
	    opt_c = TRUE;
1148
	    break;
1149
	case 'a':		/* OBSOLETE: map identifier to type */
1150
	    add_mapping("arpanet", optarg);
1151
	    break;
1152
	case 'd':		/* OBSOLETE: map identifier to type */
1153
	    add_mapping("dialup", optarg);
1154
	    break;
1155
	case 'e':		/* erase character */
1156
	    terasechar = arg_to_char();
1157
	    break;
1158
	case 'I':		/* no initialization strings */
1159
	    noinit = 1;
1160
	    break;
1161
	case 'i':		/* interrupt character */
1162
	    intrchar = arg_to_char();
1163
	    break;
1164
	case 'k':		/* kill character */
1165
	    tkillchar = arg_to_char();
1166
	    break;
1167
	case 'm':		/* map identifier to type */
1168
19
	    add_mapping(0, optarg);
1169
19
	    break;
1170
	case 'n':		/* OBSOLETE: set new tty driver */
1171
	    break;
1172
	case 'p':		/* OBSOLETE: map identifier to type */
1173
	    add_mapping("plugboard", optarg);
1174
	    break;
1175
	case 'Q':		/* don't output control key settings */
1176
	    quiet = 1;
1177
19
	    break;
1178
	case 'q':		/* display term only */
1179
	    noset = 1;
1180
	    break;
1181
	case 'r':		/* display term on stderr */
1182
	    showterm = 1;
1183
	    break;
1184
	case 'S':		/* OBSOLETE: output TERM & TERMCAP */
1185
	    Sflag = 1;
1186
	    break;
1187
	case 's':		/* output TERM set command */
1188
	    sflag = 1;
1189
19
	    break;
1190
	case 'V':		/* print curses-version */
1191
	    puts(curses_version());
1192
	    ExitProgram(EXIT_SUCCESS);
1193
	case 'w':		/* set window-size */
1194
	    opt_w = TRUE;
1195
	    break;
1196
	case '?':
1197
	default:
1198
	    usage();
1199
	}
1200
    }
1201
1202
19
    _nc_progname = _nc_rootname(*argv);
1203
19
    argc -= optind;
1204
19
    argv += optind;
1205
1206
19
    if (argc > 1)
1207
	usage();
1208
1209

38
    if (!opt_c && !opt_w)
1210
19
	opt_c = opt_w = TRUE;
1211
1212
19
    if (GET_TTY(STDERR_FILENO, &mode) < 0)
1213
	failed("standard error");
1214
19
    can_restore = TRUE;
1215
19
    original = oldmode = mode;
1216
#ifdef TERMIOS
1217
19
    ospeed = (NCURSES_OSPEED) cfgetospeed(&mode);
1218
#else
1219
    ospeed = (NCURSES_OSPEED) mode.sg_ospeed;
1220
#endif
1221
1222
19
    if (!strcmp(_nc_progname, PROG_RESET)) {
1223
	isreset = TRUE;
1224
	reset_mode();
1225
    }
1226
1227
19
    ttype = get_termcap_entry(*argv);
1228
1229
19
    if (!noset) {
1230
19
	tcolumns = columns;
1231
19
	tlines = lines;
1232
1233
#if HAVE_SIZECHANGE
1234
19
	if (opt_w) {
1235
19
	    struct winsize win;
1236
	    /* Set window size if not set already */
1237
19
	    (void) ioctl(STDERR_FILENO, TIOCGWINSZ, &win);
1238
19
	    if (win.ws_row == 0 &&
1239
		win.ws_col == 0 &&
1240
		tlines > 0 && tcolumns > 0) {
1241
		win.ws_row = tlines;
1242
		win.ws_col = tcolumns;
1243
		(void) ioctl(STDERR_FILENO, TIOCSWINSZ, &win);
1244
	    }
1245
19
	}
1246
#endif
1247
19
	if (opt_c) {
1248
19
	    set_control_chars();
1249
19
	    set_conversions();
1250
1251
19
	    if (!noinit)
1252
19
		set_init();
1253
1254
	    /* Set the modes if they've changed. */
1255
19
	    if (memcmp(&mode, &oldmode, sizeof(mode))) {
1256
19
		SET_TTY(STDERR_FILENO, &mode);
1257
19
	    }
1258
	}
1259
    }
1260
1261
    /* Get the terminal name from the entry. */
1262
19
    ttype = _nc_first_name(cur_term->type.term_names);
1263
1264
19
    if (noset)
1265
	(void) printf("%s\n", ttype);
1266
    else {
1267
19
	if (showterm)
1268
	    (void) fprintf(stderr, "Terminal type is %s.\n", ttype);
1269
	/*
1270
	 * If erase, kill and interrupt characters could have been
1271
	 * modified and not -Q, display the changes.
1272
	 */
1273
#ifdef TERMIOS
1274
19
	if (!quiet) {
1275
	    report("Erase", VERASE, CERASE);
1276
	    report("Kill", VKILL, CKILL);
1277
	    report("Interrupt", VINTR, CINTR);
1278
	}
1279
#endif
1280
    }
1281
1282
19
    if (Sflag)
1283
	err("The -S option is not supported under terminfo.");
1284
1285
19
    if (sflag) {
1286
	int len;
1287
	char *var;
1288
	char *leaf;
1289
	/*
1290
	 * Figure out what shell we're using.  A hack, we look for an
1291
	 * environmental variable SHELL ending in "csh".
1292
	 */
1293
38
	if ((var = getenv("SHELL")) != 0
1294
38
	    && ((len = (int) strlen(leaf = _nc_basename(var))) >= 3)
1295
38
	    && !strcmp(leaf + len - 3, "csh"))
1296
	    p = "set noglob;\nsetenv TERM %s;\nunset noglob;\n";
1297
	else
1298
	    p = "TERM=%s;\n";
1299
19
	(void) printf(p, ttype);
1300
19
    }
1301
1302
    ExitProgram(EXIT_SUCCESS);
1303
}