* Initial release of Alpine version 2.22.
[alpine.git] / alpine / osdep / print.c
blobcb590ed3ad5f10ae302e5f50c096fa32af8839ae
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: print.c 942 2008-03-04 18:21:33Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2020 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
18 #include <system.h>
19 #include <general.h>
21 #include "../c-client/mail.h" /* for MAILSTREAM and friends */
22 #include "../c-client/osdep.h"
23 #include "../c-client/rfc822.h" /* for soutr_t and such */
24 #include "../c-client/misc.h" /* for cpystr proto */
25 #include "../c-client/utf8.h" /* for CHARSET and such*/
26 #include "../c-client/imap4r1.h"
28 #include "../../pith/charconv/utf8.h"
29 #include "../../pith/charconv/filesys.h"
31 #include "../../pith/osdep/color.h"
32 #include "../../pith/osdep/temp_nam.h"
33 #include "../../pith/osdep/err_desc.h"
34 #include "../../pith/osdep/collate.h"
36 #include "../../pith/debug.h"
37 #include "../../pith/conf.h"
38 #include "../../pith/store.h"
39 #include "../../pith/filttype.h"
41 #include "../../pico/estruct.h" /* for ctrl() */
42 #include "../../pico/keydefs.h" /* for KEY_* */
44 #include "../status.h"
45 #include "../signal.h"
46 #include "../radio.h"
47 #include "../../pico/estruct.h"
48 #include "../../pico/pico.h"
49 #include "../mailview.h"
51 #ifdef _WINDOWS
52 #include "../../pico/osdep/mswin.h"
53 #endif
55 #include "../../pico/osdep/raw.h"
57 #include "termin.gen.h"
59 #include "print.h"
62 /*======================================================================
63 print routines
65 Functions having to do with printing on paper and forking of spoolers
67 In general one calls open_printer() to start printing. One of
68 the little print functions to send a line or string, and then
69 call print_end() when complete. This takes care of forking off a spooler
70 and piping the stuff down it. No handles or anything here because there's
71 only one printer open at a time.
73 ====*/
77 #ifndef _WINDOWS
78 static char *trailer; /* so both open and close_printer can see it */
79 static int ansi_off;
80 static CBUF_S cb;
81 #endif /* !_WINDOWS */
84 /*----------------------------------------------------------------------
85 Open the printer
87 Args: desc -- Description of item to print. Should have one trailing blank.
89 Return value: < 0 is a failure.
90 0 a success.
92 This does most of the work of popen so we can save the standard output of the
93 command we execute and send it back to the user.
94 ----*/
95 int
96 open_printer(char *desc)
98 #ifndef _WINDOWS
99 char command[201], prompt[200];
100 int cmd, rc, just_one;
101 char *p, *init, *nick;
102 char aname[100], wname[100];
103 char *printer;
104 int done = 0, i, lastprinter, cur_printer = 0;
105 HelpType help;
106 char **list;
107 static ESCKEY_S ekey[] = {
108 /* TRANSLATORS: these are command labels for printing screen */
109 {'y', 'y', "Y", N_("Yes")},
110 {'n', 'n', "N", N_("No")},
111 /* TRANSLATORS: go to Previous Printer in list */
112 {ctrl('P'), 10, "^P", N_("Prev Printer")},
113 {ctrl('N'), 11, "^N", N_("Next Printer")},
114 {-2, 0, NULL, NULL},
115 /* TRANSLATORS: use Custom Print command */
116 {'c', 'c', "C", N_("CustomPrint")},
117 {KEY_UP, 10, "", ""},
118 {KEY_DOWN, 11, "", ""},
119 {-1, 0, NULL, NULL}};
120 #define PREV_KEY 2
121 #define NEXT_KEY 3
122 #define CUSTOM_KEY 5
123 #define UP_KEY 6
124 #define DOWN_KEY 7
126 trailer = NULL;
127 init = NULL;
128 nick = NULL;
129 command[sizeof(command)-1] = '\0';
131 if(ps_global->VAR_PRINTER == NULL){
132 q_status_message(SM_ORDER | SM_DING, 3, 5,
133 "No printer has been chosen. Use SETUP on main menu to make choice.");
134 return(-1);
137 /* Is there just one print command available? */
138 just_one = (ps_global->printer_category!=3&&ps_global->printer_category!=2)
139 || (ps_global->printer_category == 2
140 && !(ps_global->VAR_STANDARD_PRINTER
141 && ps_global->VAR_STANDARD_PRINTER[0]
142 && ps_global->VAR_STANDARD_PRINTER[1]))
143 || (ps_global->printer_category == 3
144 && !(ps_global->VAR_PERSONAL_PRINT_COMMAND
145 && ps_global->VAR_PERSONAL_PRINT_COMMAND[0]
146 && ps_global->VAR_PERSONAL_PRINT_COMMAND[1]));
148 if(F_ON(F_CUSTOM_PRINT, ps_global))
149 ekey[CUSTOM_KEY].ch = 'c'; /* turn this key on */
150 else
151 ekey[CUSTOM_KEY].ch = -2; /* turn this key off */
153 if(just_one){
154 ekey[PREV_KEY].ch = -2; /* turn these keys off */
155 ekey[NEXT_KEY].ch = -2;
156 ekey[UP_KEY].ch = -2;
157 ekey[DOWN_KEY].ch = -2;
159 else{
160 ekey[PREV_KEY].ch = ctrl('P'); /* turn these keys on */
161 ekey[NEXT_KEY].ch = ctrl('N');
162 ekey[UP_KEY].ch = KEY_UP;
163 ekey[DOWN_KEY].ch = KEY_DOWN;
165 * count how many printers in list and find the default in the list
167 if(ps_global->printer_category == 2)
168 list = ps_global->VAR_STANDARD_PRINTER;
169 else
170 list = ps_global->VAR_PERSONAL_PRINT_COMMAND;
172 for(i = 0; list[i]; i++)
173 if(strcmp(ps_global->VAR_PRINTER, list[i]) == 0)
174 cur_printer = i;
176 lastprinter = i - 1;
179 help = NO_HELP;
180 ps_global->mangled_footer = 1;
182 while(!done){
183 if(init)
184 fs_give((void **)&init);
186 if(trailer)
187 fs_give((void **)&trailer);
189 if(just_one)
190 printer = ps_global->VAR_PRINTER;
191 else
192 printer = list[cur_printer];
194 parse_printer(printer, &nick, &p, &init, &trailer, NULL, NULL);
195 strncpy(command, p, sizeof(command)-1);
196 command[sizeof(command)-1] = '\0';
197 fs_give((void **)&p);
198 /* TRANSLATORS: Print something1 using something2.
199 For example, Print configuration using printer three. */
200 snprintf(prompt, sizeof(prompt), _("Print %s using \"%s\" ? "),
201 desc ? desc : "",
202 *nick ? nick : command);
203 prompt[sizeof(prompt)-1] = '\0';
205 fs_give((void **)&nick);
207 cmd = radio_buttons(prompt, -FOOTER_ROWS(ps_global),
208 ekey, 'y', 'x', help, RB_NORM);
210 switch(cmd){
211 case 'y':
212 q_status_message1(SM_ORDER, 0, 9,
213 "Printing with command \"%s\"", command);
214 done++;
215 break;
217 case 10:
218 cur_printer = (cur_printer>0)
219 ? (cur_printer-1)
220 : lastprinter;
221 break;
223 case 11:
224 cur_printer = (cur_printer<lastprinter)
225 ? (cur_printer+1)
226 : 0;
227 break;
229 case 'n':
230 case 'x':
231 done++;
232 break;
234 case 'c':
235 done++;
236 break;
238 default:
239 break;
243 if(cmd == 'c'){
244 if(init)
245 fs_give((void **)&init);
247 if(trailer)
248 fs_give((void **)&trailer);
250 snprintf(prompt, sizeof(prompt), "Enter custom command : ");
251 prompt[sizeof(prompt)-1] = '\0';
252 command[0] = '\0';
253 rc = 1;
254 help = NO_HELP;
255 while(rc){
256 int flags = OE_APPEND_CURRENT;
258 rc = optionally_enter(command, -FOOTER_ROWS(ps_global), 0,
259 sizeof(command), prompt, NULL, help, &flags);
261 if(rc == 1){
262 cmd = 'x';
263 rc = 0;
265 else if(rc == 3)
266 help = (help == NO_HELP) ? h_custom_print : NO_HELP;
267 else if(rc == 0){
268 removing_trailing_white_space(command);
269 removing_leading_white_space(command);
270 q_status_message1(SM_ORDER, 0, 9,
271 "Printing with command \"%s\"", command);
276 if(cmd == 'x' || cmd == 'n'){
277 q_status_message(SM_ORDER, 0, 2, "Print cancelled");
278 if(init)
279 fs_give((void **)&init);
281 if(trailer)
282 fs_give((void **)&trailer);
284 return(-1);
287 display_message('x');
289 ps_global->print = (PRINT_S *)fs_get(sizeof(PRINT_S));
290 memset(ps_global->print, 0, sizeof(PRINT_S));
292 strncpy(aname, ANSI_PRINTER, sizeof(aname)-1);
293 aname[sizeof(aname)-1] = '\0';
294 strncat(aname, "-no-formfeed", sizeof(aname)-strlen(aname)-1);
295 strncpy(wname, WYSE_PRINTER, sizeof(wname)-1);
296 wname[sizeof(wname)-1] = '\0';
297 strncat(wname, "-no-formfeed", sizeof(wname)-strlen(wname)-1);
298 if(strucmp(command, ANSI_PRINTER) == 0
299 || strucmp(command, aname) == 0
300 || strucmp(command, WYSE_PRINTER) == 0
301 || strucmp(command, wname) == 0){
302 /*----------- Attached printer ---------*/
303 q_status_message(SM_ORDER, 0, 9,
304 "Printing to attached desktop printer...");
305 display_message('x');
306 xonxoff_proc(1); /* make sure XON/XOFF used */
307 crlf_proc(1); /* AND LF->CR xlation */
308 if(strucmp(command, ANSI_PRINTER) == 0
309 || strucmp(command, aname) == 0){
310 fputs("\033[5i", stdout);
311 ansi_off = 1;
313 else{
314 ansi_off = 0;
315 printf("%c", 18); /* aux on for wyse60,
316 Chuck Everett <ceverett@odessa.edu> */
319 ps_global->print->fp = stdout;
320 if(strucmp(command, ANSI_PRINTER) == 0
321 || strucmp(command, WYSE_PRINTER) == 0){
322 /* put formfeed at the end of the trailer string */
323 if(trailer){
324 int len = strlen(trailer);
326 fs_resize((void **)&trailer, len+2);
327 trailer[len] = '\f';
328 trailer[len+1] = '\0';
330 else
331 trailer = cpystr("\f");
334 else{
335 /*----------- Print by forking off a UNIX command ------------*/
336 dprint((4, "Printing using command \"%s\"\n",
337 command ? command : "?"));
338 ps_global->print->result = temp_nam(NULL, "pine_prt");
339 if(ps_global->print->result &&
340 (ps_global->print->pipe = open_system_pipe(command,
341 &ps_global->print->result, NULL,
342 PIPE_WRITE | PIPE_STDERR, 0,
343 pipe_callback, NULL))){
344 ps_global->print->fp = ps_global->print->pipe->out.f;
346 else{
347 if(ps_global->print->result){
348 our_unlink(ps_global->print->result);
349 fs_give((void **)&ps_global->print->result);
352 q_status_message1(SM_ORDER | SM_DING, 3, 4,
353 "Error opening printer: %s",
354 error_description(errno));
355 dprint((2, "Error popening printer \"%s\"\n",
356 error_description(errno)));
357 if(init)
358 fs_give((void **)&init);
360 if(trailer)
361 fs_give((void **)&trailer);
363 return(-1);
367 ps_global->print->err = 0;
368 if(init){
369 if(*init)
370 fputs(init, ps_global->print->fp);
372 fs_give((void **)&init);
375 cb.cbuf[0] = '\0';
376 cb.cbufp = cb.cbuf;
377 cb.cbufend = cb.cbuf;
378 #else /* _WINDOWS */
379 int status;
380 LPTSTR desclpt = NULL;
382 if(desc)
383 desclpt = utf8_to_lptstr(desc);
385 if (status = mswin_print_ready (0, desclpt)) {
386 q_status_message1(SM_ORDER | SM_DING, 3, 4,
387 "Error starting print job: %s",
388 mswin_print_error(status));
389 if(desclpt)
390 fs_give((void **) &desclpt);
392 return(-1);
395 if(desclpt)
396 fs_give((void **) &desclpt);
398 q_status_message(SM_ORDER, 0, 9, "Printing to windows printer...");
399 display_message('x');
401 /* init print control structure */
402 ps_global->print = (PRINT_S *)fs_get(sizeof(PRINT_S));
403 memset(ps_global->print, 0, sizeof(PRINT_S));
405 ps_global->print->err = 0;
406 #endif /* _WINDOWS */
408 return(0);
413 /*----------------------------------------------------------------------
414 Close printer
416 If we're piping to a spooler close down the pipe and wait for the process
417 to finish. If we're sending to an attached printer send the escape sequence.
418 Also let the user know the result of the print
419 ----*/
420 void
421 close_printer(void)
423 #ifndef _WINDOWS
424 if(trailer){
425 if(*trailer)
426 fputs(trailer, ps_global->print->fp);
428 fs_give((void **)&trailer);
431 if(ps_global->print->fp == stdout) {
432 if(ansi_off)
433 fputs("\033[4i", stdout);
434 else
435 printf("%c", 20); /* aux off for wyse60 */
437 fflush(stdout);
438 if(F_OFF(F_PRESERVE_START_STOP, ps_global))
439 xonxoff_proc(0); /* turn off XON/XOFF */
441 crlf_proc(0); /* turn off CF->LF xlantion */
442 } else {
443 (void) close_system_pipe(&ps_global->print->pipe, NULL, pipe_callback);
444 display_output_file(ps_global->print->result, "PRINT", NULL, 1);
445 if(ps_global->print && ps_global->print->result)
446 fs_give((void **) &ps_global->print->result);
448 #else /* _WINDOWS */
449 mswin_print_done();
450 #endif /* _WINDOWS */
452 if(ps_global->print)
453 fs_give((void **) &ps_global->print);
455 q_status_message(SM_ASYNC, 0, 3, "Print command completed");
456 display_message('x');
460 /*----------------------------------------------------------------------
461 Print a single character, translate from UTF-8 to user's locale charset.
463 Args: c -- char to print
464 Returns: 1 on success, 0 on ps_global->print->err
465 ----*/
467 print_char(int c)
469 #ifndef _WINDOWS
470 int i, outchars;
471 unsigned char obuf[MAX(MB_LEN_MAX,32)];
473 if(!ps_global->print->err
474 && (outchars = utf8_to_locale(c, &cb, obuf, sizeof(obuf)))){
475 for(i = 0; i < outchars && !ps_global->print->err; i++)
476 if(putc(obuf[i], ps_global->print->fp) == EOF)
477 ps_global->print->err = 1;
479 #else /* _WINDOWS */
480 if(!ps_global->print->err
481 && (ps_global->print->err = mswin_print_char_utf8(c)))
482 q_status_message1(SM_ORDER, 0, 9, "Print cancelled: %s",
483 mswin_print_error((unsigned short)ps_global->print->err));
484 #endif /* _WINDOWS */
486 return(!ps_global->print->err);
490 /*----------------------------------------------------------------------
491 Send a line of text to the printer
493 Args: line -- Text to print
495 ----*/
496 void
497 print_text(char *line)
499 #ifndef _WINDOWS
500 int slen = strlen(line);
502 while(!ps_global->print->err && slen--)
503 if(print_char(*line++) == 0)
504 ps_global->print->err = 1;
505 #else /* _WINDOWS */
506 if(!ps_global->print->err
507 && (ps_global->print->err = mswin_print_text_utf8(line)))
508 q_status_message1(SM_ORDER, 0, 9, "Print cancelled: %s",
509 mswin_print_error((unsigned short)ps_global->print->err));
510 #endif /* _WINDOWS */
514 /*----------------------------------------------------------------------
515 printf style formatting with one arg for printer
517 Args: line -- The printf control string
518 a1 -- The 1st argument for printf
519 ----*/
520 void
521 print_text1(char *line, char *a1)
523 char buf[64000];
525 if(!ps_global->print->err && snprintf(buf, sizeof(buf), line, a1) < 0)
526 ps_global->print->err = 1;
527 else
528 print_text(buf);