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 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 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 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 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 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 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 595 decr(void) 596 { 597 if (ll > mustwr - PL) { 598 store(ll--); 599 fetch(ll); 600 } 601 } 602 603 static void 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 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 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