"Fossies" - the Fresh Open Source Software Archive

Member "xterm-379/print.c" (18 Sep 2022, 21923 Bytes) of package /linux/misc/xterm-379.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "print.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 372_vs_373.

    1 /* $XTermId: print.c,v 1.173 2022/09/18 21:00:08 tom Exp $ */
    2 
    3 /*
    4  * Copyright 1997-2021,2022 by Thomas E. Dickey
    5  *
    6  *                         All Rights Reserved
    7  *
    8  * Permission is hereby granted, free of charge, to any person obtaining a
    9  * copy of this software and associated documentation files (the
   10  * "Software"), to deal in the Software without restriction, including
   11  * without limitation the rights to use, copy, modify, merge, publish,
   12  * distribute, sublicense, and/or sell copies of the Software, and to
   13  * permit persons to whom the Software is furnished to do so, subject to
   14  * the following conditions:
   15  *
   16  * The above copyright notice and this permission notice shall be included
   17  * in all copies or substantial portions of the Software.
   18  *
   19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   22  * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
   23  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   24  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   25  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   26  *
   27  * Except as contained in this notice, the name(s) of the above copyright
   28  * holders shall not be used in advertising or otherwise to promote the
   29  * sale, use or other dealings in this Software without prior written
   30  * authorization.
   31  */
   32 
   33 #include <xterm.h>
   34 #include <data.h>
   35 #include <menu.h>
   36 #include <error.h>
   37 #include <xstrings.h>
   38 
   39 #include <stdio.h>
   40 #include <sys/stat.h>
   41 
   42 #undef  CTRL
   43 #define CTRL(c) ((c) & 0x1f)
   44 
   45 #define SHIFT_IN  '\017'
   46 #define SHIFT_OUT '\016'
   47 
   48 #define CSET_IN   'A'
   49 #define CSET_OUT  '0'
   50 
   51 #define isForm(c)      ((c) == '\r' || (c) == '\n' || (c) == '\f')
   52 #define Strlen(a)      strlen((const char *)a)
   53 #define Strcmp(a,b)    strcmp((const char *)a,(const char *)b)
   54 #define Strncmp(a,b,c) strncmp((const char *)a,(const char *)b,c)
   55 
   56 #define SPS PrinterOf(screen)
   57 
   58 #ifdef VMS
   59 #define VMS_TEMP_PRINT_FILE "sys$scratch:xterm_print.txt"
   60 #endif
   61 
   62 static void charToPrinter(XtermWidget /* xw */ ,
   63               unsigned /* chr */ );
   64 static void printLine(XtermWidget /* xw */ ,
   65               int /* row */ ,
   66               unsigned /* chr */ ,
   67               PrinterFlags * /* p */ );
   68 static void send_CharSet(XtermWidget /* xw */ ,
   69              LineData * /* ld */ );
   70 static void send_SGR(XtermWidget /* xw */ ,
   71              unsigned /* attr */ ,
   72              unsigned /* fg */ ,
   73              unsigned /* bg */ );
   74 static void stringToPrinter(XtermWidget /* xw */ ,
   75                 const char * /*str */ );
   76 
   77 #if OPT_PRINT_GRAPHICS
   78 static void setGraphicsPrintToHost(XtermWidget /* xw */ ,
   79                    int /* enabled */ );
   80 #else
   81 #define setGraphicsPrintToHost(xw, enabled) /* nothing */
   82 #endif
   83 
   84 static void
   85 closePrinter(XtermWidget xw)
   86 {
   87     TScreen *screen = TScreenOf(xw);
   88     if (SPS.fp != 0) {
   89     if (SPS.toFile) {
   90         fclose(SPS.fp);
   91         SPS.fp = 0;
   92     } else if (xtermHasPrinter(xw) != 0) {
   93 #ifdef VMS
   94         char pcommand[256];
   95         (void) sprintf(pcommand, "%s %s;",
   96                SPS.printer_command,
   97                VMS_TEMP_PRINT_FILE);
   98 #endif
   99 
  100         DEBUG_MSG("closePrinter\n");
  101         pclose(SPS.fp);
  102         TRACE(("closed printer, waiting...\n"));
  103 #ifdef VMS          /* This is a quick hack, really should use
  104                    spawn and check status or system services
  105                    and go straight to the queue */
  106         (void) system(pcommand);
  107 #else /* VMS */
  108         while (nonblocking_wait() > 0) {
  109         ;
  110         }
  111 #endif /* VMS */
  112         SPS.fp = 0;
  113         SPS.isOpen = False;
  114         TRACE(("closed printer\n"));
  115         DEBUG_MSG("...closePrinter (done)\n");
  116     }
  117     }
  118 }
  119 
  120 static void
  121 printCursorLine(XtermWidget xw)
  122 {
  123     TScreen *screen = TScreenOf(xw);
  124 
  125     TRACE(("printCursorLine\n"));
  126     printLine(xw, screen->cur_row, '\n', getPrinterFlags(xw, NULL, 0));
  127 }
  128 
  129 /*
  130  * DEC's manual doesn't document whether trailing blanks are removed, or what
  131  * happens with a line that is entirely blank.  This function prints the
  132  * characters that xterm would allow as a selection (which may include blanks).
  133  */
  134 static void
  135 printLine(XtermWidget xw, int row, unsigned chr, PrinterFlags *p)
  136 {
  137     TScreen *screen = TScreenOf(xw);
  138     int inx = ROW2INX(screen, row);
  139     LineData *ld;
  140     int last = MaxCols(screen);
  141 #if OPT_ISO_COLORS && OPT_PRINT_COLORS
  142 #define ColorOf(ld,col) (ld->color[col])
  143 #endif
  144     Pixel fg = NO_COLOR;
  145     Pixel bg = NO_COLOR;
  146 #if OPT_PRINT_COLORS
  147     Pixel last_fg = NO_COLOR;
  148     Pixel last_bg = NO_COLOR;
  149 #endif
  150 
  151     ld = getLineData(screen, inx);
  152     if (ld == 0)
  153     return;
  154 
  155     TRACE(("printLine(row=%d/%d, top=%d:%d, chr=%d):%s\n",
  156        row, ROW2INX(screen, row), screen->topline, screen->max_row, chr,
  157        visibleIChars(ld->charData, (unsigned) last)));
  158 
  159     while (last > 0) {
  160     if ((ld->attribs[last - 1] & CHARDRAWN) == 0)
  161         last--;
  162     else
  163         break;
  164     }
  165 
  166     if (last) {
  167     int col;
  168     int cs = CSET_IN;
  169     int last_cs = CSET_IN;
  170 
  171     if (p->print_attributes) {
  172         send_CharSet(xw, ld);
  173         send_SGR(xw, 0, NO_COLOR, NO_COLOR);
  174     }
  175     for (col = 0; col < last; col++) {
  176         IAttr attr = 0;
  177         unsigned ch = ld->charData[col];
  178 #if OPT_PRINT_COLORS
  179         if (screen->colorMode) {
  180         if (p->print_attributes > 1) {
  181             fg = (ld->attribs[col] & FG_COLOR)
  182             ? extract_fg(xw, ColorOf(ld, col), ld->attribs[col])
  183             : NO_COLOR;
  184             bg = (ld->attribs[col] & BG_COLOR)
  185             ? extract_bg(xw, ColorOf(ld, col), ld->attribs[col])
  186             : NO_COLOR;
  187         }
  188         }
  189 #endif
  190         if ((((ld->attribs[col] & ATTRIBUTES) != attr)
  191 #if OPT_PRINT_COLORS
  192          || (last_fg != fg) || (last_bg != bg)
  193 #endif
  194         )
  195         && ch) {
  196         attr = (IAttr) (ld->attribs[col] & ATTRIBUTES);
  197 #if OPT_PRINT_COLORS
  198         last_fg = fg;
  199         last_bg = bg;
  200 #endif
  201         if (p->print_attributes)
  202             send_SGR(xw, attr, (unsigned) fg, (unsigned) bg);
  203         }
  204 
  205         if (ch == 0)
  206         ch = ' ';
  207 
  208 #if OPT_WIDE_CHARS
  209         if (screen->utf8_mode)
  210         cs = CSET_IN;
  211         else
  212 #endif
  213         cs = (ch >= ' ' && ch != ANSI_DEL) ? CSET_IN : CSET_OUT;
  214         if (last_cs != cs) {
  215         if (p->print_attributes) {
  216             charToPrinter(xw,
  217                   (unsigned) ((cs == CSET_OUT)
  218                           ? SHIFT_OUT
  219                           : SHIFT_IN));
  220         }
  221         last_cs = cs;
  222         }
  223 
  224         /* FIXME:  we shouldn't have to map back from the
  225          * alternate character set, except that the
  226          * corresponding charset information is not encoded
  227          * into the CSETS array.
  228          */
  229         charToPrinter(xw,
  230               ((cs == CSET_OUT)
  231                ? (ch == ANSI_DEL ? 0x5f : (ch + 0x5f))
  232                : ch));
  233         if_OPT_WIDE_CHARS(screen, {
  234         size_t off;
  235         for_each_combData(off, ld) {
  236             ch = ld->combData[off][col];
  237             if (ch == 0)
  238             break;
  239             charToPrinter(xw, ch);
  240         }
  241         });
  242     }
  243     if (p->print_attributes) {
  244         send_SGR(xw, 0, NO_COLOR, NO_COLOR);
  245         if (cs != CSET_IN)
  246         charToPrinter(xw, SHIFT_IN);
  247     }
  248     }
  249 
  250     /* finish line (protocol for attributes needs a CR */
  251     if (p->print_attributes)
  252     charToPrinter(xw, '\r');
  253 
  254     if (chr && !(p->printer_newline)) {
  255     if (LineTstWrapped(ld))
  256         chr = '\0';
  257     }
  258 
  259     if (chr)
  260     charToPrinter(xw, chr);
  261 
  262     return;
  263 }
  264 
  265 #define PrintNewLine() (unsigned) (((top < bot) || p->printer_newline) ? '\n' : '\0')
  266 
  267 static void
  268 printLines(XtermWidget xw, int top, int bot, PrinterFlags *p)
  269 {
  270     TRACE(("printLines, rows %d..%d\n", top, bot));
  271     while (top <= bot) {
  272     printLine(xw, top, PrintNewLine(), p);
  273     ++top;
  274     }
  275 }
  276 
  277 void
  278 xtermPrintScreen(XtermWidget xw, Bool use_DECPEX, PrinterFlags *p)
  279 {
  280     if (XtIsRealized((Widget) xw)) {
  281     TScreen *screen = TScreenOf(xw);
  282     Bool extent = (use_DECPEX && p->printer_extent);
  283     Boolean was_open = SPS.isOpen;
  284 
  285     printLines(xw,
  286            extent ? 0 : screen->top_marg,
  287            extent ? screen->max_row : screen->bot_marg,
  288            p);
  289     if (p->printer_formfeed)
  290         charToPrinter(xw, '\f');
  291 
  292     if (!was_open || SPS.printer_autoclose) {
  293         closePrinter(xw);
  294     }
  295     } else {
  296     Bell(xw, XkbBI_MinorError, 0);
  297     }
  298 }
  299 
  300 /*
  301  * If p->print_everything is zero, use this behavior:
  302  * If the alternate screen is active, we'll print only that.  Otherwise, print
  303  * the normal screen plus all scrolled-back lines.  The distinction is made
  304  * because the normal screen's buffer is part of the overall scrollback buffer.
  305  *
  306  * Otherwise, decode bits:
  307  *  1 = current screen
  308  *  2 = normal screen
  309  *  4 = alternate screen
  310  *  8 = saved lines
  311  */
  312 void
  313 xtermPrintEverything(XtermWidget xw, PrinterFlags *p)
  314 {
  315     TScreen *screen = TScreenOf(xw);
  316     Boolean was_open = SPS.isOpen;
  317     int save_which = screen->whichBuf;
  318 
  319     DEBUG_MSG("xtermPrintEverything\n");
  320 
  321     if (p->print_everything) {
  322     int done_which = 0;
  323 
  324     if (p->print_everything & 8) {
  325         printLines(xw, -screen->savedlines, -(screen->topline + 1), p);
  326     }
  327     if (p->print_everything & 4) {
  328         SwitchBufPtrs(xw, 1);
  329         done_which |= 2;
  330         printLines(xw, 0, screen->max_row, p);
  331         SwitchBufPtrs(xw, save_which);
  332     }
  333     if (p->print_everything & 2) {
  334         SwitchBufPtrs(xw, 0);
  335         done_which |= 1;
  336         printLines(xw, 0, screen->max_row, p);
  337         SwitchBufPtrs(xw, save_which);
  338     }
  339     if (p->print_everything & 1) {
  340         if (!(done_which & (1 << screen->whichBuf))) {
  341         printLines(xw, 0, screen->max_row, p);
  342         }
  343     }
  344     } else {
  345     int top = 0;
  346     int bot = screen->max_row;
  347     if (!screen->whichBuf) {
  348         top = -screen->savedlines - screen->topline;
  349         bot -= screen->topline;
  350     }
  351     printLines(xw, top, bot, p);
  352     }
  353     if (p->printer_formfeed)
  354     charToPrinter(xw, '\f');
  355 
  356     if (!was_open || SPS.printer_autoclose) {
  357     closePrinter(xw);
  358     }
  359 }
  360 
  361 static void
  362 send_CharSet(XtermWidget xw, LineData *ld)
  363 {
  364 #if OPT_DEC_CHRSET
  365     const char *msg = 0;
  366 
  367     switch (GetLineDblCS(ld)) {
  368     case CSET_SWL:
  369     msg = "\033#5";
  370     break;
  371     case CSET_DHL_TOP:
  372     msg = "\033#3";
  373     break;
  374     case CSET_DHL_BOT:
  375     msg = "\033#4";
  376     break;
  377     case CSET_DWL:
  378     msg = "\033#6";
  379     break;
  380     }
  381     if (msg != 0)
  382     stringToPrinter(xw, msg);
  383 #else
  384     (void) xw;
  385     (void) ld;
  386 #endif /* OPT_DEC_CHRSET */
  387 }
  388 
  389 static void
  390 send_SGR(XtermWidget xw, unsigned attr, unsigned fg, unsigned bg)
  391 {
  392     char msg[80];
  393 
  394 #if OPT_ISO_COLORS && OPT_PC_COLORS
  395     if ((attr & FG_COLOR) && (fg != NO_COLOR)) {
  396     if (TScreenOf(xw)->boldColors
  397         && fg > 8
  398         && (attr & BOLD) != 0)
  399         fg -= 8;
  400     }
  401 #endif
  402     strcpy(msg, "\033[");
  403     xtermFormatSGR(xw, msg + strlen(msg), attr, (int) fg, (int) bg);
  404     strcat(msg, "m");
  405     stringToPrinter(xw, msg);
  406 }
  407 
  408 /*
  409  * This implementation only knows how to write to a pipe.
  410  */
  411 static void
  412 charToPrinter(XtermWidget xw, unsigned chr)
  413 {
  414     TScreen *screen = TScreenOf(xw);
  415 
  416     if (!SPS.isOpen && (SPS.toFile || xtermHasPrinter(xw))) {
  417     switch (SPS.toFile) {
  418         /*
  419          * write to a pipe.
  420          */
  421     case False:
  422 #ifdef VMS
  423         /*
  424          * This implementation only knows how to write to a file.  When the
  425          * file is closed the print command executes.  Print command must
  426          * be of the form:
  427          *   print/queue=name/delete [/otherflags].
  428          */
  429         SPS.fp = fopen(VMS_TEMP_PRINT_FILE, "w");
  430 #else
  431         {
  432         int my_pipe[2];
  433         pid_t my_pid;
  434 
  435         if (pipe(my_pipe))
  436             SysError(ERROR_FORK);
  437         if ((my_pid = fork()) < 0)
  438             SysError(ERROR_FORK);
  439 
  440         if (my_pid == 0) {
  441             DEBUG_MSG("charToPrinter: subprocess for printer\n");
  442             TRACE_CLOSE();
  443             close(my_pipe[1]);  /* printer is silent */
  444             close(screen->respond);
  445 
  446             close(fileno(stdout));
  447             dup2(fileno(stderr), 1);
  448 
  449             if (fileno(stderr) != 2) {
  450             dup2(fileno(stderr), 2);
  451             close(fileno(stderr));
  452             }
  453 
  454             /* don't want privileges! */
  455             if (xtermResetIds(screen) < 0)
  456             exit(1);
  457 
  458             SPS.fp = popen(SPS.printer_command, "w");
  459             if (SPS.fp != 0) {
  460             FILE *input;
  461             DEBUG_MSG("charToPrinter: opened pipe to printer\n");
  462             if ((input = fdopen(my_pipe[0], "r")) != 0) {
  463                 clearerr(input);
  464 
  465                 for (;;) {
  466                 int c;
  467 
  468                 if (ferror(input)) {
  469                     DEBUG_MSG("charToPrinter: break on ferror\n");
  470                     break;
  471                 } else if (feof(input)) {
  472                     DEBUG_MSG("charToPrinter: break on feof\n");
  473                     break;
  474                 } else if ((c = fgetc(input)) == EOF) {
  475                     DEBUG_MSG("charToPrinter: break on EOF\n");
  476                     break;
  477                 }
  478                 fputc(c, SPS.fp);
  479                 if (isForm(c))
  480                     fflush(SPS.fp);
  481                 }
  482             }
  483             DEBUG_MSG("charToPrinter: calling pclose\n");
  484             pclose(SPS.fp);
  485             if (input)
  486                 fclose(input);
  487             }
  488             exit(0);
  489         } else {
  490             close(my_pipe[0]);  /* won't read from printer */
  491             if ((SPS.fp = fdopen(my_pipe[1], "w")) != 0) {
  492             DEBUG_MSG("charToPrinter: opened printer in parent\n");
  493             TRACE(("opened printer from pid %d/%d\n",
  494                    (int) getpid(), (int) my_pid));
  495             } else {
  496             TRACE(("failed to open printer:%s\n", strerror(errno)));
  497             DEBUG_MSG("charToPrinter: could not open in parent\n");
  498             }
  499         }
  500         }
  501 #endif
  502         break;
  503     case True:
  504         TRACE(("opening \"%s\" as printer output\n", SPS.printer_command));
  505         SPS.fp = fopen(SPS.printer_command, "w");
  506         break;
  507     }
  508     SPS.isOpen = True;
  509     }
  510     if (SPS.fp != 0) {
  511 #if OPT_WIDE_CHARS
  512     if (chr > 127) {
  513         Char temp[10];
  514         *convertToUTF8(temp, chr) = 0;
  515         fputs((char *) temp, SPS.fp);
  516     } else
  517 #endif
  518         fputc((int) chr, SPS.fp);
  519     if (isForm(chr))
  520         fflush(SPS.fp);
  521     }
  522 }
  523 
  524 static void
  525 stringToPrinter(XtermWidget xw, const char *str)
  526 {
  527     while (*str)
  528     charToPrinter(xw, CharOf(*str++));
  529 }
  530 
  531 /*
  532  * This module implements the MC (Media Copy) and related printing control
  533  * sequences for VTxxx emulation.  This is based on the description in the
  534  * VT330/VT340 Programmer Reference Manual EK-VT3XX-TP-001 (Digital Equipment
  535  * Corp., March 1987).
  536  */
  537 void
  538 xtermMediaControl(XtermWidget xw, int param, int private_seq)
  539 {
  540     TRACE(("MediaCopy param=%d, private=%d\n", param, private_seq));
  541 
  542     if (private_seq) {
  543     switch (param) {
  544     case -1:
  545     case 0:     /* VT125 */
  546         setGraphicsPrintToHost(xw, 0);  /* graphics to printer */
  547         break;
  548     case 1:
  549         printCursorLine(xw);
  550         break;
  551     case 2:     /* VT125 */
  552         setGraphicsPrintToHost(xw, 1);  /* graphics to host */
  553         break;
  554     case 4:
  555         setPrinterControlMode(xw, 0);   /* autoprint disable */
  556         break;
  557     case 5:
  558         setPrinterControlMode(xw, 1);   /* autoprint enable */
  559         break;
  560     case 10:        /* VT320 */
  561         /* print whole screen, across sessions */
  562         xtermPrintScreen(xw, False, getPrinterFlags(xw, NULL, 0));
  563         break;
  564     case 11:        /* VT320 */
  565         /* print all pages in current session */
  566         xtermPrintEverything(xw, getPrinterFlags(xw, NULL, 0));
  567         break;
  568     }
  569     } else {
  570     switch (param) {
  571     case -1:
  572     case 0:
  573         xtermPrintScreen(xw, True, getPrinterFlags(xw, NULL, 0));
  574         break;
  575     case 4:
  576         setPrinterControlMode(xw, 0);   /* printer controller mode off */
  577         break;
  578     case 5:
  579         setPrinterControlMode(xw, 2);   /* printer controller mode on */
  580         break;
  581 #if OPT_SCREEN_DUMPS
  582     case 10:
  583         xtermDumpHtml(xw);
  584         break;
  585     case 11:
  586         xtermDumpSvg(xw);
  587         break;
  588 #endif
  589     }
  590     }
  591 }
  592 
  593 /*
  594  * When in autoprint mode, the printer prints a line from the screen when you
  595  * move the cursor off that line with an LF, FF, or VT character, or an
  596  * autowrap occurs.  The printed line ends with a CR and the character (LF, FF
  597  * or VT) that moved the cursor off the previous line.
  598  */
  599 void
  600 xtermAutoPrint(XtermWidget xw, unsigned chr)
  601 {
  602     TScreen *screen = TScreenOf(xw);
  603 
  604     if (SPS.printer_controlmode == 1) {
  605     TRACE(("AutoPrint %d\n", chr));
  606     printLine(xw, screen->cursorp.row, chr, getPrinterFlags(xw, NULL, 0));
  607     if (SPS.fp != 0)
  608         fflush(SPS.fp);
  609     }
  610 }
  611 
  612 /*
  613  * When in printer controller mode, the terminal sends received characters to
  614  * the printer without displaying them on the screen. The terminal sends all
  615  * characters and control sequences to the printer, except NUL, XON, XOFF, and
  616  * the printer controller sequences.
  617  *
  618  * This function eats characters, returning 0 as long as it must buffer or
  619  * divert to the printer.  We're only invoked here when in printer controller
  620  * mode, and handle the exit from that mode.
  621  */
  622 #define LB '['
  623 
  624 int
  625 xtermPrinterControl(XtermWidget xw, int chr)
  626 {
  627     TScreen *screen = TScreenOf(xw);
  628     /* *INDENT-OFF* */
  629     static const struct {
  630     const Char seq[5];
  631     int active;
  632     } tbl[] = {
  633     { { ANSI_CSI, '5', 'i'      }, 2 },
  634     { { ANSI_CSI, '4', 'i'      }, 0 },
  635     { { ANSI_ESC, LB,  '5', 'i' }, 2 },
  636     { { ANSI_ESC, LB,  '4', 'i' }, 0 },
  637     };
  638     /* *INDENT-ON* */
  639 
  640     static Char bfr[10];
  641     static size_t length;
  642     size_t n;
  643 
  644     TRACE(("In printer:%04X\n", chr));
  645 
  646     switch (chr) {
  647     case 0:
  648     case CTRL('Q'):
  649     case CTRL('S'):
  650     return 0;       /* ignored by application */
  651 
  652     case ANSI_CSI:
  653     case ANSI_ESC:
  654     case '[':
  655     case '4':
  656     case '5':
  657     case 'i':
  658     bfr[length++] = CharOf(chr);
  659     for (n = 0; n < sizeof(tbl) / sizeof(tbl[0]); n++) {
  660         size_t len = Strlen(tbl[n].seq);
  661 
  662         if (length == len
  663         && Strcmp(bfr, tbl[n].seq) == 0) {
  664         setPrinterControlMode(xw, tbl[n].active);
  665         if (SPS.printer_autoclose
  666             && SPS.printer_controlmode == 0)
  667             closePrinter(xw);
  668         length = 0;
  669         return 0;
  670         } else if (len > length
  671                && Strncmp(bfr, tbl[n].seq, length) == 0) {
  672         return 0;
  673         }
  674     }
  675     length--;
  676 
  677     /* FALLTHRU */
  678 
  679     default:
  680     for (n = 0; n < length; n++)
  681         charToPrinter(xw, bfr[n]);
  682     bfr[0] = CharOf(chr);
  683     length = 1;
  684     return 0;
  685     }
  686 }
  687 
  688 /*
  689  * If there is no printer command, we will ignore printer controls.
  690  *
  691  * If we do have a printer command, we still have to verify that it will
  692  * (perhaps) work if we pass it to popen().  At a minimum, the program
  693  * must exist and be executable.  If not, warn and disable the feature.
  694  */
  695 Bool
  696 xtermHasPrinter(XtermWidget xw)
  697 {
  698     TScreen *screen = TScreenOf(xw);
  699     Bool result = SPS.printer_checked;
  700 
  701     if (strlen(SPS.printer_command) != 0 && !result) {
  702     char **argv = x_splitargs(SPS.printer_command);
  703     if (argv) {
  704         if (argv[0]) {
  705         char *myShell = xtermFindShell(argv[0], False);
  706         if (myShell == 0) {
  707             xtermWarning("No program found for printerCommand: %s\n", SPS.printer_command);
  708             SPS.printer_command = x_strdup("");
  709         } else {
  710             free(myShell);
  711             SPS.printer_checked = True;
  712             result = True;
  713         }
  714         }
  715         x_freeargs(argv);
  716     }
  717     TRACE(("xtermHasPrinter:%d\n", result));
  718     }
  719 
  720     return result;
  721 }
  722 
  723 #if OPT_PRINT_GRAPHICS
  724 static void
  725 setGraphicsPrintToHost(XtermWidget xw, int enabled)
  726 {
  727     TScreen *screen = TScreenOf(xw);
  728 
  729     TRACE(("graphics print to host enabled=%d\n", enabled));
  730     screen->graphics_print_to_host = (Boolean) enabled;
  731 }
  732 #endif
  733 
  734 #define showPrinterControlMode(mode) \
  735         (((mode) == 0) \
  736          ? "normal" \
  737          : ((mode) == 1 \
  738             ? "autoprint" \
  739             : "printer controller"))
  740 
  741 void
  742 setPrinterControlMode(XtermWidget xw, int mode)
  743 {
  744     TScreen *screen = TScreenOf(xw);
  745 
  746     if (xtermHasPrinter(xw)
  747     && SPS.printer_controlmode != mode) {
  748     TRACE(("%s %s mode\n",
  749            (mode
  750         ? "set"
  751         : "reset"),
  752            (mode
  753         ? showPrinterControlMode(mode)
  754         : showPrinterControlMode(SPS.printer_controlmode))));
  755     SPS.printer_controlmode = mode;
  756     update_print_redir();
  757     }
  758 }
  759 
  760 PrinterFlags *
  761 getPrinterFlags(XtermWidget xw, String *params, Cardinal *param_count)
  762 {
  763     /* *INDENT-OFF* */
  764     static const struct {
  765     const char *name;
  766     unsigned    offset;
  767     int     value;
  768     } table[] = {
  769     { "noFormFeed", XtOffsetOf(PrinterFlags, printer_formfeed), 0 },
  770     { "FormFeed",   XtOffsetOf(PrinterFlags, printer_formfeed), 1 },
  771     { "noNewLine",  XtOffsetOf(PrinterFlags, printer_newline),  0 },
  772     { "NewLine",    XtOffsetOf(PrinterFlags, printer_newline),  1 },
  773     { "noAttrs",    XtOffsetOf(PrinterFlags, print_attributes), 0 },
  774     { "monoAttrs",  XtOffsetOf(PrinterFlags, print_attributes), 1 },
  775     { "colorAttrs", XtOffsetOf(PrinterFlags, print_attributes), 2 },
  776     };
  777     /* *INDENT-ON* */
  778 
  779     TScreen *screen = TScreenOf(xw);
  780     PrinterFlags *result = &(screen->printer_flags);
  781 
  782     TRACE(("getPrinterFlags %d params\n", param_count ? *param_count : 0));
  783 
  784     result->printer_extent = SPS.printer_extent;
  785     result->printer_formfeed = SPS.printer_formfeed;
  786     result->printer_newline = SPS.printer_newline;
  787     result->print_attributes = SPS.print_attributes;
  788     result->print_everything = SPS.print_everything;
  789 
  790     if (param_count != 0 && *param_count != 0) {
  791     Cardinal j;
  792     unsigned k;
  793     for (j = 0; j < *param_count; ++j) {
  794         TRACE(("param%d:%s\n", j, params[j]));
  795         for (k = 0; k < XtNumber(table); ++k) {
  796         if (!x_strcasecmp(params[j], table[k].name)) {
  797             int *ptr = (int *) (void *) ((char *) result + table[k].offset);
  798             TRACE(("...PrinterFlags(%s) %d->%d\n",
  799                table[k].name,
  800                *ptr,
  801                table[k].value));
  802             *ptr = table[k].value;
  803             break;
  804         }
  805         }
  806     }
  807     }
  808 
  809     return result;
  810 }
  811 
  812 /*
  813  * Print a timestamped copy of everything.
  814  */
  815 void
  816 xtermPrintImmediately(XtermWidget xw, String filename, int opts, int attrs)
  817 {
  818     TScreen *screen = TScreenOf(xw);
  819     PrinterState save_state = screen->printer_state;
  820     char *my_filename = malloc(TIMESTAMP_LEN + strlen(filename));
  821 
  822     if (my_filename != 0) {
  823     mode_t save_umask = umask(0177);
  824 
  825     timestamp_filename(my_filename, filename);
  826     SPS.fp = 0;
  827     SPS.isOpen = False;
  828     SPS.toFile = True;
  829     SPS.printer_command = my_filename;
  830     SPS.printer_autoclose = True;
  831     SPS.printer_formfeed = False;
  832     SPS.printer_newline = True;
  833     SPS.print_attributes = attrs;
  834     SPS.print_everything = opts;
  835     xtermPrintEverything(xw, getPrinterFlags(xw, NULL, 0));
  836 
  837     umask(save_umask);
  838     screen->printer_state = save_state;
  839     free(my_filename);
  840     }
  841 }
  842 
  843 void
  844 xtermPrintOnXError(XtermWidget xw, int n)
  845 {
  846 #if OPT_PRINT_ON_EXIT
  847     /*
  848      * The user may have requested that the contents of the screen will be
  849      * written to a file if an X error occurs.
  850      */
  851     if (TScreenOf(xw)->write_error && !IsEmpty(resource.printFileOnXError)) {
  852     Boolean printIt = False;
  853 
  854     switch (n) {
  855     case ERROR_XERROR:
  856         /* FALLTHRU */
  857     case ERROR_XIOERROR:
  858         /* FALLTHRU */
  859     case ERROR_ICEERROR:
  860         printIt = True;
  861         break;
  862     }
  863 
  864     if (printIt) {
  865         xtermPrintImmediately(xw,
  866                   resource.printFileOnXError,
  867                   resource.printOptsOnXError,
  868                   resource.printModeOnXError);
  869     }
  870     }
  871 #else
  872     (void) xw;
  873     (void) n;
  874 #endif
  875 }