PWB1/sys/source/s2/tabs.c
/*
* tabs: set tab stops at terminal. 2.7 of 4/15/77
* tabs [tabspec] [+f] [+m[n]] [+ln] +t[type]] [+q]
*/
/* max # columns used (needed for GSI) */
#define NCOLS 158
#define NTABS 41 /* max # tabs +1 (to be set) */
#define NTABSCL 21 /* max # tabs + 1 that will be cleared */
#define ESC 033
#define CLEAR '2'
#define SET '1'
#define TAB '\t'
#define CR '\r'
#define NMG 0 /* no margin setting */
#define GMG 1 /* GSI300S margin */
#define TMG 2 /* TERMINET margin */
#define DMG 3 /* DASI450 margin */
#define TCLRLN 0 /* long, repetitive, general tab clear */
char omit[] "omitted"; /* +t option omitted entirely */
char tclrsh[] {ESC,CLEAR,CR,0}; /* short sequence for most terminals */
char tclrss[] {ESC,TAB,ESC,CLEAR,CR,0}; /* short including GSIS */
char tclrgs[] {ESC,TAB,CR,0}; /* short, knowing GSIS for sure */
char tclr40[] {ESC, 'R', CR, 0}; /* TTY 40/2 */
char *termtab[] { /* terminal handling info, see struct ttab */
"", tclrsh, 158, 132, NMG, /* +t by itself */
omit, TCLRLN, 158, 132, TMG, /* +t omitted entirely */
"gsi", TCLRLN, 158, 132, NMG, /* old GSI */
"300", TCLRLN, 158, 132, NMG, /* old GSI */
"gsis", tclrgs, 158, 132, GMG, /* GSI 300S */
"300S", tclrgs, 158, 132, GMG, /* GSI 300S */
"450", tclrsh, 158, 132, DMG, /* DASI 450/DIABLO 1620 */
"1620", tclrsh, 158, 132, DMG, /* DASI 450/DIABLO 1620 */
"tn", tclrsh, 118, 118, TMG, /* TERMINET */
"hp", TCLRLN, 80, 80, NMG, /* HP2640A */
"40-2", tclr40, 80, 80, NMG, /* TTY 40/2 */
0
};
struct ttab { /* for termtab */
char *tcode; /* +tcode */
char *tclr; /* char sequence to clear tabs and return carriage */
int tmaxtab; /* maximum allowed position */
int tdefmax; /* default maximum, if +l omitted */
int tmarg; /* type of margin setting allowed */
} *tt termtab;
int maxtab; /* max tab value for repetitive spec, from +l, default 132 */
int margin; /* margin (for TN and GSIS), from +m, default 10 */
int margflg; /* >0 ==> +m option used, 0 ==> not */
char *terminal omit; /* type of terminal used */
char *tabspec "-8"; /* default tab specification */
char fsuprs; /* from +f, 0 ==> print tabs if --file, 1 ==> suppress */
char quick; /* from +q, 0 ==> clear tabs, 1 ==> omit clear */
int ttyold[3]; /* tty table */
int ttysave; /* save for modes */
int istty; /* 1 ==> is actual tty */
static char SCCSID[] "@(#)tabs.c 2.7";
main(argc,argv)
int argc;
char *argv[];
{
int tabvect[NTABS]; /* build tab list here */
char *scan; /* scan pointer to next char */
int endup();
signal(2,&endup); /* catch interrupts */
if (gtty(1, ttyold) == 0) {
ttysave = ttyold[2];
istty++;
}
tabvect[0] = 0; /* mark as not yet filled in */
while (--argc > 0) {
scan = *++argv;
if (*scan == '+')
switch (*++scan) {
case 'f':
fsuprs = 1;
break;
case 'l':
scan++;
maxtab = getnum(&scan);
break;
case 'm':
margflg++;
scan++;
if (*scan)
margin = getnum(&scan);
else
margin = 10;
break;
case 'T':
case 't':
terminal = ++scan;
break;
case 'q':
quick++;
break;
}
else
tabspec = scan; /* save tab specification */
}
termadj(); /* check terminal type and alter parameters */
scantab(tabspec,tabvect,0);
if (!tabvect[0])
repetab("8",tabvect);
settabs(tabvect);
endup();
exit(0);
}
/* scantab: scan 1 tabspec & return tab list for it */
scantab(scan,tabvect,level)
char *scan;
int tabvect[NTABS], level;
{
register char c;
if (*scan == '-')
if ((c = *++scan) == '-')
filetab(++scan,tabvect,level);
else if (c >= '0' && c <= '9')
repetab(scan,tabvect);
else if (stdtab(scan,tabvect))
abend("unknown tab code");
else;
else
arbitab(scan,tabvect);
return;
}
/* repetab: scan and set repetitve tabs, 1+n, 1+2*n, etc */
repetab(scan,tabvect)
char *scan;
int tabvect[NTABS];
{
register int incr, i, tabn;
int limit;
incr = getnum(&scan);
tabn = 1;
limit = (maxtab-1)/(incr?incr:1)-1; /* # last actual tab */
if (limit>NTABS-2)
limit = NTABS-2;
for (i = 0; i<=limit; i++)
tabvect[i] = tabn =+ incr;
tabvect[i] = 0;
return;
}
/* arbitab: handle list of arbitrary tabs */
arbitab(scan,tabvect)
char *scan;
int tabvect[NTABS];
{
register int i, t, last;
char c;
last = 0;
for (i = 0; i<NTABS-1;) {
if ((c = *scan) == '+') {
scan++; /* +n ==> increment, not absolute */
if (t = getnum(&scan))
tabvect[i++] = last =+ t;
else abend("illegal increment");
}
else {
if ((t = getnum(&scan)) > last)
tabvect[i++] = last = t;
else abend("illegal tabs");
}
if (*scan++ != ',') break;
}
if (last > NCOLS)
abend("illegal tabs");
tabvect[i] = 0;
return;
}
/* filetab: copy tabspec from existing file */
#define CARDSIZ 132
filetab(scan,tabvect,level)
char *scan;
int tabvect[NTABS];
{
register int length, i;
register char c;
int fildes;
char card[CARDSIZ]; /* buffer area for 1st card in file */
char state, found;
char *temp;
if (level)
abend("file indirection");
if ((fildes = open(scan,0)) < 0)
abend("can't open");
length = read(fildes,card,CARDSIZ);
close(fildes);
found = state = 0;
scan = 0;
for (i = 0; i<length && (c = card[i]) != '\n'; i++) {
switch (state) {
case 0:
state = (c == '<'); break;
case 1:
state = (c == ':')?2:0; break;
case 2:
if (c == 't')
state = 3;
else if (c == ':')
state = 6;
else if (c != ' ')
state = 5;
break;
case 3:
if (c == ' ')
state = 2;
else {
scan = &card[i];
state = 4;
}
break;
case 4:
if (c == ' ') {
card[i] = '\0';
state = 5;
}
else if (c == ':') {
card[i] = '\0';
state = 6;
}
break;
case 5:
if (c == ' ')
state = 2;
else if (c == ':')
state = 6;
break;
case 6:
if (c == '>') {
found = 1;
goto done;
}
else state = 5;
break;
}
}
done:
if (found && scan != 0) {
scantab(scan,tabvect,1);
temp = scan;
while (*++temp);
*temp = '\n';
if (fsuprs == 0)
write(1,scan,++temp-scan);
}
else scantab("-8",tabvect,1);
return;
}
/* termadj: check terminal type; adjust margin & maxtab if needed.
set global tt -> right entry in termtab */
termadj()
{
register struct ttab *t;
for(t =tt; t->tcode; t++) {
if (equal(terminal,t->tcode))
break;
}
if (!t->tcode)
t = tt; /* unrecognizable = +t by self */
if (maxtab == 0)
maxtab = t->tdefmax; /* use default, since +l omitted */
else if (maxtab > t->tmaxtab)
t = t->tmaxtab; /* too big, use max allowed */
tt = t;
return;
}
char *cleartabs();
/* settabs: set actual tabs at terminal */
/* note: this code caters to necessities of handling GSI and
other terminals in a consistent way. */
settabs(tabvect)
int tabvect[NTABS];
{
char setbuf[250]; /* 2+3*NTABS+2+NCOLS+NTABS (approx) */
register char *p; /* ptr for assembly in setbuf */
register int *curtab; /* ptr to tabvect item */
int i, previous, nblanks;
if (istty) {
ttyold[2] =& 0777757;
stty(1,ttyold); /* turn off cr-lf map */
}
p = setbuf;
*p++ = CR;
if (!quick)
p = cleartabs(p, tt->tclr);
if (margflg)
switch(tt->tmarg) {
case GMG: /* GSI300S */
/* NOTE: the 300S appears somewhat odd, in that there is
a column 0, but there is no way to do a direct tab to it.
The sequence ESC 'T' '\0' jumps to column 27 and prints
a '0', without changing the margin. */
*p++ = ESC;
*p++ = 'T'; /* setup for direct tab */
if (margin =& 0177) /* normal case */
*p++ = margin;
else { /* +m0 case */
*p++ = 1; /* column 1 */
*p++ = '\b'; /* column 0 */
}
*p++ = margin; /* direct horizontal tab */
*p++ = ESC;
*p++ = '0'; /* actual margin set */
break;
case TMG: /* TERMINET 300 & 1200 */
for(i = 1; i <= margin; i++)
*p++ = ' ';
break;
case DMG: /* DASI450/DIABLO 1620 */
*p++ = ESC; /* direct tab ignores margin */
*p++ = '\t';
*p++ = (margin & 0177) + 1;
*p++ = ESC;
*p++ = '9';
break;
}
/*
* actual setting: at least terminals do this consistently!
*/
previous = 1; curtab = tabvect;
while ((nblanks = *curtab-previous) >= 0 &&
previous + nblanks <= maxtab) {
for (i = 1; i <= nblanks; i++) *p++ = ' ';
previous = *curtab++;
*p++ =ESC;
*p++ = SET;
}
*p++ = CR;
if (equal(tt->tclr, tclr40))
*p++ = '\n'; /* TTY40/2 needs LF, not just CR */
write(1, setbuf, p - setbuf);
return;
}
/* cleartabs(pointer to buffer, pointer to clear sequence */
char *cleartabs(p, qq)
register char *p;
char *qq;
{
register i;
register char *q;
q = qq;
if (q == TCLRLN) { /* if repetitive sequence */
*p++ = CR;
for(i = 0; i < NTABSCL - 1; i++) {
*p++ = TAB;
*p++ = ESC;
*p++ = CLEAR;
}
*p++ = CR;
}
else {
while(*p++ = *q++); /* copy table sequence */
p--; /* adjust for null */
if (qq == tclr40) { /* TTY40 extra delays needed */
*p++ = '\0';
*p++ = '\0';
*p++ = '\0';
*p++ = '\0';
}
}
return(p);
}
/* getnum: scan and convert number, return zero if none found */
/* set scan ptr to addr of ending delimeter */
getnum(scan1)
char **scan1;
{
register int n;
register char c, *scan;
n = 0;
scan = *scan1;
while ((c = *scan++) >= '0' && c <= '9') n = n * 10 + c -'0';
*scan1 = --scan;
return(n);
}
/* abend: terminate processing with message to terminal */
abend(arg)
char *arg;
{
register char *temp;
temp = arg;
while (*++temp); /* get length */
*temp = '\n';
endup();
write(1,arg,temp+1-arg);
exit(1);
}
/* endup: make sure tty mode reset & exit */
endup()
{
if (istty) {
ttyold[2] = ttysave;
stty(1,ttyold); /* reset cr-lf to previous */
}
}
/* stdtabs: standard tabs table
format: option code letter(s), null, tabs, null */
char stdtabs[] {
'a', 0,1,10,16,36,72,0, /* IBM 370 Assembler */
'a','2',0,1,10,16,40,72,0, /* IBM Assembler alternative*/
'c', 0,1,8,12,16,20,55,0, /* COBOL, normal */
'c','2',0,1,6,10,14,49,0, /* COBOL, crunched*/
'c','3',0,1,6,10,14,18,22,26,30,34,38,42,46,50,54,58,62,67,0,
/* crunched COBOL, many tabs */
'f', 0,1,7,11,15,19,23,0, /* FORTRAN */
'p', 0,1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61,0, /* PL/I */
's', 0,1,10,55,0, /* SNOBOL */
'u', 0,1,12,20,44,0, /* UNIVAC ASM */
0};
/* stdtab: return tab list for any "canned" tab option.
entry: option points to null-terminated option string
tabvect points to vector to be filled in
exit: return(0) if legal, tabvect filled, ending with zero
return(-1) if unknown option
*/
stdtab(option,tabvect)
char option[];
int tabvect[];
{
register char *sp;
tabvect[0] = 0;
sp = stdtabs;
while (*sp) {
if (equal(option,sp)) {
while (*sp++); /* skip to 1st tab value */
while (*tabvect++ = *sp++); /* copy, make int */
return(0);
}
while(*sp++); /* skip to 1st tab value */
while(*sp++); /* skip over tab list */
}
return(-1);
}
/* equal: string comparison, return 1 if equal, 0 otherwise */
equal(s1,s2)
register char *s1,*s2;
{
register char c;
while((c = *s1++) == *s2++ && c);
if (c == *--s2)
return(1);
return(0);
}