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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1998, 2001 by Sun Microsystems, Inc. 24 * All Rights Reserved 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 ws_blank[2] = {' ', 0}; 56 char esc_chars, underline, temp_off, smart; 57 int bflag, xflag, fflag, pflag; 58 int greeked; 59 int half; 60 int cp, lp; 61 int ll, llh, mustwr; 62 int pcp = 0; 63 char *pgmname; 64 65 #define USAGEMSG "usage:\tcol [-bfxp]\n" 66 67 static void outc(wchar_t); 68 static void store(int); 69 static void fetch(int); 70 static void emit(wchar_t *, int); 71 static void incr(void); 72 static void decr(void); 73 static void wsinsert(wchar_t *, 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) line++; 135 *line++ = 'X'; 136 *line = temp_off = '\0'; 137 } 138 if (c != '\b') 139 if (esc_chars) 140 esc_chars = '\0'; 141 switch (c) { 142 case '\n': 143 if (underline && !temp_off) { 144 if (*line) 145 line++; 146 *line++ = ESC; 147 *line++ = 'Y'; 148 *line = '\0'; 149 temp_off = '1'; 150 } 151 incr(); 152 incr(); 153 cp = 0; 154 continue; 155 156 case '\0': 157 continue; 158 159 case ESC: 160 c = getwchar(); 161 switch (c) { 162 case '7': /* reverse full line feed */ 163 decr(); 164 decr(); 165 break; 166 167 case '8': /* reverse half line feed */ 168 if (fflag) 169 decr(); 170 else { 171 if (--half < -1) { 172 decr(); 173 decr(); 174 half += 2; 175 } 176 } 177 break; 178 179 case '9': /* forward half line feed */ 180 if (fflag) 181 incr(); 182 else { 183 if (++half > 0) { 184 incr(); 185 incr(); 186 half -= 2; 187 } 188 } 189 break; 190 191 default: 192 if (pflag) { /* pass through esc */ 193 outc(ESC); 194 line++; 195 *line = c; 196 line++; 197 *line = '\0'; 198 esc_chars = 1; 199 if (c == 'X') 200 underline = 1; 201 if (c == 'Y' && underline) 202 underline = temp_off = '\0'; 203 if (c == ']') 204 smart = 1; 205 if (c == '[') 206 smart = '\0'; 207 } 208 break; 209 } 210 continue; 211 212 case SO: 213 greek = GREEK; 214 greeked++; 215 continue; 216 217 case SI: 218 greek = 0; 219 continue; 220 221 case RLF: 222 decr(); 223 decr(); 224 continue; 225 226 case '\r': 227 cp = 0; 228 continue; 229 230 case '\t': 231 cp = (cp + 8) & -8; 232 continue; 233 234 case '\b': 235 if (esc_chars) { 236 *line++ = '\b'; 237 *line = '\0'; 238 } else if (cp > 0) 239 cp--; 240 continue; 241 242 case ' ': 243 cp++; 244 continue; 245 246 default: 247 if (iswprint(c)) { /* if printable */ 248 if (!greek) { 249 outc((wchar_t)c); 250 cp += wcscrwidth(c); 251 } 252 /* 253 * EUC (apply SO only when there can 254 * be corresponding character in CS1) 255 */ 256 else if (iswascii(c)) { 257 byte = (c | greek); 258 n = mbtowc(&wc, &byte, 1); 259 if (!iswcntrl(c) && !iswspace(c) && 260 n == 1) { 261 outc(wc); 262 cp += wcscrwidth(wc); 263 } else { 264 outc((wchar_t)c); 265 cp += wcscrwidth(c); 266 } 267 } else { 268 outc((wchar_t)c); 269 cp += wcscrwidth(c); 270 } 271 272 if ((cp + 1) > LINELN) { 273 (void) fprintf(stderr, 274 gettext("col: Line too long\n")); 275 exit(2); 276 } 277 } 278 continue; 279 } 280 } 281 282 for (i = 0; i < PL; i++) 283 if (page[(mustwr+i)%PL] != 0) 284 emit(page[(mustwr+i) % PL], mustwr+i-PL); 285 emit(ws_blank, (llh + 1) & -2); 286 return (0); 287 } 288 289 static void 290 outc(wchar_t c) 291 { 292 int n, i; 293 int width, widthl, widthc; 294 wchar_t *p1; 295 wchar_t c1; 296 char esc_chars = '\0'; 297 if (lp > cp) { 298 line = lbuff; 299 lp = 0; 300 } 301 302 while (lp < cp) { 303 if (*line != '\b') 304 if (esc_chars) 305 esc_chars = '\0'; 306 switch (*line) { 307 case ESC: 308 line++; 309 esc_chars = 1; 310 break; 311 case '\0': 312 *line = ' '; 313 lp++; 314 break; 315 case '\b': 316 /* if ( ! esc_chars ) */ 317 lp--; 318 break; 319 default: 320 lp += wcscrwidth(*line); 321 } 322 line++; 323 } 324 while (*line == '\b') { 325 /* 326 * EUC (For a multi-column character, backspace characters 327 * are assumed to be used like "__^H^HXX", where "XX" 328 * represents a two-column character, and a backspace 329 * always goes back by one column.) 330 */ 331 for (n = 0; *line == '\b'; line++) { 332 n++; 333 lp--; 334 } 335 while (n > 0 && lp < cp) { 336 i = *line++; 337 i = wcscrwidth(i); 338 n -= i; 339 lp += i; 340 } 341 } 342 while (*line == ESC) 343 line += 6; 344 widthc = wcscrwidth(c); 345 widthl = wcscrwidth(*line); 346 if (bflag || (*line == '\0') || *line == ' ') { 347 if (*line == '\0' || widthl == widthc) { 348 *line = c; 349 } else if (widthl > widthc) { 350 n = widthl - widthc; 351 wsinsert(line, n); 352 *line++ = c; 353 for (i = 0; i < n; i++) 354 *line++ = ' '; 355 line = lbuff; 356 lp = 0; 357 } else { 358 n = widthc - widthl; 359 for (p1 = line+1; n > 0; n -= wcscrwidth(i)) 360 i = *p1++; 361 *line = c; 362 (void) wcscpy(line+1, p1); 363 364 } 365 } else { 366 if (smart && (widthl == 1) && (widthc == 1)) { 367 wchar_t c1, c2, c3, c4, c5, c6, c7; 368 c1 = *++line; 369 *line++ = ESC; 370 c2 = *line; 371 *line++ = '['; 372 c3 = *line; 373 *line++ = '\b'; 374 c4 = *line; 375 *line++ = ESC; 376 c5 = *line; 377 *line++ = ']'; 378 c6 = *line; 379 *line++ = c; 380 while (c1) { 381 c7 = *line; 382 *line++ = c1; 383 c1 = c2; 384 c2 = c3; 385 c3 = c4; 386 c4 = c5; 387 c5 = c6; 388 c6 = c7; 389 } 390 } else { 391 if ((widthl == 1) && (widthc == 1)) { 392 wchar_t c1, c2, c3; 393 c1 = *++line; 394 *line++ = '\b'; 395 c2 = *line; 396 *line++ = c; 397 while (c1) { 398 c3 = *line; 399 *line++ = c1; 400 c1 = c2; 401 c2 = c3; 402 } 403 } else { 404 width = (widthc > widthl) ? widthc : widthl; 405 for (i = 0; i < width; i += wcscrwidth(c1)) 406 c1 = *line++; 407 wsinsert(line, width + (width - widthc + 1)); 408 for (i = 0; i < width; i++) 409 *line++ = '\b'; 410 *line++ = c; 411 for (i = widthc; i < width; i++) 412 *line++ = ' '; 413 } 414 } 415 lp = 0; 416 line = lbuff; 417 } 418 } 419 420 static void 421 store(int lno) 422 { 423 lno %= PL; 424 if (page[lno] != 0) 425 free((char *)page[lno]); 426 page[lno] = (wchar_t *)malloc((unsigned)(wcslen(lbuff) + 2) 427 * sizeof (wchar_t)); 428 if (page[lno] == 0) { 429 /* fprintf(stderr, "%s: no storage\n", pgmname); */ 430 exit(2); 431 } 432 (void) wcscpy(page[lno], lbuff); 433 } 434 435 static void 436 fetch(int lno) 437 { 438 wchar_t *p; 439 440 lno %= PL; 441 p = lbuff; 442 while (*p) 443 *p++ = '\0'; 444 line = lbuff; 445 lp = 0; 446 if (page[lno]) 447 (void) wcscpy(line, page[lno]); 448 } 449 450 static void 451 emit(wchar_t *s, int lineno) 452 { 453 static int cline = 0; 454 int ncp; 455 wchar_t *p; 456 char cshifted; 457 char chr[MB_LEN_MAX + 1]; 458 459 int c; 460 static int gflag = 0; 461 462 if (*s) { 463 if (gflag) { 464 (void) putchar(SI); 465 gflag = 0; 466 } 467 while (cline < lineno - 1) { 468 (void) putchar('\n'); 469 pcp = 0; 470 cline += 2; 471 } 472 if (cline != lineno) { 473 (void) putchar(ESC); 474 (void) putchar('9'); 475 cline++; 476 } 477 if (pcp) 478 (void) putchar('\r'); 479 pcp = 0; 480 p = s; 481 while (*p) { 482 ncp = pcp; 483 while (*p++ == ' ') { 484 if ((++ncp & 7) == 0 && !xflag) { 485 pcp = ncp; 486 (void) putchar('\t'); 487 } 488 } 489 if (!*--p) 490 break; 491 while (pcp < ncp) { 492 (void) putchar(' '); 493 pcp++; 494 } 495 if (greeked) { 496 if (wctomb(chr, *p) == 1) { 497 if (gflag != (*chr & GREEK) && 498 *p != '\b' && 499 isascii(*chr ^ (gflag ^ GREEK)) && 500 !iscntrl(*chr ^ (gflag ^ GREEK)) && 501 !isspace(*chr ^ (gflag ^ GREEK))) { 502 if (gflag) 503 (void) putchar(SI); 504 else 505 (void) putchar(SO); 506 gflag ^= GREEK; 507 } 508 } 509 } 510 c = *p; 511 if (greeked) { 512 if (wctomb(chr, (wchar_t)c) == 1) { 513 cshifted = (*chr ^ GREEK); 514 if (isascii(cshifted) && 515 !iscntrl(cshifted) && 516 !isspace(cshifted)) 517 (void) putchar(*chr & ~GREEK); 518 } else 519 (void) putwchar(c); 520 } else 521 (void) putwchar(c); 522 if (c == '\b') { 523 if (*(p-2) && *(p-2) == ESC) { 524 pcp++; 525 } else 526 pcp--; 527 } else { 528 pcp += wcscrwidth(c); 529 } 530 p++; 531 } 532 } 533 } 534 535 static void 536 incr(void) 537 { 538 store(ll++); 539 if (ll > llh) 540 llh = ll; 541 if (ll >= mustwr && page[ll%PL]) { 542 emit(page[ll%PL], ll - PL); 543 mustwr++; 544 free((char *)page[ll%PL]); 545 page[ll%PL] = 0; 546 } 547 fetch(ll); 548 } 549 550 static void 551 decr(void) 552 { 553 if (ll > mustwr - PL) { 554 store(ll--); 555 fetch(ll); 556 } 557 } 558 559 static void 560 wsinsert(wchar_t *s, int n) 561 { 562 wchar_t *p1, *p2; 563 564 565 p1 = s + wcslen(s); 566 p2 = p1 + n; 567 while (p1 >= s) 568 *p2-- = *p1--; 569 } 570 571 static int 572 wcscrwidth(wchar_t wc) 573 { 574 int nc; 575 576 if (wc == 0) { 577 /* 578 * if wc is a null character, needs to 579 * return 1 instead of 0. 580 */ 581 return (1); 582 } 583 nc = wcwidth(wc); 584 if (nc > 0) { 585 return (nc); 586 } else { 587 return (0); 588 } 589 } 590