|
|
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.