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 * col - filter reverse carraige motions 32 * 33 */ 34 35 36 #include <stdio.h> 37 #include <ctype.h> 38 #include <locale.h> 39 #include <limits.h> 40 #include <stdlib.h> 41 #include <wctype.h> 42 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 50 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; 63 64 #define USAGEMSG "usage:\tcol [-bfxp]\n" 65 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); 75 76 int 77 main(int argc, char **argv) 78 { 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]; 86 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]; 94 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); 113 } 114 115 argc -= optind; 116 if (argc >= 1) { 117 (void) fprintf(stderr, gettext(USAGEMSG)); 118 exit(2); 119 } 120 121 for (ll = 0; ll < PL; ll++) 122 page[ll] = 0; 123 124 smart = temp_off = underline = esc_chars = '\0'; 125 cp = 0; 126 ll = 0; 127 greek = 0; 128 mustwr = PL; 129 line = lbuff; 130 131 while ((c = getwchar()) != EOF) { 132 if (underline && temp_off && c > ' ') { 133 outc(ESC); 134 if (*line) { 135 incr_line(1); 136 } 137 *line = 'X'; 138 incr_line(1); 139 *line = temp_off = '\0'; 140 } 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'; 155 } 156 incr(); 157 incr(); 158 cp = 0; 159 continue; 160 161 case '\0': 162 continue; 163 164 case ESC: 165 c = getwchar(); 166 switch (c) { 167 case '7': /* reverse full line feed */ 168 decr(); 169 decr(); 170 break; 171 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; 180 } 181 } 182 break; 183 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; 192 } 193 } 194 break; 195 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'; 212 } 213 break; 214 } 215 continue; 216 217 case SO: 218 greek = GREEK; 219 greeked++; 220 continue; 221 222 case SI: 223 greek = 0; 224 continue; 225 226 case RLF: 227 decr(); 228 decr(); 229 continue; 230 231 case '\r': 232 cp = 0; 233 continue; 234 235 case '\t': 236 cp = (cp + 8) & -8; 237 continue; 238 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; 247 248 case ' ': 249 cp++; 250 continue; 251 252 default: 253 if (iswprint(c)) { /* if printable */ 254 if (!greek) { 255 outc((wchar_t)c); 256 cp += wcscrwidth(c); 257 } 258 /* 259 * EUC (apply SO only when there can 260 * be corresponding character in CS1) 261 */ 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); 272 } 273 } else { 274 outc((wchar_t)c); 275 cp += wcscrwidth(c); 276 } 277 278 if ((cp + 1) > LINELN) { 279 (void) fprintf(stderr, 280 gettext("col: Line too long\n")); 281 exit(2); 282 } 283 } 284 continue; 285 } 286 } 287 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); 293 } 294 295 static void 296 outc(wchar_t c) 297 { 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; 306 } 307 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); 327 } 328 incr_line(1); 329 } 330 while (*line == '\b') { 331 /* 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.) 336 */ 337 for (n = 0; *line == '\b'; incr_line(1)) { 338 n++; 339 lp--; 340 } 341 while (n > 0 && lp < cp) { 342 i = *line; 343 incr_line(1); 344 i = wcscrwidth(i); 345 n -= i; 346 lp += i; 347 } 348 } 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); 364 } 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++; 373 } 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); 381 } 382 } else { 383 (void) fprintf(stderr, 384 gettext("col: Line too long.\n")); 385 exit(1); 386 } 387 } 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; 420 } 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; 437 } 438 } else { 439 width = (widthc > widthl) ? widthc : widthl; 440 for (i = 0; i < width; i += wcscrwidth(c1)) { 441 c1 = *line; 442 incr_line(1); 443 } 444 wsinsert(line, width + (width - widthc + 1)); 445 for (i = 0; i < width; i++) { 446 *line = '\b'; 447 incr_line(1); 448 } 449 *line = c; 450 incr_line(1); 451 for (i = widthc; i < width; i++) { 452 *line = ' '; 453 incr_line(1); 454 } 455 } 456 } 457 lp = 0; 458 line = lbuff; 459 } 460 } 461 462 static void 463 store(int lno) 464 { 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); 473 } 474 (void) wcscpy(page[lno], lbuff); 475 } 476 477 static void 478 fetch(int lno) 479 { 480 wchar_t *p; 481 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]); 490 } 491 492 static void 493 emit(wchar_t *s, int lineno) 494 { 495 static int cline = 0; 496 int ncp; 497 wchar_t *p; 498 char cshifted; 499 char chr[MB_LEN_MAX + 1]; 500 501 int c; 502 static int gflag = 0; 503 504 if (*s) { 505 if (gflag) { 506 (void) putchar(SI); 507 gflag = 0; 508 } 509 while (cline < lineno - 1) { 510 (void) putchar('\n'); 511 pcp = 0; 512 cline += 2; 513 } 514 if (cline != lineno) { 515 (void) putchar(ESC); 516 (void) putchar('9'); 517 cline++; 518 } 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'); 529 } 530 } 531 if (!*--p) 532 break; 533 while (pcp < ncp) { 534 (void) putchar(' '); 535 pcp++; 536 } 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; 549 } 550 } 551 } 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); 571 } 572 p++; 573 } 574 } 575 } 576 577 static void 578 incr(void) 579 { 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; 588 } 589 fetch(ll); 590 } 591 592 static void 593 decr(void) 594 { 595 if (ll > mustwr - PL) { 596 store(ll--); 597 fetch(ll); 598 } 599 } 600 601 static void 602 wsinsert(wchar_t *s, int n) 603 { 604 wchar_t *p1, *p2; 605 606 607 p1 = s + wcslen(s); 608 p2 = p1 + n; 609 while (p1 >= s) 610 *p2-- = *p1--; 611 } 612 613 /* 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 617 */ 618 static void 619 incr_line(int amt) 620 { 621 if (line < lbuffend - amt + 1) { 622 line += amt; 623 } else { 624 (void) fprintf(stderr, gettext("col: Line too long.\n")); 625 exit(1); 626 } 627 } 628 629 630 static int 631 wcscrwidth(wchar_t wc) 632 { 633 int nc; 634 635 if (wc == 0) { 636 /* 637 * if wc is a null character, needs to 638 * return 1 instead of 0. 639 */ 640 return (1); 641 } 642 nc = wcwidth(wc); 643 if (nc > 0) { 644 return (nc); 645 } else { 646 return (0); 647 } 648 } 649