Back to index

tetex-bin  3.0
print-internal.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2002-2004 Paul Vojta and the xdvik development team
00003  * 
00004  * Permission is hereby granted, free of charge, to any person obtaining a copy
00005  * of this software and associated documentation files (the "Software"), to
00006  * deal in the Software without restriction, including without limitation the
00007  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
00008  * sell copies of the Software, and to permit persons to whom the Software is
00009  * furnished to do so, subject to the following conditions:
00010  * 
00011  * The above copyright notice and this permission notice shall be included in
00012  * all copies or substantial portions of the Software.
00013  * 
00014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
00017  * PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
00018  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
00019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00020  * OTHER DEALINGS IN THE SOFTWARE.
00021  */
00022 
00023 /*
00024  * SU: Printing/saving backend code (all the non-GUI stuff),
00025  * adapted from non-k xdvi's popups.c.
00026  *
00027  * Implementation notes:
00028  *   All inter-process communication happens in an asynchronous
00029  *   fasion, so that the GUI remains responsive.  This means that if
00030  *   there are several processes in sequence (e.g. dvips invocation,
00031  *   then ps2pdf invocation), the latter must be called by the child
00032  *   watching procedure of the former (e.g. ps2pdf_exited by the
00033  *   fork_process() callback in call_ps2pdf()), else we can't catch its
00034  *   return status, do cleanup afterwards etc., because the code inside
00035  *   the caller may be executed *before* the child has finished. (Note
00036  *   e.g. how unlink()ing the temporary PS file is done inside
00037  *   ps2pdf_exited() and not in the callers). This is the reason why we
00038  *   often need to pass information to the subprocess (via a data `void *').
00039  */
00040 
00041 #include "xdvi-config.h"
00042 #include "xdvi.h"
00043 #include "dvisel.h" /* for select_pages_info struct */
00044 #include "print-internal.h"
00045 #include "print-log.h"
00046 #include "search-internal.h"
00047 #include "events.h"
00048 #include "message-window.h"
00049 #include "util.h"
00050 #include "x_util.h"
00051 #include "string-utils.h"
00052 #include "my-snprintf.h"
00053 
00054 #include <errno.h>
00055 #include <signal.h>
00056 #include <sys/stat.h>
00057 
00058 /* if POSIX O_NONBLOCK is not available, use O_NDELAY */
00059 #if !defined(O_NONBLOCK) && defined(O_NDELAY)
00060 # define O_NONBLOCK O_NDELAY
00061 #endif
00062 
00063 #ifdef EWOULDBLOCK
00064 # ifdef EAGAIN
00065 #  define AGAIN_CONDITION   (errno == EWOULDBLOCK || errno == EAGAIN)
00066 # else
00067 #  define AGAIN_CONDITION   (errno == EWOULDBLOCK)
00068 # endif
00069 #else /* EWOULDBLOCK */
00070 # ifdef EAGAIN
00071 #  define AGAIN_CONDITION   (errno == EAGAIN)
00072 # endif
00073 #endif /* EWOULDBLOCK */
00074 
00075 #if HAVE_POLL
00076 # include <poll.h>
00077 # define XIO_IN POLLIN
00078 # define XIO_OUT POLLOUT
00079 #else
00080 # define XIO_IN 1
00081 # define XIO_OUT 2
00082 #endif /* HAVE_POLL */
00083 
00084 static struct xchild print_child = {NULL, 0, True, "dvips", NULL, NULL, NULL };
00085 
00086 static char *read_from_dvips(int);
00087 static struct xio print_xio = { NULL, 0, XIO_IN,
00088 #if HAVE_POLL
00089                             NULL,
00090 #endif
00091                             read_from_dvips,
00092                             NULL /* write proc - not needed */
00093 };
00094 
00095 static void dvips_alarm(struct xtimer *);
00096 static struct xtimer dvips_timer = {NULL, {0, 0}, XTM_DEFAULT, dvips_alarm
00097 #if XDVI_XT_TIMER_HACK
00098                                 , NULL, NULL
00099 #endif
00100 };
00101 
00102 static int dvips_sig;       /* SIGINT or SIGKILL */
00103 
00104 static int dvips_status;
00105 static const int DVIPS_STAT_NONE = 0;
00106 static const int DVIPS_STAT_RUN = 1;
00107 static const int  DVIPS_STAT_WAIT = 2;
00108 
00109 /*
00110   FIXME: implement this in terms of fork_process()!
00111   Currently it doesn't work since the DVI file and the
00112   output file are realized via redirecting stdin/stdout.
00113   This also obscures the log output (the I/O redirection
00114   isn't visible to users).
00115    
00116   TODO: Remove the detailed log output, and implement
00117   a progress window that only shows the page numbers.
00118   The detailed log should go to an `xdvi log output' page.
00119 */
00120 
00121 static void
00122 fork_dvips(char **argv, const struct select_pages_info *pinfo, childProcT proc)
00123 {
00124     int print_io[2];
00125     int i;
00126     const struct file_info *finfo = pinfo->finfo;
00127     /*     printlog_append(argv[0], strlen(argv[0])); */
00128     FILE *fout = NULL;
00129 
00130     if (finfo->ps_out.fname != NULL) {
00131        /* printing to PS file, open file for writing */
00132        if ((fout = XFOPEN(finfo->ps_out.fname, "w")) == NULL) {
00133            popup_message(globals.widgets.top_level,
00134                        MSG_ERR,
00135                        NULL, "Could not open %s for writing: %s.",
00136                        finfo->ps_out.fname,
00137                        strerror(errno));
00138            return;
00139        }
00140     }
00141 
00142     printlog_popup();
00143 
00144     printlog_append_str("Calling: `");
00145     printlog_append_str(argv[0]);
00146 
00147     for (i = 1; argv[i] != NULL; i++) {
00148        printlog_append_str(" ");
00149        printlog_append_str(argv[i]);
00150     }
00151     printlog_append_str("'\n");
00152     
00153     if (xpipe(print_io) != 0) {
00154        perror("[xdvi] pipe");
00155        return;
00156     }
00157  
00158     /* Fork process */
00159     
00160     /* flush output buffers to avoid double buffering (i.e. data
00161        waiting in the output buffer being written twice, by the parent
00162        and the child) */
00163     fflush(stderr);
00164     fflush(stdout);
00165 
00166     print_child.name = xstrdup(argv[0]);
00167     print_child.proc = proc;
00168     print_child.data = (void *)pinfo;
00169     print_child.pid = fork();
00170     if (print_child.pid == 0) {    /* if child */
00171        /* change into dir of DVI file so that included image files etc. are found */
00172        chdir(globals.dvi_file.dirname);
00173 
00174        if (globals.debug & DBG_FILES) {
00175            char path[MAXPATHLEN];
00176            getcwd(path, MAXPATHLEN);
00177            fprintf(stderr, "Directory of running `%s': `%s'\n",
00178                   argv[0], path);
00179        }
00180 
00181        /* make the input file pointer the STDIN of the dvips process */
00182        if (finfo->dvi_tmp.fp != NULL) { /* printing selected pages from temporary file */
00183            (void)dup2(fileno(finfo->dvi_tmp.fp), STDIN_FILENO);
00184        }
00185        else { /* printing from main or backup file */
00186            (void)dup2(fileno(finfo->dvi_in.fp), STDIN_FILENO);
00187        }
00188        (void)lseek(0, 0, SEEK_SET);
00189        
00190        if (fout != NULL) { /* printing to file, make stdout of child go to fout */
00191            (void)dup2(fileno(fout), STDOUT_FILENO);
00192            (void)close(fileno(fout));
00193        }
00194        else { /* printing to printer, make stdout of child go to print_io[1] */
00195            (void)dup2(print_io[1], STDOUT_FILENO);
00196        }
00197 
00198        /* make stderr of child go to print_io[1] */
00199        (void)dup2(print_io[1], STDERR_FILENO);
00200        (void)close(print_io[1]);
00201        (void)close(print_io[0]);
00202  
00203        if (setsid() == -1) {       /* so we can kill the process group */
00204            perror("setsid");
00205            fflush(stderr);
00206            _exit(1);
00207        }
00208        (void)execvp(*argv, argv);
00209        popup_message(globals.widgets.top_level,
00210                     MSG_ERR,
00211                     NULL,
00212                     "Execution of \"%s\" failed: %s.\n", *argv, strerror(errno));
00213        fflush(stderr);
00214        _exit(1);
00215     }
00216  
00217     if (fout != NULL)
00218        fclose(fout);
00219  
00220     if (print_child.pid == -1) {   /* error */
00221        perror("[xdvi] vfork");
00222        return;
00223     }
00224  
00225     set_chld(&print_child);
00226     dvips_sig = SIGINT;
00227 
00228     (void)close(print_io[1]);
00229  
00230     /* Set up file descriptor for non-blocking I/O */
00231     prep_fd(print_io[0], True);
00232     print_xio.fd = print_io[0];
00233     set_io(&print_xio);
00234  
00235     dvips_status = DVIPS_STAT_RUN; /* running */
00236 }
00237 
00238 /*
00239  * Create an argument list for dvips, using information from pinfo
00240  * and X resources. Returns the result list in freshly allocated
00241  * memory.
00242  */
00243 static char **
00244 create_dvips_argv(const struct select_pages_info *pinfo, Boolean do_pdf, printOrSaveActionT act)
00245 {
00246     size_t argv_len = 128; /* should be ample ... */
00247     char **argv = xmalloc(argv_len * sizeof *argv);
00248     size_t idx = 0;
00249     const char *printer_options = get_printer_options();
00250     char *dvips_options = get_dvips_options(act);
00251     char from_page[LENGTH_OF_INT];
00252     char to_page[LENGTH_OF_INT];
00253 
00254     TRACE_GUI((stderr, "dvips options: |%s|", dvips_options));
00255     
00256     argv[idx++] = xstrdup(resource.dvips_path);
00257 
00258     if (dvips_options != NULL) {
00259        char **dvips_args = get_separated_list(dvips_options, " \t", True); /* this allocates dvips_args */
00260        int i;
00261        for (i = 0; dvips_args[i] != NULL; i++) {
00262            argv[idx++] = dvips_args[i];
00263        }
00264        free(dvips_args);
00265     }
00266 
00267     if (do_pdf)
00268        argv[idx++] = xstrdup("-Ppdf");
00269     
00270     argv[idx++] = xstrdup("-f");
00271     
00272     if (printer_options != NULL && pinfo->finfo->ps_out.fname == NULL) { /* printing to printer */
00273        char **printer_args = get_separated_list(printer_options, " \t", True); /* this allocates printer_args */
00274        int i;
00275        ASSERT(*printer_args != NULL, "args should contain at least the string \"lpr\"");
00276        argv[idx] = xstrdup("-o!");
00277        argv[idx] = xstrcat(argv[idx], printer_args[0]);
00278        free(printer_args[0]);
00279        /* now append everything to the printer pipe command, so that dvips doesn't see it as an option */
00280        for (i = 1; printer_args[i] != NULL; i++) {
00281            argv[idx] = xstrcat(argv[idx], " ");
00282            argv[idx] = xstrcat(argv[idx], printer_args[i]);
00283            free(printer_args[i]);
00284        }
00285        free(printer_args);
00286        idx++;
00287     }
00288 
00289     if (pinfo->callback == check_pagerange) {
00290        /* convert back from 0-based to 1-based, also taking globals.pageno_correct into accout
00291         * (which is 1 by default, so we need to add 2) */
00292        argv[idx] = xstrdup("-p=");
00293        SNPRINTF(from_page, LENGTH_OF_INT, "%d", pinfo->from - globals.pageno_correct + 2);
00294        argv[idx] = xstrcat(argv[idx], from_page);
00295        idx++;
00296        argv[idx] = xstrdup("-l=");
00297        SNPRINTF(to_page, LENGTH_OF_INT, "%d", pinfo->to - globals.pageno_correct + 2);
00298        argv[idx] = xstrcat(argv[idx], to_page);
00299        idx++;
00300     }
00301 
00302     argv[idx] = NULL; /* terminate it */
00303     ASSERT(idx <= argv_len, "Too many elements");
00304     return argv;
00305 }
00306 
00307 /* Check if selecting pages worked, report error else */
00308 static Boolean
00309 select_pages_report_error(const struct select_pages_info *pinfo)
00310 {
00311     if (pinfo->errflag == NO_ERROR)
00312        return False;
00313     
00314     popup_message(globals.widgets.top_level,
00315                 MSG_INFO,
00316                 NULL,
00317                 "Could not save DVI file to %s: %s.",
00318                 pinfo->finfo->dvi_tmp.fname,
00319                 get_dvi_error(pinfo->errflag));
00320     return True;
00321 }
00322 
00323 
00324 /*
00325  * Collect dvips output, appending to print log.
00326  * Currently return value is a dummy (NULL); TODO: return
00327  * the output, for later filtering.
00328  */
00329 static char *
00330 read_from_dvips(int ignored)
00331 {
00332     int bytes;
00333     char line[80];
00334 
00335     UNUSED(ignored);
00336     
00337     for (;;) {
00338 #ifndef MOTIF
00339        bytes = read(print_xio.fd, line, sizeof line);
00340 #else
00341        bytes = read(print_xio.fd, line, sizeof line - 1);
00342 #endif
00343        if (bytes < 0) {
00344            if (AGAIN_CONDITION)
00345               break;
00346            perror("xdvi: read_from_dvips");
00347            break;
00348        }
00349 
00350        if (bytes == 0)
00351            break;
00352        else {
00353 #ifdef MOTIF
00354            line[bytes] = '\0';
00355 #endif
00356            printlog_append(line, bytes);
00357        }
00358     }
00359     return NULL; /* TODO */
00360 }
00361 
00362 /*
00363  * Report dvips exit status, and remove temporary DVI file if needed.
00364  */
00365 static void
00366 dvips_exited(int exitval, struct xchild *child)
00367 {
00368     char str[128] = "";
00369     int ms;
00370 
00371     struct select_pages_info *pinfo = (struct select_pages_info *)child->data;
00372     
00373     read_from_dvips(0);
00374     clear_io(&print_xio);
00375     (void)close(print_xio.fd);
00376 
00377     if (WIFEXITED(exitval)) {
00378        if (WEXITSTATUS(exitval) == 0) {
00379            if (pinfo->finfo->ps_out.fname != NULL) {
00380               printlog_append_str("\nCreated Postscript file ");
00381               printlog_append_str(pinfo->finfo->ps_out.fname);
00382               printlog_append_str(".\n");
00383            }
00384            else {
00385               printlog_append_str("Done.\n");
00386            }
00387 
00388            /* remove temporary DVI file if it exists */
00389            if (pinfo->finfo->dvi_tmp.fname != NULL) {
00390               if (globals.debug & DBG_GUI)
00391                   TRACE_GUI((stderr, "NOT removing temporary DVI file: |%s|", pinfo->finfo->dvi_tmp.fname));
00392               else
00393                   unlink(pinfo->finfo->dvi_tmp.fname);
00394            }
00395        }
00396        else
00397            sprintf(str, "\nDvips returned exit code %d.\n", WEXITSTATUS(exitval));
00398     }
00399     else if (WIFSIGNALED(exitval))
00400        sprintf(str, "\nDvips terminated by signal %d.\n", WTERMSIG(exitval));
00401     else
00402        sprintf(str, "\nDvips returned unknown status 0x%x.\n", exitval);
00403 
00404     ms = resource.dvips_hang;
00405     if (str[0] != '\0') {
00406        XBell(DISP, 0);
00407        ms = resource.dvips_fail_hang;
00408        printlog_append_str(str);
00409     }
00410 
00411     if (ms > 0) {
00412        set_timer(&dvips_timer, ms);
00413        dvips_status = DVIPS_STAT_WAIT;
00414     }
00415     else {
00416        dvips_status = DVIPS_STAT_NONE;
00417     }
00418 
00419     printlog_enable_closebutton();
00420 }
00421 
00422 /*
00423  * Report pd2pdf exit status, and remove temporary PS file if needed.
00424  */
00425 static void
00426 ps2pdf_exited(int status, struct xchild *this)
00427 {
00428     char *err_msg = NULL;
00429     int ms = resource.dvips_hang;
00430     /* if child exited with error and xio struct is available for child,
00431        print error text */
00432     if (this->io != NULL && (WIFEXITED(status) != 0)) {
00433        if ((WEXITSTATUS(status) != 0)
00434            && (err_msg = (this->io->read_proc)(this->io->fd)) != NULL) {
00435            char buf[LENGTH_OF_INT];
00436            SNPRINTF(buf, LENGTH_OF_INT, "%d", WEXITSTATUS(status));
00437            ms = resource.dvips_fail_hang;
00438            XBell(DISP, 0);
00439            printlog_append_str("\n\nError calling ");
00440            if (this->name != NULL) {
00441               printlog_append_str("\"");
00442               printlog_append_str(this->name);
00443               printlog_append_str("\" ");
00444            }
00445            printlog_append_str("\nCommand exited with error code ");
00446            printlog_append_str(buf);
00447            printlog_append_str(":\n");
00448            printlog_append_str(err_msg);
00449            free(err_msg);
00450        }
00451        else {
00452            const struct select_pages_info *pinfo = (const struct select_pages_info *)this->data;
00453            int retval;
00454            printlog_append_str("\nCreated PDF file ");
00455            printlog_append_str(pinfo->finfo->pdf_out.fname);
00456            printlog_append_str(".\n");
00457            TRACE_FILES((stderr, "Removing temporary PS file: `%s'",
00458                       pinfo->finfo->ps_out.fname));
00459            retval = unlink(pinfo->finfo->ps_out.fname);
00460            if (retval != 0) {
00461               fprintf(stderr, "Could not unlink `%s': %s.\n",
00462                      pinfo->finfo->ps_out.fname, strerror(errno));
00463            }
00464        }
00465     }
00466     else {
00467        popup_message(globals.widgets.top_level,
00468                     MSG_ERR, REPORT_XDVI_BUG_TEMPLATE,
00469                     "Internal error: ps2pdf_exited() called while child still running?");
00470     }
00471     if (ms > 0) {
00472        set_timer(&dvips_timer, ms);
00473        dvips_status = DVIPS_STAT_WAIT;
00474     }
00475     else {
00476        dvips_status = DVIPS_STAT_NONE;
00477     }
00478 
00479     printlog_enable_closebutton();
00480     
00481     free(this->name);
00482     free(this->io);
00483     free(this);
00484 }
00485 
00486 static void
00487 call_ps2pdf(const char *path, const struct select_pages_info *pinfo)
00488 {
00489     const char *argv[5];
00490     size_t idx = 0;
00491 
00492     argv[idx++] = path;
00493     argv[idx++] = pinfo->finfo->ps_out.fname;
00494     argv[idx++] = pinfo->finfo->pdf_out.fname;
00495     argv[idx++] = NULL;
00496 
00497     /* need to run this in globals.xdvi_dir again, since the dvips conversion directory
00498        globals.dvi_file.dirname may not be writable! */
00499     if (!fork_process("ps2pdf", True, globals.xdvi_dir, ps2pdf_exited, (void *)pinfo, (char **)argv)) {
00500        popup_message(globals.widgets.top_level,
00501                     MSG_ERR,
00502                     NULL, "Couldn't fork %s process: %s\n", argv[0], strerror(errno));
00503     }
00504 }
00505 
00506 static void
00507 dvips_ps2pdf(int exitval, struct xchild *child)
00508 {
00509     char str[128] = "";
00510     int ms;
00511     struct select_pages_info *pinfo = (struct select_pages_info *)child->data;
00512     
00513     read_from_dvips(0);
00514     clear_io(&print_xio);
00515     (void)close(print_xio.fd);
00516 
00517     if (WIFEXITED(exitval)) {
00518        if (WEXITSTATUS(exitval) == 0) {
00519            /* dvips ended OK; call ps2pdf: */
00520            TRACE_GUI((stderr, "Created temporary PS file |%s|", pinfo->finfo->ps_out.fname));
00521            printlog_append_str("\nCalling ");
00522            printlog_append_str(resource.ps2pdf_path);
00523            printlog_append_str(" ...");
00524 
00525            /* remove temporary DVI file if it exists */
00526            if (pinfo->finfo->dvi_tmp.fname != NULL) {
00527               if (globals.debug & DBG_GUI)
00528                   TRACE_GUI((stderr, "NOT removing temporary DVI file: |%s|", pinfo->finfo->dvi_tmp.fname));
00529               else
00530                   unlink(pinfo->finfo->dvi_tmp.fname);
00531            }
00532            /* invoke ps2pdf conversion */
00533            call_ps2pdf(resource.ps2pdf_path, pinfo);
00534        }
00535        else
00536            sprintf(str, "\nPrint process returned exit code %d.\n",
00537                   WEXITSTATUS(exitval));
00538     }
00539     else if (WIFSIGNALED(exitval))
00540        sprintf(str, "\nPrint process terminated by signal %d.\n",
00541               WTERMSIG(exitval));
00542     else
00543        sprintf(str, "\nPrint process returned unknown status 0x%x.\n",
00544               exitval);
00545 
00546     /* enable close button only if dvips conversion already failed */
00547     ms = resource.dvips_hang;
00548     if (str[0] != '\0') {
00549        XBell(DISP, 0);
00550        ms = resource.dvips_fail_hang;
00551        printlog_append_str(str);
00552 
00553        if (ms > 0) {
00554            set_timer(&dvips_timer, ms);
00555            dvips_status = DVIPS_STAT_WAIT;
00556        }
00557        else {
00558            dvips_status = DVIPS_STAT_NONE;
00559        }
00560        
00561        printlog_enable_closebutton();
00562     }
00563 }
00564 
00565 
00566 static void
00567 dvips_alarm(struct xtimer *should_be_timer)
00568 {
00569     UNUSED(should_be_timer);
00570 
00571     printlog_popdown(False);
00572     
00573     dvips_status = DVIPS_STAT_NONE;
00574 }
00575 
00576 
00577 
00578 static void
00579 cb_dvips_unkeep(Widget w, XtPointer client_data, XtPointer call_data)
00580 {
00581 
00582     UNUSED(w);
00583     UNUSED(client_data);
00584     UNUSED(call_data);
00585     
00586     /* use negative value so that we don't loose the value itself */
00587     TRACE_GUI((stderr, "toggling resource from %d to %d, %d to %d",
00588               resource.dvips_hang, -resource.dvips_hang,
00589               resource.dvips_fail_hang, -resource.dvips_fail_hang));
00590     resource.dvips_hang = -resource.dvips_hang;
00591     resource.dvips_fail_hang = -resource.dvips_fail_hang;
00592 
00593     store_preference(NULL, "dvipsHangTime", "%d", resource.dvips_hang);
00594     store_preference(NULL, "dvipsFailHangTime", "%d", resource.dvips_fail_hang);
00595 
00596     
00597     if (dvips_status == DVIPS_STAT_WAIT) {
00598        dvips_status = DVIPS_STAT_NONE;
00599        cancel_timer(&dvips_timer);
00600     }
00601 }
00602 
00603 /* if user selects the window manager destroy button */
00604 static void
00605 cb_dvips_destroy(Widget w, XtPointer client_data, XtPointer call_data)
00606 {
00607     UNUSED(w);
00608     UNUSED(client_data);
00609     UNUSED(call_data);
00610 
00611     if (dvips_status == DVIPS_STAT_RUN) {
00612        kill(print_child.pid, dvips_sig);
00613        dvips_sig = SIGKILL;
00614        printlog_append_str("^C");
00615     }
00616 
00617     if (dvips_status == DVIPS_STAT_WAIT) {
00618        dvips_status = DVIPS_STAT_NONE;
00619        cancel_timer(&dvips_timer);
00620     }
00621 
00622     printlog_reset();
00623     printlog_popdown(True);
00624 }
00625     
00626 static void
00627 cb_dvips_cancel(Widget w, XtPointer client_data, XtPointer call_data)
00628 {
00629     UNUSED(w);
00630     UNUSED(client_data);
00631     UNUSED(call_data);
00632 
00633     if (dvips_status != DVIPS_STAT_RUN)
00634        return;       /* How did we get here? */
00635 
00636     kill(print_child.pid, dvips_sig);
00637     dvips_sig = SIGKILL;
00638     printlog_append_str("^C");
00639 }
00640 
00641 static void
00642 cb_dvips_close(Widget w, XtPointer client_data, XtPointer call_data)
00643 {
00644     UNUSED(w);
00645     UNUSED(client_data);
00646     UNUSED(call_data);
00647 
00648     if (dvips_status == DVIPS_STAT_RUN)
00649        return;       /* How did we get here? */
00650 
00651     if (dvips_status == DVIPS_STAT_WAIT) {
00652        dvips_status = DVIPS_STAT_NONE;
00653        cancel_timer(&dvips_timer);
00654     }
00655 
00656     printlog_popdown(True);
00657 }
00658 
00659 
00660 void
00661 internal_save(struct select_pages_info *pinfo,
00662              outputFormatT output_format)
00663 {
00664     struct file_info *finfo = pinfo->finfo;
00665     int tmp_fd;
00666     char **argv = NULL;
00667     int i;
00668     childProcT dvips_exit_proc = dvips_exited; /* default procedure to call after fork_dvips() */
00669     Boolean do_pdf = False; /* force `-Ppdf' for dvips? (used for ps2pdf conversion) */
00670 
00671     ASSERT(output_format != FMT_NONE, "No valid output format selected!");
00672     
00673     switch (output_format) {
00674        
00675     case FMT_DVI:
00676        /* here we first create a temporary file, and if that succeeded,
00677           move it to the final position */
00678        select_pages(pinfo);
00679 
00680        if (!select_pages_report_error(pinfo)) {
00681            /*
00682             * ... else, move the temporary file to its final destination.  For that, we
00683             * try the more efficient rename() first; if this fails, try copying the file
00684             * (rename() will e.g. fail if the tmp directory is on a different file
00685             * system).  We could perhaps examine errno after the renaming attempt to
00686             * find out whether we need to do the copy attempt in the first place, but
00687             * that'd be rather error-prone ...  */
00688            ASSERT(finfo->dvi_tmp.fname != NULL, "filename mustn't be NULL");
00689            if (rename(finfo->dvi_tmp.fname, finfo->dvi_out.fname) != 0
00690               && !copy_file(finfo->dvi_tmp.fname, finfo->dvi_out.fname)) {
00691               popup_message(globals.widgets.top_level,
00692                            MSG_ERR, NULL, "Creating %s failed: %s",
00693                            finfo->dvi_out.fname, strerror(errno));
00694            }
00695            else { /* moving worked */
00696               TRACE_GUI((stderr, "renamed %s to %s\n", finfo->dvi_tmp.fname, finfo->dvi_out.fname));
00697 
00698               if (pinfo->warn_files.stack_len > 0) { /* do we need to warn user about referenced files? */
00699                   char *warn_files = xstrdup("");
00700                   size_t i;
00701                   for (i = 0; i < pinfo->warn_files.stack_len; i++) {
00702                      warn_files = xstrcat(warn_files, pinfo->warn_files.items[i].content);
00703                      warn_files = xstrcat(warn_files, "\n");
00704                   }
00705                   popup_message(globals.widgets.top_level,
00706                               MSG_INFO,
00707                               "The files listed in the message (probably images) are not part "
00708                               "of the newly created DVI file; they are only referenced by that file. "
00709                               "Therefore, if you want to distribute this file or use it on a different computer,"
00710                               "you will need to distribute all the referenced files, too.\n\n"
00711                               "Tip: A better format for distributing files is Postscript or PDF. "
00712                               "To create a Postscript file, use the `File->Print' menu, "
00713                               "then select `Print to file' in the dialog window.",
00714                               "Created %s.\n"
00715                               "Please note that the following files are referenced by this file, "
00716                               "and are needed for displaying or printing it correctly:\n%s\n",
00717                               finfo->dvi_out.fname, warn_files);
00718                   free(warn_files);
00719               }
00720               else {
00721                   popup_message(globals.widgets.top_level,
00722                               MSG_INFO,
00723                               NULL,
00724                               "Created DVI file: %s.", finfo->dvi_out.fname);
00725               }
00726               unlink(finfo->dvi_tmp.fname);
00727            }
00728        }
00729        break;
00730 
00731     case FMT_PS2PDF:
00732        tmp_fd = xdvi_temp_fd(&(finfo->ps_out.fname)); /* this allocates finfo->ps_out.fname */
00733        if (tmp_fd == -1) {
00734            popup_message(globals.widgets.top_level,
00735                        MSG_ERR, NULL,
00736                        "Couldn't create temporary PS file for conversion to PDF: %s",
00737                        strerror(errno));
00738            return;
00739        }
00740        else {
00741            TRACE_GUI((stderr, "name of temporary PS file: |%s|", finfo->ps_out.fname));
00742        }
00743        dvips_exit_proc = dvips_ps2pdf; /* call ps2pdf conversion after dvips exited */
00744        do_pdf = True;
00745        /* fall through */
00746     case FMT_PS:
00747        if (pinfo->callback == check_marked) { /* want to print selected pages? */
00748            ASSERT(finfo->dvi_tmp.fp != NULL, "Temporary file pointer musn't be NULL!");
00749            ASSERT(finfo->dvi_tmp.fname != NULL, "Temporary filename musn't be NULL!");
00750            select_pages(pinfo);
00751        }
00752        printlog_create("Xdvik: Saving", "Automatically close this window after file has been saved",
00753                      cb_dvips_close, cb_dvips_cancel, cb_dvips_destroy, cb_dvips_unkeep);
00754        
00755        if ((argv = create_dvips_argv(pinfo, do_pdf, FILE_SAVE)) == NULL) {
00756            /* something went *really* wrong; assume user has already been warned about it */
00757            return;
00758        }
00759        fork_dvips(argv, pinfo, dvips_exit_proc);
00760 
00761        /* dellocate argv */
00762        for (i = 0; argv[i] != NULL; i++) {
00763            free(argv[i]);
00764        }
00765        free(argv);
00766 
00767        break;
00768     case FMT_ISO_8859_1:
00769     case FMT_UTF8:
00770        if (search_extract_text(output_format, pinfo)) {
00771            popup_message(globals.widgets.top_level,
00772                        MSG_INFO,
00773                        NULL,
00774                        "Created text file %s.", finfo->txt_out.fname);
00775        }
00776        else {
00777            popup_message(globals.widgets.top_level,
00778                        MSG_INFO,
00779                        NULL,
00780                        "Extracting text from DVI file failed!");
00781        }
00782        break;
00783     case FMT_NONE:
00784        break;
00785     }
00786 }
00787 
00788 void
00789 internal_print(struct select_pages_info *pinfo)
00790 {
00791     char **argv = NULL;
00792     int i;
00793 
00794     if (pinfo->callback == check_marked) { /* want to print selected pages? */
00795        ASSERT(pinfo->finfo->dvi_tmp.fp != NULL, "Temporary file pointer musn't be NULL!");
00796        ASSERT(pinfo->finfo->dvi_tmp.fname != NULL, "Temporary filename musn't be NULL!");
00797        select_pages(pinfo);
00798     }
00799     printlog_create("Xdvik: Printing", "Automatically close this window when printing finishes",
00800                   cb_dvips_close, cb_dvips_cancel, cb_dvips_destroy, cb_dvips_unkeep);
00801        
00802     if ((argv = create_dvips_argv(pinfo, False, FILE_PRINT)) == NULL) {
00803        /* something went *really* wrong; assume user has already been warned about it */
00804        return;
00805     }
00806 
00807     fork_dvips(argv, pinfo, dvips_exited);
00808     
00809     /* dellocate argv */
00810     for (i = 0; argv[i] != NULL; i++) {
00811        free(argv[i]);
00812     }
00813     free(argv);
00814 }