print.c revision 4e40088c
1/* $XTermId: print.c,v 1.81 2009/01/09 01:43:01 tom Exp $ */ 2 3/* 4 * $XFree86: xc/programs/xterm/print.c,v 1.24 2006/06/19 00:36:51 dickey Exp $ 5 */ 6 7/************************************************************ 8 9Copyright 1997-2007,2009 by Thomas E. Dickey 10 11 All Rights Reserved 12 13Permission is hereby granted, free of charge, to any person obtaining a 14copy of this software and associated documentation files (the 15"Software"), to deal in the Software without restriction, including 16without limitation the rights to use, copy, modify, merge, publish, 17distribute, sublicense, and/or sell copies of the Software, and to 18permit persons to whom the Software is furnished to do so, subject to 19the following conditions: 20 21The above copyright notice and this permission notice shall be included 22in all copies or substantial portions of the Software. 23 24THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 25OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 27IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 28CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 29TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 30SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 32Except as contained in this notice, the name(s) of the above copyright 33holders shall not be used in advertising or otherwise to promote the 34sale, use or other dealings in this Software without prior written 35authorization. 36 37********************************************************/ 38 39#include <xterm.h> 40#include <data.h> 41#include <menu.h> 42#include <error.h> 43 44#include <stdio.h> 45 46#undef CTRL 47#define CTRL(c) ((c) & 0x1f) 48 49#define SHIFT_IN '\017' 50#define SHIFT_OUT '\016' 51 52#define CSET_IN 'A' 53#define CSET_OUT '0' 54 55#define isForm(c) ((c) == '\r' || (c) == '\n' || (c) == '\f') 56#define Strlen(a) strlen((char *)a) 57#define Strcmp(a,b) strcmp((char *)a,(char *)b) 58#define Strncmp(a,b,c) strncmp((char *)a,(char *)b,c) 59 60#ifdef VMS 61#define VMS_TEMP_PRINT_FILE "sys$scratch:xterm_print.txt" 62#endif 63 64static void charToPrinter(unsigned chr); 65static void printLine(int row, unsigned chr); 66static void send_CharSet(int row); 67static void send_SGR(unsigned attr, unsigned fg, unsigned bg); 68static void stringToPrinter(char *str); 69 70static FILE *Printer; 71static pid_t Printer_pid; 72static int initialized; 73 74static void 75closePrinter(void) 76{ 77 if (xtermHasPrinter() != 0) { 78#ifdef VMS 79 TScreen *screen = TScreenOf(term); 80 81 char pcommand[256]; 82 (void) sprintf(pcommand, "%s %s;", 83 screen->printer_command, 84 VMS_TEMP_PRINT_FILE); 85#endif 86 87 if (Printer != 0) { 88 fclose(Printer); 89 TRACE(("closed printer, waiting...\n")); 90#ifdef VMS /* This is a quick hack, really should use 91 spawn and check status or system services 92 and go straight to the queue */ 93 (void) system(pcommand); 94#else /* VMS */ 95 while (nonblocking_wait() > 0) 96#endif /* VMS */ 97 ; 98 Printer = 0; 99 initialized = 0; 100 TRACE(("closed printer\n")); 101 } 102 } 103} 104 105static void 106printCursorLine(void) 107{ 108 TScreen *screen = TScreenOf(term); 109 110 TRACE(("printCursorLine\n")); 111 printLine(screen->cur_row, '\n'); 112} 113 114#define NO_COLOR ((unsigned)-1) 115 116/* 117 * DEC's manual doesn't document whether trailing blanks are removed, or what 118 * happens with a line that is entirely blank. This function prints the 119 * characters that xterm would allow as a selection (which may include blanks). 120 */ 121static void 122printLine(int row, unsigned chr) 123{ 124 TScreen *screen = TScreenOf(term); 125 int inx = ROW2INX(screen, row); 126 Char *c = SCRN_BUF_CHARS(screen, inx); 127 Char *a = SCRN_BUF_ATTRS(screen, inx); 128 Char attr = 0; 129 unsigned ch; 130 int last = MaxCols(screen); 131 int col; 132#if OPT_ISO_COLORS && OPT_PRINT_COLORS 133#if OPT_EXT_COLORS 134 Char *fbf = 0; 135 Char *fbb = 0; 136#define ColorOf(col) (unsigned)((fbf[col] << 8) | fbb[col]) 137#else 138 Char *fb = 0; 139#define ColorOf(col) (fb[col]) 140#endif 141#endif 142 unsigned fg = NO_COLOR, last_fg = NO_COLOR; 143 unsigned bg = NO_COLOR, last_bg = NO_COLOR; 144 int cs = CSET_IN; 145 int last_cs = CSET_IN; 146 147 TRACE(("printLine(row=%d/%d, top=%d:%d, chr=%d):%s\n", 148 row, ROW2INX(screen, row), screen->topline, screen->max_row, chr, 149 visibleChars(PAIRED_CHARS(c, 150 (screen->utf8_mode 151 ? SCRN_BUF_WIDEC(screen, inx) 152 : 0)), 153 (unsigned) last))); 154 155 if_OPT_EXT_COLORS(screen, { 156 fbf = SCRN_BUF_FGRND(screen, inx); 157 fbb = SCRN_BUF_BGRND(screen, inx); 158 }); 159 if_OPT_ISO_TRADITIONAL_COLORS(screen, { 160 fb = SCRN_BUF_COLOR(screen, inx); 161 }); 162 while (last > 0) { 163 if ((a[last - 1] & CHARDRAWN) == 0) 164 last--; 165 else 166 break; 167 } 168 if (last) { 169 if (screen->print_attributes) { 170 send_CharSet(row); 171 send_SGR(0, NO_COLOR, NO_COLOR); 172 } 173 for (col = 0; col < last; col++) { 174 ch = c[col]; 175 if_OPT_WIDE_CHARS(screen, { 176 ch = XTERM_CELL(row, col); 177 }); 178#if OPT_PRINT_COLORS 179 if (screen->colorMode) { 180 if (screen->print_attributes > 1) { 181 fg = (a[col] & FG_COLOR) 182 ? extract_fg(term, ColorOf(col), a[col]) 183 : NO_COLOR; 184 bg = (a[col] & BG_COLOR) 185 ? extract_bg(term, ColorOf(col), a[col]) 186 : NO_COLOR; 187 } 188 } 189#endif 190 if ((((a[col] & SGR_MASK) != attr) 191#if OPT_PRINT_COLORS 192 || (last_fg != fg) || (last_bg != bg) 193#endif 194 ) 195 && ch) { 196 attr = CharOf(a[col] & SGR_MASK); 197 last_fg = fg; 198 last_bg = bg; 199 if (screen->print_attributes) 200 send_SGR(attr, fg, bg); 201 } 202 203 if (ch == 0) 204 ch = ' '; 205 206#if OPT_WIDE_CHARS 207 if (screen->utf8_mode) 208 cs = CSET_IN; 209 else 210#endif 211 cs = (ch >= ' ' && ch != ANSI_DEL) ? CSET_IN : CSET_OUT; 212 if (last_cs != cs) { 213 if (screen->print_attributes) { 214 charToPrinter((unsigned) ((cs == CSET_OUT) 215 ? SHIFT_OUT 216 : SHIFT_IN)); 217 } 218 last_cs = cs; 219 } 220 221 /* FIXME: we shouldn't have to map back from the 222 * alternate character set, except that the 223 * corresponding charset information is not encoded 224 * into the CSETS array. 225 */ 226 charToPrinter(((cs == CSET_OUT) 227 ? (ch == ANSI_DEL ? 0x5f : (ch + 0x5f)) 228 : ch)); 229 if_OPT_WIDE_CHARS(screen, { 230 int off; 231 for (off = OFF_FINAL; off < MAX_PTRS; off += 2) { 232 if ((ch = XTERM_CELLC(row, col, off)) == 0) 233 break; 234 charToPrinter(ch); 235 } 236 }); 237 } 238 if (screen->print_attributes) { 239 send_SGR(0, NO_COLOR, NO_COLOR); 240 if (cs != CSET_IN) 241 charToPrinter(SHIFT_IN); 242 } 243 } 244 if (screen->print_attributes) 245 charToPrinter('\r'); 246 charToPrinter(chr); 247} 248 249void 250xtermPrintScreen(Bool use_DECPEX) 251{ 252 if (XtIsRealized((Widget) term)) { 253 TScreen *screen = TScreenOf(term); 254 Bool extent = (use_DECPEX && screen->printer_extent); 255 int top = extent ? 0 : screen->top_marg; 256 int bot = extent ? screen->max_row : screen->bot_marg; 257 int was_open = initialized; 258 259 TRACE(("xtermPrintScreen, rows %d..%d\n", top, bot)); 260 261 while (top <= bot) 262 printLine(top++, '\n'); 263 if (screen->printer_formfeed) 264 charToPrinter('\f'); 265 266 if (!was_open || screen->printer_autoclose) { 267 closePrinter(); 268 } 269 } else { 270 Bell(XkbBI_MinorError, 0); 271 } 272} 273 274/* 275 * If the alternate screen is active, we'll print only that. Otherwise, print 276 * the normal screen plus all scrolled-back lines. The distinction is made 277 * because the normal screen's buffer is part of the overall scrollback buffer. 278 */ 279static void 280xtermPrintEverything(void) 281{ 282 TScreen *screen = TScreenOf(term); 283 int top = 0; 284 int bot = screen->max_row; 285 int was_open = initialized; 286 287 if (!screen->altbuf) 288 top = -screen->savedlines; 289 290 TRACE(("xtermPrintEverything, rows %d..%d\n", top, bot)); 291 while (top <= bot) 292 printLine(top++, '\n'); 293 if (screen->printer_formfeed) 294 charToPrinter('\f'); 295 296 if (!was_open || screen->printer_autoclose) { 297 closePrinter(); 298 } 299} 300 301static void 302send_CharSet(int row) 303{ 304#if OPT_DEC_CHRSET 305 TScreen *screen = TScreenOf(term); 306 char *msg = 0; 307 308 switch (SCRN_BUF_CSETS(screen, row)[0]) { 309 case CSET_SWL: 310 msg = "\033#5"; 311 break; 312 case CSET_DHL_TOP: 313 msg = "\033#3"; 314 break; 315 case CSET_DHL_BOT: 316 msg = "\033#4"; 317 break; 318 case CSET_DWL: 319 msg = "\033#6"; 320 break; 321 } 322 if (msg != 0) 323 stringToPrinter(msg); 324#else 325 (void) row; 326#endif /* OPT_DEC_CHRSET */ 327} 328 329static void 330send_SGR(unsigned attr, unsigned fg, unsigned bg) 331{ 332 char msg[80]; 333 strcpy(msg, "\033[0"); 334 if (attr & BOLD) 335 strcat(msg, ";1"); 336 if (attr & UNDERLINE) 337 strcat(msg, ";4"); /* typo? DEC documents this as '2' */ 338 if (attr & BLINK) 339 strcat(msg, ";5"); 340 if (attr & INVERSE) /* typo? DEC documents this as invisible */ 341 strcat(msg, ";7"); 342#if OPT_PRINT_COLORS 343 if (bg != NO_COLOR) { 344 sprintf(msg + strlen(msg), ";%u", (bg < 8) ? (40 + bg) : (92 + bg)); 345 } 346 if (fg != NO_COLOR) { 347#if OPT_PC_COLORS 348 if (term->screen.boldColors 349 && fg > 8 350 && (attr & BOLD) != 0) 351 fg -= 8; 352#endif 353 sprintf(msg + strlen(msg), ";%u", (fg < 8) ? (30 + fg) : (82 + fg)); 354 } 355#else 356 (void) bg; 357 (void) fg; 358#endif 359 strcat(msg, "m"); 360 stringToPrinter(msg); 361} 362 363/* 364 * This implementation only knows how to write to a pipe. 365 */ 366static void 367charToPrinter(unsigned chr) 368{ 369 TScreen *screen = TScreenOf(term); 370 371 if (!initialized && xtermHasPrinter()) { 372#if defined(VMS) 373 /* 374 * This implementation only knows how to write to a file. When the 375 * file is closed the print command executes. Print command must be of 376 * the form: 377 * print/que=name/delete [/otherflags]. 378 */ 379 Printer = fopen(VMS_TEMP_PRINT_FILE, "w"); 380#else 381 /* 382 * This implementation only knows how to write to a pipe. 383 */ 384 FILE *input; 385 int my_pipe[2]; 386 int c; 387 388 if (pipe(my_pipe)) 389 SysError(ERROR_FORK); 390 if ((Printer_pid = fork()) < 0) 391 SysError(ERROR_FORK); 392 393 if (Printer_pid == 0) { 394 TRACE(((char *) 0)); 395 close(my_pipe[1]); /* printer is silent */ 396 close(screen->respond); 397 398 close(fileno(stdout)); 399 dup2(fileno(stderr), 1); 400 401 if (fileno(stderr) != 2) { 402 dup2(fileno(stderr), 2); 403 close(fileno(stderr)); 404 } 405 406 /* don't want privileges! */ 407 if (xtermResetIds(screen) < 0) 408 exit(1); 409 410 Printer = popen(screen->printer_command, "w"); 411 input = fdopen(my_pipe[0], "r"); 412 while ((c = fgetc(input)) != EOF) { 413 fputc(c, Printer); 414 if (isForm(c)) 415 fflush(Printer); 416 } 417 pclose(Printer); 418 exit(0); 419 } else { 420 close(my_pipe[0]); /* won't read from printer */ 421 Printer = fdopen(my_pipe[1], "w"); 422 TRACE(("opened printer from pid %d/%d\n", 423 (int) getpid(), Printer_pid)); 424 } 425#endif 426 initialized++; 427 } 428 if (Printer != 0) { 429#if OPT_WIDE_CHARS 430 if (chr > 127) { 431 Char temp[10]; 432 *convertToUTF8(temp, chr) = 0; 433 fputs((char *) temp, Printer); 434 } else 435#endif 436 fputc((int) chr, Printer); 437 if (isForm(chr)) 438 fflush(Printer); 439 } 440} 441 442static void 443stringToPrinter(char *str) 444{ 445 while (*str) 446 charToPrinter(CharOf(*str++)); 447} 448 449/* 450 * This module implements the MC (Media Copy) and related printing control 451 * sequences for VTxxx emulation. This is based on the description in the 452 * VT330/VT340 Programmer Reference Manual EK-VT3XX-TP-001 (Digital Equipment 453 * Corp., March 1987). 454 */ 455void 456xtermMediaControl(int param, int private_seq) 457{ 458 TRACE(("MediaCopy param=%d, private=%d\n", param, private_seq)); 459 460 if (private_seq) { 461 switch (param) { 462 case 1: 463 printCursorLine(); 464 break; 465 case 4: 466 setPrinterControlMode(0); 467 break; 468 case 5: 469 setPrinterControlMode(1); 470 break; 471 case 10: /* VT320 */ 472 xtermPrintScreen(False); 473 break; 474 case 11: /* VT320 */ 475 xtermPrintEverything(); 476 break; 477 } 478 } else { 479 switch (param) { 480 case -1: 481 case 0: 482 xtermPrintScreen(True); 483 break; 484 case 4: 485 setPrinterControlMode(0); 486 break; 487 case 5: 488 setPrinterControlMode(2); 489 break; 490 } 491 } 492} 493 494/* 495 * When in autoprint mode, the printer prints a line from the screen when you 496 * move the cursor off that line with an LF, FF, or VT character, or an 497 * autowrap occurs. The printed line ends with a CR and the character (LF, FF 498 * or VT) that moved the cursor off the previous line. 499 */ 500void 501xtermAutoPrint(unsigned chr) 502{ 503 TScreen *screen = TScreenOf(term); 504 505 if (screen->printer_controlmode == 1) { 506 TRACE(("AutoPrint %d\n", chr)); 507 printLine(screen->cursorp.row, chr); 508 if (Printer != 0) 509 fflush(Printer); 510 } 511} 512 513/* 514 * When in printer controller mode, the terminal sends received characters to 515 * the printer without displaying them on the screen. The terminal sends all 516 * characters and control sequences to the printer, except NUL, XON, XOFF, and 517 * the printer controller sequences. 518 * 519 * This function eats characters, returning 0 as long as it must buffer or 520 * divert to the printer. We're only invoked here when in printer controller 521 * mode, and handle the exit from that mode. 522 */ 523#define LB '[' 524 525int 526xtermPrinterControl(int chr) 527{ 528 TScreen *screen = TScreenOf(term); 529 /* *INDENT-OFF* */ 530 static struct { 531 Char seq[5]; 532 int active; 533 } tbl[] = { 534 { { ANSI_CSI, '5', 'i' }, 2 }, 535 { { ANSI_CSI, '4', 'i' }, 0 }, 536 { { ANSI_ESC, LB, '5', 'i' }, 2 }, 537 { { ANSI_ESC, LB, '4', 'i' }, 0 }, 538 }; 539 /* *INDENT-ON* */ 540 541 static Char bfr[10]; 542 static size_t length; 543 size_t n; 544 545 TRACE(("In printer:%04X\n", chr)); 546 547 switch (chr) { 548 case 0: 549 case CTRL('Q'): 550 case CTRL('S'): 551 return 0; /* ignored by application */ 552 553 case ANSI_CSI: 554 case ANSI_ESC: 555 case '[': 556 case '4': 557 case '5': 558 case 'i': 559 bfr[length++] = CharOf(chr); 560 for (n = 0; n < sizeof(tbl) / sizeof(tbl[0]); n++) { 561 size_t len = Strlen(tbl[n].seq); 562 563 if (length == len 564 && Strcmp(bfr, tbl[n].seq) == 0) { 565 setPrinterControlMode(tbl[n].active); 566 if (screen->printer_autoclose 567 && screen->printer_controlmode == 0) 568 closePrinter(); 569 length = 0; 570 return 0; 571 } else if (len > length 572 && Strncmp(bfr, tbl[n].seq, length) == 0) { 573 return 0; 574 } 575 } 576 length--; 577 578 /* FALLTHRU */ 579 580 default: 581 for (n = 0; n < length; n++) 582 charToPrinter(bfr[n]); 583 bfr[0] = CharOf(chr); 584 length = 1; 585 return 0; 586 } 587} 588 589/* 590 * If there is no printer command, we will ignore printer controls. 591 */ 592Bool 593xtermHasPrinter(void) 594{ 595 TScreen *screen = TScreenOf(term); 596 597 return (strlen(screen->printer_command) != 0); 598} 599 600#define showPrinterControlMode(mode) \ 601 (((mode) == 0) \ 602 ? "normal" \ 603 : ((mode) == 1 \ 604 ? "autoprint" \ 605 : "printer controller")) 606 607void 608setPrinterControlMode(int mode) 609{ 610 if (xtermHasPrinter() 611 && term->screen.printer_controlmode != mode) { 612 TRACE(("%s %s mode\n", 613 (mode 614 ? "set" 615 : "reset"), 616 (mode 617 ? showPrinterControlMode(mode) 618 : showPrinterControlMode(term->screen.printer_controlmode)))); 619 term->screen.printer_controlmode = mode; 620 update_print_redir(); 621 } 622} 623