|
|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - download
Length: 11218 (0x2bd2)
Types: TextFile
Notes: UNIX file
Names: »col.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
└─⟦f4b8d8c84⟧ UNIX Filesystem
└─⟦this⟧ »cmd/col.c«
/*
* Col(1). Virtual typewriter, performs motions physical typewriters cannot,
* like reverse line feeds. Also filters control characters.
* Two global variables, LineNo and ColNo, have the current line and column
* numbers. Both start at zero.
*/
#include <stdio.h>
#define Unfetch(c) Unfetched = (c)
#define PAGESIZE 256 /* Default Page size. */
#define IBUFSIZE 800 /* Number of possible columns. */
#define EBUFSIZ 4 /* Increment size for buf in EXTRA struct. */
/*
* Fetch returns the control characters \n, \b, \t, \r, HVT, VT, HLF, LF.
* We choose HVT and HLF to make a switch statement more compact in Main().
*/
#define OSTRK 037 /* Flag for overstruck chars. */
#define ESC 033 /* Escape character. */
#define SO 017 /* Shift out to alternate char set. */
#define SI 016 /* Shift in from alternate char set. */
#define HVT 014 /* Half vertical tab. */
#define VT 013 /* Vertical tab, or Rev. Line Feed. */
#define HLF 007 /* Half line feed. */
#define LF 006 /* Line feed (but not return!). */
#define EOT 004 /* End of line signal to InsChar. */
#define BOT 003 /* Start of line signal to InsChar. */
#define not !
#define or ||
#define and &&
#define TRUE (0==0)
#define FALSE (not TRUE)
#define NOTREACHED return
typedef unsigned int uint;
typedef unsigned char uchar;
typedef uchar bool;
typedef struct extra {
struct extra *Next;
int Posn;
uchar Howmany;
char Ebuf[EBUFSIZ];
} EXTRA;
typedef struct {
int Len; /* Number of valid columns in Line. */
char *Line;
struct extra *Extra;
} LINE;
/*
* Error Messages.
*/
char Usage[] = "Usage: col [-bdfx] [-pnum]";
char BackScroll[] = "Scrolling backwards over top of page window.";
char Confused[] = "Seem to have lost Overstruck characters.";
/*
* Flags.
*/
bool Bflag = FALSE; /* Command line option 'b'. */
bool Dflag = FALSE; /* Command line option 'd'. */
bool Fflag = FALSE; /* Command line option 'f'. */
bool Pflag = FALSE; /* Command line option 'p'. */
bool Xflag = FALSE; /* Command line option 'x'. */
/*
* External Variables.
*/
char Ibuf[BUFSIZ]; /* Buffer for input lines. */
LINE *Page; /* Page window. */
LINE *CurLine; /* Ptr to *(Page[LineNo % PageSize]). */
int PageSize = PAGESIZE; /* Actual size of Page. */
int Ibuflen; /* Current length of line in Ibuf. */
int Unfetched; /* storage for unfetched char */
int Top; /* Line number of top of Page window. */
int Bottom; /* Line number of bottom of Page window. */
int Wmark; /* High-water-mark of Lineno. */
int LineNo; /* Current line number in Page. */
int ColNo; /* Current column number. */
/*
* Functions returning non-int.
*/
bool Ostrikeout();
char *realloc();
main(ac, av)
int ac;
char *av[];
{
register int c;
Aarghh(ac, av);
Init();
InsChar(BOT);
while ((c = Fetch()) != EOF)
switch (c) {
case '\b':
if (ColNo > 0)
--ColNo;
break;
case '\r':
ColNo = 0;
break;
case '\t':
ColNo = (ColNo & ~07) + 8;
break;
case '\n':
ColNo = 0;
case VT:
case LF:
case HVT:
case HLF:
InsChar(EOT); /* Close current line. */
VertMove(c);
InsChar(BOT); /* Open current line. */
break;
default:
if (c != ' ')
InsChar(c);
++ColNo;
break;
}
InsChar(EOT); /* Close current line. */
Flush();
return (0);
}
/*
* Process command line arguments.
*/
Aarghh(ac, av)
int ac;
register char *av[];
{
register char *cp;
register int c;
ac = 0; /* To avoid "Strict" warning from compiler. */
while ((cp = *++av) != NULL) {
if (*cp++ != '-') {
Fatal(Usage);
NOTREACHED;
}
while ((c = *cp++) != '\0')
switch (c) {
case 'b':
Bflag = TRUE;
break;
case 'd':
Dflag = TRUE;
break;
case 'f':
Fflag = TRUE;
break;
case 'x':
Xflag = TRUE;
break;
case 'p':
Pflag = TRUE;
if ((PageSize = 2 * atoi(cp)) <= 0)
Fatal("Bad page length");
break;
default:
Fatal(Usage);
NOTREACHED;
}
}
}
Init()
{
register LINE *lp;
lp = CurLine = Page = (LINE *) malloc(sizeof(LINE) * PageSize);
lp += PageSize - 1;
while (lp-- > Page) {
lp->Len = 0;
lp->Line = lp->Extra = NULL;
}
Bottom = PageSize;
return;
}
/*
* Fetch grabs input characters and returns them after filtering.
*/
Fetch()
{
static acset = 0; /* alternate character set flag */
register int c, c1;
if ((c = Unfetched) != '\0') {
Unfetched = '\0';
return (c);
}
for(;;) {
if ((c = getchar()) > ' ' && c < 0177)
return (acset ? c | 0200 : c);
switch (c) {
case ' ':
case '\t':
case '\n':
case '\b':
case '\r':
case VT:
case EOF:
return (c);
case SO:
acset = 1;
continue;
case SI:
acset = 0;
continue;
case ESC:
switch (c1 = getchar()) {
case '7':
return (VT);
case '8':
return (HVT);
case '9':
return (HLF);
case 'B':
return (LF);
}
ungetc(c1, stdin);
continue;
}
}
}
VertMove(c)
register int c;
{
static bool warnflag = FALSE; /* Warn of move over top of page. */
Unfetch(c);
for (;;) {
switch (c = Fetch()) {
case VT:
LineNo -= 2;
break;
case LF:
LineNo += 2;
break;
case HVT:
LineNo -= 1;
break;
case HLF:
LineNo += 1;
break;
case '\n':
ColNo = 0;
LineNo += 2;
break;
default:
Unfetch(c);
CurLine = Page + LineNo % PageSize;
return;
}
/*
* Have we scrolled over the top of the Page window?
*/
if (LineNo < Top)
if (not warnflag) {
Warning(BackScroll);
warnflag = TRUE;
}
/*
* Update the water mark if needed.
*/
if (LineNo > Wmark)
Wmark = LineNo;
/*
* If we've scrolled past the bottom of the window then we
* put out the Top line and move the window down.
*/
if (Bottom <= LineNo) {
PutLine(Top);
Top += 2;
Bottom += 2;
}
}
}
/*
* Insert the character c into Ibuf at column ColNo.
*/
InsChar(c)
register int c;
{
/*
* If (c == BOT) or (c == EOT) we open or close the line in Ibuf.
* Otherwise we are really adding a character to Ibuf.
*/
if (c == BOT) {
register LINE *lp = CurLine;
if ((Ibuflen = lp->Len) != 0) {
strncpy(Ibuf, lp->Line, Ibuflen);
free(lp->Line);
}
return;
}
else if (c == EOT) {
register LINE *lp = CurLine;
if ((lp->Len = Ibuflen) != 0) {
lp->Line = malloc(Ibuflen);
strncpy(lp->Line, Ibuf, Ibuflen);
}
return;
}
/*
* The case of appending a char to the end of Ibuf. Very common.
* Note that in this case Ibuf[ColNo] is virgin territory.
*/
if (ColNo == Ibuflen) {
Ibuf[Ibuflen++] = c;
return;
}
/*
* The case of adding a char beyond the end of Ibuf. We must pad the
* intervening space with spaces.
*/
if (ColNo > Ibuflen) {
register char *ibuf = Ibuf;
while (Ibuflen < ColNo)
ibuf[Ibuflen++] = ' ';
ibuf[Ibuflen++] = c;
return;
}
/*
* The remaining case is adding a char into the interior of Ibuf. If
* the present char is a space or if Bflag is set we just insert c,
* otherwise we have to overstrike.
*/
{ register char *cp = Ibuf + ColNo;
register int c1;
if (Bflag or (c1 = *cp) == ' ') {
*cp = c;
return;
}
if (c1 != OSTRK) {
Overstrike(c1);
*cp = OSTRK;
}
Overstrike(c);
}
return;
}
/*
* Handle overstruck characters.
*/
Overstrike(c)
int c;
{
register EXTRA *ep;
register EXTRA **epp;
/*
* Find the right EXTRA struct in CurLine.
*/
epp = &CurLine->Extra;
for (ep = *epp; ep != NULL; epp = &ep->Next, ep = *epp) {
if (ep->Posn != ColNo)
continue;
/*
* We found it, check for overflow and add the char c.
*/
if (ep->Howmany % EBUFSIZ == 0)
ep = *epp = (EXTRA *) realloc((char *)ep,
sizeof(EXTRA) + ep->Howmany);
ep->Ebuf[ep->Howmany++] = c;
return;
}
/*
* We didn't find it, so make it, and install the char c.
*/
ep = (EXTRA *) malloc(sizeof(EXTRA));
ep->Howmany = 1;
ep->Posn = ColNo;
ep->Ebuf[0] = c;
ep->Next = CurLine->Extra;
CurLine->Extra = ep;
return;
}
Flush()
{
register int i;
for (i = Top; i <= Wmark; i += 2)
PutLine(i);
return;
}
/*
* Output one full line, which is possibly two half lines. All control for
* Dflag and Fflag takes place here.
*/
PutLine(n)
int n;
{
static char HCR[] = {'\r', ESC, '9', '\0'}; /* Half Crg. Return */
register LINE *lp;
lp = Page + n % PageSize;
if (lp->Len != 0)
PutHalf(lp);
++lp;
if (lp->Len == 0) {
if (Dflag and not Fflag) {
putchar('\n');
putchar('\n');
} else
putchar('\n');
} else {
if (Fflag) {
fputs(HCR, stdout);
PutHalf(lp);
fputs(HCR, stdout);
} else {
putchar('\n');
PutHalf(lp);
putchar('\n');
}
}
return;
}
/*
* PutHalf() outputs the half-line lp with no vertical motion at the end.
* Alternate character sets are handled here. Entabbing is handled by Tab.
* Overstrikes are handled by Ostrikeout(), which also may have to handle
* alternate character sets.
*/
PutHalf(lp)
register LINE *lp;
{
register int c;
register int colno;
register bool acset = FALSE;
/*
* Note that since lp->Len is the number of valid columns, the number
* of the last valid column is (lp->Len - 1). That's why the test is
* "<" instead of "<=".
*/
for (colno = 0; colno < lp->Len; ++colno)
switch (c = lp->Line[colno]) {
case OSTRK:
acset = Ostrikeout(lp, colno, acset);
break;
case ' ':
if (Xflag) {
putchar(' ');
break;
}
/* Entab white space. */
c = colno;
while (lp->Line[c] == ' ')
if (++c % 8 == 0) {
putchar('\t');
colno = c;
}
while (colno < c) {
putchar(' ');
++colno;
}
--colno;
break;
default:
if (c & 0200) {
if (not acset) {
acset = TRUE;
putchar(SO);
}
putchar(c & ~0200);
}
else {
if (acset) {
acset = FALSE;
putchar(SI);
}
putchar(c);
}
break;
}
if (acset)
putchar(SI);
free(lp->Line);
lp->Len = 0;
lp->Line = lp->Extra = NULL;
return;
}
/*
* Ostrikeout puts out all the characters overstruck in position ColNo in the
* LINE lp. It pays attention to alternate character sets.
*/
bool
Ostrikeout(lp, col, acset)
LINE *lp;
int col;
bool acset;
{
EXTRA *ep;
/*
* Find the EXTRA struct for position 'col' and remove it from
* the EXTRA list.
*/
{ register EXTRA **epp = &lp->Extra;
register EXTRA *e;
register int n = col;
for (e = *epp; e != NULL; epp = &e->Next, e = *epp) {
if (e->Posn != n)
continue;
*epp = e->Next;
ep = e;
goto FOUNDIT;
}
/*
* Didn't find it in the list.
*/
putchar(' ');
Warning(Confused);
return (acset);
}
/*
* Now that we found it, write out all the characters in ep->Ebuf,
* paying attention to alternate char sets, then free ep.
*/
FOUNDIT:
{ register int c;
register bool ac = acset;
register int count = ep->Howmany;
register char *cp = ep->Ebuf;
while (count-- > 0) {
if ((c = *cp++) & 0200) {
if (not ac) {
ac = TRUE;
putchar(SO);
}
putchar(c & ~0200);
}
else {
if (ac) {
ac = FALSE;
putchar(SI);
}
putchar(c);
}
if (count > 0)
putchar('\b');
}
free(ep);
return (ac);
}
}
Fatal(cp)
char *cp;
{
Warning(cp);
exit(1);
}
Warning(cp)
char *cp;
{
fputs(cp, stderr);
putc('\n', stderr);
return;
}