BSD 3 development
[unix-history] / .ref-BSD-2 / upgrade / src / tset.c
CommitLineData
7c1e1a13
BJ
1# include <sgtty.h>
2# include <stdio.h>
3
4/*
5** TSET -- set terminal modes
6**
7** This program does sophisticated terminal initialization.
8** I recommend that you include it in your .start_up or .login
9** file to initialize whatever terminal you are on.
10**
11** There are several features:
12**
13** A special file or sequence (as controlled by the ttycap file)
14** is sent to the terminal.
15**
16** Mode bits are set on a per-terminal_type basis (much better
17** than UNIX itself). This allows special delays, automatic
18** tabs, etc.
19**
20** Erase and Kill characters can be set to whatever you want.
21** Default is to change erase to control-H on a terminal which
22** can overstrike, and leave it alone on anything else. Kill
23** is always left alone unless specifically requested.
24**
25** Terminals which are dialups or plugboard types can be aliased
26** to whatever type you may have in your home or office. Thus,
27** if you know that when you dial up you will always be on a
28** TI 733, you can specify that fact to tset.
29**
30** The htmp file, used by ex, etc., can be updated.
31**
32** The current terminal type can be queried.
33**
34** Usage:
35** tset [-] [-r] [-EC] [-eC] [-d type] [-p type]
36** [-b type] [-h] [-u] [type]
37**
38** In systems with environments, use:
39** setenv TERM `tset - ...`
40**
41** Positional Parameters:
42** type -- the terminal type to force. If this is
43** specified, initialization is for this
44** terminal type.
45**
46** Flags:
47** - -- report terminal type. Whatever type is
48** decided on is reported.
49** -r -- report to user, on diagnostic output instead
50** of standard output.
51** -EC -- set the erase character to C on all terminals
52** except those which cannot backspace (e.g.,
53** a TTY 33). C defaults to control-H.
54** -eC -- set the erase character to C on all terminals.
55** C defaults to control-H. If neither -E or -e
56** are specified, the erase character is set to
57** control-H if the terminal can both backspace
58** and not overstrike (e.g., a CRT). If the erase
59** character is NULL (zero byte), it will be reset
60** to '#' if nothing else is specified.
61** -kC -- set the kill character to C on all terminals.
62** Default for C is control-X. If not specified,
63** the kill character is untouched; however, if
64** not specified and the kill character is NULL
65** (zero byte), the kill character is set to '@'.
66** -d type -- set the dialup type to be type. If the
67** terminal type seems to be dialup, make it
68** 'type' instead. There need not be a space
69** between 'd' and 'type'.
70** -p type -- ditto for a plugboard.
71** -b type -- ditto for a bussiplexer.
72** -h -- don't read htmp file. Normally the terminal type
73** is determined by reading the htmp file (unless
74** -d or -p are specified). This forces a read
75** of the ttytype file -- useful when htmp is
76** somehow wrong. On a version seven system, this
77** flag means don't look at the TERM entry in
78** the environment.
79** -u -- don't update htmp. It seemed like this should
80** be put in. Note that htmp is never actually
81** written if there are no changes, so don't bother
82** bother using this for efficiency reasons alone.
83** On version seven systems this flag is ignored.
84**
85** Files:
86** /etc/ttytype
87** contains a terminal id -> terminal type
88** mapping; used when -h, -d, or -p is used.
89** /etc/termcap
90** a terminal_type -> terminal_capabilities
91** mapping.
92**
93** Return Codes:
94** -1 -- couldn't open ttycap.
95** 1 -- bad terminal type, or standard output not tty.
96** 0 -- ok.
97**
98** Defined Constants:
99** DIALUP -- the type code for a dialup port
100** PLUGBOARD -- the code for a plugboard port.
101** BUSSIPLEXER -- the code for a bussiplexer port.
102** BACKSPACE -- control-H, the default for -e.
103** CONTROLX -- control-X, the default for -k.
104** OLDERASE -- the system default erase character.
105** OLDKILL -- the system default kill character.
106** FILEDES -- the file descriptor to do the operation
107** on, nominally 1 or 2.
108** STDOUT -- the standard output file descriptor.
109** UIDMASK -- the bit pattern to mask with the getuid()
110** call to get just the user id.
111**
112** Requires:
113** Routines to handle htmp, ttytype, and ttycap.
114**
115** Compilation Flags:
116** PLUGBOARD -- if defined, accept the -p flag.
117** BUSSIPLEXER -- if defined, accept the -b flag.
118** FULLLOGIN -- if defined, login sets the ttytype from
119** /etc/ttytype file.
120** VERSION7 -- if set, use environments, not htmp.
121** Also, use 'ioctl' not 'stty' -- to get type-
122** ahead.
123** GTTYN -- if set, uses generalized tty names.
124**
125** Compilation Instructions:
126** cc -n -O tset.c -lX
127** mv a.out tset
128** chown bin tset
129** chmod 4755 tset
130**
131** where 'bin' should be whoever owns the 'htmp' file.
132** If 'htmp' is 666, then tset need not be setuid.
133**
134** Author:
135** Eric Allman
136** Electronics Research Labs
137** U.C. Berkeley
138**
139** History:
140** 3/79 -- Use ioctl in version7.
141** 12/78 -- modified for eventual migration to VAX/UNIX,
142** so the '-' option is changed to output only
143** the terminal type to STDOUT instead of
144** FILEDES. FULLLOGIN flag added. BUSSIPLEXER
145** and -r added.
146** 9/78 -- '-' and '-p' options added (now fully
147** compatible with ttytype!), and spaces are
148** permitted between the -d and the type.
149** 8/78 -- The sense of -h and -u were reversed, and the
150** -f flag is dropped -- same effect is available
151** by just stating the terminal type.
152** 10/77 -- This version, in much it's previous state,
153** written by Eric Allman.
154*/
155
156# define BACKSPACE ('H' & 037)
157# define CONTROLX ('X' & 037)
158# define OLDERASE '#'
159# define OLDKILL '@'
160
161# define FILEDES 2
162# define STDOUT 1
163
164# define DIALUP "du"
165# define PLUGBOARD "pb"
166# define BUSSIPLEXER "bx"
167/* # define FULLLOGIN FULLLOGIN login does everything */
168/* # define VERSION7 VERSION7 version seven flag */
169/* # define GTTYN GTTYN general tty names */
170
171# ifdef VERSION7
172# define UIDMASK 0177777
173# else
174# define UIDMASK 0377
175# endif
176
177# ifdef GTTYN
178typedef char *ttyid_t;
179# else
180typedef char ttyid_t;
181# endif
182
183# define NOTTY 0
184
185
186
187
188
189char Erase_char; /* new erase character */
190char Kill_char; /* new kill character */
191char Specialerase; /* set => Erase_char only on terminals with backspace */
192
193ttyid_t Ttyid = NOTTY; /* terminal identifier */
194char *Ttytype; /* type of terminal */
195char *Dialtype; /* override type if dialup terminal */
196char *Plugtype; /* override type if plugboard port */
197char *Bxtype; /* override type if bussiplexer port */
198int Dash_u; /* don't-update-htmp flag */
199int Dash_h; /* don't-read-htmp flag */
200int Report; /* report current type */
201int Ureport; /* report to user */
202
203char Usage[] = "usage: tset [-] [-r] [-eC] [-kC] [-d T] [-p T] [-b T] [-h] [-u] [type]\n";
204
205char Capbuf[256]; /* line from /etc/ttycap for this Ttytype */
206
207struct delay
208{
209 int d_delay;
210 int d_bits;
211};
212
213# include "tset.del.h"
214
215
216
217main(argc, argv)
218int argc;
219char *argv[];
220{
221 struct sgttyb mode;
222 struct sgttyb oldmode;
223 char buf[256];
224 auto char *bufp;
225 register char *p;
226 char *command;
227 register int i;
228 register int error;
229 int mdvect[2];
230 extern char *stypeof();
231# ifndef VERSION7
232 extern char *hsgettype();
233# else
234 extern char *getenv();
235# endif
236# ifdef GTTYN
237 extern char *ttyname();
238# endif
239
240 /* scan argument list and collect flags */
241 error = 0;
242 command = argv[0];
243 argc--;
244 while (--argc >= 0)
245 {
246 p = *++argv;
247 if (p[0] == '-')
248 {
249 switch (p[1])
250 {
251
252 case 0: /* report current terminal type */
253 Report++;
254 continue;
255
256 case 'r': /* report to user */
257 Ureport++;
258 continue;
259
260 case 'E': /* special erase: operate on all but TTY33 */
261 Specialerase++;
262 /* exlicit fall-through to -e case */
263
264 case 'e': /* erase character */
265 if (p[2] == 0)
266 Erase_char = BACKSPACE;
267 else
268 Erase_char = p[2];
269 continue;
270
271 case 'k': /* kill character */
272 if (p[2] == 0)
273 Kill_char = CONTROLX;
274 else
275 Kill_char = p[2];
276 continue;
277
278 case 'd': /* dialup type */
279 if (p[2] != 0)
280 Dialtype = &p[2];
281 else if (--argc < 0 || argv[1][0] == '-')
282 error++;
283 else
284 Dialtype = *++argv;
285 continue;
286
287# ifdef PLUGBOARD
288 case 'p': /* plugboard type */
289 if (p[2] != 0)
290 Plugtype = &p[2];
291 else if (--argc < 0 || argv[1][0] == '-')
292 error++;
293 else
294 Plugtype = *++argv;
295 continue;
296# endif
297
298# ifdef BUSSIPLEXER
299 case 'b': /* bussiplexer type */
300 if (p[2] != 0)
301 Bxtype = &p[2];
302 else if (--argc < 0 || argv[1][0] == '-')
303 error++;
304 else
305 Bxtype = *++argv;
306# endif
307
308 case 'h': /* don't get type from htmp */
309 Dash_h++;
310 continue;
311
312# ifndef VERSION7
313 case 'u': /* don't update htmp */
314 Dash_u++;
315 continue;
316# endif
317
318 default:
319 prs("Bad flag ");
320 prs(p);
321 prs("\n");
322 error++;
323
324 }
325 }
326 else
327 {
328 /* terminal type */
329 Ttytype = p;
330 }
331 }
332
333 if (error)
334 {
335 prs(Usage);
336 exit(1);
337 }
338
339# ifndef FULLLOGIN
340 /* if dialup is specified, check ttytype not htmp */
341 if (Dialtype != 0 || Plugtype != 0 || Bxtype != 0)
342 Dash_h++;
343# endif
344
345 /* determine terminal id if needed */
346 if (Ttyid == NOTTY && (Ttytype == 0 || !Dash_h || !Dash_u))
347# ifndef VERSION7
348 Ttyid = ttyn(FILEDES);
349# else
350 Ttyid = ttyname(FILEDES);
351# endif
352
353# ifndef VERSION7
354 /* get htmp if ever used */
355 if (!Dash_u || (Ttytype == 0 && !Dash_h))
356 {
357 /* get htmp entry */
358 hget(Ttyid);
359
360 /* if not for this user, look at ttytype file */
361 if (hgettype() == 0 || hgetuid() != (getuid() & UIDMASK))
362 Dash_h++;
363 }
364# endif
365
366 /* find terminal type (if not already known) */
367 if (Ttytype == 0)
368 {
369 /* get type from /etc/ttytype or /etc/htmp */
370 if (!Dash_h)
371 {
372# ifndef VERSION7
373 Ttytype = hsgettype();
374# else
375 Ttytype = getenv("TERM");
376# endif
377 }
378 if (Ttytype == 0)
379 {
380 Ttytype = stypeof(Ttyid);
381 }
382
383 /* check for dialup or plugboard override */
384 if (Dialtype != 0 && bequal(Ttytype, DIALUP, 2))
385 Ttytype = Dialtype;
386# ifdef PLUGBOARD
387 else if (Plugtype != 0 && bequal(Ttytype, PLUGBOARD, 2))
388 Ttytype = Plugtype;
389# endif
390# ifdef BUSSIPLEXER
391 else if (Bxtype != 0 && bequal(Ttytype, BUSSIPLEXER, 2))
392 Ttytype = Bxtype;
393# endif
394 }
395
396 /* Ttytype now contains a pointer to the type of the terminal */
397
398 if (gtty(FILEDES, &mode) < 0)
399 {
400 prs("Not a terminal\n");
401 exit(1);
402 }
403 bmove(&mode, &oldmode, sizeof mode);
404
405 /* get terminal capabilities */
406 switch (tgetent(Capbuf, Ttytype))
407 {
408
409 case -1:
410 prs("Cannot open ttycap file\n");
411 exit(-1);
412
413 case 0:
414 prs("Type ");
415 prs(Ttytype);
416 prs(" unknown\n");
417 exit(1);
418 }
419
420 /* report type if appropriate */
421 if (Report || Ureport)
422 {
423 /* find first alias (if any) */
424 for (p = Capbuf; *p != 0 && *p != '|' && *p != ':'; p++)
425 continue;
426 if (*p == 0 || *p == ':')
427 p = Capbuf;
428 else
429 p++;
430 bufp = p;
431 while (*p != '|' && *p != ':' && *p != 0)
432 p++;
433 i = *p;
434 if (Report)
435 {
436 *p = '\n';
437 write(STDOUT, bufp, p + 1 - bufp);
438 }
439 if (Ureport)
440 {
441 *p = '\0';
442 prs("Terminal type is ");
443 prs(bufp);
444 prs("\n");
445 }
446 *p = i;
447 }
448
449 /* determine erase and kill characters */
450 if (Specialerase && !tgetflag("bs"))
451 Erase_char = 0;
452 if (Erase_char == 0)
453 {
454 if (mode.sg_erase == 0)
455 mode.sg_erase = OLDERASE;
456 if (tgetflag("bs") && !tgetflag("os"))
457 mode.sg_erase = BACKSPACE;
458 }
459 else
460 mode.sg_erase = Erase_char;
461
462 if (mode.sg_kill == 0)
463 mode.sg_kill = OLDKILL;
464 if (Kill_char != 0)
465 mode.sg_kill = Kill_char;
466
467 /* set modes */
468 setdelay("dC", CRdelay, CRbits, &mode.sg_flags);
469 setdelay("dN", NLdelay, NLbits, &mode.sg_flags);
470 setdelay("dB", BSdelay, BSbits, &mode.sg_flags);
471 setdelay("dF", FFdelay, FFbits, &mode.sg_flags);
472 setdelay("dT", TBdelay, TBbits, &mode.sg_flags);
473 if (tgetflag("UC") || command[0] == 'T')
474 mode.sg_flags |= LCASE;
475 else if (tgetflag("LC"))
476 mode.sg_flags &= ~LCASE;
477 mode.sg_flags &= ~(EVENP | ODDP | RAW);
478 if (tgetflag("EP"))
479 mode.sg_flags |= EVENP;
480 if (tgetflag("OP"))
481 mode.sg_flags |= ODDP;
482 if ((mode.sg_flags & (EVENP | ODDP)) == 0)
483 mode.sg_flags |= EVENP | ODDP;
484 mode.sg_flags |= CRMOD | ECHO | XTABS;
485 if (tgetflag("NL")) /* new line, not line feed */
486 mode.sg_flags &= ~CRMOD;
487 if (tgetflag("HD")) /* half duplex */
488 mode.sg_flags &= ~ECHO;
489 if (tgetflag("pt")) /* print tabs */
490 mode.sg_flags &= ~XTABS;
491# ifdef VERSION7
492 if (!bequal(&mode, &oldmode, sizeof mode))
493 ioctl(FILEDES, TIOCSETN, &mode);
494# else
495 if (!bequal(&mode, &oldmode, sizeof mode))
496 stty(FILEDES, &mode);
497# endif
498
499 /* output startup string */
500 bufp = buf;
501 if (tgetstr("is", &bufp) != 0)
502 prs(buf);
503 bufp = buf;
504 if (tgetstr("if", &bufp) != 0)
505 cat(buf);
506
507 /* tell about changing erase and kill characters */
508 reportek("Erase", mode.sg_erase, oldmode.sg_erase, OLDERASE);
509 reportek("Kill", mode.sg_kill, oldmode.sg_kill, OLDKILL);
510
511# ifndef VERSION7
512 /* update htmp */
513 if (!Dash_u)
514 {
515 if (Ttyid == 0)
516 Ttyid = ttyn(FILEDES);
517 if (Ttyid == 'x')
518 prs("Cannot update htmp\n");
519 else
520 {
521 /* update htmp file only if changed */
522 if (!bequal(Capbuf, hsgettype(), 2))
523 {
524 hsettype(Capbuf[0] | (Capbuf[1] << 8));
525 hput(Ttyid);
526 }
527 }
528 }
529# endif
530
531 exit(0);
532}
533
534
535reportek(name, new, old, def)
536char *name;
537char old;
538char new;
539char def;
540{
541 register char o;
542 register char n;
543 register char *p;
544
545 o = old;
546 n = new;
547
548 if (o == n && n == def)
549 return;
550 prs(name);
551 if (o == n)
552 prs(" is ");
553 else
554 prs(" set to ");
555 if (n < 040)
556 {
557 prs("control-");
558 n = (n & 037) | 0100;
559 }
560 p = "x\n";
561 p[0] = n;
562 prs(p);
563}
564
565
566
567
568setdelay(cap, dtab, bits, flags)
569char *cap;
570struct delay dtab[];
571int bits;
572int *flags;
573{
574 register int i;
575 register struct delay *p;
576
577 /* see if this capability exists at all */
578 i = tgetnum(cap);
579 if (i < 0)
580 i = 0;
581
582 /* clear out the bits, replace with new ones */
583 *flags &= ~bits;
584
585 /* scan dtab for first entry with adequate delay */
586 for (p = dtab; p->d_delay >= 0; p++)
587 {
588 if (p->d_delay >= i)
589 {
590 p++;
591 break;
592 }
593 }
594
595 /* use last entry if none will do */
596 *flags |= (--p)->d_bits;
597}
598
599
600prs(s)
601char *s;
602{
603 register char *p;
604 register char *q;
605 register int i;
606
607 p = q = s;
608 i = 0;
609 while (*q++ != 0)
610 i++;
611
612 if (i > 0)
613 write(FILEDES, p, i);
614}
615
616
617cat(file)
618char *file;
619{
620 register int fd;
621 register int i;
622 char buf[512];
623
624 fd = open(file, 0);
625 if (fd < 0)
626 {
627 prs("Cannot open ");
628 prs(file);
629 prs("\n");
630 exit(-1);
631 }
632
633 while ((i = read(fd, buf, 512)) > 0)
634 write(FILEDES, buf, i);
635
636 close(fd);
637}
638
639
640
641bmove(from, to, length)
642char *from;
643char *to;
644int length;
645{
646 register char *p, *q;
647 register int i;
648
649 i = length;
650 p = from;
651 q = to;
652
653 while (i-- > 0)
654 *q++ = *p++;
655}
656
657
658
659bequal(a, b, len)
660char *a;
661char *b;
662int len;
663{
664 register char *p, *q;
665 register int i;
666
667 i = len;
668 p = a;
669 q = b;
670
671 while (i-- > 0)
672 if (*p++ != *q++)
673 return (0);
674 return (1);
675}