Unleashed v1.4
[unleashed.git] / usr / src / cmd / col / col.c
blob14b6637e576d359f3f41e5a82a722fb8e5b8dbd2
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 * col - filter reverse carraige motions
36 #include <stdio.h>
37 #include <ctype.h>
38 #include <locale.h>
39 #include <limits.h>
40 #include <stdlib.h>
41 #include <wctype.h>
43 #define PL 256
44 #define ESC '\033'
45 #define RLF '\013'
46 #define SI '\017'
47 #define SO '\016'
48 #define GREEK 0200
49 #define LINELN 4096
51 wchar_t *page[PL];
52 wchar_t lbuff[LINELN], *line;
53 wchar_t *lbuffend = lbuff + LINELN - 1;
54 wchar_t ws_blank[2] = {' ', 0};
55 char esc_chars, underline, temp_off, smart;
56 int bflag, xflag, fflag, pflag;
57 int greeked;
58 int half;
59 int cp, lp;
60 int ll, llh, mustwr;
61 int pcp = 0;
62 char *pgmname;
64 #define USAGEMSG "usage:\tcol [-bfxp]\n"
66 static void outc(wchar_t);
67 static void store(int);
68 static void fetch(int);
69 static void emit(wchar_t *, int);
70 static void incr(void);
71 static void decr(void);
72 static void wsinsert(wchar_t *, int);
73 static void incr_line(int);
74 static int wcscrwidth(wchar_t);
76 int
77 main(int argc, char **argv)
79 int i, n;
80 int opt;
81 int greek;
82 int c;
83 wchar_t wc;
84 char byte;
85 static char fbuff[BUFSIZ];
87 setbuf(stdout, fbuff);
88 (void) setlocale(LC_ALL, "");
89 #if !defined(TEXT_DOMAIN)
90 #define TEXT_DOMAIN "SYS_TEST"
91 #endif
92 (void) textdomain(TEXT_DOMAIN);
93 pgmname = argv[0];
95 while ((opt = getopt(argc, argv, "bfxp")) != EOF)
96 switch (opt) {
97 case 'b':
98 bflag++;
99 break;
100 case 'x':
101 xflag++;
102 break;
103 case 'f':
104 fflag++;
105 break;
106 case 'p':
107 pflag++;
108 break;
109 case '?':
110 default:
111 (void) fprintf(stderr, gettext(USAGEMSG));
112 exit(2);
115 argc -= optind;
116 if (argc >= 1) {
117 (void) fprintf(stderr, gettext(USAGEMSG));
118 exit(2);
121 for (ll = 0; ll < PL; ll++)
122 page[ll] = 0;
124 smart = temp_off = underline = esc_chars = '\0';
125 cp = 0;
126 ll = 0;
127 greek = 0;
128 mustwr = PL;
129 line = lbuff;
131 while ((c = getwchar()) != EOF) {
132 if (underline && temp_off && c > ' ') {
133 outc(ESC);
134 if (*line) {
135 incr_line(1);
137 *line = 'X';
138 incr_line(1);
139 *line = temp_off = '\0';
141 if (c != '\b')
142 if (esc_chars)
143 esc_chars = '\0';
144 switch (c) {
145 case '\n':
146 if (underline && !temp_off) {
147 if (*line)
148 incr_line(1);
149 *line = ESC;
150 incr_line(1);
151 *line = 'Y';
152 incr_line(1);
153 *line = '\0';
154 temp_off = '1';
156 incr();
157 incr();
158 cp = 0;
159 continue;
161 case '\0':
162 continue;
164 case ESC:
165 c = getwchar();
166 switch (c) {
167 case '7': /* reverse full line feed */
168 decr();
169 decr();
170 break;
172 case '8': /* reverse half line feed */
173 if (fflag)
174 decr();
175 else {
176 if (--half < -1) {
177 decr();
178 decr();
179 half += 2;
182 break;
184 case '9': /* forward half line feed */
185 if (fflag)
186 incr();
187 else {
188 if (++half > 0) {
189 incr();
190 incr();
191 half -= 2;
194 break;
196 default:
197 if (pflag) { /* pass through esc */
198 outc(ESC);
199 incr_line(1);
200 *line = c;
201 incr_line(1);
202 *line = '\0';
203 esc_chars = 1;
204 if (c == 'X')
205 underline = 1;
206 if (c == 'Y' && underline)
207 underline = temp_off = '\0';
208 if (c == ']')
209 smart = 1;
210 if (c == '[')
211 smart = '\0';
213 break;
215 continue;
217 case SO:
218 greek = GREEK;
219 greeked++;
220 continue;
222 case SI:
223 greek = 0;
224 continue;
226 case RLF:
227 decr();
228 decr();
229 continue;
231 case '\r':
232 cp = 0;
233 continue;
235 case '\t':
236 cp = (cp + 8) & -8;
237 continue;
239 case '\b':
240 if (esc_chars) {
241 *line = '\b';
242 incr_line(1);
243 *line = '\0';
244 } else if (cp > 0)
245 cp--;
246 continue;
248 case ' ':
249 cp++;
250 continue;
252 default:
253 if (iswprint(c)) { /* if printable */
254 if (!greek) {
255 outc((wchar_t)c);
256 cp += wcscrwidth(c);
259 * EUC (apply SO only when there can
260 * be corresponding character in CS1)
262 else if (iswascii(c)) {
263 byte = (c | greek);
264 n = mbtowc(&wc, &byte, 1);
265 if (!iswcntrl(c) && !iswspace(c) &&
266 n == 1) {
267 outc(wc);
268 cp += wcscrwidth(wc);
269 } else {
270 outc((wchar_t)c);
271 cp += wcscrwidth(c);
273 } else {
274 outc((wchar_t)c);
275 cp += wcscrwidth(c);
278 if ((cp + 1) > LINELN) {
279 (void) fprintf(stderr,
280 gettext("col: Line too long\n"));
281 exit(2);
284 continue;
288 for (i = 0; i < PL; i++)
289 if (page[(mustwr+i)%PL] != 0)
290 emit(page[(mustwr+i) % PL], mustwr+i-PL);
291 emit(ws_blank, (llh + 1) & -2);
292 return (0);
295 static void
296 outc(wchar_t c)
298 int n, i;
299 int width, widthl, widthc;
300 wchar_t *p1;
301 wchar_t c1;
302 char esc_chars = '\0';
303 if (lp > cp) {
304 line = lbuff;
305 lp = 0;
308 while (lp < cp) {
309 if (*line != '\b')
310 if (esc_chars)
311 esc_chars = '\0';
312 switch (*line) {
313 case ESC:
314 incr_line(1);
315 esc_chars = 1;
316 break;
317 case '\0':
318 *line = ' ';
319 lp++;
320 break;
321 case '\b':
322 /* if ( ! esc_chars ) */
323 lp--;
324 break;
325 default:
326 lp += wcscrwidth(*line);
328 incr_line(1);
330 while (*line == '\b') {
332 * EUC (For a multi-column character, backspace characters
333 * are assumed to be used like "__^H^HXX", where "XX"
334 * represents a two-column character, and a backspace
335 * always goes back by one column.)
337 for (n = 0; *line == '\b'; incr_line(1)) {
338 n++;
339 lp--;
341 while (n > 0 && lp < cp) {
342 i = *line;
343 incr_line(1);
344 i = wcscrwidth(i);
345 n -= i;
346 lp += i;
349 while (*line == ESC)
350 incr_line(6);
351 widthc = wcscrwidth(c);
352 widthl = wcscrwidth(*line);
353 if (bflag || (*line == '\0') || *line == ' ') {
354 if (*line == '\0' || widthl == widthc) {
355 *line = c;
356 } else if (widthl > widthc) {
357 n = widthl - widthc;
358 wsinsert(line, n);
359 *line = c;
360 incr_line(1);
361 for (i = 0; i < n; i++) {
362 *line = ' ';
363 incr_line(1);
365 line = lbuff;
366 lp = 0;
367 } else {
368 n = widthc - widthl;
369 if (line < lbuffend) {
370 for (p1 = line+1; n > 0 && p1 < lbuffend;
371 n -= wcscrwidth(i)) {
372 i = *p1++;
374 *line = c;
375 if (p1 < lbuffend) {
376 (void) wcscpy(line+1, p1);
377 } else {
378 (void) fprintf(stderr,
379 gettext("col: Line too long.\n"));
380 exit(1);
382 } else {
383 (void) fprintf(stderr,
384 gettext("col: Line too long.\n"));
385 exit(1);
388 } else {
389 if (smart && (widthl == 1) && (widthc == 1)) {
390 wchar_t c1, c2, c3, c4, c5, c6, c7;
391 incr_line(1);
392 c1 = *line;
393 *line = ESC;
394 incr_line(1);
395 c2 = *line;
396 *line = '[';
397 incr_line(1);
398 c3 = *line;
399 *line = '\b';
400 incr_line(1);
401 c4 = *line;
402 *line = ESC;
403 incr_line(1);
404 c5 = *line;
405 *line = ']';
406 incr_line(1);
407 c6 = *line;
408 *line = c;
409 incr_line(1);
410 while (c1) {
411 c7 = *line;
412 *line = c1;
413 incr_line(1);
414 c1 = c2;
415 c2 = c3;
416 c3 = c4;
417 c4 = c5;
418 c5 = c6;
419 c6 = c7;
421 } else {
422 if ((widthl == 1) && (widthc == 1)) {
423 wchar_t c1, c2, c3;
424 incr_line(1);
425 c1 = *line;
426 *line = '\b';
427 incr_line(1);
428 c2 = *line;
429 *line = c;
430 incr_line(1);
431 while (c1) {
432 c3 = *line;
433 *line = c1;
434 incr_line(1);
435 c1 = c2;
436 c2 = c3;
438 } else {
439 width = (widthc > widthl) ? widthc : widthl;
440 for (i = 0; i < width; i += wcscrwidth(c1)) {
441 c1 = *line;
442 incr_line(1);
444 wsinsert(line, width + (width - widthc + 1));
445 for (i = 0; i < width; i++) {
446 *line = '\b';
447 incr_line(1);
449 *line = c;
450 incr_line(1);
451 for (i = widthc; i < width; i++) {
452 *line = ' ';
453 incr_line(1);
457 lp = 0;
458 line = lbuff;
462 static void
463 store(int lno)
465 lno %= PL;
466 if (page[lno] != 0)
467 free((char *)page[lno]);
468 page[lno] = (wchar_t *)malloc((unsigned)(wcslen(lbuff) + 2)
469 * sizeof (wchar_t));
470 if (page[lno] == 0) {
471 /* fprintf(stderr, "%s: no storage\n", pgmname); */
472 exit(2);
474 (void) wcscpy(page[lno], lbuff);
477 static void
478 fetch(int lno)
480 wchar_t *p;
482 lno %= PL;
483 p = lbuff;
484 while (*p)
485 *p++ = '\0';
486 line = lbuff;
487 lp = 0;
488 if (page[lno])
489 (void) wcscpy(line, page[lno]);
492 static void
493 emit(wchar_t *s, int lineno)
495 static int cline = 0;
496 int ncp;
497 wchar_t *p;
498 char cshifted;
499 char chr[MB_LEN_MAX + 1];
501 int c;
502 static int gflag = 0;
504 if (*s) {
505 if (gflag) {
506 (void) putchar(SI);
507 gflag = 0;
509 while (cline < lineno - 1) {
510 (void) putchar('\n');
511 pcp = 0;
512 cline += 2;
514 if (cline != lineno) {
515 (void) putchar(ESC);
516 (void) putchar('9');
517 cline++;
519 if (pcp)
520 (void) putchar('\r');
521 pcp = 0;
522 p = s;
523 while (*p) {
524 ncp = pcp;
525 while (*p++ == ' ') {
526 if ((++ncp & 7) == 0 && !xflag) {
527 pcp = ncp;
528 (void) putchar('\t');
531 if (!*--p)
532 break;
533 while (pcp < ncp) {
534 (void) putchar(' ');
535 pcp++;
537 if (greeked) {
538 if (wctomb(chr, *p) == 1) {
539 if (gflag != (*chr & GREEK) &&
540 *p != '\b' &&
541 isascii(*chr ^ (gflag ^ GREEK)) &&
542 !iscntrl(*chr ^ (gflag ^ GREEK)) &&
543 !isspace(*chr ^ (gflag ^ GREEK))) {
544 if (gflag)
545 (void) putchar(SI);
546 else
547 (void) putchar(SO);
548 gflag ^= GREEK;
552 c = *p;
553 if (greeked) {
554 if (wctomb(chr, (wchar_t)c) == 1) {
555 cshifted = (*chr ^ GREEK);
556 if (isascii(cshifted) &&
557 !iscntrl(cshifted) &&
558 !isspace(cshifted))
559 (void) putchar(*chr & ~GREEK);
560 } else
561 (void) putwchar(c);
562 } else
563 (void) putwchar(c);
564 if (c == '\b') {
565 if (*(p-2) && *(p-2) == ESC) {
566 pcp++;
567 } else
568 pcp--;
569 } else {
570 pcp += wcscrwidth(c);
572 p++;
577 static void
578 incr(void)
580 store(ll++);
581 if (ll > llh)
582 llh = ll;
583 if (ll >= mustwr && page[ll%PL]) {
584 emit(page[ll%PL], ll - PL);
585 mustwr++;
586 free((char *)page[ll%PL]);
587 page[ll%PL] = 0;
589 fetch(ll);
592 static void
593 decr(void)
595 if (ll > mustwr - PL) {
596 store(ll--);
597 fetch(ll);
601 static void
602 wsinsert(wchar_t *s, int n)
604 wchar_t *p1, *p2;
607 p1 = s + wcslen(s);
608 p2 = p1 + n;
609 while (p1 >= s)
610 *p2-- = *p1--;
614 * incr_line - increments line pointer and checks for array out of bounds
615 * amt: assumed to be >= 1
616 * exit on error to avoid line pointer accessing out of the array
618 static void
619 incr_line(int amt)
621 if (line < lbuffend - amt + 1) {
622 line += amt;
623 } else {
624 (void) fprintf(stderr, gettext("col: Line too long.\n"));
625 exit(1);
630 static int
631 wcscrwidth(wchar_t wc)
633 int nc;
635 if (wc == 0) {
637 * if wc is a null character, needs to
638 * return 1 instead of 0.
640 return (1);
642 nc = wcwidth(wc);
643 if (nc > 0) {
644 return (nc);
645 } else {
646 return (0);