|
|
1.1 root 1: /*
2: * Col(1). Virtual typewriter, performs motions physical typewriters cannot,
3: * like reverse line feeds. Also filters control characters.
4: * Two global variables, LineNo and ColNo, have the current line and column
5: * numbers. Both start at zero.
6: */
7: #include <stdio.h>
8:
9: #define Unfetch(c) Unfetched = (c)
10:
11: #define PAGESIZE 256 /* Default Page size. */
12: #define IBUFSIZE 800 /* Number of possible columns. */
13: #define EBUFSIZ 4 /* Increment size for buf in EXTRA struct. */
14:
15: /*
16: * Fetch returns the control characters \n, \b, \t, \r, HVT, VT, HLF, LF.
17: * We choose HVT and HLF to make a switch statement more compact in Main().
18: */
19: #define OSTRK 037 /* Flag for overstruck chars. */
20: #define ESC 033 /* Escape character. */
21: #define SO 017 /* Shift out to alternate char set. */
22: #define SI 016 /* Shift in from alternate char set. */
23: #define HVT 014 /* Half vertical tab. */
24: #define VT 013 /* Vertical tab, or Rev. Line Feed. */
25: #define HLF 007 /* Half line feed. */
26: #define LF 006 /* Line feed (but not return!). */
27: #define EOT 004 /* End of line signal to InsChar. */
28: #define BOT 003 /* Start of line signal to InsChar. */
29:
30: #define not !
31: #define or ||
32: #define and &&
33: #define TRUE (0==0)
34: #define FALSE (not TRUE)
35: #define NOTREACHED return
36:
37: typedef unsigned int uint;
38: typedef unsigned char uchar;
39: typedef uchar bool;
40:
41: typedef struct extra {
42: struct extra *Next;
43: int Posn;
44: uchar Howmany;
45: char Ebuf[EBUFSIZ];
46: } EXTRA;
47:
48: typedef struct {
49: int Len; /* Number of valid columns in Line. */
50: char *Line;
51: struct extra *Extra;
52: } LINE;
53:
54:
55: /*
56: * Error Messages.
57: */
58: char Usage[] = "Usage: col [-bdfx] [-pnum]";
59: char BackScroll[] = "Scrolling backwards over top of page window.";
60: char Confused[] = "Seem to have lost Overstruck characters.";
61:
62: /*
63: * Flags.
64: */
65: bool Bflag = FALSE; /* Command line option 'b'. */
66: bool Dflag = FALSE; /* Command line option 'd'. */
67: bool Fflag = FALSE; /* Command line option 'f'. */
68: bool Pflag = FALSE; /* Command line option 'p'. */
69: bool Xflag = FALSE; /* Command line option 'x'. */
70:
71: /*
72: * External Variables.
73: */
74: char Ibuf[BUFSIZ]; /* Buffer for input lines. */
75:
76: LINE *Page; /* Page window. */
77: LINE *CurLine; /* Ptr to *(Page[LineNo % PageSize]). */
78:
79: int PageSize = PAGESIZE; /* Actual size of Page. */
80: int Ibuflen; /* Current length of line in Ibuf. */
81: int Unfetched; /* storage for unfetched char */
82: int Top; /* Line number of top of Page window. */
83: int Bottom; /* Line number of bottom of Page window. */
84: int Wmark; /* High-water-mark of Lineno. */
85: int LineNo; /* Current line number in Page. */
86: int ColNo; /* Current column number. */
87:
88:
89: /*
90: * Functions returning non-int.
91: */
92: bool Ostrikeout();
93: char *realloc();
94:
95:
96: main(ac, av)
97: int ac;
98: char *av[];
99: {
100: register int c;
101:
102: Aarghh(ac, av);
103: Init();
104: InsChar(BOT);
105: while ((c = Fetch()) != EOF)
106: switch (c) {
107: case '\b':
108: if (ColNo > 0)
109: --ColNo;
110: break;
111: case '\r':
112: ColNo = 0;
113: break;
114: case '\t':
115: ColNo = (ColNo & ~07) + 8;
116: break;
117: case '\n':
118: ColNo = 0;
119: case VT:
120: case LF:
121: case HVT:
122: case HLF:
123: InsChar(EOT); /* Close current line. */
124: VertMove(c);
125: InsChar(BOT); /* Open current line. */
126: break;
127: default:
128: if (c != ' ')
129: InsChar(c);
130: ++ColNo;
131: break;
132: }
133: InsChar(EOT); /* Close current line. */
134: Flush();
135: return (0);
136: }
137:
138:
139: /*
140: * Process command line arguments.
141: */
142: Aarghh(ac, av)
143: int ac;
144: register char *av[];
145: {
146: register char *cp;
147: register int c;
148:
149: ac = 0; /* To avoid "Strict" warning from compiler. */
150: while ((cp = *++av) != NULL) {
151: if (*cp++ != '-') {
152: Fatal(Usage);
153: NOTREACHED;
154: }
155: while ((c = *cp++) != '\0')
156: switch (c) {
157: case 'b':
158: Bflag = TRUE;
159: break;
160: case 'd':
161: Dflag = TRUE;
162: break;
163: case 'f':
164: Fflag = TRUE;
165: break;
166: case 'x':
167: Xflag = TRUE;
168: break;
169: case 'p':
170: Pflag = TRUE;
171: if ((PageSize = 2 * atoi(cp)) <= 0)
172: Fatal("Bad page length");
173: break;
174: default:
175: Fatal(Usage);
176: NOTREACHED;
177: }
178: }
179: }
180:
181: Init()
182: {
183: register LINE *lp;
184:
185: lp = CurLine = Page = (LINE *) malloc(sizeof(LINE) * PageSize);
186: lp += PageSize - 1;
187: while (lp-- > Page) {
188: lp->Len = 0;
189: lp->Line = lp->Extra = NULL;
190: }
191: Bottom = PageSize;
192: return;
193: }
194:
195:
196: /*
197: * Fetch grabs input characters and returns them after filtering.
198: */
199: Fetch()
200: {
201: static acset = 0; /* alternate character set flag */
202: register int c, c1;
203:
204: if ((c = Unfetched) != '\0') {
205: Unfetched = '\0';
206: return (c);
207: }
208: for(;;) {
209: if ((c = getchar()) > ' ' && c < 0177)
210: return (acset ? c | 0200 : c);
211: switch (c) {
212: case ' ':
213: case '\t':
214: case '\n':
215: case '\b':
216: case '\r':
217: case VT:
218: case EOF:
219: return (c);
220: case SO:
221: acset = 1;
222: continue;
223: case SI:
224: acset = 0;
225: continue;
226: case ESC:
227: switch (c1 = getchar()) {
228: case '7':
229: return (VT);
230: case '8':
231: return (HVT);
232: case '9':
233: return (HLF);
234: case 'B':
235: return (LF);
236: }
237: ungetc(c1, stdin);
238: continue;
239: }
240: }
241: }
242:
243:
244: VertMove(c)
245: register int c;
246: {
247: static bool warnflag = FALSE; /* Warn of move over top of page. */
248:
249: Unfetch(c);
250: for (;;) {
251: switch (c = Fetch()) {
252: case VT:
253: LineNo -= 2;
254: break;
255: case LF:
256: LineNo += 2;
257: break;
258: case HVT:
259: LineNo -= 1;
260: break;
261: case HLF:
262: LineNo += 1;
263: break;
264: case '\n':
265: ColNo = 0;
266: LineNo += 2;
267: break;
268: default:
269: Unfetch(c);
270: CurLine = Page + LineNo % PageSize;
271: return;
272: }
273:
274: /*
275: * Have we scrolled over the top of the Page window?
276: */
277: if (LineNo < Top)
278: if (not warnflag) {
279: Warning(BackScroll);
280: warnflag = TRUE;
281: }
282:
283: /*
284: * Update the water mark if needed.
285: */
286: if (LineNo > Wmark)
287: Wmark = LineNo;
288:
289:
290: /*
291: * If we've scrolled past the bottom of the window then we
292: * put out the Top line and move the window down.
293: */
294: if (Bottom <= LineNo) {
295: PutLine(Top);
296: Top += 2;
297: Bottom += 2;
298: }
299: }
300: }
301:
302: /*
303: * Insert the character c into Ibuf at column ColNo.
304: */
305: InsChar(c)
306: register int c;
307: {
308:
309: /*
310: * If (c == BOT) or (c == EOT) we open or close the line in Ibuf.
311: * Otherwise we are really adding a character to Ibuf.
312: */
313: if (c == BOT) {
314: register LINE *lp = CurLine;
315: if ((Ibuflen = lp->Len) != 0) {
316: strncpy(Ibuf, lp->Line, Ibuflen);
317: free(lp->Line);
318: }
319: return;
320: }
321: else if (c == EOT) {
322: register LINE *lp = CurLine;
323: if ((lp->Len = Ibuflen) != 0) {
324: lp->Line = malloc(Ibuflen);
325: strncpy(lp->Line, Ibuf, Ibuflen);
326: }
327: return;
328: }
329:
330: /*
331: * The case of appending a char to the end of Ibuf. Very common.
332: * Note that in this case Ibuf[ColNo] is virgin territory.
333: */
334: if (ColNo == Ibuflen) {
335: Ibuf[Ibuflen++] = c;
336: return;
337: }
338:
339: /*
340: * The case of adding a char beyond the end of Ibuf. We must pad the
341: * intervening space with spaces.
342: */
343: if (ColNo > Ibuflen) {
344: register char *ibuf = Ibuf;
345: while (Ibuflen < ColNo)
346: ibuf[Ibuflen++] = ' ';
347: ibuf[Ibuflen++] = c;
348: return;
349: }
350:
351:
352: /*
353: * The remaining case is adding a char into the interior of Ibuf. If
354: * the present char is a space or if Bflag is set we just insert c,
355: * otherwise we have to overstrike.
356: */
357: { register char *cp = Ibuf + ColNo;
358: register int c1;
359:
360: if (Bflag or (c1 = *cp) == ' ') {
361: *cp = c;
362: return;
363: }
364: if (c1 != OSTRK) {
365: Overstrike(c1);
366: *cp = OSTRK;
367: }
368: Overstrike(c);
369: }
370: return;
371: }
372:
373:
374: /*
375: * Handle overstruck characters.
376: */
377: Overstrike(c)
378: int c;
379: {
380: register EXTRA *ep;
381: register EXTRA **epp;
382:
383: /*
384: * Find the right EXTRA struct in CurLine.
385: */
386: epp = &CurLine->Extra;
387: for (ep = *epp; ep != NULL; epp = &ep->Next, ep = *epp) {
388: if (ep->Posn != ColNo)
389: continue;
390: /*
391: * We found it, check for overflow and add the char c.
392: */
393: if (ep->Howmany % EBUFSIZ == 0)
394: ep = *epp = (EXTRA *) realloc((char *)ep,
395: sizeof(EXTRA) + ep->Howmany);
396: ep->Ebuf[ep->Howmany++] = c;
397: return;
398: }
399:
400: /*
401: * We didn't find it, so make it, and install the char c.
402: */
403: ep = (EXTRA *) malloc(sizeof(EXTRA));
404: ep->Howmany = 1;
405: ep->Posn = ColNo;
406: ep->Ebuf[0] = c;
407: ep->Next = CurLine->Extra;
408: CurLine->Extra = ep;
409: return;
410: }
411:
412:
413: Flush()
414: {
415: register int i;
416:
417: for (i = Top; i <= Wmark; i += 2)
418: PutLine(i);
419: return;
420: }
421:
422:
423: /*
424: * Output one full line, which is possibly two half lines. All control for
425: * Dflag and Fflag takes place here.
426: */
427: PutLine(n)
428: int n;
429: {
430: static char HCR[] = {'\r', ESC, '9', '\0'}; /* Half Crg. Return */
431: register LINE *lp;
432:
433: lp = Page + n % PageSize;
434: if (lp->Len != 0)
435: PutHalf(lp);
436: ++lp;
437:
438: if (lp->Len == 0) {
439: if (Dflag and not Fflag) {
440: putchar('\n');
441: putchar('\n');
442: } else
443: putchar('\n');
444: } else {
445: if (Fflag) {
446: fputs(HCR, stdout);
447: PutHalf(lp);
448: fputs(HCR, stdout);
449: } else {
450: putchar('\n');
451: PutHalf(lp);
452: putchar('\n');
453: }
454: }
455: return;
456: }
457:
458: /*
459: * PutHalf() outputs the half-line lp with no vertical motion at the end.
460: * Alternate character sets are handled here. Entabbing is handled by Tab.
461: * Overstrikes are handled by Ostrikeout(), which also may have to handle
462: * alternate character sets.
463: */
464: PutHalf(lp)
465: register LINE *lp;
466: {
467: register int c;
468: register int colno;
469: register bool acset = FALSE;
470:
471: /*
472: * Note that since lp->Len is the number of valid columns, the number
473: * of the last valid column is (lp->Len - 1). That's why the test is
474: * "<" instead of "<=".
475: */
476: for (colno = 0; colno < lp->Len; ++colno)
477: switch (c = lp->Line[colno]) {
478: case OSTRK:
479: acset = Ostrikeout(lp, colno, acset);
480: break;
481: case ' ':
482: if (Xflag) {
483: putchar(' ');
484: break;
485: }
486: /* Entab white space. */
487: c = colno;
488: while (lp->Line[c] == ' ')
489: if (++c % 8 == 0) {
490: putchar('\t');
491: colno = c;
492: }
493: while (colno < c) {
494: putchar(' ');
495: ++colno;
496: }
497: --colno;
498: break;
499: default:
500: if (c & 0200) {
501: if (not acset) {
502: acset = TRUE;
503: putchar(SO);
504: }
505: putchar(c & ~0200);
506: }
507: else {
508: if (acset) {
509: acset = FALSE;
510: putchar(SI);
511: }
512: putchar(c);
513: }
514: break;
515: }
516: if (acset)
517: putchar(SI);
518: free(lp->Line);
519: lp->Len = 0;
520: lp->Line = lp->Extra = NULL;
521: return;
522: }
523:
524:
525: /*
526: * Ostrikeout puts out all the characters overstruck in position ColNo in the
527: * LINE lp. It pays attention to alternate character sets.
528: */
529: bool
530: Ostrikeout(lp, col, acset)
531: LINE *lp;
532: int col;
533: bool acset;
534: {
535: EXTRA *ep;
536:
537: /*
538: * Find the EXTRA struct for position 'col' and remove it from
539: * the EXTRA list.
540: */
541: { register EXTRA **epp = &lp->Extra;
542: register EXTRA *e;
543: register int n = col;
544:
545: for (e = *epp; e != NULL; epp = &e->Next, e = *epp) {
546: if (e->Posn != n)
547: continue;
548: *epp = e->Next;
549: ep = e;
550: goto FOUNDIT;
551: }
552: /*
553: * Didn't find it in the list.
554: */
555: putchar(' ');
556: Warning(Confused);
557: return (acset);
558: }
559:
560: /*
561: * Now that we found it, write out all the characters in ep->Ebuf,
562: * paying attention to alternate char sets, then free ep.
563: */
564: FOUNDIT:
565: { register int c;
566: register bool ac = acset;
567: register int count = ep->Howmany;
568: register char *cp = ep->Ebuf;
569:
570: while (count-- > 0) {
571: if ((c = *cp++) & 0200) {
572: if (not ac) {
573: ac = TRUE;
574: putchar(SO);
575: }
576: putchar(c & ~0200);
577: }
578: else {
579: if (ac) {
580: ac = FALSE;
581: putchar(SI);
582: }
583: putchar(c);
584: }
585: if (count > 0)
586: putchar('\b');
587: }
588: free(ep);
589: return (ac);
590: }
591: }
592:
593:
594: Fatal(cp)
595: char *cp;
596: {
597: Warning(cp);
598: exit(1);
599: }
600:
601: Warning(cp)
602: char *cp;
603: {
604: fputs(cp, stderr);
605: putc('\n', stderr);
606: return;
607: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.