]> cygwin.com Git - cygwin-apps/cygutils.git/blob - src/col/col.c
Fixes for cygstart; bump version number and documentation
[cygwin-apps/cygutils.git] / src / col / col.c
1 /*-
2 * Copyright (c) 1990, 1993, 1994
3 * The Regents of the University of California. 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, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 /* 2002-05-22 David A. Willis: modified license to the 'BSD no advert'
34 * license, as required by the Director of the Office of
35 * Technology Licensing of the University of California on
36 * July 22, 1999.
37 * See http://www.opensource.org/licenses/bsd-license.html
38 * 2002-05-22 David A. Willis: "Ported" code for compilation under
39 * ix86-pc-cygwin (just added the #if HAVE_CONFIG_H block,
40 * really.)
41 */
42
43 #ifndef lint
44 static char copyright[] =
45 "@(#) Copyright (c) 1990, 1993, 1994\n\
46 The Regents of the University of California. All rights reserved.\n";
47 #endif /* not lint */
48
49 #ifndef lint
50 static char sccsid[] = "@(#)col.c 8.3 (Berkeley) 4/2/94";
51 #endif /* not lint */
52
53 #if HAVE_CONFIG_H
54 #include "config.h"
55 #endif
56 #include "common.h"
57
58 #include "err.h"
59 #include <locale.h>
60
61 /* These headers are included via common.h, except for <err.h> */
62 /* err.h is located in CWD, so include using " " instead of < > */
63 #if 0
64 #include <ctype.h>
65 #include <err.h>
66 #include <string.h>
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <locale.h>
70
71 #ifdef HAVE_UNISTD_H
72 #include <unistd.h>
73 #endif /* HAVE_UNISTD_H */
74
75 #endif
76
77 #define BS '\b' /* backspace */
78 #define TAB '\t' /* tab */
79 #define SPACE ' ' /* space */
80 #define NL '\n' /* newline */
81 #define CR '\r' /* carriage return */
82 #define ESC '\033' /* escape */
83 #define SI '\017' /* shift in to normal character set */
84 #define SO '\016' /* shift out to alternate character set */
85 #define VT '\013' /* vertical tab (aka reverse line feed) */
86 #define RLF '7' /* ESC-7 reverse line feed */
87 #define RHLF '8' /* ESC-8 reverse half-line feed */
88 #define FHLF '9' /* ESC-9 forward half-line feed */
89
90 /* build up at least this many lines before flushing them out */
91 #define BUFFER_MARGIN 32
92
93 typedef char CSET;
94
95 typedef struct char_str {
96 #define CS_NORMAL 1
97 #define CS_ALTERNATE 2
98 short c_column; /* column character is in */
99 CSET c_set; /* character set (currently only 2) */
100 char c_char; /* character in question */
101 } CHARACTER;
102
103 typedef struct line_str LINE;
104 struct line_str {
105 CHARACTER *l_line; /* characters on the line */
106 LINE *l_prev; /* previous line */
107 LINE *l_next; /* next line */
108 int l_lsize; /* allocated sizeof l_line */
109 int l_line_len; /* strlen(l_line) */
110 int l_needs_sort; /* set if chars went in out of order */
111 int l_max_col; /* max column in the line */
112 };
113
114 LINE *alloc_line __P((void));
115 void dowarn __P((int));
116 void flush_line __P((LINE *));
117 void flush_lines __P((int));
118 void flush_blanks __P((void));
119 void free_line __P((LINE *));
120 void usage __P((void));
121 void wrerr __P((void));
122 void *xmalloc __P((void *, size_t));
123
124 CSET last_set; /* char_set of last char printed */
125 LINE *lines;
126 int compress_spaces; /* if doing space -> tab conversion */
127 int fine; /* if `fine' resolution (half lines) */
128 int max_bufd_lines; /* max # lines to keep in memory */
129 int nblank_lines; /* # blanks after last flushed line */
130 int no_backspaces; /* if not to output any backspaces */
131
132 #define PUTC(ch) \
133 if (putchar(ch) == EOF) \
134 wrerr();
135
136 int
137 main(argc, argv)
138 int argc;
139 char **argv;
140 {
141 int ch;
142 CHARACTER *c;
143 CSET cur_set; /* current character set */
144 LINE *l; /* current line */
145 int extra_lines; /* # of lines above first line */
146 int cur_col; /* current column */
147 int cur_line; /* line number of current position */
148 int max_line; /* max value of cur_line */
149 int this_line; /* line l points to */
150 int nflushd_lines; /* number of lines that were flushed */
151 int adjust, opt, warned;
152
153 err_setprogname(argv[0]);
154 setlocale(LC_ALL, "");
155
156 max_bufd_lines = 128;
157 compress_spaces = 1; /* compress spaces into tabs */
158 while ((opt = getopt(argc, argv, "bfhl:x")) != EOF)
159 switch (opt) {
160 case 'b': /* do not output backspaces */
161 no_backspaces = 1;
162 break;
163 case 'f': /* allow half forward line feeds */
164 fine = 1;
165 break;
166 case 'h': /* compress spaces into tabs */
167 compress_spaces = 1;
168 break;
169 case 'l': /* buffered line count */
170 if ((max_bufd_lines = atoi(optarg)) <= 0) {
171 (void)fprintf(stderr,
172 "col: bad -l argument %s.\n", optarg);
173 exit(1);
174 }
175 break;
176 case 'x': /* do not compress spaces into tabs */
177 compress_spaces = 0;
178 break;
179 case '?':
180 default:
181 usage();
182 }
183
184 if (optind != argc)
185 usage();
186
187 /* this value is in half lines */
188 max_bufd_lines *= 2;
189
190 adjust = cur_col = extra_lines = warned = 0;
191 cur_line = max_line = nflushd_lines = this_line = 0;
192 cur_set = last_set = CS_NORMAL;
193 lines = l = alloc_line();
194
195 while ((ch = getchar()) != EOF) {
196 if (!isgraph(ch)) {
197 switch (ch) {
198 case BS: /* can't go back further */
199 if (cur_col == 0)
200 continue;
201 --cur_col;
202 continue;
203 case CR:
204 cur_col = 0;
205 continue;
206 case ESC: /* just ignore EOF */
207 switch(getchar()) {
208 case RLF:
209 cur_line -= 2;
210 break;
211 case RHLF:
212 cur_line--;
213 break;
214 case FHLF:
215 cur_line++;
216 if (cur_line > max_line)
217 max_line = cur_line;
218 }
219 continue;
220 case NL:
221 cur_line += 2;
222 if (cur_line > max_line)
223 max_line = cur_line;
224 cur_col = 0;
225 continue;
226 case SPACE:
227 ++cur_col;
228 continue;
229 case SI:
230 cur_set = CS_NORMAL;
231 continue;
232 case SO:
233 cur_set = CS_ALTERNATE;
234 continue;
235 case TAB: /* adjust column */
236 cur_col |= 7;
237 ++cur_col;
238 continue;
239 case VT:
240 cur_line -= 2;
241 continue;
242 }
243 continue;
244 }
245
246 /* Must stuff ch in a line - are we at the right one? */
247 if (cur_line != this_line - adjust) {
248 LINE *lnew;
249 int nmove;
250
251 adjust = 0;
252 nmove = cur_line - this_line;
253 if (!fine) {
254 /* round up to next line */
255 if (cur_line & 1) {
256 adjust = 1;
257 nmove++;
258 }
259 }
260 if (nmove < 0) {
261 for (; nmove < 0 && l->l_prev; nmove++)
262 l = l->l_prev;
263 if (nmove) {
264 if (nflushd_lines == 0) {
265 /*
266 * Allow backup past first
267 * line if nothing has been
268 * flushed yet.
269 */
270 for (; nmove < 0; nmove++) {
271 lnew = alloc_line();
272 l->l_prev = lnew;
273 lnew->l_next = l;
274 l = lines = lnew;
275 extra_lines++;
276 }
277 } else {
278 if (!warned++)
279 dowarn(cur_line);
280 cur_line -= nmove;
281 }
282 }
283 } else {
284 /* may need to allocate here */
285 for (; nmove > 0 && l->l_next; nmove--)
286 l = l->l_next;
287 for (; nmove > 0; nmove--) {
288 lnew = alloc_line();
289 lnew->l_prev = l;
290 l->l_next = lnew;
291 l = lnew;
292 }
293 }
294 this_line = cur_line + adjust;
295 nmove = this_line - nflushd_lines;
296 if (nmove >= max_bufd_lines + BUFFER_MARGIN) {
297 nflushd_lines += nmove - max_bufd_lines;
298 flush_lines(nmove - max_bufd_lines);
299 }
300 }
301 /* grow line's buffer? */
302 if (l->l_line_len + 1 >= l->l_lsize) {
303 int need;
304
305 need = l->l_lsize ? l->l_lsize * 2 : 90;
306 l->l_line = (CHARACTER *)xmalloc((void *) l->l_line,
307 (unsigned) need * sizeof(CHARACTER));
308 l->l_lsize = need;
309 }
310 c = &l->l_line[l->l_line_len++];
311 c->c_char = ch;
312 c->c_set = cur_set;
313 c->c_column = cur_col;
314 /*
315 * If things are put in out of order, they will need sorting
316 * when it is flushed.
317 */
318 if (cur_col < l->l_max_col)
319 l->l_needs_sort = 1;
320 else
321 l->l_max_col = cur_col;
322 cur_col++;
323 }
324 /* goto the last line that had a character on it */
325 for (; l->l_next; l = l->l_next)
326 this_line++;
327 flush_lines(this_line - nflushd_lines + extra_lines + 1);
328
329 /* make sure we leave things in a sane state */
330 if (last_set != CS_NORMAL)
331 PUTC('\017');
332
333 /* flush out the last few blank lines */
334 nblank_lines = max_line - this_line;
335 if (max_line & 1)
336 nblank_lines++;
337 else if (!nblank_lines)
338 /* missing a \n on the last line? */
339 nblank_lines = 2;
340 flush_blanks();
341 exit(0);
342 }
343
344 void
345 flush_lines(nflush)
346 int nflush;
347 {
348 LINE *l;
349
350 while (--nflush >= 0) {
351 l = lines;
352 lines = l->l_next;
353 if (l->l_line) {
354 flush_blanks();
355 flush_line(l);
356 }
357 nblank_lines++;
358 if (l->l_line)
359 (void)free((void *)l->l_line);
360 free_line(l);
361 }
362 if (lines)
363 lines->l_prev = NULL;
364 }
365
366 /*
367 * Print a number of newline/half newlines. If fine flag is set, nblank_lines
368 * is the number of half line feeds, otherwise it is the number of whole line
369 * feeds.
370 */
371 void
372 flush_blanks()
373 {
374 int half, i, nb;
375
376 half = 0;
377 nb = nblank_lines;
378 if (nb & 1) {
379 if (fine)
380 half = 1;
381 else
382 nb++;
383 }
384 nb /= 2;
385 for (i = nb; --i >= 0;)
386 PUTC('\n');
387 if (half) {
388 PUTC('\033');
389 PUTC('9');
390 if (!nb)
391 PUTC('\r');
392 }
393 nblank_lines = 0;
394 }
395
396 /*
397 * Write a line to stdout taking care of space to tab conversion (-h flag)
398 * and character set shifts.
399 */
400 void
401 flush_line(l)
402 LINE *l;
403 {
404 CHARACTER *c, *endc;
405 int nchars, last_col, this_col;
406
407 last_col = 0;
408 nchars = l->l_line_len;
409
410 if (l->l_needs_sort) {
411 static CHARACTER *sorted;
412 static int count_size, *count, i, save, sorted_size, tot;
413
414 /*
415 * Do an O(n) sort on l->l_line by column being careful to
416 * preserve the order of characters in the same column.
417 */
418 if (l->l_lsize > sorted_size) {
419 sorted_size = l->l_lsize;
420 sorted = (CHARACTER *)xmalloc((void *)sorted,
421 (unsigned)sizeof(CHARACTER) * sorted_size);
422 }
423 if (l->l_max_col >= count_size) {
424 count_size = l->l_max_col + 1;
425 count = (int *)xmalloc((void *)count,
426 (unsigned)sizeof(int) * count_size);
427 }
428 memset((char *)count, 0, sizeof(int) * l->l_max_col + 1);
429 for (i = nchars, c = l->l_line; --i >= 0; c++)
430 count[c->c_column]++;
431
432 /*
433 * calculate running total (shifted down by 1) to use as
434 * indices into new line.
435 */
436 for (tot = 0, i = 0; i <= l->l_max_col; i++) {
437 save = count[i];
438 count[i] = tot;
439 tot += save;
440 }
441
442 for (i = nchars, c = l->l_line; --i >= 0; c++)
443 sorted[count[c->c_column]++] = *c;
444 c = sorted;
445 } else
446 c = l->l_line;
447 while (nchars > 0) {
448 this_col = c->c_column;
449 endc = c;
450 do {
451 ++endc;
452 } while (--nchars > 0 && this_col == endc->c_column);
453
454 /* if -b only print last character */
455 if (no_backspaces)
456 c = endc - 1;
457
458 if (this_col > last_col) {
459 int nspace = this_col - last_col;
460
461 if (compress_spaces && nspace > 1) {
462 int ntabs;
463
464 ntabs = this_col / 8 - last_col / 8;
465 nspace -= ntabs * 8;
466 while (--ntabs >= 0)
467 PUTC('\t');
468 }
469 while (--nspace >= 0)
470 PUTC(' ');
471 last_col = this_col;
472 }
473 last_col++;
474
475 for (;;) {
476 if (c->c_set != last_set) {
477 switch (c->c_set) {
478 case CS_NORMAL:
479 PUTC('\017');
480 break;
481 case CS_ALTERNATE:
482 PUTC('\016');
483 }
484 last_set = c->c_set;
485 }
486 PUTC(c->c_char);
487 if (++c >= endc)
488 break;
489 PUTC('\b');
490 }
491 }
492 }
493
494 #define NALLOC 64
495
496 static LINE *line_freelist;
497
498 LINE *
499 alloc_line()
500 {
501 LINE *l;
502 int i;
503
504 if (!line_freelist) {
505 l = (LINE *)xmalloc((void *)NULL, sizeof(LINE) * NALLOC);
506 line_freelist = l;
507 for (i = 1; i < NALLOC; i++, l++)
508 l->l_next = l + 1;
509 l->l_next = NULL;
510 }
511 l = line_freelist;
512 line_freelist = l->l_next;
513
514 memset(l, 0, sizeof(LINE));
515 return (l);
516 }
517
518 void
519 free_line(l)
520 LINE *l;
521 {
522
523 l->l_next = line_freelist;
524 line_freelist = l;
525 }
526
527 void *
528 xmalloc(p, size)
529 void *p;
530 size_t size;
531 {
532
533 if (!(p = (void *)realloc(p, size)))
534 err(1, NULL);
535 return (p);
536 }
537
538 void
539 usage()
540 {
541
542 (void)fprintf(stderr, "usage: col [-bfx] [-l nline]\n");
543 exit(1);
544 }
545
546 void
547 wrerr()
548 {
549
550 (void)fprintf(stderr, "col: write error.\n");
551 exit(1);
552 }
553
554 void
555 dowarn(line)
556 int line;
557 {
558
559 warnx("warning: can't back up %s",
560 line < 0 ? "past first line" : "-- line already flushed");
561 }
This page took 0.061045 seconds and 5 git commands to generate.