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