PWB1/sys/source/s2/reform.c
/*
* reform: reformat file, esp. non-UNIX source. 0 of 4/24/77
* perform other manipulations needing meaning of tabs
*/
#define NTABS 21 /* max # tab settings + 1 */
#define NCOLS 512 /* max size of untabbed line */
#define ESC 033
int tabin[NTABS],tabout[NTABS];
char *tspec[2]; /* ptrs to tabspec1 & 2 */
#define TSPEC1 tspec[0]
#define TSPEC2 tspec[1]
int ntspec; /* number of tabspecs found so far */
int optb, /* >0 ==> blank out 1st optb chars if blank/digits */
opte, /* >0 ==> extend length of each line to opte */
optf, /* >0 ==> write format line in front of file */
opti 1, /* # of interior blanks required to make tab */
optm, /* >0 ==> move left optm characters */
optp, /* >0 ==> prepend optp characters */
opts, /* >0 ==> SCCS R.L moved to end of line */
optt; /* >0 ==> truncate lines to optt chars */
/*
* main working buffer, ptr to first and last (nl) char,
* ptrs to rightmost and leftmost legal positions
*/
char line[2*NCOLS+1];
char *pfirst, *plast;
char *liner &line[2*NCOLS],
*linel &line[0];
#define LINESZ1 132
char line1[LINESZ1]; /* buffer area for 1st line1 in file */
char found; /* flag to note existence of format spec */
char srelev[8]; /* if +s, save SCCS SID: chars* tab */
int savem; /* will have m from file fspec */
struct buf {
int fildes; /* file descriptor */
int nleft; /* bytes left */
char *nextp; /* ptr to next char */
char buffer[512]; /* i/o area */
} fin, fout;
static char SCCSID[] "@(#)reform.c 2.11";
main(argc,argv)
int argc; char **argv;
{
get1line(); /* initialize input buffer with 1 line (at least) */
options(argc,argv);
fout.fildes = 1;
if (optf)
fwrite(); /* format spec into new file */
while (getline(tabin) >= 0) {
if (optp) prepend(optp);
if (optt) truncate(optt);
remtrail();
if (opte) extend(opte);
if (opts) sccsadd();
if (optb) blankout(optb);
if (optm) movleft(optm);
contract(tabout,opti);
}
fflush(&fout);
exit(0);
}
/*
* get1line: prime fin buffer with 1st line, for use with -- tabspecs.
* leaves fin so that getchal rereads 1st line
*/
get1line()
{
register int i;
register char *p;
fin.nextp = p = &fin.buffer[0];
for (i = 0; i <= 511;) {
if (read(0, p, 1) != 1) break;
i++;
if (*p++ == '\n') break;
}
fin.nleft = i;
return;
}
/* options: scan option flags, setting flags opt? */
options(argc,argv)
int argc; char **argv;
{
register char c;
register int n;
char *scan;
while (--argc > 0) {
scan = *++argv; /* next arg, skipping 1st */
if (*scan == '+') {
c = *++scan; /* option code */
scan++;
n = getnum(&scan);
switch(c) {
case 'b':
optb = n?n:6; break;
case 'e':
opte = n?n:72; break;
case 'f':
optf++;
break;
case 'i':
opti = n?n:2; break;
case 'm':
optm = n?n:6; break;
case 'p':
optp = n?n:6; break;
case 's':
opts++;
if (!opte) opte = 72;
break;
case 't':
optt = n?n:72; break;
}
}
else if (ntspec < 2)
tspec[ntspec++] = scan; /* save tabspec */
}
if (!TSPEC1)
TSPEC1 = "--"; /* 1st defaults to 1st line of file */
if (!TSPEC2)
TSPEC2 = TSPEC1; /* but 2nd defaults to 1st tabspec */
scantab(TSPEC1,tabin,0);
if (opts)
opte =- savem;
scantab(TSPEC2,tabout,0);
return;
}
/*
* functions scantab, repetab, arbitab, and getnum are identical to
* those in tabs(I). filetab is similar, but saves 1st-line read
* and found flag externally, so that fwrite may use them
*/
/* 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))
err("unknown tab code");
else;
else
arbitab(scan,tabvect);
return;
}
/* repetab: scan and set repetitive 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 = (NCOLS - 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 err("illegal increment");
}
else {
if ((t = getnum(&scan)) > last)
tabvect[i++] = last = t;
else err("illegal tabs");
}
if (*scan++ != ',') break;
}
if (last > NCOLS)
err("illegal tabs");
tabvect[i] = 0;
return;
}
/* filetab: copy tabspec from existing file */
filetab(scan,tabvect,level)
char *scan;
int tabvect[NTABS];
{
register int length, i;
register char c;
char *endspec; /* ptr to blank or colon after tspec*/
char savchar;
int fildes;
char state;
char *temp;
if (level)
err("file indirection");
if (*scan == '\0') { /* tabspec was -- by itself, get from 1st line*/
temp = &fin.buffer[0];
if (opts) {
while ((c = *temp++) != '\t' && c != '\n');
if (c != '\t')
err("not SCCS -m");
}
for (i = 0; i < LINESZ1;)
line1[i++] = *temp++; /* copy to safe place */
length = LINESZ1;
}
else {
if ((fildes = open(scan,0)) < 0)
err("can't open");
length = read(fildes,line1,LINESZ1);
close(fildes);
}
found = state = 0;
scan = 0;
for (i = 0; i<length && (c = line1[i]) != '\n'; i++) {
switch (state) {
case 0:
state = (c == '<'); break;
case 1:
state = (c == ':')?2:0; break;
case 2:
x:
if (c == 't')
state = 3;
else if (c == ':')
state = 6;
else if (c != ' ') {
if (c != 'm')
state = 5;
else {
savem = line1[++i]-'0';
c = line1[++i];
goto x;
}
}
break;
case 3:
if (c == ' ')
state = 2;
else {
scan = &line1[i];
if (*(scan+1) == 'c' && *(scan+2) == '3')
savem = 6;
state = 4;
}
break;
case 4:
if (c == ' ') {
endspec = &line1[i];
state = 5;
}
else if (c == ':') {
endspec = &line1[i];
state = 6;
}
break;
case 5:
if (c == 'm')
goto x;
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) {
savchar = *endspec;
*endspec ='\0';
scantab(scan,tabvect,1);
*endspec = savchar;
}
else scantab("-8",tabvect,1);
return;
}
/*
* fwrite: write format specification (tabout) as 1st line of file
* entry: if tabspec from --file, expects line1 and found to be set
*/
fwrite()
{
if(TSPEC2[0] == '-' && TSPEC2[1] == '-')
if (found)
output(line1);
else
output("<:t-8 d:>\n");
else {
output("<:t");
output(TSPEC2);
output(" d:>\n");
}
return;
}
/* getline: read next line into line, expand tabs into blanks */
/* exit: return -1 on EOF, >= 0 otherwise */
getline(itab)
int itab[];
{
char c;
int i; /* counter for sccs prefix */
int ctab, pctab;
register int nextcol; /* logical position (allow BS/CTRL,etc*/
register char *p;
if (opts) { /* if SCCS, stash SID away */
for(i = 0; i <= 7; i++)
srelev[i] = ' ';
i = 0;
while ((c = getchal()) != '\t') {
if (c == -1)
return(-1); /* EOF or error */
if (c == '\n')
err("not SCCS -m");
if (i <= 7)
srelev[i++] = c;
else
srelev[7] = '*'; /* overflow, mark */
}
}
nextcol = 1;
p = pfirst = line+NCOLS; /* start at middle of workarea */
do {
if ((c = getchal()) < 0)
break;
if (c == '\t') {
while ((ctab = *itab) && nextcol >= ctab) itab++;
pctab = ctab ? p + (ctab-nextcol) : p+1;
nextcol =+ pctab-p; /* adjust column for tab */
while(p < pctab) *p++ = ' ';
}
else if ((*p++ = c) == '\n') break;
if (c >= ' ') nextcol++; /* all normal chars */
else if (c == '\b' || c == ESC) nextcol--;
/* ESC+anything or ctrl char = 0-width */
} while (p <= liner);
plast = p-1;
if (p > liner)
err("line too long");
return(c);
}
/*
* getchal: local variant of getchar with stop mode processing:
* make sure output flushed if last char was (nl), thus allowing
* nroff -s or .rd requests with small overhead.
* note EOF return is -1 rather than 0
*/
getchal()
{
if (fin.nleft <= 0) {
if (fin.nextp != 0 && *--fin.nextp == '\n')
fflush(&fout);
fin.nleft = read(fin.fildes,fin.buffer,512);
fin.nextp = fin.buffer;
}
if (fin.nleft-- > 0) return(*fin.nextp++);
return(-1);
}
/* prepend: add blanks to front of line */
prepend(n)
register int n;
{
register char *p;
p = pfirst;
if (pfirst < linel+n)
err("line too long");
while(n--) *--p = ' ';
pfirst = p;
return;
}
/* truncate: truncate line to n chars max */
truncate(n)
register int n;
{
if (plast > pfirst+n)
*(plast = pfirst+n) = '\n';
return;
}
/* remtrail: delete trailing blanks */
remtrail()
{
register char *p;
p = plast;
while(*--p == ' ' && p >= pfirst);
*++p = '\n';
plast = p;
return;
}
/* blankout: blank first n chars, iff they are blanks/digits */
blankout(n)
register int n;
{
register char *p, *ptemp;
char doblank;
doblank = 1;
ptemp = ((p = pfirst+n-1)<plast)?p:plast-1;
for (p = pfirst; p <= ptemp && doblank; p++)
if ((*p < '0' || *p > '9') && *p != ' ') doblank = 0;
if (doblank)
for(p = pfirst; p <= ptemp;) *p++ = ' ';
return;
}
/* extend: extend lines to fixed length */
extend(n)
register int n;
{
register char *p;
register char *ptemp;
p = plast;
plast = ((ptemp = pfirst+n) < liner)?ptemp:liner-1;
ptemp = plast;
while (p < ptemp) *p++ = ' ';
*p = '\n';
return;
}
/* movleft: shift line left (remove leading chars) */
movleft(n)
register int n;
{
pfirst = ((pfirst =+ n)<plast)?pfirst:plast;
return;
}
/*
* sccsadd: add SCCS SID (release.level[etc...] onto end of line)
*/
sccsadd()
{
register char *p;
register int i;
p = plast; /* current loc of '\n' */
for (i = 0; i <= 7; i++)
if (srelev[i] != ' ')
*p++ = srelev[i];
*p = '\n';
plast = p;
return;
}
/* contract: compress blanks into tabs and write output */
contract(itab,n)
int itab[], n;
{
char outbuf[NCOLS];
register char *p; /* ptr to next char of input */
register char *pout; /* ptr to next empty slot of outbuf */
register char c;
int blnkcnt; /* count of length of contiguous blank string */
int nextcol; /* next column # to be filled */
int t, it;
p = pfirst;
pout = outbuf;
blnkcnt = 0;
nextcol = 1;
do {
while ((c = *p++) == ' ')
blnkcnt++;
if (blnkcnt) { /* 1st nonblank after blank string */
while (*itab && nextcol >= *itab) itab++;
if (blnkcnt >= n || nextcol == 1) /* always lead blnks */
while((it = *itab) && it <= nextcol+blnkcnt) {
*pout++ = '\t';
t = it-nextcol;
blnkcnt =- t;
nextcol =+ t;
itab++;
}
}
nextcol =+ blnkcnt; /* leftover blanks, if any */
while(blnkcnt--)
*pout++ = ' ';
blnkcnt = 0;
*pout++ = c; /* finally, actual input char */
if (c >= ' ') nextcol++; /* normal chars */
else if (c == '\b' || c == ESC)
nextcol--;
/* ESC-anything or other ctrl chars = 0-width */
} while(c != '\n');
output(outbuf);
return;
}
/* output: write 1 line (terminated by '\0' or '\n') */
output(str)
register char *str;
{
register char c;
while (c = *str++) {
putc(c,&fout);
if (c == '\n') break;
}
return;
}
/* err: terminate with error and print diagnostic */
err(mesg)
register char *mesg;
{
int ttydes,ttyold[3];
char *p;
if (gtty(ttydes = 2,ttyold)) ttydes = 1;
write(ttydes,"reform:",7);
p = mesg;
while(*p++);
write(ttydes,mesg,p-mesg);
write(ttydes,"\n",1);
exit(1);
}
/*
* getnum: scan and convert number, return zero if none found
* set scan ptr to addr of ending delimiter
*/
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);
}
/*
* 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)
char *s1,*s2;
{
register char c;
while((c = *s1++) == *s2++ && c);
if (c == *--s2)
return(1);
return(0);
}