BSD 4 development
[unix-history] / usr / src / cmd / tset / tset.c
CommitLineData
4b52d01b
BJ
1/*
2# define DEB
3/*
4** TSET -- set terminal modes
5**
6** This program does sophisticated terminal initialization.
7** I recommend that you include it in your .start_up or .login
8** file to initialize whatever terminal you are on.
9**
10** There are several features:
11**
12** A special file or sequence (as controlled by the ttycap file)
13** is sent to the terminal.
14**
15** Mode bits are set on a per-terminal_type basis (much better
16** than UNIX itself). This allows special delays, automatic
17** tabs, etc.
18**
19** Erase and Kill characters can be set to whatever you want.
20** Default is to change erase to control-H on a terminal which
21** can overstrike, and leave it alone on anything else. Kill
22** is always left alone unless specifically requested. These
23** characters can be represented as "^X" meaning control-X;
24** X is any character.
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. You can represent
30** a type as "?type". This will ask you what type you want it
31** to be -- if you reply with just a newline, it will default
32** to the type given.
33**
34** The htmp file, used by ex, etc., can be updated.
35**
36** The current terminal type can be queried.
37**
38** Usage:
39** tset [-] [-EC] [-eC] [-kC] [-s] [-h] [-u] [-r]
40** [-m [ident] [test baudrate] :type]
41** [-Q] [-I] [-S] [type]
42**
43** In systems with environments, use:
44** `tset -s ...`
45** Actually, this doesn't work because of a shell bug.
46** Instead, use:
47** tset -s ... > tset.tmp
48** source tset.tmp
49** rm tset.tmp
50** or:
51** set noglob
52** set term=(`tset -S ....`)
53** setenv TERM $term[1]
54** setenv TERMCAP "$term[2]"
55** unset term
56** unset noglob
57**
58** Positional Parameters:
59** type -- the terminal type to force. If this is
60** specified, initialization is for this
61** terminal type.
62**
63** Flags:
64** - -- report terminal type. Whatever type is
65** decided on is reported. If no other flags
66** are stated, the only affect is to write
67** the terminal type on the standard output.
68** -r -- report to user in addition to other flags.
69** -EC -- set the erase character to C on all terminals
70** except those which cannot backspace (e.g.,
71** a TTY 33). C defaults to control-H.
72** -eC -- set the erase character to C on all terminals.
73** C defaults to control-H. If neither -E or -e
74** are specified, the erase character is set to
75** control-H if the terminal can both backspace
76** and not overstrike (e.g., a CRT). If the erase
77** character is NULL (zero byte), it will be reset
78** to '#' if nothing else is specified.
79** -kC -- set the kill character to C on all terminals.
80** Default for C is control-X. If not specified,
81** the kill character is untouched; however, if
82** not specified and the kill character is NULL
83** (zero byte), the kill character is set to '@'.
84** -iC -- reserved for setable interrupt character.
85** -qC -- reserved for setable quit character.
86** -m -- map the system identified type to some user
87** specified type. The mapping can be baud rate
88** dependent. This replaces the old -d, -p flags.
89** (-d type -> -m dialup:type)
90** (-p type -> -m plug:type)
91** Syntax: -m identifier [test baudrate] :type
92** where: ``identifier'' is whatever is found in
93** /etc/ttytype for this port, (abscence of an identifier
94** matches any identifier); ``test'' may be any combination
95** of > = < ! @; ``baudrate'' is as with stty(1);
96** ``type'' is the actual terminal type to use if the
97** mapping condition is met. Multiple maps are scanned
98** in order and the first match prevails.
99** -h -- don't read htmp file. Normally the terminal type
100** is determined by reading the htmp file or the
101** environment (unless some mapping is specified).
102** This forces a read of the ttytype file -- useful
103** when htmp is somehow wrong.
104** -u -- don't update htmp. It seemed like this should
105** be put in. Note that htmp is never actually
106** written if there are no changes, so don't bother
107** bother using this for efficiency reasons alone.
108** -s -- output setenv commands for TERM. This can be
109** used with
110** `tset -s ...`
111** and is to be prefered to:
112** setenv TERM `tset - ...`
113** because -s sets the TERMCAP variable also.
114** -S -- Similar to -s but outputs 2 strings suitable for
115** use in csh .login files as follows:
116** set noglob
117** set term=(`tset -S .....`)
118** setenv TERM $term[1]
119** setenv TERMCAP "$term[2]"
120** unset term
121** unset noglob
122** -Q -- be quiet. don't output 'Erase set to' etc.
123** -I -- don't do terminal initialization (is & if
124** strings).
125**
126** Files:
127** /etc/ttytype
128** contains a terminal id -> terminal type
129** mapping; used when any user mapping is specified,
130** or the environment doesn't have TERM set.
131** /etc/termcap
132** a terminal_type -> terminal_capabilities
133** mapping.
134**
135** Return Codes:
136** -1 -- couldn't open ttycap.
137** 1 -- bad terminal type, or standard output not tty.
138** 0 -- ok.
139**
140** Defined Constants:
141** DIALUP -- the type code for a dialup port
142** PLUGBOARD -- the code for a plugboard port.
143** ARPANET -- the code for an arpanet port.
144** BACKSPACE -- control-H, the default for -e.
145** CONTROLX -- control-X, the default for -k.
146** OLDERASE -- the system default erase character.
147** OLDKILL -- the system default kill character.
148** FILEDES -- the file descriptor to do the operation
149** on, nominally 1 or 2.
150** STDOUT -- the standard output file descriptor.
151** UIDMASK -- the bit pattern to mask with the getuid()
152** call to get just the user id.
153** GTTYN -- defines file containing generalized ttynames
154** and compiles code to look there.
155**
156** Requires:
157** Routines to handle htmp, ttytype, and ttycap.
158**
159** Compilation Flags:
160** OLDDIALUP -- accept the -d flag. Map "sd" to "dialup".
161** OLDPLUGBOARD -- accept the -p flag. Map "sp" to "plugboard".
162** OLDARPANET -- accept the -a flag. Map "sa" to "arpanet".
163** OLDFLAGS -- must be defined to compile code for any of
164** the -d, -p, or -a flags.
165** FULLLOGIN -- if defined, login sets the ttytype from
166** /etc/ttytype file.
167** V6 -- if clear, use environments, not htmp.
168** also use TIOCSETN rather than stty to avoid flushing
169** GTTYN -- if set, compiles code to look at /etc/ttytype.
170**
171** Trace Flags:
172** none
173**
174** Diagnostics:
175** Bad flag
176** An incorrect option was specified.
177** Too few args
178** more command line arguments are required.
179** Unexpected arg
180** wrong type of argument was encountered.
181** Cannot open ...
182** The specified file could not be openned.
183** Type ... unknown
184** An unknown terminal type was specified.
185** Cannot update htmp
186** Cannot update htmp file when the standard
187** output is not a terminal.
188** Erase set to ...
189** Telling that the erase character has been
190** set to the specified character.
191** Kill set to ...
192** Ditto for kill
193** Erase is ... Kill is ...
194** Tells that the erase/kill characters were
195** wierd before, but they are being left as-is.
196** Not a terminal
197** Set if FILEDES is not a terminal.
198**
199** Compilation Instructions:
200** cc -n -O tset.c -ltermlib
201** mv a.out tset
202** chown bin tset
203** chmod 4755 tset
204**
205** where 'bin' should be whoever owns the 'htmp' file.
206** If 'htmp' is 666, then tset need not be setuid.
207**
208** Author:
209** Eric Allman
210** Electronics Research Labs
211** U.C. Berkeley
212**
213** History:
214** 7/80 -- '-S' added. -m mapping added. TERMCAP string
215** cleaned up.
216** 3/80 -- Changed to use tputs. Prc & flush added.
217** 10/79 -- '-s' option extended to handle TERMCAP
218** variable, set noglob, quote the entry,
219** and know about the Bourne shell. Terminal
220** initialization moved to before any information
221** output so screen clears would not screw you.
222** '-Q' option added.
223** 8/79 -- '-' option alone changed to only output
224** type. '-s' option added. 'VERSION7'
225** changed to 'V6' for compatibility.
226** 12/78 -- modified for eventual migration to VAX/UNIX,
227** so the '-' option is changed to output only
228** the terminal type to STDOUT instead of
229** FILEDES. FULLLOGIN flag added.
230** 9/78 -- '-' and '-p' options added (now fully
231** compatible with ttytype!), and spaces are
232** permitted between the -d and the type.
233** 8/78 -- The sense of -h and -u were reversed, and the
234** -f flag is dropped -- same effect is available
235** by just stating the terminal type.
236** 10/77 -- Written.
237*/
238
239/*
240# define FULLLOGIN 1
241*/
242# ifndef V6
243# define GTTYN "/etc/ttytype"
244# endif
245
246# include <ctype.h>
247# include <sgtty.h>
248# include <stdio.h>
249
250# define BACKSPACE ('H' & 037)
251# define CONTROLX ('X' & 037)
252# define OLDERASE '#'
253# define OLDKILL '@'
254
255# define FILEDES 2
256# define STDOUT 1
257
258# ifdef V6
259# define UIDMASK 0377
260# else
261# define UIDMASK -1
262# endif
263
264# define DEFTYPE "unknown"
265# define USAGE\
266"usage: tset [-] [-hrsuIQS] [-eC] [-kC] [-m [ident][test speed]:type] [type]\n"
267
268# define DIALUP "dialup"
269# define OLDDIALUP "sd"
270# define PLUGBOARD "plugboard"
271# define OLDPLUGBOARD "sp"
272/***
273# define ARPANET "arpanet"
274# define OLDARPANET "sa"
275***/
276# define OLDFLAGS
277
278
279
280# ifdef GTTYN
281typedef char *ttyid_t;
282# define NOTTY 0
283# else
284typedef char ttyid_t;
285# define NOTTY 'x'
286# endif
287
288/*
289 * Baud Rate Conditionals
290 */
291# define ANY 0
292# define GT 1
293# define EQ 2
294# define LT 4
295# define GE (GT|EQ)
296# define LE (LT|EQ)
297# define NE (GT|LT)
298# define ALL (GT|EQ|LT)
299
300
301
302# define NMAP 10
303
304struct map {
305 char *Ident;
306 char Test;
307 char Speed;
308 char *Type;
309} map[NMAP];
310
311struct map *Map = map;
312
313struct
314{
315 char *string;
316 int speed;
317} speeds[] = {
318 "0", B0,
319 "50", B50,
320 "75", B75,
321 "110", B110,
322 "134", B134,
323 "134.5",B134,
324 "150", B150,
325 "200", B200,
326 "300", B300,
327 "600", B600,
328 "1200", B1200,
329 "1800", B1800,
330 "2400", B2400,
331 "4800", B4800,
332 "9600", B9600,
333 "exta", EXTA,
334 "extb", EXTB,
335 0,
336};
337
338char Erase_char; /* new erase character */
339char Kill_char; /* new kill character */
340char Specialerase; /* set => Erase_char only on terminals with backspace */
341
342ttyid_t Ttyid = NOTTY; /* terminal identifier */
343char *TtyType; /* type of terminal */
344char *DefType; /* default type if none other computed */
345char *NewType; /* mapping identifier based on old flags */
346int Dash_u; /* don't update htmp */
347int Dash_h; /* don't read htmp */
348int DoSetenv; /* output setenv commands */
349int BeQuiet; /* be quiet */
350int NoInit; /* don't output initialization string */
351int Report; /* report current type */
352int Ureport; /* report to user */
353int RepOnly; /* report only */
354int CmndLine; /* output full command lines (-s option) */
355int Ask; /* ask user for termtype */
356
357# define CAPBUFSIZ 1024
358char Capbuf[CAPBUFSIZ]; /* line from /etc/termcap for this TtyType */
359char *Ttycap; /* termcap line from termcap or environ */
360
361struct delay
362{
363 int d_delay;
364 int d_bits;
365};
366
367# include "tset.delays.h"
368
369
370
371main(argc, argv)
372int argc;
373char *argv[];
374{
375 struct sgttyb mode;
376 struct sgttyb oldmode;
377 char buf[256];
378 auto char *bufp;
379 register char *p;
380 char *command;
381 register int i;
382 int Break;
383 int Not;
384 int Mapped;
385 extern char *nextarg();
386 extern char *mapped();
387# ifdef V6
388 extern char *hsgettype();
389# else
390 extern char *getenv();
391# endif
392# ifdef GTTYN
393 extern char *stypeof();
394 extern char *ttyname();
395 extern char *tgetstr();
396# endif
397 char bs_char;
398 int csh;
399 extern prc();
400 extern char PC;
401 extern short ospeed;
402
403 if (gtty(FILEDES, &mode) < 0)
404 {
405 prs("Not a terminal\n");
406 exit(1);
407 }
408 bmove(&mode, &oldmode, sizeof mode);
409 ospeed = mode.sg_ospeed;
410
411 /* scan argument list and collect flags */
412 command = argv[0];
413 if (argc == 2 && argv[1][0] == '-' && argv[1][1] == '\0')
414 {
415 RepOnly++;
416 Dash_u++;
417 }
418 argc--;
419 while (--argc >= 0)
420 {
421 p = *++argv;
422 if (*p == '-')
423 {
424 if (*++p == NULL)
425 Report++; /* report current terminal type */
426 else while (*p) switch (*p++)
427 {
428
429 case 'r': /* report to user */
430 Ureport++;
431 continue;
432
433 case 'E': /* special erase: operate on all but TTY33 */
434 Specialerase++;
435 /* explicit fall-through to -e case */
436
437 case 'e': /* erase character */
438 if (*p == NULL)
439 Erase_char = -1;
440 else
441 {
442 if (*p == '^' && p[1] != NULL)
443 Erase_char = *++p & 037;
444 else
445 Erase_char = *p;
446 p++;
447 }
448 continue;
449
450 case 'k': /* kill character */
451 if (*p == NULL)
452 Kill_char = CONTROLX;
453 else
454 {
455 if (*p == '^' && p[1] != NULL)
456 Kill_char = *++p & 037;
457 else
458 Kill_char = *p;
459 p++;
460 }
461 continue;
462
463# ifdef OLDFLAGS
464# ifdef OLDDIALUP
465 case 'd': /* dialup type */
466 NewType = DIALUP;
467 goto mapold;
468# endif
469
470# ifdef OLDPLUGBOARD
471 case 'p': /* plugboard type */
472 NewType = PLUGBOARD;
473 goto mapold;
474# endif
475
476# ifdef OLDARPANET
477 case 'a': /* arpanet type */
478 Newtype = ARPANET;
479 goto mapold;
480# endif
481
482mapold: Map->Ident = NewType;
483 Map->Test = ALL;
484 if (*p == NULL)
485 {
486 p = nextarg(argc--, argv++);
487 }
488 Map->Type = p;
489 Map++;
490 Mapped++;
491 p = "";
492 continue;
493# endif
494
495 case 'm': /* map identifier to type */
496 /* This code is very loose. Almost no
497 ** syntax checking is done!! However,
498 ** illegal syntax will only produce
499 ** weird results.
500 */
501 if (*p == NULL)
502 {
503 p = nextarg(argc--, argv++);
504 }
505 if (isalnum(*p))
506 {
507 Map->Ident = p; /* identifier */
508 while (isalnum(*p)) p++;
509 }
510 else
511 Map->Ident = "";
512 Break = 0;
513 Not = 0;
514 while (!Break) switch (*p)
515 {
516 case NULL:
517 p = nextarg(argc--, argv++);
518 continue;
519
520 case ':': /* mapped type */
521 *p++ = NULL;
522 Break++;
523 continue;
524
525 case '>': /* conditional */
526 Map->Test |= GT;
527 *p++ = NULL;
528 continue;
529
530 case '<': /* conditional */
531 Map->Test |= LT;
532 *p++ = NULL;
533 continue;
534
535 case '=': /* conditional */
536 case '@':
537 Map->Test |= EQ;
538 *p++ = NULL;
539 continue;
540
541 case '!': /* invert conditions */
542 Not = ~Not;
543 *p++ = NULL;
544 continue;
545
546 case 'B': /* Baud rate */
547 p++;
548 /* intentional fallthru */
549 default:
550 if (isdigit(*p) || *p == 'e')
551 {
552 Map->Speed = baudrate(p);
553 while (isalnum(*p) || *p == '.')
554 p++;
555 }
556 else
557 Break++;
558 continue;
559 }
560 if (Not) /* invert sense of test */
561 {
562 Map->Test = (~(Map->Test))&ALL;
563 }
564 if (*p == NULL)
565 {
566 p = nextarg(argc--, argv++);
567 }
568 Map->Type = p;
569 p = "";
570 Map++;
571 Mapped++;
572 continue;
573
574 case 'h': /* don't get type from htmp or env */
575 Dash_h++;
576 continue;
577
578 case 'u': /* don't update htmp */
579 Dash_u++;
580 continue;
581
582 case 's': /* output setenv commands */
583 DoSetenv++;
584 CmndLine++;
585 continue;
586
587 case 'S': /* output setenv strings */
588 DoSetenv++;
589 CmndLine=0;
590 continue;
591
592 case 'Q': /* be quiet */
593 BeQuiet++;
594 continue;
595
596 case 'I': /* no initialization */
597 NoInit++;
598 continue;
599
600 case 'A': /* Ask user */
601 Ask++;
602 continue;
603
604 default:
605 *p-- = NULL;
606 fatal("Bad flag -", p);
607 }
608 }
609 else
610 {
611 /* terminal type */
612 DefType = p;
613 }
614 }
615
616 if (DefType)
617 {
618 if (Mapped)
619 {
620 Map->Ident = ""; /* means "map any type" */
621 Map->Test = ALL; /* at all baud rates */
622 Map->Type = DefType; /* to the default type */
623 }
624 else
625 TtyType = DefType;
626 }
627
628# ifndef V6
629 /* get current idea of terminal type from environment */
630 if (!Dash_h && !Mapped && TtyType == 0)
631 TtyType = getenv("TERM");
632# endif
633
634 /* determine terminal id if needed */
635# ifdef V6
636 if (Ttyid == NOTTY && (TtyType == 0 || !Dash_h || !Dash_u))
637 Ttyid = ttyn(FILEDES);
638# else
639 if (!RepOnly && Ttyid == NOTTY && (TtyType == 0 || !Dash_h))
640 Ttyid = ttyname(FILEDES);
641# endif
642
643# ifdef V6
644 /* get htmp if ever used */
645 if (!Dash_u || (TtyType == 0 && !Dash_h))
646 {
647 /* get htmp entry -- if error or wrong user use ttytype */
648 if (Ttyid == NOTTY || hget(Ttyid) < 0 ||
649 hgettype() == 0 || hgetuid() != (getuid() & UIDMASK))
650 Dash_h++;
651 }
652
653 /* find terminal type (if not already known) */
654 if (TtyType == 0 && !Dash_h)
655 {
656 /* get type from /etc/htmp */
657 TtyType = hsgettype();
658 }
659# endif
660
661# ifdef GTTYN
662 /* If still undefined, look at /etc/ttytype */
663 if (TtyType == 0)
664 {
665 TtyType = stypeof(Ttyid);
666 }
667# endif
668
669 /* If still undefined, use DEFTYPE */
670 if (TtyType == 0)
671 {
672 TtyType = DEFTYPE;
673 }
674
675 /* check for dialup or other mapping */
676 if (Mapped)
677 TtyType = mapped(TtyType, ospeed);
678
679 /* TtyType now contains a pointer to the type of the terminal */
680 /* If the first character is '?', ask the user */
681 if (TtyType[0] == '?')
682 {
683 Ask++;
684 TtyType++;
685 if (TtyType[0] == '\0')
686 TtyType = DEFTYPE;
687 }
688 if (Ask)
689 {
690 prs("TERM = (");
691 prs(TtyType);
692 prs(") ");
693 flush();
694
695 /* read the terminal. If not empty, set type */
696 i = read(2, buf, sizeof buf - 1);
697 if (i >= 0)
698 {
699 if (buf[i - 1] == '\n')
700 i--;
701 buf[i] = '\0';
702 if (buf[0] != '\0')
703 TtyType = buf;
704 }
705 }
706
707 if (Ttycap == 0)
708 {
709 /* get terminal capabilities */
710 switch (tgetent(Capbuf, TtyType))
711 {
712 case -1:
713 prs("Cannot open termcap file\n");
714 flush();
715 exit(-1);
716
717 case 0:
718 prs("Type ");
719 prs(TtyType);
720 prs(" unknown\n");
721 flush();
722 exit(1);
723 }
724 Ttycap = Capbuf;
725 }
726
727 if (!RepOnly)
728 {
729 /* determine erase and kill characters */
730 if (Specialerase && !tgetflag("bs"))
731 Erase_char = 0;
732 bufp = buf;
733 p = tgetstr("kb", &bufp);
734 if (p == NULL || p[1] != '\0')
735 p = tgetstr("bc", &bufp);
736 if (p != NULL && p[1] == '\0')
737 bs_char = p[0];
738 else if (tgetflag("bs"))
739 bs_char = BACKSPACE;
740 else
741 bs_char = 0;
742 if (Erase_char == 0 && !tgetflag("os") && mode.sg_erase == OLDERASE)
743 {
744 if (tgetflag("bs") || bs_char != 0)
745 Erase_char = -1;
746 }
747 if (Erase_char < 0)
748 Erase_char = (bs_char != 0) ? bs_char : BACKSPACE;
749
750 if (mode.sg_erase == 0)
751 mode.sg_erase = OLDERASE;
752 if (Erase_char != 0)
753 mode.sg_erase = Erase_char;
754
755 if (mode.sg_kill == 0)
756 mode.sg_kill = OLDKILL;
757 if (Kill_char != 0)
758 mode.sg_kill = Kill_char;
759
760 /* set modes */
761 setdelay("dC", CRdelay, CRbits, &mode.sg_flags);
762 setdelay("dN", NLdelay, NLbits, &mode.sg_flags);
763 setdelay("dB", BSdelay, BSbits, &mode.sg_flags);
764 setdelay("dF", FFdelay, FFbits, &mode.sg_flags);
765 setdelay("dT", TBdelay, TBbits, &mode.sg_flags);
766 if (tgetflag("UC") || command[0] == 'T')
767 mode.sg_flags |= LCASE;
768 else if (tgetflag("LC"))
769 mode.sg_flags &= ~LCASE;
770 mode.sg_flags &= ~(EVENP | ODDP | RAW);
771# ifndef V6
772 mode.sg_flags &= ~CBREAK;
773# endif
774 if (tgetflag("EP"))
775 mode.sg_flags |= EVENP;
776 if (tgetflag("OP"))
777 mode.sg_flags |= ODDP;
778 if ((mode.sg_flags & (EVENP | ODDP)) == 0)
779 mode.sg_flags |= EVENP | ODDP;
780 mode.sg_flags |= CRMOD | ECHO | XTABS;
781 if (tgetflag("NL")) /* new line, not line feed */
782 mode.sg_flags &= ~CRMOD;
783 if (tgetflag("HD")) /* half duplex */
784 mode.sg_flags &= ~ECHO;
785 if (tgetflag("pt")) /* print tabs */
786 mode.sg_flags &= ~XTABS;
787 if (!bequal(&mode, &oldmode, sizeof mode))
788# ifndef V6
789 ioctl(FILEDES, TIOCSETN, &mode);
790# else
791 stty(FILEDES, &mode);
792# endif
793
794 /* get pad character */
795 bufp = buf;
796 if (tgetstr("pc", &bufp) != 0)
797 PC = buf[0];
798
799 /* output startup string */
800 if (!NoInit)
801 {
802 bufp = buf;
803 if (tgetstr("is", &bufp) != 0)
804 tputs(buf, 0, prc);
805 flush();
806 bufp = buf;
807 if (tgetstr("if", &bufp) != 0)
808 cat(buf);
809 sleep(1); /* let terminal settle down */
810 }
811
812 /* set up environment for the shell we are using */
813 /* (this code is rather heuristic) */
814 csh = 0;
815 if (DoSetenv)
816 {
817# ifndef V6
818 char *sh;
819
820 if ((sh = getenv("SHELL")) && (i = strlen(sh)) >= 3)
821 {
822 p = &sh[i-3];
823 if ((csh = sequal(p, "csh")) && CmndLine)
824 write(STDOUT, "set noglob;\n", 12);
825 }
826 if (!csh)
827# endif
828 /* running system shell */
829 write(STDOUT, "export TERMCAP TERM;\n", 21);
830 }
831 }
832
833 /* report type if appropriate */
834 if (DoSetenv || Report || Ureport)
835 {
836 /* find first alias (if any) */
837 for (p = Ttycap; *p != 0 && *p != '|' && *p != ':'; p++)
838 continue;
839 if (*p == 0 || *p == ':')
840 p = Ttycap;
841 else
842 p++;
843 bufp = p;
844 while (*p != '|' && *p != ':' && *p != 0)
845 p++;
846 i = *p;
847 if (DoSetenv)
848 {
849 if (csh)
850 {
851 if (CmndLine)
852 write(STDOUT, "setenv TERM ", 12);
853 write(STDOUT, bufp, p - bufp);
854 write(STDOUT, " ", 1);
855 if (CmndLine)
856 write(STDOUT, ";\n", 2);
857 }
858 else
859 {
860 write(STDOUT, "TERM=", 5);
861 write(STDOUT, bufp, p - bufp);
862 write(STDOUT, ";\n", 2);
863 }
864 }
865 if (Report && !DoSetenv)
866 {
867 write(STDOUT, bufp, p - bufp);
868 write(STDOUT, "\n", 1);
869 }
870 if (Ureport)
871 {
872 *p = '\0';
873 prs("Terminal type is ");
874 prs(bufp);
875 prs("\n");
876 flush();
877 }
878 *p = i;
879 if (DoSetenv)
880 {
881 if (csh)
882 {
883 if (CmndLine)
884 write(STDOUT, "setenv TERMCAP '", 16);
885 }
886 else
887 write(STDOUT, "TERMCAP='", 9);
888 wrtermcap(Ttycap);
889 if (csh)
890 {
891 if (CmndLine)
892 {
893 write(STDOUT, "';\n", 3);
894 write(STDOUT, "unset noglob;\n", 14);
895 }
896 }
897 else
898 write(STDOUT, "';\n", 3);
899 }
900 }
901
902 if (RepOnly)
903 exit(0);
904
905 /* tell about changing erase and kill characters */
906 reportek("Erase", mode.sg_erase, oldmode.sg_erase, OLDERASE);
907 reportek("Kill", mode.sg_kill, oldmode.sg_kill, OLDKILL);
908
909# ifdef V6
910 /* update htmp */
911 if (!Dash_u)
912 {
913 if (Ttyid == 0)
914 Ttyid = ttyn(FILEDES);
915 if (Ttyid == 'x')
916 {
917 prs("Cannot update htmp\n");
918 flush();
919 }
920 else
921 {
922 /* update htmp file only if changed */
923 if (!bequal(Capbuf, hsgettype(), 2))
924 {
925 hsettype(Capbuf[0] | (Capbuf[1] << 8));
926 hput(Ttyid);
927 }
928 }
929 }
930# endif
931
932 exit(0);
933}
934
935reportek(name, new, old, def)
936char *name;
937char old;
938char new;
939char def;
940{
941 register char o;
942 register char n;
943 register char *p;
944
945 if (BeQuiet)
946 return;
947 o = old;
948 n = new;
949
950 if (o == n && n == def)
951 return;
952 prs(name);
953 if (o == n)
954 prs(" is ");
955 else
956 prs(" set to ");
957 if (n < 040)
958 {
959 prs("control-");
960 n = (n & 037) | 0100;
961 }
962 p = "x\n";
963 p[0] = n;
964 prs(p);
965 flush();
966}
967
968
969
970
971setdelay(cap, dtab, bits, flags)
972char *cap;
973struct delay dtab[];
974int bits;
975int *flags;
976{
977 register int i;
978 register struct delay *p;
979
980 /* see if this capability exists at all */
981 i = tgetnum(cap);
982 if (i < 0)
983 i = 0;
984
985 /* clear out the bits, replace with new ones */
986 *flags &= ~bits;
987
988 /* scan dtab for first entry with adequate delay */
989 for (p = dtab; p->d_delay >= 0; p++)
990 {
991 if (p->d_delay >= i)
992 {
993 p++;
994 break;
995 }
996 }
997
998 /* use last entry if none will do */
999 *flags |= (--p)->d_bits;
1000}
1001
1002
1003prs(s)
1004char *s;
1005{
1006 while (*s != '\0')
1007 prc(*s++);
1008}
1009
1010
1011char OutBuf[256];
1012int OutPtr;
1013
1014prc(c)
1015 char c;
1016{
1017 OutBuf[OutPtr++] = c;
1018 if (OutPtr >= sizeof OutBuf)
1019 flush();
1020}
1021
1022flush()
1023{
1024 if (OutPtr > 0)
1025 write(2, OutBuf, OutPtr);
1026 OutPtr = 0;
1027}
1028
1029
1030cat(file)
1031char *file;
1032{
1033 register int fd;
1034 register int i;
1035 char buf[BUFSIZ];
1036
1037 fd = open(file, 0);
1038 if (fd < 0)
1039 {
1040 prs("Cannot open ");
1041 prs(file);
1042 prs("\n");
1043 flush();
1044 exit(-1);
1045 }
1046
1047 flush();
1048 while ((i = read(fd, buf, BUFSIZ)) > 0)
1049 write(FILEDES, buf, i);
1050
1051 close(fd);
1052}
1053
1054
1055
1056bmove(from, to, length)
1057char *from;
1058char *to;
1059int length;
1060{
1061 register char *p, *q;
1062 register int i;
1063
1064 i = length;
1065 p = from;
1066 q = to;
1067
1068 while (i-- > 0)
1069 *q++ = *p++;
1070}
1071
1072
1073
1074bequal(a, b, len)
1075char *a;
1076char *b;
1077int len;
1078{
1079 register char *p, *q;
1080 register int i;
1081
1082 i = len;
1083 p = a;
1084 q = b;
1085
1086 while (*p && *q && (*p == *q) && --i > 0)
1087 {
1088 p++; q++;
1089 }
1090 return ((*p == *q) && i >= 0);
1091}
1092
1093sequal(a, b)
1094char *a;
1095char *b;
1096{
1097 register char *p = a, *q = b;
1098
1099 while (*p && *q && (*p == *q))
1100 {
1101 p++; q++;
1102 }
1103 return (*p == *q);
1104}
1105
1106# ifdef GTTYN
1107char *
1108stypeof(ttyid)
1109char *ttyid;
1110{
1111 static char typebuf[50];
1112 register char *PortType;
1113 register char *PortName;
1114 register char *TtyId;
1115 register char *p;
1116 register FILE *f;
1117
1118 if (ttyid == NOTTY)
1119 return (DEFTYPE);
1120 f = fopen(GTTYN, "r");
1121 if (f == NULL)
1122 return (DEFTYPE);
1123
1124 /* split off end of name */
1125 TtyId = ttyid;
1126 while (*ttyid)
1127 if (*ttyid++ == '/')
1128 TtyId = ttyid;
1129
1130 /* scan the file */
1131 while (fgets(typebuf, sizeof typebuf, f) != NULL)
1132 {
1133 p = PortType = typebuf;
1134 while (*p && isalnum(*p))
1135 p++;
1136 *p++ = NULL;
1137
1138 /* skip separator */
1139 while (*p && !isalnum(*p))
1140 p++;
1141
1142 PortName = p;
1143 /* put NULL at end of name */
1144 while (*p && isalnum(*p))
1145 p++;
1146 *p = NULL;
1147
1148 /* check match on port name */
1149 if (sequal(PortName, TtyId)) /* found it */
1150 {
1151# ifdef OLDDIALUP
1152 if (sequal(PortType, OLDDIALUP))
1153 PortType = DIALUP;
1154# endif
1155
1156# ifdef OLDPLUGBOARD
1157 if (sequal(PortType, OLDPLUGBOARD))
1158 PortType = PLUGBOARD;
1159# endif
1160
1161# ifdef OLDARPANET
1162 if (sequal(PortType, OLDARPANET))
1163 PortType = ARPANET;
1164# endif
1165 fclose (f);
1166 return(PortType);
1167 }
1168 }
1169 fclose (f);
1170 return (DEFTYPE);
1171}
1172# endif
1173
1174#define YES 1
1175#define NO 0
1176/*
1177 * routine to output the string for the environment TERMCAP variable
1178 */
1179#define WHITE(c) (c == ' ' || c == '\t')
1180char delcap[128][2];
1181int ncap = 0;
1182
1183wrtermcap(bp)
1184char *bp;
1185{
1186 char buf[CAPBUFSIZ];
1187 char *p = buf;
1188 char *tp;
1189 char *putbuf();
1190 int space, empty;
1191
1192 /* discard names with blanks */
1193/** May not be desireable ? **/
1194 while (*bp && *bp != ':') {
1195 if (*bp == '|') {
1196 tp = bp+1;
1197 space = NO;
1198 while (*tp && *tp != '|' && *tp != ':') {
1199 space = (space || WHITE(*tp) );
1200 tp++;
1201 }
1202 if (space) {
1203 bp = tp;
1204 continue;
1205 }
1206 }
1207 *p++ = *bp++;
1208 }
1209/**/
1210
1211 while (*bp) {
1212 switch (*bp) {
1213 case ':': /* discard empty, cancelled or dupl fields */
1214 tp = bp+1;
1215 empty = YES;
1216 while (*tp && *tp != ':') {
1217 empty = (empty && WHITE(*tp) );
1218 tp++;
1219 }
1220 if (empty || cancelled(bp+1)) {
1221 bp = tp;
1222 continue;
1223 }
1224 break;
1225
1226 case ' ': /* no spaces in output */
1227 p = putbuf(p, "\\040");
1228 bp++;
1229 continue;
1230
1231 case '"': /* no quotes in output */
1232 p = putbuf(p, "\\042");
1233 bp++;
1234 continue;
1235
1236 case '\'': /* no quotes in output */
1237 p = putbuf(p, "\\047");
1238 bp++;
1239 continue;
1240
1241 case '\\':
1242 case '^': /* anything following is OK */
1243 *p++ = *bp++;
1244 }
1245 *p++ = *bp++;
1246 }
1247 write (STDOUT, buf, p-buf);
1248}
1249
1250cancelled(cap)
1251char *cap;
1252{
1253 register int i;
1254
1255 for (i = 0; i < ncap; i++)
1256 {
1257 if (cap[0] == delcap[i][0] && cap[1] == delcap[i][1])
1258 return (YES);
1259 }
1260 /* delete a second occurrance of the same capability */
1261 delcap[ncap][0] = cap[0];
1262 delcap[ncap][1] = cap[1];
1263 ncap++;
1264 return (cap[2] == '@');
1265}
1266
1267char *
1268putbuf(ptr, str)
1269char *ptr;
1270char *str;
1271{
1272 while (*str)
1273 *ptr++ = *str++;
1274 return (ptr);
1275}
1276
1277
1278baudrate(p)
1279char *p;
1280{
1281 char buf[8];
1282 int i = 0;
1283
1284 while (i < 7 && (isalnum(*p) || *p == '.'))
1285 buf[i++] = *p++;
1286 buf[i] = NULL;
1287 for (i=0; speeds[i].string; i++)
1288 if (sequal(speeds[i].string, buf))
1289 return (speeds[i].speed);
1290 return (-1);
1291}
1292
1293char *
1294mapped(type, speed)
1295char *type;
1296short speed;
1297{
1298 int match;
1299
1300# ifdef DEB
1301 printf ("spd:%d\n", speed);
1302 prmap();
1303# endif
1304 Map = map;
1305 while (Map->Ident)
1306 {
1307 if (*(Map->Ident) == NULL || bequal(Map->Ident, type, 4))
1308 {
1309 match = NO;
1310 switch (Map->Test)
1311 {
1312 case ANY: /* no test specified */
1313 case ALL:
1314 match = YES;
1315 break;
1316
1317 case GT:
1318 match = (speed > Map->Speed);
1319 break;
1320
1321 case GE:
1322 match = (speed >= Map->Speed);
1323 break;
1324
1325 case EQ:
1326 match = (speed == Map->Speed);
1327 break;
1328
1329 case LE:
1330 match = (speed <= Map->Speed);
1331 break;
1332
1333 case LT:
1334 match = (speed < Map->Speed);
1335 break;
1336
1337 case NE:
1338 match = (speed != Map->Speed);
1339 break;
1340 }
1341 if (match)
1342 return (Map->Type);
1343 }
1344 Map++;
1345 }
1346 /* no match found; return given type */
1347 return (type);
1348}
1349
1350# ifdef DEB
1351prmap()
1352{
1353 Map = map;
1354 while (Map->Ident)
1355 {
1356 printf ("%s t:%d s:%d %s\n",
1357 Map->Ident, Map->Test, Map->Speed, Map->Type);
1358 Map++;
1359 }
1360}
1361# endif
1362
1363char *
1364nextarg(argc, argv)
1365int argc;
1366char *argv[];
1367{
1368 if (argc <= 0)
1369 fatal ("Too few args: ", *argv);
1370 if (*(*++argv) == '-')
1371 fatal ("Unexpected arg: ", *argv);
1372 return (*argv);
1373}
1374
1375fatal (mesg, obj)
1376char *mesg;
1377char *obj;
1378{
1379 prs (mesg);
1380 prs (obj);
1381 prc ('\n');
1382 prs (USAGE);
1383 flush();
1384 exit(1);
1385}