Back to index

lightning-sunbird  0.9+nobinonly
xprintutil_printtofile.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the X11 print system utilities library.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "xprintutil.h"
00039  
00040 #include <stdlib.h>
00041 #include <string.h>
00042 #include <stdio.h>
00043 #include <limits.h>
00044 #include <errno.h>
00045 #ifdef XPU_USE_THREADS
00046 #include <time.h>
00047 #ifdef XPU_USE_NSPR
00048 #include <prthread.h>
00049 #else
00050 #include <pthread.h>
00051 #endif /* XPU_USE_NSPR */
00052 #endif /* XPU_USE_THREADS */
00053 #include <unistd.h>
00054 #include <sys/time.h>
00055 #include <sys/types.h>
00056 #include <sys/wait.h>
00057 
00058 /* local prototypes */
00059 #ifdef DEBUG
00060 static void PrintXPGetDocStatus( XPGetDocStatus status );
00061 #endif
00062 static Bool  XNextEventTimeout( Display *display, XEvent *event_return, struct timeval *timeout );
00063 static void *XpuPrintToFile( Display *pdpy, XPContext pcontext, const char *filename );
00064 static void  MyPrintToFileProc( Display *pdpy, XPContext pcontext, unsigned char *data, unsigned int data_len, XPointer client_data );
00065 static void  MyFinishProc( Display *pdpy, XPContext pcontext, XPGetDocStatus  status, XPointer client_data );
00066 #ifdef XPU_USE_NSPR
00067 static void PrintToFile_Consumer( void *handle );
00068 #else
00069 static void *PrintToFile_Consumer( void *handle );
00070 #endif
00071 
00072 void XpuStartJobToSpooler(Display *pdpy)
00073 {
00074   XpStartJob(pdpy, XPSpool);
00075 }
00076 
00077 void *XpuStartJobToFile( Display *pdpy, XPContext pcontext, const char *filename )
00078 {
00079   void *handle;
00080 
00081   XpStartJob(pdpy, XPGetData);
00082   handle = XpuPrintToFile(pdpy, pcontext, filename);
00083   
00084   if (!handle)
00085   {
00086     /* Cancel the print job and discard all events... */
00087     XpCancelJob(pdpy, True);
00088   }
00089 
00090   return(handle);
00091 }
00092 
00093 #ifdef DEBUG
00094 /* DEBUG: Print XPGetDocStatus */
00095 static
00096 void PrintXPGetDocStatus( XPGetDocStatus status )
00097 {
00098   switch(status)
00099   {
00100     case XPGetDocFinished:        puts("PrintXPGetDocStatus: XPGetDocFinished");       break;
00101     case XPGetDocSecondConsumer:  puts("PrintXPGetDocStatus: XPGetDocSecondConsumer"); break;
00102     case XPGetDocError:           puts("PrintXPGetDocStatus: XPGetDocError");          break;
00103     default:                      puts("PrintXPGetDocStatus: <unknown value");         break;
00104   }
00105 }
00106 #endif /* DEBUG */
00107 
00108 
00109 /* XNextEvent() with timeout */
00110 static
00111 Bool XNextEventTimeout( Display *display, XEvent *event_return, struct timeval *timeout ) 
00112 {
00113   int      res;
00114   fd_set   readfds;
00115   int      display_fd = XConnectionNumber(display);
00116 
00117   /* small shortcut... */
00118   if( timeout == NULL )
00119   {
00120     XNextEvent(display, event_return);
00121     return(True);
00122   }
00123   
00124   FD_ZERO(&readfds);
00125   FD_SET(display_fd, &readfds);
00126 
00127   /* Note/bug: In the case of internal X events (like used to trigger callbacks 
00128    * registered by XpGetDocumentData()&co.) select() will return with "new info" 
00129    * - but XNextEvent() below processes these _internal_ events silently - and 
00130    * will block if there are no other non-internal events.
00131    * The workaround here is to check with XEventsQueued() if there are non-internal 
00132    * events queued - if not select() will be called again - unfortunately we use 
00133    * the old timeout here instead of the "remaining" time... (this only would hurt 
00134    * if the timeout would be really long - but for current use with values below
00135    * 1/2 secs it does not hurt... =:-)
00136    */
00137   while( XEventsQueued(display, QueuedAfterFlush) == 0 )
00138   {
00139     res = select(display_fd+1, &readfds, NULL, NULL, timeout);
00140   
00141     switch(res)
00142     {
00143       case -1: /* select() error - should not happen */ 
00144           perror("XNextEventTimeout: select() failure"); 
00145           return(False);
00146       case  0: /* timeout */
00147         return(False);
00148     }
00149   }
00150   
00151   XNextEvent(display, event_return); 
00152   return(True);
00153 }
00154 
00155 
00156 #ifdef XPU_USE_THREADS
00157 
00177 typedef struct 
00178 {
00179 #ifdef XPU_USE_NSPR
00180   PRThread      *prthread;
00181 #else
00182   pthread_t      tid;
00183 #endif    
00184   char          *displayname;
00185   Display       *pdpy;
00186   Display       *parent_pdpy;
00187   XPContext      pcontext;
00188   const char    *file_name;
00189   FILE          *file;
00190   XPGetDocStatus status;
00191   Bool           done;
00192 } MyPrintFileData;
00193 
00194 
00195 static
00196 void *XpuPrintToFile( Display *pdpy, XPContext pcontext, const char *filename )
00197 {
00198   MyPrintFileData *mpfd; /* warning: shared between threads !! */
00199          
00200   if( (mpfd = malloc(sizeof(MyPrintFileData))) == NULL )
00201     return(NULL);
00202   
00203   mpfd->parent_pdpy = pdpy;
00204   mpfd->displayname = XDisplayString(pdpy);
00205   mpfd->pdpy        = NULL;
00206   mpfd->pcontext    = pcontext;
00207   mpfd->file_name   = filename;
00208   mpfd->file        = NULL;      
00209   mpfd->status      = XPGetDocError;
00210   
00211   /* make sure we can open the file for writing */
00212   if( (mpfd->file = fopen(mpfd->file_name, "w")) == NULL ) 
00213   {
00214     /* fopen() error */
00215     free(mpfd);
00216     return(NULL);
00217   }
00218   
00219   /* its important to flush before we start the consumer thread, 
00220    * to make sure that the XpStartJob gets through first in the parent
00221    */
00222   XFlush(pdpy);
00223 #ifdef XPU_USE_NSPR
00224   if( (mpfd->prthread = PR_CreateThread(PR_SYSTEM_THREAD, PrintToFile_Consumer, mpfd, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0)) == NULL )
00225 #else    
00226   if( pthread_create(&(mpfd->tid), NULL, PrintToFile_Consumer, mpfd) != 0 ) 
00227 #endif
00228   {
00229     /* pthread_create() error */
00230     fclose(mpfd->file);
00231     free(mpfd);
00232     return(NULL);
00233   }
00234   
00235   /* we're still in the parent */
00236   XPU_DEBUG_ONLY(printf("### parent started consumer thread.\n" ));
00237   return(mpfd);
00238 }
00239 
00240 
00241 XPGetDocStatus XpuWaitForPrintFileChild( void *handle )
00242 {
00243   MyPrintFileData *mpfd   = (MyPrintFileData *)handle;
00244   void            *res;
00245   XPGetDocStatus   status;
00246   
00247   /* Flush data a last time to make sure we send all data to Xprt and that
00248    * the Xlib internal hooks have called a last time */
00249   XFlush(mpfd->parent_pdpy);
00250 
00251 #ifdef XPU_USE_NSPR
00252   if( PR_JoinThread(mpfd->prthread) != PR_SUCCESS  )
00253     perror("XpuWaitForPrintFileChild: PR_JoinThread() failure"); /* fixme(later): use NSPR error handling calls... */
00254 #else   
00255   if( XPU_TRACE(pthread_join(mpfd->tid, &res)) != 0 )
00256     perror("XpuWaitForPrintFileChild: pthread_join() failure");
00257 #endif
00258       
00259   status = mpfd->status;      
00260   free(handle);  
00261               
00262   XPU_DEBUG_ONLY(PrintXPGetDocStatus(status));
00263   return(status);
00264 }
00265 
00266 #else /* XPU_USE_THREADS */
00267 
00286 typedef struct 
00287 {
00288   pid_t           pid;
00289   int             pipe[2];  /* child-->parent communication pipe */
00290   const char     *displayname;
00291   Display        *pdpy;
00292   Display        *parent_pdpy;
00293   XPContext       pcontext;
00294   const char     *file_name;
00295   FILE           *file;
00296   XPGetDocStatus  status;
00297   Bool            done;
00298 } MyPrintFileData;
00299 
00300 
00301 static
00302 void *XpuPrintToFile( Display *pdpy, XPContext pcontext, const char *filename )
00303 {
00304   MyPrintFileData *mpfd;
00305 
00306   if( (mpfd = (MyPrintFileData *)malloc(sizeof(MyPrintFileData))) == NULL )
00307     return(NULL);
00308 
00309   /* create pipe */
00310   if( pipe(mpfd->pipe) == -1 ) 
00311   {
00312     /* this should never happen, but... */
00313     perror("XpuPrintToFile: cannot create pipe");
00314     free(mpfd);
00315     return(NULL);
00316   }
00317         
00318   mpfd->parent_pdpy = pdpy;
00319   mpfd->displayname = XDisplayString(pdpy);
00320   mpfd->pcontext    = pcontext;
00321   mpfd->file_name   = filename;
00322   mpfd->file        = NULL;
00323   mpfd->status      = XPGetDocError;
00324   
00325   /* make sure we can open the file for writing */
00326   if( (mpfd->file = fopen(mpfd->file_name, "w")) == NULL ) 
00327   {
00328     /* fopen() error */
00329     close(mpfd->pipe[1]);
00330     close(mpfd->pipe[0]);      
00331     free(mpfd);
00332     return(NULL);
00333   }
00334   
00335   /* its important to flush before we fork, to make sure that the
00336    * XpStartJob gets through first in the parent
00337    */
00338   XFlush(pdpy);     
00339   
00340   mpfd->pid = fork();
00341 
00342   if( mpfd->pid == 0 ) 
00343   {
00344     /* we're now in the fork()'ed child */
00345     PrintToFile_Consumer(mpfd);
00346   }
00347   else if( mpfd->pid < 0 ) 
00348   {
00349     /* fork() error */
00350     close(mpfd->pipe[1]);
00351     close(mpfd->pipe[0]);
00352     fclose(mpfd->file);
00353     free(mpfd);
00354     return(NULL);
00355   }
00356   
00357   /* we're still in the parent */
00358   XPU_DEBUG_ONLY(printf("### parent fork()'ed consumer child.\n"));
00359   
00360   /* child will write into file - we don't need it anymore here... :-) */
00361   fclose(mpfd->file);
00362   close(mpfd->pipe[1]);
00363   return(mpfd);      
00364 }
00365 
00366 
00367 XPGetDocStatus XpuWaitForPrintFileChild( void *handle )
00368 {
00369   MyPrintFileData *mpfd   = (MyPrintFileData *)handle;
00370   int              res;
00371   XPGetDocStatus   status = XPGetDocError; /* used when read() from pipe fails */
00372 
00373   /* Flush data a last time to make sure we send all data to Xprt and that
00374    * the Xlib internal hooks have called a last time */
00375   XFlush(mpfd->parent_pdpy);
00376   
00377   if( XPU_TRACE(waitpid(mpfd->pid, &res, 0)) == -1 )
00378     perror("XpuWaitForPrintFileChild: waitpid failure");
00379 
00380   /* read the status from the child */ 
00381   if( read(mpfd->pipe[0], &status, sizeof(XPGetDocStatus)) != sizeof(XPGetDocStatus) )
00382   {
00383     perror("XpuWaitForPrintFileChild: can't read XPGetDocStatus");
00384   }    
00385   close(mpfd->pipe[0]);
00386     
00387   free(handle);
00388   
00389   XPU_DEBUG_ONLY(PrintXPGetDocStatus(status));
00390   return(status);
00391 }
00392 #endif /* XPU_USE_THREADS */
00393 
00394 
00395 static 
00396 void MyPrintToFileProc( Display       *pdpy,
00397                         XPContext      pcontext,
00398                         unsigned char *data, 
00399                         unsigned int   data_len,
00400                         XPointer       client_data )
00401 {
00402   MyPrintFileData *mpfd = (MyPrintFileData *)client_data;
00403   
00404   /* write to the file */
00405   XPU_TRACE_CHILD((void)fwrite(data, data_len, 1, mpfd->file)); /* what about error handling ? */
00406 }   
00407 
00408 
00409 static 
00410 void MyFinishProc( Display        *pdpy,
00411                    XPContext       pcontext,
00412                    XPGetDocStatus  status,
00413                    XPointer        client_data )
00414 {
00415   MyPrintFileData *mpfd = (MyPrintFileData *)client_data;
00416 
00417   /* remove the file if unsuccessful */
00418   if( status != XPGetDocFinished )
00419   {
00420     XPU_DEBUG_ONLY(printf("MyFinishProc: error %d\n", (int)status));
00421     XPU_TRACE_CHILD(remove(mpfd->file_name));
00422   }  
00423   
00424   XPU_TRACE_CHILD((void)fclose(mpfd->file)); /* what about error handling ? */
00425   
00426   mpfd->status = status; 
00427   mpfd->done   = True;    
00428 }
00429 
00430 
00431 static
00432 #ifdef XPU_USE_NSPR
00433 void PrintToFile_Consumer( void *handle )
00434 #else
00435 void *PrintToFile_Consumer( void *handle )
00436 #endif
00437 {
00438   MyPrintFileData *mpfd = (MyPrintFileData *)handle;
00439   XEvent           dummy;
00440   struct timeval   timeout;
00441   
00442   timeout.tv_sec  = 0;
00443   timeout.tv_usec = 100000; /* 1/10 s */
00444         
00445   XPU_DEBUG_ONLY(printf("### child running, getting data from '%s'.\n", mpfd->displayname));
00446   
00447   /* we cannot reuse fork()'ed display handles - our child needs his own one */
00448   if( (mpfd->pdpy = XPU_TRACE_CHILD(XOpenDisplay(mpfd->displayname))) == NULL )
00449   {
00450     perror("child cannot open display");
00451 #ifdef XPU_USE_NSPR
00452     return;
00453 #else
00454     return(NULL);
00455 #endif
00456   }
00457     
00458   mpfd->done = False;
00459     
00460   /* register "consumer" callbacks */
00461   if( XPU_TRACE_CHILD(XpGetDocumentData(mpfd->pdpy, mpfd->pcontext,
00462                                         MyPrintToFileProc, MyFinishProc, 
00463                                         (XPointer)mpfd)) == 0 )
00464   {
00465     XPU_DEBUG_ONLY(printf("XpGetDocumentData cannot register callbacks\n"));
00466 #ifdef XPU_USE_NSPR
00467       return;
00468 #else
00469       return(NULL);
00470 #endif
00471   }      
00472   
00473   /* loop forever - libXp has registered hidden event callbacks for the consumer 
00474    * callbacks - the finishCB will call set the "done" boolean after all...
00475    */
00476   while( mpfd->done != True )
00477   {
00478     XNextEventTimeout(mpfd->pdpy, &dummy, &timeout);
00479   }
00480   
00481   XCloseDisplay(mpfd->pdpy);
00482 
00483 #ifdef XPU_USE_THREADS
00484 #ifdef XPU_USE_NSPR
00485   return;
00486 #else
00487   return(NULL);
00488 #endif
00489 #else   
00490   /* write the status to the parent */
00491   if( XPU_TRACE_CHILD(write(mpfd->pipe[1], &mpfd->status, sizeof(XPGetDocStatus))) != sizeof(XPGetDocStatus) )
00492   {
00493     perror("PrintToFile_Consumer: can't write XPGetDocStatus");
00494   }
00495   
00496   /* we don't do any free's or close's, as we are just
00497    * going to exit, in fact, get out without calling any C++
00498    * destructors, etc., as we don't want anything funny to happen
00499    * to the parent 
00500    */
00501    XPU_TRACE_CHILD(_exit(EXIT_SUCCESS));
00502 #endif /* XPU_USE_THREADS */           
00503 }
00504 
00505 /* EOF. */