print.c revision 04b94745
104b94745Smrg/* $XTermId: print.c,v 1.176 2023/11/24 12:16:37 tom Exp $ */ 2d522f475Smrg 30bd37d32Smrg/* 404b94745Smrg * Copyright 1997-2022,2023 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 77f2e35a3aSmrg#if OPT_PRINT_GRAPHICS 78f2e35a3aSmrgstatic void setGraphicsPrintToHost(XtermWidget /* xw */ , 79f2e35a3aSmrg int /* enabled */ ); 80f2e35a3aSmrg#else 81ad37e533Smrg#define setGraphicsPrintToHost(xw, enabled) /* nothing */ 82f2e35a3aSmrg#endif 83f2e35a3aSmrg 840bd37d32Smrgstatic void 85f2e35a3aSmrgclosePrinter(XtermWidget xw) 86d522f475Smrg{ 87f2e35a3aSmrg TScreen *screen = TScreenOf(xw); 88f2e35a3aSmrg if (SPS.fp != 0) { 89f2e35a3aSmrg if (SPS.toFile) { 90f2e35a3aSmrg fclose(SPS.fp); 91f2e35a3aSmrg SPS.fp = 0; 92f2e35a3aSmrg } else if (xtermHasPrinter(xw) != 0) { 93e39b573cSmrg#ifdef VMS 94f2e35a3aSmrg char pcommand[256]; 95f2e35a3aSmrg (void) sprintf(pcommand, "%s %s;", 96f2e35a3aSmrg SPS.printer_command, 97f2e35a3aSmrg VMS_TEMP_PRINT_FILE); 98d522f475Smrg#endif 99d522f475Smrg 1000bd37d32Smrg DEBUG_MSG("closePrinter\n"); 1010bd37d32Smrg pclose(SPS.fp); 102d522f475Smrg TRACE(("closed printer, waiting...\n")); 103d522f475Smrg#ifdef VMS /* This is a quick hack, really should use 104d522f475Smrg spawn and check status or system services 105d522f475Smrg and go straight to the queue */ 106d522f475Smrg (void) system(pcommand); 107d522f475Smrg#else /* VMS */ 1080bd37d32Smrg while (nonblocking_wait() > 0) { 109d522f475Smrg ; 1100bd37d32Smrg } 1110bd37d32Smrg#endif /* VMS */ 112e39b573cSmrg SPS.fp = 0; 113e39b573cSmrg SPS.isOpen = False; 114d522f475Smrg TRACE(("closed printer\n")); 1150bd37d32Smrg DEBUG_MSG("...closePrinter (done)\n"); 116d522f475Smrg } 117d522f475Smrg } 118d522f475Smrg} 119d522f475Smrg 120d522f475Smrgstatic void 121956cc18dSsnjprintCursorLine(XtermWidget xw) 122d522f475Smrg{ 123956cc18dSsnj TScreen *screen = TScreenOf(xw); 124d522f475Smrg 125d522f475Smrg TRACE(("printCursorLine\n")); 12620d2c4d2Smrg printLine(xw, screen->cur_row, '\n', getPrinterFlags(xw, NULL, 0)); 127d522f475Smrg} 128d522f475Smrg 129d522f475Smrg/* 130d522f475Smrg * DEC's manual doesn't document whether trailing blanks are removed, or what 131d522f475Smrg * happens with a line that is entirely blank. This function prints the 132d522f475Smrg * characters that xterm would allow as a selection (which may include blanks). 133d522f475Smrg */ 134d522f475Smrgstatic void 135894e0ac8SmrgprintLine(XtermWidget xw, int row, unsigned chr, PrinterFlags *p) 136d522f475Smrg{ 137956cc18dSsnj TScreen *screen = TScreenOf(xw); 138d522f475Smrg int inx = ROW2INX(screen, row); 139956cc18dSsnj LineData *ld; 140d522f475Smrg int last = MaxCols(screen); 141d522f475Smrg#if OPT_ISO_COLORS && OPT_PRINT_COLORS 142956cc18dSsnj#define ColorOf(ld,col) (ld->color[col]) 143d522f475Smrg#endif 144f2e35a3aSmrg Pixel fg = NO_COLOR; 145f2e35a3aSmrg Pixel bg = NO_COLOR; 146913cc679Smrg#if OPT_PRINT_COLORS 147f2e35a3aSmrg Pixel last_fg = NO_COLOR; 148f2e35a3aSmrg Pixel last_bg = NO_COLOR; 149913cc679Smrg#endif 150d522f475Smrg 151956cc18dSsnj ld = getLineData(screen, inx); 15220d2c4d2Smrg if (ld == 0) 15320d2c4d2Smrg return; 15420d2c4d2Smrg 155d522f475Smrg TRACE(("printLine(row=%d/%d, top=%d:%d, chr=%d):%s\n", 156d522f475Smrg row, ROW2INX(screen, row), screen->topline, screen->max_row, chr, 157956cc18dSsnj visibleIChars(ld->charData, (unsigned) last))); 158956cc18dSsnj 159d522f475Smrg while (last > 0) { 160956cc18dSsnj if ((ld->attribs[last - 1] & CHARDRAWN) == 0) 161d522f475Smrg last--; 162d522f475Smrg else 163d522f475Smrg break; 164d522f475Smrg } 1652e4f8982Smrg 166d522f475Smrg if (last) { 1672e4f8982Smrg int col; 1682e4f8982Smrg int cs = CSET_IN; 1692e4f8982Smrg int last_cs = CSET_IN; 1702e4f8982Smrg 17120d2c4d2Smrg if (p->print_attributes) { 172956cc18dSsnj send_CharSet(xw, ld); 173956cc18dSsnj send_SGR(xw, 0, NO_COLOR, NO_COLOR); 174d522f475Smrg } 175d522f475Smrg for (col = 0; col < last; col++) { 1762e4f8982Smrg IAttr attr = 0; 1772e4f8982Smrg unsigned ch = ld->charData[col]; 178d522f475Smrg#if OPT_PRINT_COLORS 179d522f475Smrg if (screen->colorMode) { 18020d2c4d2Smrg if (p->print_attributes > 1) { 181956cc18dSsnj fg = (ld->attribs[col] & FG_COLOR) 182956cc18dSsnj ? extract_fg(xw, ColorOf(ld, col), ld->attribs[col]) 183d522f475Smrg : NO_COLOR; 184956cc18dSsnj bg = (ld->attribs[col] & BG_COLOR) 185956cc18dSsnj ? extract_bg(xw, ColorOf(ld, col), ld->attribs[col]) 186d522f475Smrg : NO_COLOR; 187d522f475Smrg } 188d522f475Smrg } 189d522f475Smrg#endif 190f2e35a3aSmrg if ((((ld->attribs[col] & ATTRIBUTES) != attr) 191d522f475Smrg#if OPT_PRINT_COLORS 192d522f475Smrg || (last_fg != fg) || (last_bg != bg) 193d522f475Smrg#endif 194d522f475Smrg ) 195d522f475Smrg && ch) { 196f2e35a3aSmrg attr = (IAttr) (ld->attribs[col] & ATTRIBUTES); 19720d2c4d2Smrg#if OPT_PRINT_COLORS 198d522f475Smrg last_fg = fg; 199d522f475Smrg last_bg = bg; 20020d2c4d2Smrg#endif 20120d2c4d2Smrg if (p->print_attributes) 202f2e35a3aSmrg send_SGR(xw, attr, (unsigned) fg, (unsigned) bg); 203d522f475Smrg } 204d522f475Smrg 205d522f475Smrg if (ch == 0) 206d522f475Smrg ch = ' '; 207d522f475Smrg 208d522f475Smrg#if OPT_WIDE_CHARS 209d522f475Smrg if (screen->utf8_mode) 210d522f475Smrg cs = CSET_IN; 211d522f475Smrg else 212d522f475Smrg#endif 213d522f475Smrg cs = (ch >= ' ' && ch != ANSI_DEL) ? CSET_IN : CSET_OUT; 214d522f475Smrg if (last_cs != cs) { 21520d2c4d2Smrg if (p->print_attributes) { 216956cc18dSsnj charToPrinter(xw, 217956cc18dSsnj (unsigned) ((cs == CSET_OUT) 218d522f475Smrg ? SHIFT_OUT 219d522f475Smrg : SHIFT_IN)); 220d522f475Smrg } 221d522f475Smrg last_cs = cs; 222d522f475Smrg } 223d522f475Smrg 224d522f475Smrg /* FIXME: we shouldn't have to map back from the 225d522f475Smrg * alternate character set, except that the 226d522f475Smrg * corresponding charset information is not encoded 227d522f475Smrg * into the CSETS array. 228d522f475Smrg */ 229956cc18dSsnj charToPrinter(xw, 230956cc18dSsnj ((cs == CSET_OUT) 231d522f475Smrg ? (ch == ANSI_DEL ? 0x5f : (ch + 0x5f)) 232d522f475Smrg : ch)); 233d522f475Smrg if_OPT_WIDE_CHARS(screen, { 234956cc18dSsnj size_t off; 235956cc18dSsnj for_each_combData(off, ld) { 236956cc18dSsnj ch = ld->combData[off][col]; 237956cc18dSsnj if (ch == 0) 238d522f475Smrg break; 239956cc18dSsnj charToPrinter(xw, ch); 240d522f475Smrg } 241d522f475Smrg }); 242d522f475Smrg } 24320d2c4d2Smrg if (p->print_attributes) { 244956cc18dSsnj send_SGR(xw, 0, NO_COLOR, NO_COLOR); 245d522f475Smrg if (cs != CSET_IN) 246956cc18dSsnj charToPrinter(xw, SHIFT_IN); 247d522f475Smrg } 248d522f475Smrg } 24920d2c4d2Smrg 25020d2c4d2Smrg /* finish line (protocol for attributes needs a CR */ 25120d2c4d2Smrg if (p->print_attributes) 252956cc18dSsnj charToPrinter(xw, '\r'); 25320d2c4d2Smrg 25420d2c4d2Smrg if (chr && !(p->printer_newline)) { 25520d2c4d2Smrg if (LineTstWrapped(ld)) 25620d2c4d2Smrg chr = '\0'; 25720d2c4d2Smrg } 25820d2c4d2Smrg 25920d2c4d2Smrg if (chr) 26020d2c4d2Smrg charToPrinter(xw, chr); 261956cc18dSsnj 262956cc18dSsnj return; 263d522f475Smrg} 264d522f475Smrg 26520d2c4d2Smrg#define PrintNewLine() (unsigned) (((top < bot) || p->printer_newline) ? '\n' : '\0') 26620d2c4d2Smrg 267e39b573cSmrgstatic void 268894e0ac8SmrgprintLines(XtermWidget xw, int top, int bot, PrinterFlags *p) 269e39b573cSmrg{ 270e39b573cSmrg TRACE(("printLines, rows %d..%d\n", top, bot)); 271e39b573cSmrg while (top <= bot) { 272e39b573cSmrg printLine(xw, top, PrintNewLine(), p); 273e39b573cSmrg ++top; 274e39b573cSmrg } 275e39b573cSmrg} 276e39b573cSmrg 277d522f475Smrgvoid 278894e0ac8SmrgxtermPrintScreen(XtermWidget xw, Bool use_DECPEX, PrinterFlags *p) 279d522f475Smrg{ 280956cc18dSsnj if (XtIsRealized((Widget) xw)) { 281956cc18dSsnj TScreen *screen = TScreenOf(xw); 28220d2c4d2Smrg Bool extent = (use_DECPEX && p->printer_extent); 283e39b573cSmrg Boolean was_open = SPS.isOpen; 284d522f475Smrg 285e39b573cSmrg printLines(xw, 286e39b573cSmrg extent ? 0 : screen->top_marg, 287e39b573cSmrg extent ? screen->max_row : screen->bot_marg, 288e39b573cSmrg p); 28920d2c4d2Smrg if (p->printer_formfeed) 290956cc18dSsnj charToPrinter(xw, '\f'); 291d522f475Smrg 292e39b573cSmrg if (!was_open || SPS.printer_autoclose) { 293956cc18dSsnj closePrinter(xw); 294d522f475Smrg } 295d522f475Smrg } else { 29620d2c4d2Smrg Bell(xw, XkbBI_MinorError, 0); 297d522f475Smrg } 298d522f475Smrg} 299d522f475Smrg 300d522f475Smrg/* 301e39b573cSmrg * If p->print_everything is zero, use this behavior: 302d522f475Smrg * If the alternate screen is active, we'll print only that. Otherwise, print 303d522f475Smrg * the normal screen plus all scrolled-back lines. The distinction is made 304d522f475Smrg * because the normal screen's buffer is part of the overall scrollback buffer. 305e39b573cSmrg * 306e39b573cSmrg * Otherwise, decode bits: 307e39b573cSmrg * 1 = current screen 308e39b573cSmrg * 2 = normal screen 309e39b573cSmrg * 4 = alternate screen 310e39b573cSmrg * 8 = saved lines 311d522f475Smrg */ 312956cc18dSsnjvoid 313894e0ac8SmrgxtermPrintEverything(XtermWidget xw, PrinterFlags *p) 314d522f475Smrg{ 315956cc18dSsnj TScreen *screen = TScreenOf(xw); 316e39b573cSmrg Boolean was_open = SPS.isOpen; 317e39b573cSmrg int save_which = screen->whichBuf; 318d522f475Smrg 3190bd37d32Smrg DEBUG_MSG("xtermPrintEverything\n"); 3202e4f8982Smrg 321e39b573cSmrg if (p->print_everything) { 3222e4f8982Smrg int done_which = 0; 3232e4f8982Smrg 324e39b573cSmrg if (p->print_everything & 8) { 325e39b573cSmrg printLines(xw, -screen->savedlines, -(screen->topline + 1), p); 326e39b573cSmrg } 327e39b573cSmrg if (p->print_everything & 4) { 3285307cd1aSmrg SwitchBufPtrs(xw, 1); 329e39b573cSmrg done_which |= 2; 330e39b573cSmrg printLines(xw, 0, screen->max_row, p); 3315307cd1aSmrg SwitchBufPtrs(xw, save_which); 332e39b573cSmrg } 333e39b573cSmrg if (p->print_everything & 2) { 3345307cd1aSmrg SwitchBufPtrs(xw, 0); 335e39b573cSmrg done_which |= 1; 336e39b573cSmrg printLines(xw, 0, screen->max_row, p); 3375307cd1aSmrg SwitchBufPtrs(xw, save_which); 338e39b573cSmrg } 339e39b573cSmrg if (p->print_everything & 1) { 340e39b573cSmrg if (!(done_which & (1 << screen->whichBuf))) { 341e39b573cSmrg printLines(xw, 0, screen->max_row, p); 342e39b573cSmrg } 343e39b573cSmrg } 344e39b573cSmrg } else { 345e39b573cSmrg int top = 0; 346e39b573cSmrg int bot = screen->max_row; 347e39b573cSmrg if (!screen->whichBuf) { 348e39b573cSmrg top = -screen->savedlines - screen->topline; 349e39b573cSmrg bot -= screen->topline; 350e39b573cSmrg } 351e39b573cSmrg printLines(xw, top, bot, p); 35220d2c4d2Smrg } 35320d2c4d2Smrg if (p->printer_formfeed) 354956cc18dSsnj charToPrinter(xw, '\f'); 355d522f475Smrg 356e39b573cSmrg if (!was_open || SPS.printer_autoclose) { 357956cc18dSsnj closePrinter(xw); 358d522f475Smrg } 359d522f475Smrg} 360d522f475Smrg 361d522f475Smrgstatic void 362894e0ac8Smrgsend_CharSet(XtermWidget xw, LineData *ld) 363d522f475Smrg{ 364d522f475Smrg#if OPT_DEC_CHRSET 36520d2c4d2Smrg const char *msg = 0; 366d522f475Smrg 367956cc18dSsnj switch (GetLineDblCS(ld)) { 368d522f475Smrg case CSET_SWL: 369d522f475Smrg msg = "\033#5"; 370d522f475Smrg break; 371d522f475Smrg case CSET_DHL_TOP: 372d522f475Smrg msg = "\033#3"; 373d522f475Smrg break; 374d522f475Smrg case CSET_DHL_BOT: 375d522f475Smrg msg = "\033#4"; 376d522f475Smrg break; 377d522f475Smrg case CSET_DWL: 378d522f475Smrg msg = "\033#6"; 379d522f475Smrg break; 380d522f475Smrg } 381d522f475Smrg if (msg != 0) 382956cc18dSsnj stringToPrinter(xw, msg); 383d522f475Smrg#else 384956cc18dSsnj (void) xw; 385956cc18dSsnj (void) ld; 386d522f475Smrg#endif /* OPT_DEC_CHRSET */ 387d522f475Smrg} 388d522f475Smrg 389d522f475Smrgstatic void 390956cc18dSsnjsend_SGR(XtermWidget xw, unsigned attr, unsigned fg, unsigned bg) 391d522f475Smrg{ 392d522f475Smrg char msg[80]; 393e39b573cSmrg 394f2e35a3aSmrg#if OPT_ISO_COLORS && OPT_PC_COLORS 395f2e35a3aSmrg if ((attr & FG_COLOR) && (fg != NO_COLOR)) { 39620d2c4d2Smrg if (TScreenOf(xw)->boldColors 397d522f475Smrg && fg > 8 39804b94745Smrg && fg < 16 399d522f475Smrg && (attr & BOLD) != 0) 400d522f475Smrg fg -= 8; 401d522f475Smrg } 402d522f475Smrg#endif 403f2e35a3aSmrg strcpy(msg, "\033["); 404f2e35a3aSmrg xtermFormatSGR(xw, msg + strlen(msg), attr, (int) fg, (int) bg); 405d522f475Smrg strcat(msg, "m"); 406956cc18dSsnj stringToPrinter(xw, msg); 407d522f475Smrg} 408d522f475Smrg 409d522f475Smrg/* 410d522f475Smrg * This implementation only knows how to write to a pipe. 411d522f475Smrg */ 412d522f475Smrgstatic void 413956cc18dSsnjcharToPrinter(XtermWidget xw, unsigned chr) 414d522f475Smrg{ 415956cc18dSsnj TScreen *screen = TScreenOf(xw); 416d522f475Smrg 41704b94745Smrg#if OPT_WIDE_CHARS 41804b94745Smrg if (screen->wide_chars && screen->utf8_mode) { 41904b94745Smrg if (chr == UCS_REPL) { 42004b94745Smrg stringToPrinter(xw, screen->default_string); 42104b94745Smrg return; 42204b94745Smrg } 42304b94745Smrg } 42404b94745Smrg#endif 42504b94745Smrg if (is_NON_CHAR(chr)) 42604b94745Smrg return; 42704b94745Smrg 428f2e35a3aSmrg if (!SPS.isOpen && (SPS.toFile || xtermHasPrinter(xw))) { 429e39b573cSmrg switch (SPS.toFile) { 430e39b573cSmrg /* 431e39b573cSmrg * write to a pipe. 432e39b573cSmrg */ 433e39b573cSmrg case False: 434e39b573cSmrg#ifdef VMS 435e39b573cSmrg /* 436e39b573cSmrg * This implementation only knows how to write to a file. When the 437e39b573cSmrg * file is closed the print command executes. Print command must 438e39b573cSmrg * be of the form: 439f2e35a3aSmrg * print/queue=name/delete [/otherflags]. 440e39b573cSmrg */ 441e39b573cSmrg SPS.fp = fopen(VMS_TEMP_PRINT_FILE, "w"); 442d522f475Smrg#else 443e39b573cSmrg { 444e39b573cSmrg int my_pipe[2]; 445e39b573cSmrg pid_t my_pid; 446e39b573cSmrg 447e39b573cSmrg if (pipe(my_pipe)) 448e39b573cSmrg SysError(ERROR_FORK); 449e39b573cSmrg if ((my_pid = fork()) < 0) 450e39b573cSmrg SysError(ERROR_FORK); 451e39b573cSmrg 452e39b573cSmrg if (my_pid == 0) { 4530bd37d32Smrg DEBUG_MSG("charToPrinter: subprocess for printer\n"); 454e39b573cSmrg TRACE_CLOSE(); 455e39b573cSmrg close(my_pipe[1]); /* printer is silent */ 456e39b573cSmrg close(screen->respond); 457e39b573cSmrg 458e39b573cSmrg close(fileno(stdout)); 459e39b573cSmrg dup2(fileno(stderr), 1); 460e39b573cSmrg 461e39b573cSmrg if (fileno(stderr) != 2) { 462e39b573cSmrg dup2(fileno(stderr), 2); 463e39b573cSmrg close(fileno(stderr)); 464e39b573cSmrg } 465e39b573cSmrg 466e39b573cSmrg /* don't want privileges! */ 467e39b573cSmrg if (xtermResetIds(screen) < 0) 46804b94745Smrg exit(ERROR_MISC); 469e39b573cSmrg 470e39b573cSmrg SPS.fp = popen(SPS.printer_command, "w"); 4710bd37d32Smrg if (SPS.fp != 0) { 4722e4f8982Smrg FILE *input; 4730bd37d32Smrg DEBUG_MSG("charToPrinter: opened pipe to printer\n"); 474913cc679Smrg if ((input = fdopen(my_pipe[0], "r")) != 0) { 475913cc679Smrg clearerr(input); 476913cc679Smrg 477913cc679Smrg for (;;) { 478913cc679Smrg int c; 479913cc679Smrg 480913cc679Smrg if (ferror(input)) { 481913cc679Smrg DEBUG_MSG("charToPrinter: break on ferror\n"); 482913cc679Smrg break; 483913cc679Smrg } else if (feof(input)) { 484913cc679Smrg DEBUG_MSG("charToPrinter: break on feof\n"); 485913cc679Smrg break; 486913cc679Smrg } else if ((c = fgetc(input)) == EOF) { 487913cc679Smrg DEBUG_MSG("charToPrinter: break on EOF\n"); 488913cc679Smrg break; 489913cc679Smrg } 490913cc679Smrg fputc(c, SPS.fp); 491913cc679Smrg if (isForm(c)) 492913cc679Smrg fflush(SPS.fp); 4930bd37d32Smrg } 4940bd37d32Smrg } 4950bd37d32Smrg DEBUG_MSG("charToPrinter: calling pclose\n"); 4960bd37d32Smrg pclose(SPS.fp); 497913cc679Smrg if (input) 498913cc679Smrg fclose(input); 499e39b573cSmrg } 500e39b573cSmrg exit(0); 501e39b573cSmrg } else { 502e39b573cSmrg close(my_pipe[0]); /* won't read from printer */ 5030bd37d32Smrg if ((SPS.fp = fdopen(my_pipe[1], "w")) != 0) { 5040bd37d32Smrg DEBUG_MSG("charToPrinter: opened printer in parent\n"); 5050bd37d32Smrg TRACE(("opened printer from pid %d/%d\n", 5060bd37d32Smrg (int) getpid(), (int) my_pid)); 5070bd37d32Smrg } else { 5080bd37d32Smrg TRACE(("failed to open printer:%s\n", strerror(errno))); 5090bd37d32Smrg DEBUG_MSG("charToPrinter: could not open in parent\n"); 5100bd37d32Smrg } 511e39b573cSmrg } 512d522f475Smrg } 513d522f475Smrg#endif 514e39b573cSmrg break; 515e39b573cSmrg case True: 516e39b573cSmrg TRACE(("opening \"%s\" as printer output\n", SPS.printer_command)); 517e39b573cSmrg SPS.fp = fopen(SPS.printer_command, "w"); 518e39b573cSmrg break; 519e39b573cSmrg } 520e39b573cSmrg SPS.isOpen = True; 521d522f475Smrg } 522e39b573cSmrg if (SPS.fp != 0) { 523d522f475Smrg#if OPT_WIDE_CHARS 524d522f475Smrg if (chr > 127) { 525d522f475Smrg Char temp[10]; 526d522f475Smrg *convertToUTF8(temp, chr) = 0; 527e39b573cSmrg fputs((char *) temp, SPS.fp); 528d522f475Smrg } else 529d522f475Smrg#endif 530e39b573cSmrg fputc((int) chr, SPS.fp); 531d522f475Smrg if (isForm(chr)) 532e39b573cSmrg fflush(SPS.fp); 533d522f475Smrg } 534d522f475Smrg} 535d522f475Smrg 536d522f475Smrgstatic void 53720d2c4d2SmrgstringToPrinter(XtermWidget xw, const char *str) 538d522f475Smrg{ 539d522f475Smrg while (*str) 540956cc18dSsnj charToPrinter(xw, CharOf(*str++)); 541d522f475Smrg} 542d522f475Smrg 543d522f475Smrg/* 544d522f475Smrg * This module implements the MC (Media Copy) and related printing control 545d522f475Smrg * sequences for VTxxx emulation. This is based on the description in the 546d522f475Smrg * VT330/VT340 Programmer Reference Manual EK-VT3XX-TP-001 (Digital Equipment 547d522f475Smrg * Corp., March 1987). 548d522f475Smrg */ 549d522f475Smrgvoid 550956cc18dSsnjxtermMediaControl(XtermWidget xw, int param, int private_seq) 551d522f475Smrg{ 552d522f475Smrg TRACE(("MediaCopy param=%d, private=%d\n", param, private_seq)); 553d522f475Smrg 554d522f475Smrg if (private_seq) { 555d522f475Smrg switch (param) { 556f2e35a3aSmrg case -1: 557f2e35a3aSmrg case 0: /* VT125 */ 558f2e35a3aSmrg setGraphicsPrintToHost(xw, 0); /* graphics to printer */ 559f2e35a3aSmrg break; 560d522f475Smrg case 1: 561956cc18dSsnj printCursorLine(xw); 562d522f475Smrg break; 563f2e35a3aSmrg case 2: /* VT125 */ 564f2e35a3aSmrg setGraphicsPrintToHost(xw, 1); /* graphics to host */ 565f2e35a3aSmrg break; 566d522f475Smrg case 4: 567f2e35a3aSmrg setPrinterControlMode(xw, 0); /* autoprint disable */ 568d522f475Smrg break; 569d522f475Smrg case 5: 570f2e35a3aSmrg setPrinterControlMode(xw, 1); /* autoprint enable */ 571d522f475Smrg break; 572d522f475Smrg case 10: /* VT320 */ 573f2e35a3aSmrg /* print whole screen, across sessions */ 57420d2c4d2Smrg xtermPrintScreen(xw, False, getPrinterFlags(xw, NULL, 0)); 575d522f475Smrg break; 576d522f475Smrg case 11: /* VT320 */ 577f2e35a3aSmrg /* print all pages in current session */ 57820d2c4d2Smrg xtermPrintEverything(xw, getPrinterFlags(xw, NULL, 0)); 579d522f475Smrg break; 580d522f475Smrg } 581d522f475Smrg } else { 582d522f475Smrg switch (param) { 583d522f475Smrg case -1: 584d522f475Smrg case 0: 58520d2c4d2Smrg xtermPrintScreen(xw, True, getPrinterFlags(xw, NULL, 0)); 586d522f475Smrg break; 587d522f475Smrg case 4: 588f2e35a3aSmrg setPrinterControlMode(xw, 0); /* printer controller mode off */ 589d522f475Smrg break; 590d522f475Smrg case 5: 591f2e35a3aSmrg setPrinterControlMode(xw, 2); /* printer controller mode on */ 592d522f475Smrg break; 5932e4f8982Smrg#if OPT_SCREEN_DUMPS 5942e4f8982Smrg case 10: 5952e4f8982Smrg xtermDumpHtml(xw); 5962e4f8982Smrg break; 5972e4f8982Smrg case 11: 5982e4f8982Smrg xtermDumpSvg(xw); 5992e4f8982Smrg break; 6002e4f8982Smrg#endif 601d522f475Smrg } 602d522f475Smrg } 603d522f475Smrg} 604d522f475Smrg 605d522f475Smrg/* 606d522f475Smrg * When in autoprint mode, the printer prints a line from the screen when you 607d522f475Smrg * move the cursor off that line with an LF, FF, or VT character, or an 608d522f475Smrg * autowrap occurs. The printed line ends with a CR and the character (LF, FF 609d522f475Smrg * or VT) that moved the cursor off the previous line. 610d522f475Smrg */ 611d522f475Smrgvoid 612956cc18dSsnjxtermAutoPrint(XtermWidget xw, unsigned chr) 613d522f475Smrg{ 614956cc18dSsnj TScreen *screen = TScreenOf(xw); 615d522f475Smrg 616e39b573cSmrg if (SPS.printer_controlmode == 1) { 617d522f475Smrg TRACE(("AutoPrint %d\n", chr)); 61820d2c4d2Smrg printLine(xw, screen->cursorp.row, chr, getPrinterFlags(xw, NULL, 0)); 619e39b573cSmrg if (SPS.fp != 0) 620e39b573cSmrg fflush(SPS.fp); 621d522f475Smrg } 622d522f475Smrg} 623d522f475Smrg 624d522f475Smrg/* 625d522f475Smrg * When in printer controller mode, the terminal sends received characters to 626d522f475Smrg * the printer without displaying them on the screen. The terminal sends all 627d522f475Smrg * characters and control sequences to the printer, except NUL, XON, XOFF, and 628d522f475Smrg * the printer controller sequences. 629d522f475Smrg * 630d522f475Smrg * This function eats characters, returning 0 as long as it must buffer or 631d522f475Smrg * divert to the printer. We're only invoked here when in printer controller 632d522f475Smrg * mode, and handle the exit from that mode. 633d522f475Smrg */ 634d522f475Smrg#define LB '[' 635d522f475Smrg 636d522f475Smrgint 637956cc18dSsnjxtermPrinterControl(XtermWidget xw, int chr) 638d522f475Smrg{ 639956cc18dSsnj TScreen *screen = TScreenOf(xw); 640d522f475Smrg /* *INDENT-OFF* */ 641e39b573cSmrg static const struct { 642e39b573cSmrg const Char seq[5]; 643d522f475Smrg int active; 644d522f475Smrg } tbl[] = { 645d522f475Smrg { { ANSI_CSI, '5', 'i' }, 2 }, 646d522f475Smrg { { ANSI_CSI, '4', 'i' }, 0 }, 647d522f475Smrg { { ANSI_ESC, LB, '5', 'i' }, 2 }, 648d522f475Smrg { { ANSI_ESC, LB, '4', 'i' }, 0 }, 649d522f475Smrg }; 650d522f475Smrg /* *INDENT-ON* */ 651d522f475Smrg 652d522f475Smrg static Char bfr[10]; 653d522f475Smrg static size_t length; 654d522f475Smrg size_t n; 655d522f475Smrg 656d522f475Smrg TRACE(("In printer:%04X\n", chr)); 657d522f475Smrg 658d522f475Smrg switch (chr) { 659d522f475Smrg case 0: 660d522f475Smrg case CTRL('Q'): 661d522f475Smrg case CTRL('S'): 662d522f475Smrg return 0; /* ignored by application */ 663d522f475Smrg 664d522f475Smrg case ANSI_CSI: 665d522f475Smrg case ANSI_ESC: 666d522f475Smrg case '[': 667d522f475Smrg case '4': 668d522f475Smrg case '5': 669d522f475Smrg case 'i': 6704e40088cSchristos bfr[length++] = CharOf(chr); 671d522f475Smrg for (n = 0; n < sizeof(tbl) / sizeof(tbl[0]); n++) { 672d522f475Smrg size_t len = Strlen(tbl[n].seq); 673d522f475Smrg 674d522f475Smrg if (length == len 675d522f475Smrg && Strcmp(bfr, tbl[n].seq) == 0) { 676956cc18dSsnj setPrinterControlMode(xw, tbl[n].active); 677e39b573cSmrg if (SPS.printer_autoclose 678e39b573cSmrg && SPS.printer_controlmode == 0) 679956cc18dSsnj closePrinter(xw); 680d522f475Smrg length = 0; 681d522f475Smrg return 0; 682d522f475Smrg } else if (len > length 683d522f475Smrg && Strncmp(bfr, tbl[n].seq, length) == 0) { 684d522f475Smrg return 0; 685d522f475Smrg } 686d522f475Smrg } 687d522f475Smrg length--; 688d522f475Smrg 689d522f475Smrg /* FALLTHRU */ 690d522f475Smrg 691d522f475Smrg default: 692d522f475Smrg for (n = 0; n < length; n++) 693956cc18dSsnj charToPrinter(xw, bfr[n]); 6944e40088cSchristos bfr[0] = CharOf(chr); 695d522f475Smrg length = 1; 696d522f475Smrg return 0; 697d522f475Smrg } 698d522f475Smrg} 699d522f475Smrg 700d522f475Smrg/* 701d522f475Smrg * If there is no printer command, we will ignore printer controls. 7020bd37d32Smrg * 7030bd37d32Smrg * If we do have a printer command, we still have to verify that it will 7040bd37d32Smrg * (perhaps) work if we pass it to popen(). At a minimum, the program 7050bd37d32Smrg * must exist and be executable. If not, warn and disable the feature. 706d522f475Smrg */ 707d522f475SmrgBool 708956cc18dSsnjxtermHasPrinter(XtermWidget xw) 709d522f475Smrg{ 710956cc18dSsnj TScreen *screen = TScreenOf(xw); 7110bd37d32Smrg Bool result = SPS.printer_checked; 7120bd37d32Smrg 7130bd37d32Smrg if (strlen(SPS.printer_command) != 0 && !result) { 7140bd37d32Smrg char **argv = x_splitargs(SPS.printer_command); 7150bd37d32Smrg if (argv) { 7160bd37d32Smrg if (argv[0]) { 7170bd37d32Smrg char *myShell = xtermFindShell(argv[0], False); 7180bd37d32Smrg if (myShell == 0) { 7190bd37d32Smrg xtermWarning("No program found for printerCommand: %s\n", SPS.printer_command); 7200bd37d32Smrg SPS.printer_command = x_strdup(""); 7210bd37d32Smrg } else { 7220bd37d32Smrg free(myShell); 7230bd37d32Smrg SPS.printer_checked = True; 7240bd37d32Smrg result = True; 7250bd37d32Smrg } 7260bd37d32Smrg } 7270bd37d32Smrg x_freeargs(argv); 7280bd37d32Smrg } 7290bd37d32Smrg TRACE(("xtermHasPrinter:%d\n", result)); 7300bd37d32Smrg } 731d522f475Smrg 7320bd37d32Smrg return result; 733d522f475Smrg} 734d522f475Smrg 735f2e35a3aSmrg#if OPT_PRINT_GRAPHICS 736f2e35a3aSmrgstatic void 737f2e35a3aSmrgsetGraphicsPrintToHost(XtermWidget xw, int enabled) 738f2e35a3aSmrg{ 739f2e35a3aSmrg TScreen *screen = TScreenOf(xw); 740f2e35a3aSmrg 741f2e35a3aSmrg TRACE(("graphics print to host enabled=%d\n", enabled)); 742f2e35a3aSmrg screen->graphics_print_to_host = (Boolean) enabled; 743f2e35a3aSmrg} 744f2e35a3aSmrg#endif 745f2e35a3aSmrg 746d522f475Smrg#define showPrinterControlMode(mode) \ 747d522f475Smrg (((mode) == 0) \ 748d522f475Smrg ? "normal" \ 749d522f475Smrg : ((mode) == 1 \ 750d522f475Smrg ? "autoprint" \ 751d522f475Smrg : "printer controller")) 752d522f475Smrg 753d522f475Smrgvoid 754956cc18dSsnjsetPrinterControlMode(XtermWidget xw, int mode) 755d522f475Smrg{ 75620d2c4d2Smrg TScreen *screen = TScreenOf(xw); 75720d2c4d2Smrg 758956cc18dSsnj if (xtermHasPrinter(xw) 759e39b573cSmrg && SPS.printer_controlmode != mode) { 760d522f475Smrg TRACE(("%s %s mode\n", 761d522f475Smrg (mode 762d522f475Smrg ? "set" 763d522f475Smrg : "reset"), 764d522f475Smrg (mode 765d522f475Smrg ? showPrinterControlMode(mode) 766e39b573cSmrg : showPrinterControlMode(SPS.printer_controlmode)))); 767e39b573cSmrg SPS.printer_controlmode = mode; 768d522f475Smrg update_print_redir(); 769d522f475Smrg } 770d522f475Smrg} 77120d2c4d2Smrg 77220d2c4d2SmrgPrinterFlags * 773894e0ac8SmrggetPrinterFlags(XtermWidget xw, String *params, Cardinal *param_count) 77420d2c4d2Smrg{ 77520d2c4d2Smrg /* *INDENT-OFF* */ 77620d2c4d2Smrg static const struct { 77720d2c4d2Smrg const char *name; 77820d2c4d2Smrg unsigned offset; 77920d2c4d2Smrg int value; 78020d2c4d2Smrg } table[] = { 78120d2c4d2Smrg { "noFormFeed", XtOffsetOf(PrinterFlags, printer_formfeed), 0 }, 78220d2c4d2Smrg { "FormFeed", XtOffsetOf(PrinterFlags, printer_formfeed), 1 }, 78320d2c4d2Smrg { "noNewLine", XtOffsetOf(PrinterFlags, printer_newline), 0 }, 78420d2c4d2Smrg { "NewLine", XtOffsetOf(PrinterFlags, printer_newline), 1 }, 78520d2c4d2Smrg { "noAttrs", XtOffsetOf(PrinterFlags, print_attributes), 0 }, 78620d2c4d2Smrg { "monoAttrs", XtOffsetOf(PrinterFlags, print_attributes), 1 }, 78720d2c4d2Smrg { "colorAttrs", XtOffsetOf(PrinterFlags, print_attributes), 2 }, 78820d2c4d2Smrg }; 78920d2c4d2Smrg /* *INDENT-ON* */ 79020d2c4d2Smrg 79120d2c4d2Smrg TScreen *screen = TScreenOf(xw); 79220d2c4d2Smrg PrinterFlags *result = &(screen->printer_flags); 79320d2c4d2Smrg 79420d2c4d2Smrg TRACE(("getPrinterFlags %d params\n", param_count ? *param_count : 0)); 79520d2c4d2Smrg 796e39b573cSmrg result->printer_extent = SPS.printer_extent; 797e39b573cSmrg result->printer_formfeed = SPS.printer_formfeed; 798e39b573cSmrg result->printer_newline = SPS.printer_newline; 799e39b573cSmrg result->print_attributes = SPS.print_attributes; 800e39b573cSmrg result->print_everything = SPS.print_everything; 80120d2c4d2Smrg 80220d2c4d2Smrg if (param_count != 0 && *param_count != 0) { 80320d2c4d2Smrg Cardinal j; 80420d2c4d2Smrg unsigned k; 80520d2c4d2Smrg for (j = 0; j < *param_count; ++j) { 80620d2c4d2Smrg TRACE(("param%d:%s\n", j, params[j])); 80720d2c4d2Smrg for (k = 0; k < XtNumber(table); ++k) { 80820d2c4d2Smrg if (!x_strcasecmp(params[j], table[k].name)) { 809a1f3da82Smrg int *ptr = (int *) (void *) ((char *) result + table[k].offset); 81020d2c4d2Smrg TRACE(("...PrinterFlags(%s) %d->%d\n", 81120d2c4d2Smrg table[k].name, 81220d2c4d2Smrg *ptr, 81320d2c4d2Smrg table[k].value)); 81420d2c4d2Smrg *ptr = table[k].value; 81520d2c4d2Smrg break; 81620d2c4d2Smrg } 81720d2c4d2Smrg } 81820d2c4d2Smrg } 81920d2c4d2Smrg } 82020d2c4d2Smrg 82120d2c4d2Smrg return result; 82220d2c4d2Smrg} 823e39b573cSmrg 824e39b573cSmrg/* 825e39b573cSmrg * Print a timestamped copy of everything. 826e39b573cSmrg */ 827e39b573cSmrgvoid 828e39b573cSmrgxtermPrintImmediately(XtermWidget xw, String filename, int opts, int attrs) 829e39b573cSmrg{ 830e39b573cSmrg TScreen *screen = TScreenOf(xw); 831e39b573cSmrg PrinterState save_state = screen->printer_state; 832e39b573cSmrg char *my_filename = malloc(TIMESTAMP_LEN + strlen(filename)); 833e39b573cSmrg 834e39b573cSmrg if (my_filename != 0) { 835913cc679Smrg mode_t save_umask = umask(0177); 836e39b573cSmrg 837e39b573cSmrg timestamp_filename(my_filename, filename); 838e39b573cSmrg SPS.fp = 0; 839e39b573cSmrg SPS.isOpen = False; 840e39b573cSmrg SPS.toFile = True; 841e39b573cSmrg SPS.printer_command = my_filename; 842e39b573cSmrg SPS.printer_autoclose = True; 843e39b573cSmrg SPS.printer_formfeed = False; 844e39b573cSmrg SPS.printer_newline = True; 845e39b573cSmrg SPS.print_attributes = attrs; 846e39b573cSmrg SPS.print_everything = opts; 847e39b573cSmrg xtermPrintEverything(xw, getPrinterFlags(xw, NULL, 0)); 848e39b573cSmrg 849e39b573cSmrg umask(save_umask); 850e39b573cSmrg screen->printer_state = save_state; 851ad37e533Smrg free(my_filename); 852e39b573cSmrg } 853e39b573cSmrg} 854e39b573cSmrg 855e39b573cSmrgvoid 856e39b573cSmrgxtermPrintOnXError(XtermWidget xw, int n) 857e39b573cSmrg{ 858e39b573cSmrg#if OPT_PRINT_ON_EXIT 859e39b573cSmrg /* 860e39b573cSmrg * The user may have requested that the contents of the screen will be 861e39b573cSmrg * written to a file if an X error occurs. 862e39b573cSmrg */ 863e39b573cSmrg if (TScreenOf(xw)->write_error && !IsEmpty(resource.printFileOnXError)) { 864e39b573cSmrg Boolean printIt = False; 865e39b573cSmrg 866e39b573cSmrg switch (n) { 867e39b573cSmrg case ERROR_XERROR: 868e39b573cSmrg /* FALLTHRU */ 869e39b573cSmrg case ERROR_XIOERROR: 870e39b573cSmrg /* FALLTHRU */ 871e39b573cSmrg case ERROR_ICEERROR: 872e39b573cSmrg printIt = True; 873e39b573cSmrg break; 874e39b573cSmrg } 875e39b573cSmrg 876e39b573cSmrg if (printIt) { 877e39b573cSmrg xtermPrintImmediately(xw, 878e39b573cSmrg resource.printFileOnXError, 879e39b573cSmrg resource.printOptsOnXError, 880e39b573cSmrg resource.printModeOnXError); 881e39b573cSmrg } 882e39b573cSmrg } 883e39b573cSmrg#else 884e39b573cSmrg (void) xw; 885e39b573cSmrg (void) n; 886e39b573cSmrg#endif 887e39b573cSmrg} 888