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