1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29
30
31 #pragma ident "%Z%%M% %I% %E% SMI"
32 /*
33 * col - filter reverse carraige motions
34 *
35 */
36
37
38 #include <stdio.h>
39 #include <ctype.h>
40 #include <locale.h>
41 #include <limits.h>
42 #include <stdlib.h>
43 #include <wctype.h>
44
45 #define PL 256
46 #define ESC '\033'
47 #define RLF '\013'
48 #define SI '\017'
49 #define SO '\016'
50 #define GREEK 0200
51 #define LINELN 4096
52
53 wchar_t *page[PL];
54 wchar_t lbuff[LINELN], *line;
55 wchar_t *lbuffend = lbuff + LINELN - 1;
56 wchar_t ws_blank[2] = {' ', 0};
57 char esc_chars, underline, temp_off, smart;
58 int bflag, xflag, fflag, pflag;
59 int greeked;
60 int half;
61 int cp, lp;
62 int ll, llh, mustwr;
63 int pcp = 0;
64 char *pgmname;
65
66 #define USAGEMSG "usage:\tcol [-bfxp]\n"
67
68 static void outc(wchar_t);
69 static void store(int);
70 static void fetch(int);
71 static void emit(wchar_t *, int);
72 static void incr(void);
73 static void decr(void);
74 static void wsinsert(wchar_t *, int);
75 static void incr_line(int);
76 static int wcscrwidth(wchar_t);
77
78 int
main(int argc,char ** argv)79 main(int argc, char **argv)
80 {
81 int i, n;
82 int opt;
83 int greek;
84 int c;
85 wchar_t wc;
86 char byte;
87 static char fbuff[BUFSIZ];
88
89 setbuf(stdout, fbuff);
90 (void) setlocale(LC_ALL, "");
91 #if !defined(TEXT_DOMAIN)
92 #define TEXT_DOMAIN "SYS_TEST"
93 #endif
94 (void) textdomain(TEXT_DOMAIN);
95 pgmname = argv[0];
96
97 while ((opt = getopt(argc, argv, "bfxp")) != EOF)
98 switch (opt) {
99 case 'b':
100 bflag++;
101 break;
102 case 'x':
103 xflag++;
104 break;
105 case 'f':
106 fflag++;
107 break;
108 case 'p':
109 pflag++;
110 break;
111 case '?':
112 default:
113 (void) fprintf(stderr, gettext(USAGEMSG));
114 exit(2);
115 }
116
117 argc -= optind;
118 if (argc >= 1) {
119 (void) fprintf(stderr, gettext(USAGEMSG));
120 exit(2);
121 }
122
123 for (ll = 0; ll < PL; ll++)
124 page[ll] = 0;
125
126 smart = temp_off = underline = esc_chars = '\0';
127 cp = 0;
128 ll = 0;
129 greek = 0;
130 mustwr = PL;
131 line = lbuff;
132
133 while ((c = getwchar()) != EOF) {
134 if (underline && temp_off && c > ' ') {
135 outc(ESC);
136 if (*line) {
137 incr_line(1);
138 }
139 *line = 'X';
140 incr_line(1);
141 *line = temp_off = '\0';
142 }
143 if (c != '\b')
144 if (esc_chars)
145 esc_chars = '\0';
146 switch (c) {
147 case '\n':
148 if (underline && !temp_off) {
149 if (*line)
150 incr_line(1);
151 *line = ESC;
152 incr_line(1);
153 *line = 'Y';
154 incr_line(1);
155 *line = '\0';
156 temp_off = '1';
157 }
158 incr();
159 incr();
160 cp = 0;
161 continue;
162
163 case '\0':
164 continue;
165
166 case ESC:
167 c = getwchar();
168 switch (c) {
169 case '7': /* reverse full line feed */
170 decr();
171 decr();
172 break;
173
174 case '8': /* reverse half line feed */
175 if (fflag)
176 decr();
177 else {
178 if (--half < -1) {
179 decr();
180 decr();
181 half += 2;
182 }
183 }
184 break;
185
186 case '9': /* forward half line feed */
187 if (fflag)
188 incr();
189 else {
190 if (++half > 0) {
191 incr();
192 incr();
193 half -= 2;
194 }
195 }
196 break;
197
198 default:
199 if (pflag) { /* pass through esc */
200 outc(ESC);
201 incr_line(1);
202 *line = c;
203 incr_line(1);
204 *line = '\0';
205 esc_chars = 1;
206 if (c == 'X')
207 underline = 1;
208 if (c == 'Y' && underline)
209 underline = temp_off = '\0';
210 if (c == ']')
211 smart = 1;
212 if (c == '[')
213 smart = '\0';
214 }
215 break;
216 }
217 continue;
218
219 case SO:
220 greek = GREEK;
221 greeked++;
222 continue;
223
224 case SI:
225 greek = 0;
226 continue;
227
228 case RLF:
229 decr();
230 decr();
231 continue;
232
233 case '\r':
234 cp = 0;
235 continue;
236
237 case '\t':
238 cp = (cp + 8) & -8;
239 continue;
240
241 case '\b':
242 if (esc_chars) {
243 *line = '\b';
244 incr_line(1);
245 *line = '\0';
246 } else if (cp > 0)
247 cp--;
248 continue;
249
250 case ' ':
251 cp++;
252 continue;
253
254 default:
255 if (iswprint(c)) { /* if printable */
256 if (!greek) {
257 outc((wchar_t)c);
258 cp += wcscrwidth(c);
259 }
260 /*
261 * EUC (apply SO only when there can
262 * be corresponding character in CS1)
263 */
264 else if (iswascii(c)) {
265 byte = (c | greek);
266 n = mbtowc(&wc, &byte, 1);
267 if (!iswcntrl(c) && !iswspace(c) &&
268 n == 1) {
269 outc(wc);
270 cp += wcscrwidth(wc);
271 } else {
272 outc((wchar_t)c);
273 cp += wcscrwidth(c);
274 }
275 } else {
276 outc((wchar_t)c);
277 cp += wcscrwidth(c);
278 }
279
280 if ((cp + 1) > LINELN) {
281 (void) fprintf(stderr,
282 gettext("col: Line too long\n"));
283 exit(2);
284 }
285 }
286 continue;
287 }
288 }
289
290 for (i = 0; i < PL; i++)
291 if (page[(mustwr+i)%PL] != 0)
292 emit(page[(mustwr+i) % PL], mustwr+i-PL);
293 emit(ws_blank, (llh + 1) & -2);
294 return (0);
295 }
296
297 static void
outc(wchar_t c)298 outc(wchar_t c)
299 {
300 int n, i;
301 int width, widthl, widthc;
302 wchar_t *p1;
303 wchar_t c1;
304 char esc_chars = '\0';
305 if (lp > cp) {
306 line = lbuff;
307 lp = 0;
308 }
309
310 while (lp < cp) {
311 if (*line != '\b')
312 if (esc_chars)
313 esc_chars = '\0';
314 switch (*line) {
315 case ESC:
316 incr_line(1);
317 esc_chars = 1;
318 break;
319 case '\0':
320 *line = ' ';
321 lp++;
322 break;
323 case '\b':
324 /* if ( ! esc_chars ) */
325 lp--;
326 break;
327 default:
328 lp += wcscrwidth(*line);
329 }
330 incr_line(1);
331 }
332 while (*line == '\b') {
333 /*
334 * EUC (For a multi-column character, backspace characters
335 * are assumed to be used like "__^H^HXX", where "XX"
336 * represents a two-column character, and a backspace
337 * always goes back by one column.)
338 */
339 for (n = 0; *line == '\b'; incr_line(1)) {
340 n++;
341 lp--;
342 }
343 while (n > 0 && lp < cp) {
344 i = *line;
345 incr_line(1);
346 i = wcscrwidth(i);
347 n -= i;
348 lp += i;
349 }
350 }
351 while (*line == ESC)
352 incr_line(6);
353 widthc = wcscrwidth(c);
354 widthl = wcscrwidth(*line);
355 if (bflag || (*line == '\0') || *line == ' ') {
356 if (*line == '\0' || widthl == widthc) {
357 *line = c;
358 } else if (widthl > widthc) {
359 n = widthl - widthc;
360 wsinsert(line, n);
361 *line = c;
362 incr_line(1);
363 for (i = 0; i < n; i++) {
364 *line = ' ';
365 incr_line(1);
366 }
367 line = lbuff;
368 lp = 0;
369 } else {
370 n = widthc - widthl;
371 if (line < lbuffend) {
372 for (p1 = line+1; n > 0 && p1 < lbuffend;
373 n -= wcscrwidth(i)) {
374 i = *p1++;
375 }
376 *line = c;
377 if (p1 < lbuffend) {
378 (void) wcscpy(line+1, p1);
379 } else {
380 (void) fprintf(stderr,
381 gettext("col: Line too long.\n"));
382 exit(1);
383 }
384 } else {
385 (void) fprintf(stderr,
386 gettext("col: Line too long.\n"));
387 exit(1);
388 }
389 }
390 } else {
391 if (smart && (widthl == 1) && (widthc == 1)) {
392 wchar_t c1, c2, c3, c4, c5, c6, c7;
393 incr_line(1);
394 c1 = *line;
395 *line = ESC;
396 incr_line(1);
397 c2 = *line;
398 *line = '[';
399 incr_line(1);
400 c3 = *line;
401 *line = '\b';
402 incr_line(1);
403 c4 = *line;
404 *line = ESC;
405 incr_line(1);
406 c5 = *line;
407 *line = ']';
408 incr_line(1);
409 c6 = *line;
410 *line = c;
411 incr_line(1);
412 while (c1) {
413 c7 = *line;
414 *line = c1;
415 incr_line(1);
416 c1 = c2;
417 c2 = c3;
418 c3 = c4;
419 c4 = c5;
420 c5 = c6;
421 c6 = c7;
422 }
423 } else {
424 if ((widthl == 1) && (widthc == 1)) {
425 wchar_t c1, c2, c3;
426 incr_line(1);
427 c1 = *line;
428 *line = '\b';
429 incr_line(1);
430 c2 = *line;
431 *line = c;
432 incr_line(1);
433 while (c1) {
434 c3 = *line;
435 *line = c1;
436 incr_line(1);
437 c1 = c2;
438 c2 = c3;
439 }
440 } else {
441 width = (widthc > widthl) ? widthc : widthl;
442 for (i = 0; i < width; i += wcscrwidth(c1)) {
443 c1 = *line;
444 incr_line(1);
445 }
446 wsinsert(line, width + (width - widthc + 1));
447 for (i = 0; i < width; i++) {
448 *line = '\b';
449 incr_line(1);
450 }
451 *line = c;
452 incr_line(1);
453 for (i = widthc; i < width; i++) {
454 *line = ' ';
455 incr_line(1);
456 }
457 }
458 }
459 lp = 0;
460 line = lbuff;
461 }
462 }
463
464 static void
store(int lno)465 store(int lno)
466 {
467 lno %= PL;
468 if (page[lno] != 0)
469 free((char *)page[lno]);
470 page[lno] = (wchar_t *)malloc((unsigned)(wcslen(lbuff) + 2)
471 * sizeof (wchar_t));
472 if (page[lno] == 0) {
473 /* fprintf(stderr, "%s: no storage\n", pgmname); */
474 exit(2);
475 }
476 (void) wcscpy(page[lno], lbuff);
477 }
478
479 static void
fetch(int lno)480 fetch(int lno)
481 {
482 wchar_t *p;
483
484 lno %= PL;
485 p = lbuff;
486 while (*p)
487 *p++ = '\0';
488 line = lbuff;
489 lp = 0;
490 if (page[lno])
491 (void) wcscpy(line, page[lno]);
492 }
493
494 static void
emit(wchar_t * s,int lineno)495 emit(wchar_t *s, int lineno)
496 {
497 static int cline = 0;
498 int ncp;
499 wchar_t *p;
500 char cshifted;
501 char chr[MB_LEN_MAX + 1];
502
503 int c;
504 static int gflag = 0;
505
506 if (*s) {
507 if (gflag) {
508 (void) putchar(SI);
509 gflag = 0;
510 }
511 while (cline < lineno - 1) {
512 (void) putchar('\n');
513 pcp = 0;
514 cline += 2;
515 }
516 if (cline != lineno) {
517 (void) putchar(ESC);
518 (void) putchar('9');
519 cline++;
520 }
521 if (pcp)
522 (void) putchar('\r');
523 pcp = 0;
524 p = s;
525 while (*p) {
526 ncp = pcp;
527 while (*p++ == ' ') {
528 if ((++ncp & 7) == 0 && !xflag) {
529 pcp = ncp;
530 (void) putchar('\t');
531 }
532 }
533 if (!*--p)
534 break;
535 while (pcp < ncp) {
536 (void) putchar(' ');
537 pcp++;
538 }
539 if (greeked) {
540 if (wctomb(chr, *p) == 1) {
541 if (gflag != (*chr & GREEK) &&
542 *p != '\b' &&
543 isascii(*chr ^ (gflag ^ GREEK)) &&
544 !iscntrl(*chr ^ (gflag ^ GREEK)) &&
545 !isspace(*chr ^ (gflag ^ GREEK))) {
546 if (gflag)
547 (void) putchar(SI);
548 else
549 (void) putchar(SO);
550 gflag ^= GREEK;
551 }
552 }
553 }
554 c = *p;
555 if (greeked) {
556 if (wctomb(chr, (wchar_t)c) == 1) {
557 cshifted = (*chr ^ GREEK);
558 if (isascii(cshifted) &&
559 !iscntrl(cshifted) &&
560 !isspace(cshifted))
561 (void) putchar(*chr & ~GREEK);
562 } else
563 (void) putwchar(c);
564 } else
565 (void) putwchar(c);
566 if (c == '\b') {
567 if (*(p-2) && *(p-2) == ESC) {
568 pcp++;
569 } else
570 pcp--;
571 } else {
572 pcp += wcscrwidth(c);
573 }
574 p++;
575 }
576 }
577 }
578
579 static void
incr(void)580 incr(void)
581 {
582 store(ll++);
583 if (ll > llh)
584 llh = ll;
585 if (ll >= mustwr && page[ll%PL]) {
586 emit(page[ll%PL], ll - PL);
587 mustwr++;
588 free((char *)page[ll%PL]);
589 page[ll%PL] = 0;
590 }
591 fetch(ll);
592 }
593
594 static void
decr(void)595 decr(void)
596 {
597 if (ll > mustwr - PL) {
598 store(ll--);
599 fetch(ll);
600 }
601 }
602
603 static void
wsinsert(wchar_t * s,int n)604 wsinsert(wchar_t *s, int n)
605 {
606 wchar_t *p1, *p2;
607
608
609 p1 = s + wcslen(s);
610 p2 = p1 + n;
611 while (p1 >= s)
612 *p2-- = *p1--;
613 }
614
615 /*
616 * incr_line - increments line pointer and checks for array out of bounds
617 * amt: assumed to be >= 1
618 * exit on error to avoid line pointer accessing out of the array
619 */
620 static void
incr_line(int amt)621 incr_line(int amt)
622 {
623 if (line < lbuffend - amt + 1) {
624 line += amt;
625 } else {
626 (void) fprintf(stderr, gettext("col: Line too long.\n"));
627 exit(1);
628 }
629 }
630
631
632 static int
wcscrwidth(wchar_t wc)633 wcscrwidth(wchar_t wc)
634 {
635 int nc;
636
637 if (wc == 0) {
638 /*
639 * if wc is a null character, needs to
640 * return 1 instead of 0.
641 */
642 return (1);
643 }
644 nc = wcwidth(wc);
645 if (nc > 0) {
646 return (nc);
647 } else {
648 return (0);
649 }
650 }
651