print.c revision 2e4f8982
12e4f8982Smrg/* $XTermId: print.c,v 1.155 2016/05/22 16:31:59 tom Exp $ */ 2d522f475Smrg 30bd37d32Smrg/* 42e4f8982Smrg * Copyright 1997-2014,2016 by Thomas E. Dickey 50bd37d32Smrg * 60bd37d32Smrg * All Rights Reserved 70bd37d32Smrg * 80bd37d32Smrg * Permission is hereby granted, free of charge, to any person obtaining a 90bd37d32Smrg * copy of this software and associated documentation files (the 100bd37d32Smrg * "Software"), to deal in the Software without restriction, including 110bd37d32Smrg * without limitation the rights to use, copy, modify, merge, publish, 120bd37d32Smrg * distribute, sublicense, and/or sell copies of the Software, and to 130bd37d32Smrg * permit persons to whom the Software is furnished to do so, subject to 140bd37d32Smrg * the following conditions: 150bd37d32Smrg * 160bd37d32Smrg * The above copyright notice and this permission notice shall be included 170bd37d32Smrg * in all copies or substantial portions of the Software. 180bd37d32Smrg * 190bd37d32Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 200bd37d32Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 210bd37d32Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 220bd37d32Smrg * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 230bd37d32Smrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 240bd37d32Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 250bd37d32Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 260bd37d32Smrg * 270bd37d32Smrg * Except as contained in this notice, the name(s) of the above copyright 280bd37d32Smrg * holders shall not be used in advertising or otherwise to promote the 290bd37d32Smrg * sale, use or other dealings in this Software without prior written 300bd37d32Smrg * authorization. 310bd37d32Smrg */ 32d522f475Smrg 33d522f475Smrg#include <xterm.h> 34d522f475Smrg#include <data.h> 35d522f475Smrg#include <menu.h> 36d522f475Smrg#include <error.h> 3720d2c4d2Smrg#include <xstrings.h> 38d522f475Smrg 39d522f475Smrg#include <stdio.h> 40e39b573cSmrg#include <sys/stat.h> 41d522f475Smrg 42d522f475Smrg#undef CTRL 43d522f475Smrg#define CTRL(c) ((c) & 0x1f) 44d522f475Smrg 45d522f475Smrg#define SHIFT_IN '\017' 46d522f475Smrg#define SHIFT_OUT '\016' 47d522f475Smrg 48d522f475Smrg#define CSET_IN 'A' 49d522f475Smrg#define CSET_OUT '0' 50d522f475Smrg 51d522f475Smrg#define isForm(c) ((c) == '\r' || (c) == '\n' || (c) == '\f') 52e39b573cSmrg#define Strlen(a) strlen((const char *)a) 53e39b573cSmrg#define Strcmp(a,b) strcmp((const char *)a,(const char *)b) 54e39b573cSmrg#define Strncmp(a,b,c) strncmp((const char *)a,(const char *)b,c) 55e39b573cSmrg 56e39b573cSmrg#define SPS PrinterOf(screen) 57d522f475Smrg 58d522f475Smrg#ifdef VMS 59d522f475Smrg#define VMS_TEMP_PRINT_FILE "sys$scratch:xterm_print.txt" 60d522f475Smrg#endif 61d522f475Smrg 62956cc18dSsnjstatic void charToPrinter(XtermWidget /* xw */ , 63956cc18dSsnj unsigned /* chr */ ); 64956cc18dSsnjstatic void printLine(XtermWidget /* xw */ , 65956cc18dSsnj int /* row */ , 6620d2c4d2Smrg unsigned /* chr */ , 6720d2c4d2Smrg PrinterFlags * /* p */ ); 68956cc18dSsnjstatic void send_CharSet(XtermWidget /* xw */ , 69956cc18dSsnj LineData * /* ld */ ); 70956cc18dSsnjstatic void send_SGR(XtermWidget /* xw */ , 71956cc18dSsnj unsigned /* attr */ , 72956cc18dSsnj unsigned /* fg */ , 73956cc18dSsnj unsigned /* bg */ ); 74956cc18dSsnjstatic void stringToPrinter(XtermWidget /* xw */ , 7520d2c4d2Smrg const char * /*str */ ); 76d522f475Smrg 770bd37d32Smrgstatic void 78956cc18dSsnjclosePrinter(XtermWidget xw GCC_UNUSED) 79d522f475Smrg{ 80956cc18dSsnj if (xtermHasPrinter(xw) != 0) { 81956cc18dSsnj TScreen *screen = TScreenOf(xw); 82e39b573cSmrg#ifdef VMS 83d522f475Smrg char pcommand[256]; 84d522f475Smrg (void) sprintf(pcommand, "%s %s;", 85e39b573cSmrg SPS.printer_command, 86d522f475Smrg VMS_TEMP_PRINT_FILE); 87d522f475Smrg#endif 88d522f475Smrg 89e39b573cSmrg if (SPS.fp != 0) { 900bd37d32Smrg DEBUG_MSG("closePrinter\n"); 910bd37d32Smrg pclose(SPS.fp); 92d522f475Smrg TRACE(("closed printer, waiting...\n")); 93d522f475Smrg#ifdef VMS /* This is a quick hack, really should use 94d522f475Smrg spawn and check status or system services 95d522f475Smrg and go straight to the queue */ 96d522f475Smrg (void) system(pcommand); 97d522f475Smrg#else /* VMS */ 980bd37d32Smrg while (nonblocking_wait() > 0) { 99d522f475Smrg ; 1000bd37d32Smrg } 1010bd37d32Smrg#endif /* VMS */ 102e39b573cSmrg SPS.fp = 0; 103e39b573cSmrg SPS.isOpen = False; 104d522f475Smrg TRACE(("closed printer\n")); 1050bd37d32Smrg DEBUG_MSG("...closePrinter (done)\n"); 106d522f475Smrg } 107d522f475Smrg } 108d522f475Smrg} 109d522f475Smrg 110d522f475Smrgstatic void 111956cc18dSsnjprintCursorLine(XtermWidget xw) 112d522f475Smrg{ 113956cc18dSsnj TScreen *screen = TScreenOf(xw); 114d522f475Smrg 115d522f475Smrg TRACE(("printCursorLine\n")); 11620d2c4d2Smrg printLine(xw, screen->cur_row, '\n', getPrinterFlags(xw, NULL, 0)); 117d522f475Smrg} 118d522f475Smrg 119d522f475Smrg#define NO_COLOR ((unsigned)-1) 120d522f475Smrg 121d522f475Smrg/* 122d522f475Smrg * DEC's manual doesn't document whether trailing blanks are removed, or what 123d522f475Smrg * happens with a line that is entirely blank. This function prints the 124d522f475Smrg * characters that xterm would allow as a selection (which may include blanks). 125d522f475Smrg */ 126d522f475Smrgstatic void 127894e0ac8SmrgprintLine(XtermWidget xw, int row, unsigned chr, PrinterFlags *p) 128d522f475Smrg{ 129956cc18dSsnj TScreen *screen = TScreenOf(xw); 130d522f475Smrg int inx = ROW2INX(screen, row); 131956cc18dSsnj LineData *ld; 132d522f475Smrg int last = MaxCols(screen); 133d522f475Smrg#if OPT_ISO_COLORS && OPT_PRINT_COLORS 134956cc18dSsnj#define ColorOf(ld,col) (ld->color[col]) 135d522f475Smrg#endif 136d522f475Smrg unsigned fg = NO_COLOR, last_fg = NO_COLOR; 137d522f475Smrg unsigned bg = NO_COLOR, last_bg = NO_COLOR; 138d522f475Smrg 139956cc18dSsnj ld = getLineData(screen, inx); 14020d2c4d2Smrg if (ld == 0) 14120d2c4d2Smrg return; 14220d2c4d2Smrg 143d522f475Smrg TRACE(("printLine(row=%d/%d, top=%d:%d, chr=%d):%s\n", 144d522f475Smrg row, ROW2INX(screen, row), screen->topline, screen->max_row, chr, 145956cc18dSsnj visibleIChars(ld->charData, (unsigned) last))); 146956cc18dSsnj 147d522f475Smrg while (last > 0) { 148956cc18dSsnj if ((ld->attribs[last - 1] & CHARDRAWN) == 0) 149d522f475Smrg last--; 150d522f475Smrg else 151d522f475Smrg break; 152d522f475Smrg } 1532e4f8982Smrg 154d522f475Smrg if (last) { 1552e4f8982Smrg int col; 1562e4f8982Smrg int cs = CSET_IN; 1572e4f8982Smrg int last_cs = CSET_IN; 1582e4f8982Smrg 15920d2c4d2Smrg if (p->print_attributes) { 160956cc18dSsnj send_CharSet(xw, ld); 161956cc18dSsnj send_SGR(xw, 0, NO_COLOR, NO_COLOR); 162d522f475Smrg } 163d522f475Smrg for (col = 0; col < last; col++) { 1642e4f8982Smrg IAttr attr = 0; 1652e4f8982Smrg unsigned ch = ld->charData[col]; 166d522f475Smrg#if OPT_PRINT_COLORS 167d522f475Smrg if (screen->colorMode) { 16820d2c4d2Smrg if (p->print_attributes > 1) { 169956cc18dSsnj fg = (ld->attribs[col] & FG_COLOR) 170956cc18dSsnj ? extract_fg(xw, ColorOf(ld, col), ld->attribs[col]) 171d522f475Smrg : NO_COLOR; 172956cc18dSsnj bg = (ld->attribs[col] & BG_COLOR) 173956cc18dSsnj ? extract_bg(xw, ColorOf(ld, col), ld->attribs[col]) 174d522f475Smrg : NO_COLOR; 175d522f475Smrg } 176d522f475Smrg } 177d522f475Smrg#endif 178956cc18dSsnj if ((((ld->attribs[col] & SGR_MASK) != attr) 179d522f475Smrg#if OPT_PRINT_COLORS 180d522f475Smrg || (last_fg != fg) || (last_bg != bg) 181d522f475Smrg#endif 182d522f475Smrg ) 183d522f475Smrg && ch) { 184894e0ac8Smrg attr = ld->attribs[col] & SGR_MASK; 18520d2c4d2Smrg#if OPT_PRINT_COLORS 186d522f475Smrg last_fg = fg; 187d522f475Smrg last_bg = bg; 18820d2c4d2Smrg#endif 18920d2c4d2Smrg if (p->print_attributes) 190956cc18dSsnj send_SGR(xw, attr, fg, bg); 191d522f475Smrg } 192d522f475Smrg 193d522f475Smrg if (ch == 0) 194d522f475Smrg ch = ' '; 195d522f475Smrg 196d522f475Smrg#if OPT_WIDE_CHARS 197d522f475Smrg if (screen->utf8_mode) 198d522f475Smrg cs = CSET_IN; 199d522f475Smrg else 200d522f475Smrg#endif 201d522f475Smrg cs = (ch >= ' ' && ch != ANSI_DEL) ? CSET_IN : CSET_OUT; 202d522f475Smrg if (last_cs != cs) { 20320d2c4d2Smrg if (p->print_attributes) { 204956cc18dSsnj charToPrinter(xw, 205956cc18dSsnj (unsigned) ((cs == CSET_OUT) 206d522f475Smrg ? SHIFT_OUT 207d522f475Smrg : SHIFT_IN)); 208d522f475Smrg } 209d522f475Smrg last_cs = cs; 210d522f475Smrg } 211d522f475Smrg 212d522f475Smrg /* FIXME: we shouldn't have to map back from the 213d522f475Smrg * alternate character set, except that the 214d522f475Smrg * corresponding charset information is not encoded 215d522f475Smrg * into the CSETS array. 216d522f475Smrg */ 217956cc18dSsnj charToPrinter(xw, 218956cc18dSsnj ((cs == CSET_OUT) 219d522f475Smrg ? (ch == ANSI_DEL ? 0x5f : (ch + 0x5f)) 220d522f475Smrg : ch)); 221d522f475Smrg if_OPT_WIDE_CHARS(screen, { 222956cc18dSsnj size_t off; 223956cc18dSsnj for_each_combData(off, ld) { 224956cc18dSsnj ch = ld->combData[off][col]; 225956cc18dSsnj if (ch == 0) 226d522f475Smrg break; 227956cc18dSsnj charToPrinter(xw, ch); 228d522f475Smrg } 229d522f475Smrg }); 230d522f475Smrg } 23120d2c4d2Smrg if (p->print_attributes) { 232956cc18dSsnj send_SGR(xw, 0, NO_COLOR, NO_COLOR); 233d522f475Smrg if (cs != CSET_IN) 234956cc18dSsnj charToPrinter(xw, SHIFT_IN); 235d522f475Smrg } 236d522f475Smrg } 23720d2c4d2Smrg 23820d2c4d2Smrg /* finish line (protocol for attributes needs a CR */ 23920d2c4d2Smrg if (p->print_attributes) 240956cc18dSsnj charToPrinter(xw, '\r'); 24120d2c4d2Smrg 24220d2c4d2Smrg if (chr && !(p->printer_newline)) { 24320d2c4d2Smrg if (LineTstWrapped(ld)) 24420d2c4d2Smrg chr = '\0'; 24520d2c4d2Smrg } 24620d2c4d2Smrg 24720d2c4d2Smrg if (chr) 24820d2c4d2Smrg charToPrinter(xw, chr); 249956cc18dSsnj 250956cc18dSsnj return; 251d522f475Smrg} 252d522f475Smrg 25320d2c4d2Smrg#define PrintNewLine() (unsigned) (((top < bot) || p->printer_newline) ? '\n' : '\0') 25420d2c4d2Smrg 255e39b573cSmrgstatic void 256894e0ac8SmrgprintLines(XtermWidget xw, int top, int bot, PrinterFlags *p) 257e39b573cSmrg{ 258e39b573cSmrg TRACE(("printLines, rows %d..%d\n", top, bot)); 259e39b573cSmrg while (top <= bot) { 260e39b573cSmrg printLine(xw, top, PrintNewLine(), p); 261e39b573cSmrg ++top; 262e39b573cSmrg } 263e39b573cSmrg} 264e39b573cSmrg 265d522f475Smrgvoid 266894e0ac8SmrgxtermPrintScreen(XtermWidget xw, Bool use_DECPEX, PrinterFlags *p) 267d522f475Smrg{ 268956cc18dSsnj if (XtIsRealized((Widget) xw)) { 269956cc18dSsnj TScreen *screen = TScreenOf(xw); 27020d2c4d2Smrg Bool extent = (use_DECPEX && p->printer_extent); 271e39b573cSmrg Boolean was_open = SPS.isOpen; 272d522f475Smrg 273e39b573cSmrg printLines(xw, 274e39b573cSmrg extent ? 0 : screen->top_marg, 275e39b573cSmrg extent ? screen->max_row : screen->bot_marg, 276e39b573cSmrg p); 27720d2c4d2Smrg if (p->printer_formfeed) 278956cc18dSsnj charToPrinter(xw, '\f'); 279d522f475Smrg 280e39b573cSmrg if (!was_open || SPS.printer_autoclose) { 281956cc18dSsnj closePrinter(xw); 282d522f475Smrg } 283d522f475Smrg } else { 28420d2c4d2Smrg Bell(xw, XkbBI_MinorError, 0); 285d522f475Smrg } 286d522f475Smrg} 287d522f475Smrg 288d522f475Smrg/* 289e39b573cSmrg * If p->print_everything is zero, use this behavior: 290d522f475Smrg * If the alternate screen is active, we'll print only that. Otherwise, print 291d522f475Smrg * the normal screen plus all scrolled-back lines. The distinction is made 292d522f475Smrg * because the normal screen's buffer is part of the overall scrollback buffer. 293e39b573cSmrg * 294e39b573cSmrg * Otherwise, decode bits: 295e39b573cSmrg * 1 = current screen 296e39b573cSmrg * 2 = normal screen 297e39b573cSmrg * 4 = alternate screen 298e39b573cSmrg * 8 = saved lines 299d522f475Smrg */ 300956cc18dSsnjvoid 301894e0ac8SmrgxtermPrintEverything(XtermWidget xw, PrinterFlags *p) 302d522f475Smrg{ 303956cc18dSsnj TScreen *screen = TScreenOf(xw); 304e39b573cSmrg Boolean was_open = SPS.isOpen; 305e39b573cSmrg int save_which = screen->whichBuf; 306d522f475Smrg 3070bd37d32Smrg DEBUG_MSG("xtermPrintEverything\n"); 3082e4f8982Smrg 309e39b573cSmrg if (p->print_everything) { 3102e4f8982Smrg int done_which = 0; 3112e4f8982Smrg 312e39b573cSmrg if (p->print_everything & 8) { 313e39b573cSmrg printLines(xw, -screen->savedlines, -(screen->topline + 1), p); 314e39b573cSmrg } 315e39b573cSmrg if (p->print_everything & 4) { 316e39b573cSmrg screen->whichBuf = 1; 317e39b573cSmrg done_which |= 2; 318e39b573cSmrg printLines(xw, 0, screen->max_row, p); 319e39b573cSmrg screen->whichBuf = save_which; 320e39b573cSmrg } 321e39b573cSmrg if (p->print_everything & 2) { 322e39b573cSmrg screen->whichBuf = 0; 323e39b573cSmrg done_which |= 1; 324e39b573cSmrg printLines(xw, 0, screen->max_row, p); 325e39b573cSmrg screen->whichBuf = save_which; 326e39b573cSmrg } 327e39b573cSmrg if (p->print_everything & 1) { 328e39b573cSmrg if (!(done_which & (1 << screen->whichBuf))) { 329e39b573cSmrg printLines(xw, 0, screen->max_row, p); 330e39b573cSmrg } 331e39b573cSmrg } 332e39b573cSmrg } else { 333e39b573cSmrg int top = 0; 334e39b573cSmrg int bot = screen->max_row; 335e39b573cSmrg if (!screen->whichBuf) { 336e39b573cSmrg top = -screen->savedlines - screen->topline; 337e39b573cSmrg bot -= screen->topline; 338e39b573cSmrg } 339e39b573cSmrg printLines(xw, top, bot, p); 34020d2c4d2Smrg } 34120d2c4d2Smrg if (p->printer_formfeed) 342956cc18dSsnj charToPrinter(xw, '\f'); 343d522f475Smrg 344e39b573cSmrg if (!was_open || SPS.printer_autoclose) { 345956cc18dSsnj closePrinter(xw); 346d522f475Smrg } 347d522f475Smrg} 348d522f475Smrg 349d522f475Smrgstatic void 350894e0ac8Smrgsend_CharSet(XtermWidget xw, LineData *ld) 351d522f475Smrg{ 352d522f475Smrg#if OPT_DEC_CHRSET 35320d2c4d2Smrg const char *msg = 0; 354d522f475Smrg 355956cc18dSsnj switch (GetLineDblCS(ld)) { 356d522f475Smrg case CSET_SWL: 357d522f475Smrg msg = "\033#5"; 358d522f475Smrg break; 359d522f475Smrg case CSET_DHL_TOP: 360d522f475Smrg msg = "\033#3"; 361d522f475Smrg break; 362d522f475Smrg case CSET_DHL_BOT: 363d522f475Smrg msg = "\033#4"; 364d522f475Smrg break; 365d522f475Smrg case CSET_DWL: 366d522f475Smrg msg = "\033#6"; 367d522f475Smrg break; 368d522f475Smrg } 369d522f475Smrg if (msg != 0) 370956cc18dSsnj stringToPrinter(xw, msg); 371d522f475Smrg#else 372956cc18dSsnj (void) xw; 373956cc18dSsnj (void) ld; 374d522f475Smrg#endif /* OPT_DEC_CHRSET */ 375d522f475Smrg} 376d522f475Smrg 377d522f475Smrgstatic void 378956cc18dSsnjsend_SGR(XtermWidget xw, unsigned attr, unsigned fg, unsigned bg) 379d522f475Smrg{ 380d522f475Smrg char msg[80]; 381e39b573cSmrg 382d522f475Smrg strcpy(msg, "\033[0"); 383d522f475Smrg if (attr & BOLD) 384d522f475Smrg strcat(msg, ";1"); 385894e0ac8Smrg#if OPT_WIDE_ATTRS 386894e0ac8Smrg if (attr & ATR_FAINT) 387894e0ac8Smrg strcat(msg, ";2"); 388894e0ac8Smrg if (attr & ATR_ITALIC) 389894e0ac8Smrg strcat(msg, ";3"); 390894e0ac8Smrg#endif 391d522f475Smrg if (attr & UNDERLINE) 392d522f475Smrg strcat(msg, ";4"); /* typo? DEC documents this as '2' */ 393d522f475Smrg if (attr & BLINK) 394d522f475Smrg strcat(msg, ";5"); 395d522f475Smrg if (attr & INVERSE) /* typo? DEC documents this as invisible */ 396d522f475Smrg strcat(msg, ";7"); 397d522f475Smrg#if OPT_PRINT_COLORS 398d522f475Smrg if (bg != NO_COLOR) { 399d522f475Smrg sprintf(msg + strlen(msg), ";%u", (bg < 8) ? (40 + bg) : (92 + bg)); 400d522f475Smrg } 401d522f475Smrg if (fg != NO_COLOR) { 402d522f475Smrg#if OPT_PC_COLORS 40320d2c4d2Smrg if (TScreenOf(xw)->boldColors 404d522f475Smrg && fg > 8 405d522f475Smrg && (attr & BOLD) != 0) 406d522f475Smrg fg -= 8; 407d522f475Smrg#endif 408d522f475Smrg sprintf(msg + strlen(msg), ";%u", (fg < 8) ? (30 + fg) : (82 + fg)); 409d522f475Smrg } 410d522f475Smrg#else 411d522f475Smrg (void) bg; 412d522f475Smrg (void) fg; 413d522f475Smrg#endif 414d522f475Smrg strcat(msg, "m"); 415956cc18dSsnj stringToPrinter(xw, msg); 416d522f475Smrg} 417d522f475Smrg 418d522f475Smrg/* 419d522f475Smrg * This implementation only knows how to write to a pipe. 420d522f475Smrg */ 421d522f475Smrgstatic void 422956cc18dSsnjcharToPrinter(XtermWidget xw, unsigned chr) 423d522f475Smrg{ 424956cc18dSsnj TScreen *screen = TScreenOf(xw); 425d522f475Smrg 426e39b573cSmrg if (!SPS.isOpen && xtermHasPrinter(xw)) { 427e39b573cSmrg switch (SPS.toFile) { 428e39b573cSmrg /* 429e39b573cSmrg * write to a pipe. 430e39b573cSmrg */ 431e39b573cSmrg case False: 432e39b573cSmrg#ifdef VMS 433e39b573cSmrg /* 434e39b573cSmrg * This implementation only knows how to write to a file. When the 435e39b573cSmrg * file is closed the print command executes. Print command must 436e39b573cSmrg * be of the form: 437e39b573cSmrg * print/que=name/delete [/otherflags]. 438e39b573cSmrg */ 439e39b573cSmrg SPS.fp = fopen(VMS_TEMP_PRINT_FILE, "w"); 440d522f475Smrg#else 441e39b573cSmrg { 442e39b573cSmrg int my_pipe[2]; 443e39b573cSmrg pid_t my_pid; 444e39b573cSmrg 445e39b573cSmrg if (pipe(my_pipe)) 446e39b573cSmrg SysError(ERROR_FORK); 447e39b573cSmrg if ((my_pid = fork()) < 0) 448e39b573cSmrg SysError(ERROR_FORK); 449e39b573cSmrg 450e39b573cSmrg if (my_pid == 0) { 4510bd37d32Smrg DEBUG_MSG("charToPrinter: subprocess for printer\n"); 452e39b573cSmrg TRACE_CLOSE(); 453e39b573cSmrg close(my_pipe[1]); /* printer is silent */ 454e39b573cSmrg close(screen->respond); 455e39b573cSmrg 456e39b573cSmrg close(fileno(stdout)); 457e39b573cSmrg dup2(fileno(stderr), 1); 458e39b573cSmrg 459e39b573cSmrg if (fileno(stderr) != 2) { 460e39b573cSmrg dup2(fileno(stderr), 2); 461e39b573cSmrg close(fileno(stderr)); 462e39b573cSmrg } 463e39b573cSmrg 464e39b573cSmrg /* don't want privileges! */ 465e39b573cSmrg if (xtermResetIds(screen) < 0) 466e39b573cSmrg exit(1); 467e39b573cSmrg 468e39b573cSmrg SPS.fp = popen(SPS.printer_command, "w"); 4690bd37d32Smrg if (SPS.fp != 0) { 4702e4f8982Smrg FILE *input; 4710bd37d32Smrg DEBUG_MSG("charToPrinter: opened pipe to printer\n"); 4720bd37d32Smrg input = fdopen(my_pipe[0], "r"); 4730bd37d32Smrg clearerr(input); 4742e4f8982Smrg 4750bd37d32Smrg for (;;) { 4762e4f8982Smrg int c; 4772e4f8982Smrg 4780bd37d32Smrg if (ferror(input)) { 4790bd37d32Smrg DEBUG_MSG("charToPrinter: break on ferror\n"); 4800bd37d32Smrg break; 4810bd37d32Smrg } else if (feof(input)) { 4820bd37d32Smrg DEBUG_MSG("charToPrinter: break on feof\n"); 4830bd37d32Smrg break; 4840bd37d32Smrg } else if ((c = fgetc(input)) == EOF) { 4850bd37d32Smrg DEBUG_MSG("charToPrinter: break on EOF\n"); 4860bd37d32Smrg break; 4870bd37d32Smrg } 4880bd37d32Smrg fputc(c, SPS.fp); 4890bd37d32Smrg if (isForm(c)) 4900bd37d32Smrg fflush(SPS.fp); 4910bd37d32Smrg } 4922e4f8982Smrg 4930bd37d32Smrg DEBUG_MSG("charToPrinter: calling pclose\n"); 4940bd37d32Smrg pclose(SPS.fp); 495e39b573cSmrg } 496e39b573cSmrg exit(0); 497e39b573cSmrg } else { 498e39b573cSmrg close(my_pipe[0]); /* won't read from printer */ 4990bd37d32Smrg if ((SPS.fp = fdopen(my_pipe[1], "w")) != 0) { 5000bd37d32Smrg DEBUG_MSG("charToPrinter: opened printer in parent\n"); 5010bd37d32Smrg TRACE(("opened printer from pid %d/%d\n", 5020bd37d32Smrg (int) getpid(), (int) my_pid)); 5030bd37d32Smrg } else { 5040bd37d32Smrg TRACE(("failed to open printer:%s\n", strerror(errno))); 5050bd37d32Smrg DEBUG_MSG("charToPrinter: could not open in parent\n"); 5060bd37d32Smrg } 507e39b573cSmrg } 508d522f475Smrg } 509d522f475Smrg#endif 510e39b573cSmrg break; 511e39b573cSmrg case True: 512e39b573cSmrg TRACE(("opening \"%s\" as printer output\n", SPS.printer_command)); 513e39b573cSmrg SPS.fp = fopen(SPS.printer_command, "w"); 514e39b573cSmrg break; 515e39b573cSmrg } 516e39b573cSmrg SPS.isOpen = True; 517d522f475Smrg } 518e39b573cSmrg if (SPS.fp != 0) { 519d522f475Smrg#if OPT_WIDE_CHARS 520d522f475Smrg if (chr > 127) { 521d522f475Smrg Char temp[10]; 522d522f475Smrg *convertToUTF8(temp, chr) = 0; 523e39b573cSmrg fputs((char *) temp, SPS.fp); 524d522f475Smrg } else 525d522f475Smrg#endif 526e39b573cSmrg fputc((int) chr, SPS.fp); 527d522f475Smrg if (isForm(chr)) 528e39b573cSmrg fflush(SPS.fp); 529d522f475Smrg } 530d522f475Smrg} 531d522f475Smrg 532d522f475Smrgstatic void 53320d2c4d2SmrgstringToPrinter(XtermWidget xw, const char *str) 534d522f475Smrg{ 535d522f475Smrg while (*str) 536956cc18dSsnj charToPrinter(xw, CharOf(*str++)); 537d522f475Smrg} 538d522f475Smrg 539d522f475Smrg/* 540d522f475Smrg * This module implements the MC (Media Copy) and related printing control 541d522f475Smrg * sequences for VTxxx emulation. This is based on the description in the 542d522f475Smrg * VT330/VT340 Programmer Reference Manual EK-VT3XX-TP-001 (Digital Equipment 543d522f475Smrg * Corp., March 1987). 544d522f475Smrg */ 545d522f475Smrgvoid 546956cc18dSsnjxtermMediaControl(XtermWidget xw, int param, int private_seq) 547d522f475Smrg{ 548d522f475Smrg TRACE(("MediaCopy param=%d, private=%d\n", param, private_seq)); 549d522f475Smrg 550d522f475Smrg if (private_seq) { 551d522f475Smrg switch (param) { 552d522f475Smrg case 1: 553956cc18dSsnj printCursorLine(xw); 554d522f475Smrg break; 555d522f475Smrg case 4: 556956cc18dSsnj setPrinterControlMode(xw, 0); 557d522f475Smrg break; 558d522f475Smrg case 5: 559956cc18dSsnj setPrinterControlMode(xw, 1); 560d522f475Smrg break; 561d522f475Smrg case 10: /* VT320 */ 56220d2c4d2Smrg xtermPrintScreen(xw, False, getPrinterFlags(xw, NULL, 0)); 563d522f475Smrg break; 564d522f475Smrg case 11: /* VT320 */ 56520d2c4d2Smrg xtermPrintEverything(xw, getPrinterFlags(xw, NULL, 0)); 566d522f475Smrg break; 567d522f475Smrg } 568d522f475Smrg } else { 569d522f475Smrg switch (param) { 570d522f475Smrg case -1: 571d522f475Smrg case 0: 57220d2c4d2Smrg xtermPrintScreen(xw, True, getPrinterFlags(xw, NULL, 0)); 573d522f475Smrg break; 574d522f475Smrg case 4: 575956cc18dSsnj setPrinterControlMode(xw, 0); 576d522f475Smrg break; 577d522f475Smrg case 5: 578956cc18dSsnj setPrinterControlMode(xw, 2); 579d522f475Smrg break; 5802e4f8982Smrg#if OPT_SCREEN_DUMPS 5812e4f8982Smrg case 10: 5822e4f8982Smrg xtermDumpHtml(xw); 5832e4f8982Smrg break; 5842e4f8982Smrg case 11: 5852e4f8982Smrg xtermDumpSvg(xw); 5862e4f8982Smrg break; 5872e4f8982Smrg#endif 588d522f475Smrg } 589d522f475Smrg } 590d522f475Smrg} 591d522f475Smrg 592d522f475Smrg/* 593d522f475Smrg * When in autoprint mode, the printer prints a line from the screen when you 594d522f475Smrg * move the cursor off that line with an LF, FF, or VT character, or an 595d522f475Smrg * autowrap occurs. The printed line ends with a CR and the character (LF, FF 596d522f475Smrg * or VT) that moved the cursor off the previous line. 597d522f475Smrg */ 598d522f475Smrgvoid 599956cc18dSsnjxtermAutoPrint(XtermWidget xw, unsigned chr) 600d522f475Smrg{ 601956cc18dSsnj TScreen *screen = TScreenOf(xw); 602d522f475Smrg 603e39b573cSmrg if (SPS.printer_controlmode == 1) { 604d522f475Smrg TRACE(("AutoPrint %d\n", chr)); 60520d2c4d2Smrg printLine(xw, screen->cursorp.row, chr, getPrinterFlags(xw, NULL, 0)); 606e39b573cSmrg if (SPS.fp != 0) 607e39b573cSmrg fflush(SPS.fp); 608d522f475Smrg } 609d522f475Smrg} 610d522f475Smrg 611d522f475Smrg/* 612d522f475Smrg * When in printer controller mode, the terminal sends received characters to 613d522f475Smrg * the printer without displaying them on the screen. The terminal sends all 614d522f475Smrg * characters and control sequences to the printer, except NUL, XON, XOFF, and 615d522f475Smrg * the printer controller sequences. 616d522f475Smrg * 617d522f475Smrg * This function eats characters, returning 0 as long as it must buffer or 618d522f475Smrg * divert to the printer. We're only invoked here when in printer controller 619d522f475Smrg * mode, and handle the exit from that mode. 620d522f475Smrg */ 621d522f475Smrg#define LB '[' 622d522f475Smrg 623d522f475Smrgint 624956cc18dSsnjxtermPrinterControl(XtermWidget xw, int chr) 625d522f475Smrg{ 626956cc18dSsnj TScreen *screen = TScreenOf(xw); 627d522f475Smrg /* *INDENT-OFF* */ 628e39b573cSmrg static const struct { 629e39b573cSmrg const Char seq[5]; 630d522f475Smrg int active; 631d522f475Smrg } tbl[] = { 632d522f475Smrg { { ANSI_CSI, '5', 'i' }, 2 }, 633d522f475Smrg { { ANSI_CSI, '4', 'i' }, 0 }, 634d522f475Smrg { { ANSI_ESC, LB, '5', 'i' }, 2 }, 635d522f475Smrg { { ANSI_ESC, LB, '4', 'i' }, 0 }, 636d522f475Smrg }; 637d522f475Smrg /* *INDENT-ON* */ 638d522f475Smrg 639d522f475Smrg static Char bfr[10]; 640d522f475Smrg static size_t length; 641d522f475Smrg size_t n; 642d522f475Smrg 643d522f475Smrg TRACE(("In printer:%04X\n", chr)); 644d522f475Smrg 645d522f475Smrg switch (chr) { 646d522f475Smrg case 0: 647d522f475Smrg case CTRL('Q'): 648d522f475Smrg case CTRL('S'): 649d522f475Smrg return 0; /* ignored by application */ 650d522f475Smrg 651d522f475Smrg case ANSI_CSI: 652d522f475Smrg case ANSI_ESC: 653d522f475Smrg case '[': 654d522f475Smrg case '4': 655d522f475Smrg case '5': 656d522f475Smrg case 'i': 6574e40088cSchristos bfr[length++] = CharOf(chr); 658d522f475Smrg for (n = 0; n < sizeof(tbl) / sizeof(tbl[0]); n++) { 659d522f475Smrg size_t len = Strlen(tbl[n].seq); 660d522f475Smrg 661d522f475Smrg if (length == len 662d522f475Smrg && Strcmp(bfr, tbl[n].seq) == 0) { 663956cc18dSsnj setPrinterControlMode(xw, tbl[n].active); 664e39b573cSmrg if (SPS.printer_autoclose 665e39b573cSmrg && SPS.printer_controlmode == 0) 666956cc18dSsnj closePrinter(xw); 667d522f475Smrg length = 0; 668d522f475Smrg return 0; 669d522f475Smrg } else if (len > length 670d522f475Smrg && Strncmp(bfr, tbl[n].seq, length) == 0) { 671d522f475Smrg return 0; 672d522f475Smrg } 673d522f475Smrg } 674d522f475Smrg length--; 675d522f475Smrg 676d522f475Smrg /* FALLTHRU */ 677d522f475Smrg 678d522f475Smrg default: 679d522f475Smrg for (n = 0; n < length; n++) 680956cc18dSsnj charToPrinter(xw, bfr[n]); 6814e40088cSchristos bfr[0] = CharOf(chr); 682d522f475Smrg length = 1; 683d522f475Smrg return 0; 684d522f475Smrg } 685d522f475Smrg} 686d522f475Smrg 687d522f475Smrg/* 688d522f475Smrg * If there is no printer command, we will ignore printer controls. 6890bd37d32Smrg * 6900bd37d32Smrg * If we do have a printer command, we still have to verify that it will 6910bd37d32Smrg * (perhaps) work if we pass it to popen(). At a minimum, the program 6920bd37d32Smrg * must exist and be executable. If not, warn and disable the feature. 693d522f475Smrg */ 694d522f475SmrgBool 695956cc18dSsnjxtermHasPrinter(XtermWidget xw) 696d522f475Smrg{ 697956cc18dSsnj TScreen *screen = TScreenOf(xw); 6980bd37d32Smrg Bool result = SPS.printer_checked; 6990bd37d32Smrg 7000bd37d32Smrg if (strlen(SPS.printer_command) != 0 && !result) { 7010bd37d32Smrg char **argv = x_splitargs(SPS.printer_command); 7020bd37d32Smrg if (argv) { 7030bd37d32Smrg if (argv[0]) { 7040bd37d32Smrg char *myShell = xtermFindShell(argv[0], False); 7050bd37d32Smrg if (myShell == 0) { 7060bd37d32Smrg xtermWarning("No program found for printerCommand: %s\n", SPS.printer_command); 7070bd37d32Smrg SPS.printer_command = x_strdup(""); 7080bd37d32Smrg } else { 7090bd37d32Smrg free(myShell); 7100bd37d32Smrg SPS.printer_checked = True; 7110bd37d32Smrg result = True; 7120bd37d32Smrg } 7130bd37d32Smrg } 7140bd37d32Smrg x_freeargs(argv); 7150bd37d32Smrg } 7160bd37d32Smrg TRACE(("xtermHasPrinter:%d\n", result)); 7170bd37d32Smrg } 718d522f475Smrg 7190bd37d32Smrg return result; 720d522f475Smrg} 721d522f475Smrg 722d522f475Smrg#define showPrinterControlMode(mode) \ 723d522f475Smrg (((mode) == 0) \ 724d522f475Smrg ? "normal" \ 725d522f475Smrg : ((mode) == 1 \ 726d522f475Smrg ? "autoprint" \ 727d522f475Smrg : "printer controller")) 728d522f475Smrg 729d522f475Smrgvoid 730956cc18dSsnjsetPrinterControlMode(XtermWidget xw, int mode) 731d522f475Smrg{ 73220d2c4d2Smrg TScreen *screen = TScreenOf(xw); 73320d2c4d2Smrg 734956cc18dSsnj if (xtermHasPrinter(xw) 735e39b573cSmrg && SPS.printer_controlmode != mode) { 736d522f475Smrg TRACE(("%s %s mode\n", 737d522f475Smrg (mode 738d522f475Smrg ? "set" 739d522f475Smrg : "reset"), 740d522f475Smrg (mode 741d522f475Smrg ? showPrinterControlMode(mode) 742e39b573cSmrg : showPrinterControlMode(SPS.printer_controlmode)))); 743e39b573cSmrg SPS.printer_controlmode = mode; 744d522f475Smrg update_print_redir(); 745d522f475Smrg } 746d522f475Smrg} 74720d2c4d2Smrg 74820d2c4d2SmrgPrinterFlags * 749894e0ac8SmrggetPrinterFlags(XtermWidget xw, String *params, Cardinal *param_count) 75020d2c4d2Smrg{ 75120d2c4d2Smrg /* *INDENT-OFF* */ 75220d2c4d2Smrg static const struct { 75320d2c4d2Smrg const char *name; 75420d2c4d2Smrg unsigned offset; 75520d2c4d2Smrg int value; 75620d2c4d2Smrg } table[] = { 75720d2c4d2Smrg { "noFormFeed", XtOffsetOf(PrinterFlags, printer_formfeed), 0 }, 75820d2c4d2Smrg { "FormFeed", XtOffsetOf(PrinterFlags, printer_formfeed), 1 }, 75920d2c4d2Smrg { "noNewLine", XtOffsetOf(PrinterFlags, printer_newline), 0 }, 76020d2c4d2Smrg { "NewLine", XtOffsetOf(PrinterFlags, printer_newline), 1 }, 76120d2c4d2Smrg { "noAttrs", XtOffsetOf(PrinterFlags, print_attributes), 0 }, 76220d2c4d2Smrg { "monoAttrs", XtOffsetOf(PrinterFlags, print_attributes), 1 }, 76320d2c4d2Smrg { "colorAttrs", XtOffsetOf(PrinterFlags, print_attributes), 2 }, 76420d2c4d2Smrg }; 76520d2c4d2Smrg /* *INDENT-ON* */ 76620d2c4d2Smrg 76720d2c4d2Smrg TScreen *screen = TScreenOf(xw); 76820d2c4d2Smrg PrinterFlags *result = &(screen->printer_flags); 76920d2c4d2Smrg 77020d2c4d2Smrg TRACE(("getPrinterFlags %d params\n", param_count ? *param_count : 0)); 77120d2c4d2Smrg 772e39b573cSmrg result->printer_extent = SPS.printer_extent; 773e39b573cSmrg result->printer_formfeed = SPS.printer_formfeed; 774e39b573cSmrg result->printer_newline = SPS.printer_newline; 775e39b573cSmrg result->print_attributes = SPS.print_attributes; 776e39b573cSmrg result->print_everything = SPS.print_everything; 77720d2c4d2Smrg 77820d2c4d2Smrg if (param_count != 0 && *param_count != 0) { 77920d2c4d2Smrg Cardinal j; 78020d2c4d2Smrg unsigned k; 78120d2c4d2Smrg for (j = 0; j < *param_count; ++j) { 78220d2c4d2Smrg TRACE(("param%d:%s\n", j, params[j])); 78320d2c4d2Smrg for (k = 0; k < XtNumber(table); ++k) { 78420d2c4d2Smrg if (!x_strcasecmp(params[j], table[k].name)) { 785a1f3da82Smrg int *ptr = (int *) (void *) ((char *) result + table[k].offset); 78620d2c4d2Smrg TRACE(("...PrinterFlags(%s) %d->%d\n", 78720d2c4d2Smrg table[k].name, 78820d2c4d2Smrg *ptr, 78920d2c4d2Smrg table[k].value)); 79020d2c4d2Smrg *ptr = table[k].value; 79120d2c4d2Smrg break; 79220d2c4d2Smrg } 79320d2c4d2Smrg } 79420d2c4d2Smrg } 79520d2c4d2Smrg } 79620d2c4d2Smrg 79720d2c4d2Smrg return result; 79820d2c4d2Smrg} 799e39b573cSmrg 800e39b573cSmrg/* 801e39b573cSmrg * Print a timestamped copy of everything. 802e39b573cSmrg */ 803e39b573cSmrgvoid 804e39b573cSmrgxtermPrintImmediately(XtermWidget xw, String filename, int opts, int attrs) 805e39b573cSmrg{ 806e39b573cSmrg TScreen *screen = TScreenOf(xw); 807e39b573cSmrg PrinterState save_state = screen->printer_state; 808e39b573cSmrg char *my_filename = malloc(TIMESTAMP_LEN + strlen(filename)); 809e39b573cSmrg 810e39b573cSmrg if (my_filename != 0) { 811e39b573cSmrg unsigned save_umask = umask(0177); 812e39b573cSmrg 813e39b573cSmrg timestamp_filename(my_filename, filename); 814e39b573cSmrg SPS.fp = 0; 815e39b573cSmrg SPS.isOpen = False; 816e39b573cSmrg SPS.toFile = True; 817e39b573cSmrg SPS.printer_command = my_filename; 818e39b573cSmrg SPS.printer_autoclose = True; 819e39b573cSmrg SPS.printer_formfeed = False; 820e39b573cSmrg SPS.printer_newline = True; 821e39b573cSmrg SPS.print_attributes = attrs; 822e39b573cSmrg SPS.print_everything = opts; 823e39b573cSmrg xtermPrintEverything(xw, getPrinterFlags(xw, NULL, 0)); 824e39b573cSmrg 825e39b573cSmrg umask(save_umask); 826e39b573cSmrg screen->printer_state = save_state; 827e39b573cSmrg } 828e39b573cSmrg} 829e39b573cSmrg 830e39b573cSmrgvoid 831e39b573cSmrgxtermPrintOnXError(XtermWidget xw, int n) 832e39b573cSmrg{ 833e39b573cSmrg#if OPT_PRINT_ON_EXIT 834e39b573cSmrg /* 835e39b573cSmrg * The user may have requested that the contents of the screen will be 836e39b573cSmrg * written to a file if an X error occurs. 837e39b573cSmrg */ 838e39b573cSmrg if (TScreenOf(xw)->write_error && !IsEmpty(resource.printFileOnXError)) { 839e39b573cSmrg Boolean printIt = False; 840e39b573cSmrg 841e39b573cSmrg switch (n) { 842e39b573cSmrg case ERROR_XERROR: 843e39b573cSmrg /* FALLTHRU */ 844e39b573cSmrg case ERROR_XIOERROR: 845e39b573cSmrg /* FALLTHRU */ 846e39b573cSmrg case ERROR_ICEERROR: 847e39b573cSmrg printIt = True; 848e39b573cSmrg break; 849e39b573cSmrg } 850e39b573cSmrg 851e39b573cSmrg if (printIt) { 852e39b573cSmrg xtermPrintImmediately(xw, 853e39b573cSmrg resource.printFileOnXError, 854e39b573cSmrg resource.printOptsOnXError, 855e39b573cSmrg resource.printModeOnXError); 856e39b573cSmrg } 857e39b573cSmrg } 858e39b573cSmrg#else 859e39b573cSmrg (void) xw; 860e39b573cSmrg (void) n; 861e39b573cSmrg#endif 862e39b573cSmrg} 863