|
|
1.1 ! root 1: /*- ! 2: * Copyright (c) 1990 The Regents of the University of California. ! 3: * All rights reserved. ! 4: * ! 5: * This code is derived from software contributed to Berkeley by ! 6: * Michael Rendell of the Memorial University of Newfoundland. ! 7: * ! 8: * Redistribution and use in source and binary forms are permitted provided ! 9: * that: (1) source distributions retain this entire copyright notice and ! 10: * comment, and (2) distributions including binaries display the following ! 11: * acknowledgement: ``This product includes software developed by the ! 12: * University of California, Berkeley and its contributors'' in the ! 13: * documentation or other materials provided with the distribution and in ! 14: * all advertising materials mentioning features or use of this software. ! 15: * Neither the name of the University nor the names of its contributors may ! 16: * be used to endorse or promote products derived from this software without ! 17: * specific prior written permission. ! 18: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED ! 19: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF ! 20: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ! 21: */ ! 22: ! 23: #ifndef lint ! 24: char copyright[] = ! 25: "@(#) Copyright (c) 1990 The Regents of the University of California.\n\ ! 26: All rights reserved.\n"; ! 27: #endif /* not lint */ ! 28: ! 29: #ifndef lint ! 30: static char sccsid[] = "@(#)col.c 5.2 (Berkeley) 5/24/90"; ! 31: #endif /* not lint */ ! 32: ! 33: #include <errno.h> ! 34: #include <ctype.h> ! 35: #include <string.h> ! 36: #include <stdio.h> ! 37: ! 38: #define BS '\b' /* backspace */ ! 39: #define TAB '\t' /* tab */ ! 40: #define SPACE ' ' /* space */ ! 41: #define NL '\n' /* newline */ ! 42: #define CR '\r' /* carriage return */ ! 43: #define ESC '\033' /* escape */ ! 44: #define SI '\017' /* shift in to normal character set */ ! 45: #define SO '\016' /* shift out to alternate character set */ ! 46: #define VT '\013' /* vertical tab (aka reverse line feed) */ ! 47: #define RLF '\07' /* ESC-07 reverse line feed */ ! 48: #define RHLF '\08' /* ESC-08 reverse half-line feed */ ! 49: #define FHLF '\09' /* ESC-09 forward half-line feed */ ! 50: ! 51: /* build up at least this many lines before flushing them out */ ! 52: #define BUFFER_MARGIN 32 ! 53: ! 54: typedef char CSET; ! 55: ! 56: typedef struct char_str { ! 57: #define CS_NORMAL 1 ! 58: #define CS_ALTERNATE 2 ! 59: short c_column; /* column character is in */ ! 60: CSET c_set; /* character set (currently only 2) */ ! 61: char c_char; /* character in question */ ! 62: } CHAR; ! 63: ! 64: typedef struct line_str LINE; ! 65: struct line_str { ! 66: CHAR *l_line; /* characters on the line */ ! 67: LINE *l_prev; /* previous line */ ! 68: LINE *l_next; /* next line */ ! 69: int l_lsize; /* allocated sizeof l_line */ ! 70: int l_line_len; /* strlen(l_line) */ ! 71: int l_needs_sort; /* set if chars went in out of order */ ! 72: int l_max_col; /* max column in the line */ ! 73: }; ! 74: ! 75: LINE *alloc_line(); ! 76: void *xmalloc(); ! 77: ! 78: CSET last_set; /* char_set of last char printed */ ! 79: LINE *lines; ! 80: int compress_spaces; /* if doing space -> tab conversion */ ! 81: int fine; /* if `fine' resolution (half lines) */ ! 82: int max_bufd_lines; /* max # lines to keep in memory */ ! 83: int nblank_lines; /* # blanks after last flushed line */ ! 84: int no_backspaces; /* if not to output any backspaces */ ! 85: ! 86: #define PUTC(ch) \ ! 87: if (putchar(ch) == EOF) \ ! 88: wrerr(); ! 89: ! 90: main(argc, argv) ! 91: int argc; ! 92: char **argv; ! 93: { ! 94: extern int optind; ! 95: extern char *optarg; ! 96: register int ch; ! 97: CHAR *c; ! 98: CSET cur_set; /* current character set */ ! 99: LINE *l; /* current line */ ! 100: int extra_lines; /* # of lines above first line */ ! 101: int cur_col; /* current column */ ! 102: int cur_line; /* line number of current position */ ! 103: int max_line; /* max value of cur_line */ ! 104: int this_line; /* line l points to */ ! 105: int nflushd_lines; /* number of lines that were flushed */ ! 106: int adjust, opt, warned; ! 107: ! 108: max_bufd_lines = 128; ! 109: compress_spaces = 1; /* compress spaces into tabs */ ! 110: while ((opt = getopt(argc, argv, "bfhl:x")) != EOF) ! 111: switch (opt) { ! 112: case 'b': /* do not output backspaces */ ! 113: no_backspaces = 1; ! 114: break; ! 115: case 'f': /* allow half forward line feeds */ ! 116: fine = 1; ! 117: break; ! 118: case 'h': /* compress spaces into tabs */ ! 119: compress_spaces = 1; ! 120: break; ! 121: case 'l': /* buffered line count */ ! 122: if ((max_bufd_lines = atoi(optarg)) <= 0) { ! 123: (void)fprintf(stderr, ! 124: "col: bad -l argument %s.\n", optarg); ! 125: exit(1); ! 126: } ! 127: break; ! 128: case 'x': /* do not compress spaces into tabs */ ! 129: compress_spaces = 0; ! 130: break; ! 131: case '?': ! 132: default: ! 133: usage(); ! 134: } ! 135: ! 136: if (optind != argc) ! 137: usage(); ! 138: ! 139: /* this value is in half lines */ ! 140: max_bufd_lines *= 2; ! 141: ! 142: adjust = cur_col = extra_lines = warned = 0; ! 143: cur_line = max_line = nflushd_lines = this_line = 0; ! 144: cur_set = last_set = CS_NORMAL; ! 145: lines = l = alloc_line(); ! 146: ! 147: while ((ch = getchar()) != EOF) { ! 148: if (!isgraph(ch)) { ! 149: switch (ch) { ! 150: case BS: /* can't go back further */ ! 151: if (cur_col == 0) ! 152: continue; ! 153: --cur_col; ! 154: continue; ! 155: case CR: ! 156: cur_col = 0; ! 157: continue; ! 158: case ESC: /* just ignore EOF */ ! 159: switch(getchar()) { ! 160: case RLF: ! 161: cur_line -= 2; ! 162: break; ! 163: case RHLF: ! 164: cur_line--; ! 165: break; ! 166: case FHLF: ! 167: cur_line++; ! 168: if (cur_line > max_line) ! 169: max_line = cur_line; ! 170: } ! 171: continue; ! 172: case NL: ! 173: cur_line += 2; ! 174: if (cur_line > max_line) ! 175: max_line = cur_line; ! 176: cur_col = 0; ! 177: continue; ! 178: case SPACE: ! 179: ++cur_col; ! 180: continue; ! 181: case SI: ! 182: cur_set = CS_NORMAL; ! 183: continue; ! 184: case SO: ! 185: cur_set = CS_ALTERNATE; ! 186: continue; ! 187: case TAB: /* adjust column */ ! 188: cur_col |= 7; ! 189: ++cur_col; ! 190: continue; ! 191: case VT: ! 192: cur_line -= 2; ! 193: continue; ! 194: } ! 195: continue; ! 196: } ! 197: ! 198: /* Must stuff ch in a line - are we at the right one? */ ! 199: if (cur_line != this_line - adjust) { ! 200: LINE *lnew; ! 201: int nmove; ! 202: ! 203: adjust = 0; ! 204: nmove = cur_line - this_line; ! 205: if (!fine) { ! 206: /* round up to next line */ ! 207: if (cur_line & 1) { ! 208: adjust = 1; ! 209: nmove++; ! 210: } ! 211: } ! 212: if (nmove < 0) { ! 213: for (; nmove < 0 && l->l_prev; nmove++) ! 214: l = l->l_prev; ! 215: if (nmove) { ! 216: if (nflushd_lines == 0) { ! 217: /* ! 218: * Allow backup past first ! 219: * line if nothing has been ! 220: * flushed yet. ! 221: */ ! 222: for (; nmove < 0; nmove++) { ! 223: lnew = alloc_line(); ! 224: l->l_prev = lnew; ! 225: lnew->l_next = l; ! 226: l = lines = lnew; ! 227: extra_lines++; ! 228: } ! 229: } else { ! 230: if (!warned++) ! 231: warn(cur_line); ! 232: cur_line -= nmove; ! 233: } ! 234: } ! 235: } else { ! 236: /* may need to allocate here */ ! 237: for (; nmove > 0 && l->l_next; nmove--) ! 238: l = l->l_next; ! 239: for (; nmove > 0; nmove--) { ! 240: lnew = alloc_line(); ! 241: lnew->l_prev = l; ! 242: l->l_next = lnew; ! 243: l = lnew; ! 244: } ! 245: } ! 246: this_line = cur_line + adjust; ! 247: nmove = this_line - nflushd_lines; ! 248: if (nmove >= max_bufd_lines + BUFFER_MARGIN) { ! 249: nflushd_lines += nmove - max_bufd_lines; ! 250: flush_lines(nmove - max_bufd_lines); ! 251: } ! 252: } ! 253: /* grow line's buffer? */ ! 254: if (l->l_line_len + 1 >= l->l_lsize) { ! 255: int need; ! 256: ! 257: need = l->l_lsize ? l->l_lsize * 2 : 90; ! 258: l->l_line = (CHAR *)xmalloc((void *) l->l_line, ! 259: (unsigned) need * sizeof(CHAR)); ! 260: l->l_lsize = need; ! 261: } ! 262: c = &l->l_line[l->l_line_len++]; ! 263: c->c_char = ch; ! 264: c->c_set = cur_set; ! 265: c->c_column = cur_col; ! 266: /* ! 267: * If things are put in out of order, they will need sorting ! 268: * when it is flushed. ! 269: */ ! 270: if (cur_col < l->l_max_col) ! 271: l->l_needs_sort = 1; ! 272: else ! 273: l->l_max_col = cur_col; ! 274: cur_col++; ! 275: } ! 276: /* goto the last line that had a character on it */ ! 277: for (; l->l_next; l = l->l_next) ! 278: this_line++; ! 279: flush_lines(this_line - nflushd_lines + extra_lines + 1); ! 280: ! 281: /* make sure we leave things in a sane state */ ! 282: if (last_set != CS_NORMAL) ! 283: PUTC('\017'); ! 284: ! 285: /* flush out the last few blank lines */ ! 286: nblank_lines = max_line - this_line; ! 287: if (max_line & 1) ! 288: nblank_lines++; ! 289: else if (!nblank_lines) ! 290: /* missing a \n on the last line? */ ! 291: nblank_lines = 2; ! 292: flush_blanks(); ! 293: exit(0); ! 294: } ! 295: ! 296: flush_lines(nflush) ! 297: int nflush; ! 298: { ! 299: LINE *l; ! 300: ! 301: while (--nflush >= 0) { ! 302: l = lines; ! 303: lines = l->l_next; ! 304: if (l->l_line) { ! 305: flush_blanks(); ! 306: flush_line(l); ! 307: } ! 308: nblank_lines++; ! 309: if (l->l_line) ! 310: (void)free((void *)l->l_line); ! 311: free_line(l); ! 312: } ! 313: if (lines) ! 314: lines->l_prev = NULL; ! 315: } ! 316: ! 317: /* ! 318: * Print a number of newline/half newlines. If fine flag is set, nblank_lines ! 319: * is the number of half line feeds, otherwise it is the number of whole line ! 320: * feeds. ! 321: */ ! 322: flush_blanks() ! 323: { ! 324: int half, i, nb; ! 325: ! 326: half = 0; ! 327: nb = nblank_lines; ! 328: if (nb & 1) { ! 329: if (fine) ! 330: half = 1; ! 331: else ! 332: nb++; ! 333: } ! 334: nb /= 2; ! 335: for (i = nb; --i >= 0;) ! 336: PUTC('\n'); ! 337: if (half) { ! 338: PUTC('\033'); ! 339: PUTC('9'); ! 340: if (!nb) ! 341: PUTC('\r'); ! 342: } ! 343: nblank_lines = 0; ! 344: } ! 345: ! 346: /* ! 347: * Write a line to stdout taking care of space to tab conversion (-h flag) ! 348: * and character set shifts. ! 349: */ ! 350: flush_line(l) ! 351: LINE *l; ! 352: { ! 353: CHAR *c, *endc; ! 354: int nchars, last_col, this_col; ! 355: ! 356: last_col = 0; ! 357: nchars = l->l_line_len; ! 358: ! 359: if (l->l_needs_sort) { ! 360: static CHAR *sorted; ! 361: static int count_size, *count, i, save, sorted_size, tot; ! 362: ! 363: /* ! 364: * Do an O(n) sort on l->l_line by column being careful to ! 365: * preserve the order of characters in the same column. ! 366: */ ! 367: if (l->l_lsize > sorted_size) { ! 368: sorted_size = l->l_lsize; ! 369: sorted = (CHAR *)xmalloc((void *)sorted, ! 370: (unsigned)sizeof(CHAR) * sorted_size); ! 371: } ! 372: if (l->l_max_col >= count_size) { ! 373: count_size = l->l_max_col + 1; ! 374: count = (int *)xmalloc((void *)count, ! 375: (unsigned)sizeof(int) * count_size); ! 376: } ! 377: bzero((char *)count, sizeof(int) * l->l_max_col + 1); ! 378: for (i = nchars, c = l->l_line; --i >= 0; c++) ! 379: count[c->c_column]++; ! 380: ! 381: /* ! 382: * calculate running total (shifted down by 1) to use as ! 383: * indices into new line. ! 384: */ ! 385: for (tot = 0, i = 0; i <= l->l_max_col; i++) { ! 386: save = count[i]; ! 387: count[i] = tot; ! 388: tot += save; ! 389: } ! 390: ! 391: for (i = nchars, c = l->l_line; --i >= 0; c++) ! 392: sorted[count[c->c_column]++] = *c; ! 393: c = sorted; ! 394: } else ! 395: c = l->l_line; ! 396: while (nchars > 0) { ! 397: this_col = c->c_column; ! 398: endc = c; ! 399: do { ! 400: ++endc; ! 401: } while (--nchars > 0 && this_col == endc->c_column); ! 402: ! 403: /* if -b only print last character */ ! 404: if (no_backspaces) ! 405: c = endc - 1; ! 406: ! 407: if (this_col > last_col) { ! 408: int nspace = this_col - last_col; ! 409: ! 410: if (compress_spaces && nspace > 1) { ! 411: int ntabs; ! 412: ! 413: ntabs = this_col / 8 - last_col / 8; ! 414: nspace -= ntabs * 8; ! 415: while (--ntabs >= 0) ! 416: PUTC('\t'); ! 417: } ! 418: while (--nspace >= 0) ! 419: PUTC(' '); ! 420: last_col = this_col; ! 421: } ! 422: last_col++; ! 423: ! 424: for (;;) { ! 425: if (c->c_set != last_set) { ! 426: switch (c->c_set) { ! 427: case CS_NORMAL: ! 428: PUTC('\017'); ! 429: break; ! 430: case CS_ALTERNATE: ! 431: PUTC('\016'); ! 432: } ! 433: last_set = c->c_set; ! 434: } ! 435: PUTC(c->c_char); ! 436: if (++c >= endc) ! 437: break; ! 438: PUTC('\b'); ! 439: } ! 440: } ! 441: } ! 442: ! 443: #define NALLOC 64 ! 444: ! 445: static LINE *line_freelist; ! 446: ! 447: LINE * ! 448: alloc_line() ! 449: { ! 450: LINE *l; ! 451: int i; ! 452: ! 453: if (!line_freelist) { ! 454: l = (LINE *)xmalloc((void *)NULL, sizeof(LINE) * NALLOC); ! 455: line_freelist = l; ! 456: for (i = 1; i < NALLOC; i++, l++) ! 457: l->l_next = l + 1; ! 458: l->l_next = NULL; ! 459: } ! 460: l = line_freelist; ! 461: line_freelist = l->l_next; ! 462: ! 463: bzero(l, sizeof(LINE)); ! 464: return(l); ! 465: } ! 466: ! 467: free_line(l) ! 468: LINE *l; ! 469: { ! 470: l->l_next = line_freelist; ! 471: line_freelist = l; ! 472: } ! 473: ! 474: void * ! 475: xmalloc(p, size) ! 476: void *p; ! 477: size_t size; ! 478: { ! 479: if (!(p = (void *)realloc(p, size))) { ! 480: (void)fprintf(stderr, "col: %s.\n", strerror(ENOMEM)); ! 481: exit(1); ! 482: } ! 483: return(p); ! 484: } ! 485: ! 486: usage() ! 487: { ! 488: (void)fprintf(stderr, "usage: col [-bfx] [-l nline]\n"); ! 489: exit(1); ! 490: } ! 491: ! 492: wrerr() ! 493: { ! 494: (void)fprintf(stderr, "col: write error.\n"); ! 495: exit(1); ! 496: } ! 497: ! 498: warn(line) ! 499: int line; ! 500: { ! 501: (void)fprintf(stderr, ! 502: "col: warning: can't back up %s.\n", line < 0 ? ! 503: "past first line" : "-- line already flushed"); ! 504: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.