* New version 2.26
[alpine.git] / alpine / osdep / print.c
blob943cd85ef44d46ebd33423847a1ceccee9341325
1 /*
2 * ========================================================================
3 * Copyright 2006-2008 University of Washington
4 * Copyright 2013-2022 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
14 #include <system.h>
15 #include <general.h>
17 #include "../c-client/mail.h" /* for MAILSTREAM and friends */
18 #include "../c-client/osdep.h"
19 #include "../c-client/rfc822.h" /* for soutr_t and such */
20 #include "../c-client/misc.h" /* for cpystr proto */
21 #include "../c-client/utf8.h" /* for CHARSET and such*/
22 #include "../c-client/imap4r1.h"
24 #include "../../pith/charconv/utf8.h"
25 #include "../../pith/charconv/filesys.h"
27 #include "../../pith/osdep/color.h"
28 #include "../../pith/osdep/temp_nam.h"
29 #include "../../pith/osdep/err_desc.h"
30 #include "../../pith/osdep/collate.h"
32 #include "../../pith/debug.h"
33 #include "../../pith/conf.h"
34 #include "../../pith/store.h"
35 #include "../../pith/filttype.h"
37 #include "../../pico/estruct.h" /* for ctrl() */
38 #include "../../pico/keydefs.h" /* for KEY_* */
40 #include "../status.h"
41 #include "../signal.h"
42 #include "../radio.h"
43 #include "../../pico/estruct.h"
44 #include "../../pico/pico.h"
45 #include "../mailview.h"
47 #ifdef _WINDOWS
48 #include "../../pico/osdep/mswin.h"
49 #endif
51 #include "../../pico/osdep/raw.h"
53 #include "termin.gen.h"
55 #include "print.h"
58 /*======================================================================
59 print routines
61 Functions having to do with printing on paper and forking of spoolers
63 In general one calls open_printer() to start printing. One of
64 the little print functions to send a line or string, and then
65 call print_end() when complete. This takes care of forking off a spooler
66 and piping the stuff down it. No handles or anything here because there's
67 only one printer open at a time.
69 ====*/
73 #ifndef _WINDOWS
74 static char *trailer; /* so both open and close_printer can see it */
75 static int ansi_off;
76 static CBUF_S cb;
77 #endif /* !_WINDOWS */
80 /*----------------------------------------------------------------------
81 Open the printer
83 Args: desc -- Description of item to print. Should have one trailing blank.
85 Return value: < 0 is a failure.
86 0 a success.
88 This does most of the work of popen so we can save the standard output of the
89 command we execute and send it back to the user.
90 ----*/
91 int
92 open_printer(char *desc)
94 #ifndef _WINDOWS
95 char command[201], prompt[200];
96 int cmd, rc, just_one;
97 char *p, *init, *nick;
98 char aname[100], wname[100];
99 char *printer;
100 int done = 0, i, lastprinter = 0, cur_printer = 0;
101 HelpType help;
102 char **list;
103 static ESCKEY_S ekey[] = {
104 /* TRANSLATORS: these are command labels for printing screen */
105 {'y', 'y', "Y", N_("Yes")},
106 {'n', 'n', "N", N_("No")},
107 /* TRANSLATORS: go to Previous Printer in list */
108 {ctrl('P'), 10, "^P", N_("Prev Printer")},
109 {ctrl('N'), 11, "^N", N_("Next Printer")},
110 {-2, 0, NULL, NULL},
111 /* TRANSLATORS: use Custom Print command */
112 {'c', 'c', "C", N_("CustomPrint")},
113 {KEY_UP, 10, "", ""},
114 {KEY_DOWN, 11, "", ""},
115 {-1, 0, NULL, NULL}};
116 #define PREV_KEY 2
117 #define NEXT_KEY 3
118 #define CUSTOM_KEY 5
119 #define UP_KEY 6
120 #define DOWN_KEY 7
122 trailer = NULL;
123 init = NULL;
124 nick = NULL;
125 command[sizeof(command)-1] = '\0';
127 if(ps_global->VAR_PRINTER == NULL){
128 q_status_message(SM_ORDER | SM_DING, 3, 5,
129 "No printer has been chosen. Use SETUP on main menu to make choice.");
130 return(-1);
133 /* Is there just one print command available? */
134 just_one = (ps_global->printer_category!=3&&ps_global->printer_category!=2)
135 || (ps_global->printer_category == 2
136 && !(ps_global->VAR_STANDARD_PRINTER
137 && ps_global->VAR_STANDARD_PRINTER[0]
138 && ps_global->VAR_STANDARD_PRINTER[1]))
139 || (ps_global->printer_category == 3
140 && !(ps_global->VAR_PERSONAL_PRINT_COMMAND
141 && ps_global->VAR_PERSONAL_PRINT_COMMAND[0]
142 && ps_global->VAR_PERSONAL_PRINT_COMMAND[1]));
144 if(F_ON(F_CUSTOM_PRINT, ps_global))
145 ekey[CUSTOM_KEY].ch = 'c'; /* turn this key on */
146 else
147 ekey[CUSTOM_KEY].ch = -2; /* turn this key off */
149 if(just_one){
150 ekey[PREV_KEY].ch = -2; /* turn these keys off */
151 ekey[NEXT_KEY].ch = -2;
152 ekey[UP_KEY].ch = -2;
153 ekey[DOWN_KEY].ch = -2;
155 else{
156 ekey[PREV_KEY].ch = ctrl('P'); /* turn these keys on */
157 ekey[NEXT_KEY].ch = ctrl('N');
158 ekey[UP_KEY].ch = KEY_UP;
159 ekey[DOWN_KEY].ch = KEY_DOWN;
161 * count how many printers in list and find the default in the list
163 if(ps_global->printer_category == 2)
164 list = ps_global->VAR_STANDARD_PRINTER;
165 else
166 list = ps_global->VAR_PERSONAL_PRINT_COMMAND;
168 for(i = 0; list[i]; i++)
169 if(strcmp(ps_global->VAR_PRINTER, list[i]) == 0)
170 cur_printer = i;
172 lastprinter = i - 1;
175 help = NO_HELP;
176 ps_global->mangled_footer = 1;
178 while(!done){
179 if(init)
180 fs_give((void **)&init);
182 if(trailer)
183 fs_give((void **)&trailer);
185 if(just_one)
186 printer = ps_global->VAR_PRINTER;
187 else
188 printer = list[cur_printer];
190 parse_printer(printer, &nick, &p, &init, &trailer, NULL, NULL);
191 strncpy(command, p, sizeof(command)-1);
192 command[sizeof(command)-1] = '\0';
193 fs_give((void **)&p);
194 /* TRANSLATORS: Print something1 using something2.
195 For example, Print configuration using printer three. */
196 snprintf(prompt, sizeof(prompt), _("Print %s using \"%s\" ? "),
197 desc ? desc : "",
198 *nick ? nick : command);
199 prompt[sizeof(prompt)-1] = '\0';
201 fs_give((void **)&nick);
203 cmd = radio_buttons(prompt, -FOOTER_ROWS(ps_global),
204 ekey, 'y', 'x', help, RB_NORM);
206 switch(cmd){
207 case 'y':
208 q_status_message1(SM_ORDER, 0, 9,
209 "Printing with command \"%s\"", command);
210 done++;
211 break;
213 case 10:
214 cur_printer = (cur_printer>0)
215 ? (cur_printer-1)
216 : lastprinter;
217 break;
219 case 11:
220 cur_printer = (cur_printer<lastprinter)
221 ? (cur_printer+1)
222 : 0;
223 break;
225 case 'n':
226 case 'x':
227 done++;
228 break;
230 case 'c':
231 done++;
232 break;
234 default:
235 break;
239 if(cmd == 'c'){
240 if(init)
241 fs_give((void **)&init);
243 if(trailer)
244 fs_give((void **)&trailer);
246 snprintf(prompt, sizeof(prompt), "Enter custom command : ");
247 prompt[sizeof(prompt)-1] = '\0';
248 command[0] = '\0';
249 rc = 1;
250 help = NO_HELP;
251 while(rc){
252 int flags = OE_APPEND_CURRENT;
254 rc = optionally_enter(command, -FOOTER_ROWS(ps_global), 0,
255 sizeof(command), prompt, NULL, help, &flags);
257 if(rc == 1){
258 cmd = 'x';
259 rc = 0;
261 else if(rc == 3)
262 help = (help == NO_HELP) ? h_custom_print : NO_HELP;
263 else if(rc == 0){
264 removing_trailing_white_space(command);
265 removing_leading_white_space(command);
266 q_status_message1(SM_ORDER, 0, 9,
267 "Printing with command \"%s\"", command);
272 if(cmd == 'x' || cmd == 'n'){
273 q_status_message(SM_ORDER, 0, 2, "Print cancelled");
274 if(init)
275 fs_give((void **)&init);
277 if(trailer)
278 fs_give((void **)&trailer);
280 return(-1);
283 display_message('x');
285 ps_global->print = (PRINT_S *)fs_get(sizeof(PRINT_S));
286 memset(ps_global->print, 0, sizeof(PRINT_S));
288 strncpy(aname, ANSI_PRINTER, sizeof(aname)-1);
289 aname[sizeof(aname)-1] = '\0';
290 strncat(aname, "-no-formfeed", sizeof(aname)-strlen(aname)-1);
291 strncpy(wname, WYSE_PRINTER, sizeof(wname)-1);
292 wname[sizeof(wname)-1] = '\0';
293 strncat(wname, "-no-formfeed", sizeof(wname)-strlen(wname)-1);
294 if(strucmp(command, ANSI_PRINTER) == 0
295 || strucmp(command, aname) == 0
296 || strucmp(command, WYSE_PRINTER) == 0
297 || strucmp(command, wname) == 0){
298 /*----------- Attached printer ---------*/
299 q_status_message(SM_ORDER, 0, 9,
300 "Printing to attached desktop printer...");
301 display_message('x');
302 xonxoff_proc(1); /* make sure XON/XOFF used */
303 crlf_proc(1); /* AND LF->CR xlation */
304 if(strucmp(command, ANSI_PRINTER) == 0
305 || strucmp(command, aname) == 0){
306 fputs("\033[5i", stdout);
307 ansi_off = 1;
309 else{
310 ansi_off = 0;
311 printf("%c", 18); /* aux on for wyse60,
312 Chuck Everett <ceverett@odessa.edu> */
315 ps_global->print->fp = stdout;
316 if(strucmp(command, ANSI_PRINTER) == 0
317 || strucmp(command, WYSE_PRINTER) == 0){
318 /* put formfeed at the end of the trailer string */
319 if(trailer){
320 int len = strlen(trailer);
322 fs_resize((void **)&trailer, len+2);
323 trailer[len] = '\f';
324 trailer[len+1] = '\0';
326 else
327 trailer = cpystr("\f");
330 else{
331 /*----------- Print by forking off a UNIX command ------------*/
332 dprint((4, "Printing using command \"%s\"\n",
333 command ? command : "?"));
334 ps_global->print->result = temp_nam(NULL, "pine_prt");
335 if(ps_global->print->result &&
336 (ps_global->print->pipe = open_system_pipe(command,
337 &ps_global->print->result, NULL,
338 PIPE_WRITE | PIPE_STDERR, 0,
339 pipe_callback, NULL))){
340 ps_global->print->fp = ps_global->print->pipe->out.f;
342 else{
343 if(ps_global->print->result){
344 our_unlink(ps_global->print->result);
345 fs_give((void **)&ps_global->print->result);
348 q_status_message1(SM_ORDER | SM_DING, 3, 4,
349 "Error opening printer: %s",
350 error_description(errno));
351 dprint((2, "Error popening printer \"%s\"\n",
352 error_description(errno)));
353 if(init)
354 fs_give((void **)&init);
356 if(trailer)
357 fs_give((void **)&trailer);
359 return(-1);
363 ps_global->print->err = 0;
364 if(init){
365 if(*init)
366 fputs(init, ps_global->print->fp);
368 fs_give((void **)&init);
371 cb.cbuf[0] = '\0';
372 cb.cbufp = cb.cbuf;
373 cb.cbufend = cb.cbuf;
374 #else /* _WINDOWS */
375 int status;
376 LPTSTR desclpt = NULL;
378 if(desc)
379 desclpt = utf8_to_lptstr(desc);
381 if (status = mswin_print_ready (0, desclpt)) {
382 q_status_message1(SM_ORDER | SM_DING, 3, 4,
383 "Error starting print job: %s",
384 mswin_print_error(status));
385 if(desclpt)
386 fs_give((void **) &desclpt);
388 return(-1);
391 if(desclpt)
392 fs_give((void **) &desclpt);
394 q_status_message(SM_ORDER, 0, 9, "Printing to windows printer...");
395 display_message('x');
397 /* init print control structure */
398 ps_global->print = (PRINT_S *)fs_get(sizeof(PRINT_S));
399 memset(ps_global->print, 0, sizeof(PRINT_S));
401 ps_global->print->err = 0;
402 #endif /* _WINDOWS */
404 return(0);
409 /*----------------------------------------------------------------------
410 Close printer
412 If we're piping to a spooler close down the pipe and wait for the process
413 to finish. If we're sending to an attached printer send the escape sequence.
414 Also let the user know the result of the print
415 ----*/
416 void
417 close_printer(void)
419 #ifndef _WINDOWS
420 if(trailer){
421 if(*trailer)
422 fputs(trailer, ps_global->print->fp);
424 fs_give((void **)&trailer);
427 if(ps_global->print->fp == stdout) {
428 if(ansi_off)
429 fputs("\033[4i", stdout);
430 else
431 printf("%c", 20); /* aux off for wyse60 */
433 fflush(stdout);
434 if(F_OFF(F_PRESERVE_START_STOP, ps_global))
435 xonxoff_proc(0); /* turn off XON/XOFF */
437 crlf_proc(0); /* turn off CF->LF xlantion */
438 } else {
439 (void) close_system_pipe(&ps_global->print->pipe, NULL, pipe_callback);
440 display_output_file(ps_global->print->result, "PRINT", NULL, 1);
441 if(ps_global->print && ps_global->print->result)
442 fs_give((void **) &ps_global->print->result);
444 #else /* _WINDOWS */
445 mswin_print_done();
446 #endif /* _WINDOWS */
448 if(ps_global->print)
449 fs_give((void **) &ps_global->print);
451 q_status_message(SM_ASYNC, 0, 3, "Print command completed");
452 display_message('x');
456 /*----------------------------------------------------------------------
457 Print a single character, translate from UTF-8 to user's locale charset.
459 Args: c -- char to print
460 Returns: 1 on success, 0 on ps_global->print->err
461 ----*/
463 print_char(int c)
465 #ifndef _WINDOWS
466 int i, outchars;
467 unsigned char obuf[MAX(MB_LEN_MAX,32)];
469 if(!ps_global->print->err
470 && (outchars = utf8_to_locale(c, &cb, obuf, sizeof(obuf)))){
471 for(i = 0; i < outchars && !ps_global->print->err; i++)
472 if(putc(obuf[i], ps_global->print->fp) == EOF)
473 ps_global->print->err = 1;
475 #else /* _WINDOWS */
476 if(!ps_global->print->err
477 && (ps_global->print->err = mswin_print_char_utf8(c)))
478 q_status_message1(SM_ORDER, 0, 9, "Print cancelled: %s",
479 mswin_print_error((unsigned short)ps_global->print->err));
480 #endif /* _WINDOWS */
482 return(!ps_global->print->err);
486 /*----------------------------------------------------------------------
487 Send a line of text to the printer
489 Args: line -- Text to print
491 ----*/
492 void
493 print_text(char *line)
495 #ifndef _WINDOWS
496 int slen = strlen(line);
498 while(!ps_global->print->err && slen--)
499 if(print_char(*line++) == 0)
500 ps_global->print->err = 1;
501 #else /* _WINDOWS */
502 if(!ps_global->print->err
503 && (ps_global->print->err = mswin_print_text_utf8(line)))
504 q_status_message1(SM_ORDER, 0, 9, "Print cancelled: %s",
505 mswin_print_error((unsigned short)ps_global->print->err));
506 #endif /* _WINDOWS */
510 /*----------------------------------------------------------------------
511 printf style formatting with one arg for printer
513 Args: line -- The printf control string
514 a1 -- The 1st argument for printf
515 ----*/
516 void
517 print_text1(char *line, char *a1)
519 char buf[64000];
521 if(!ps_global->print->err && snprintf(buf, sizeof(buf), line, a1) < 0)
522 ps_global->print->err = 1;
523 else
524 print_text(buf);