Bug fixes.
[wine] / misc / spooler.c
1 /*
2  * Print spooler and PQ functions
3  *
4  * Copyright 1996 John Harvey
5  *           1998 Huw Davies
6  *
7  */
8
9 #include <ctype.h>
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include "winbase.h"
16 #include "wine/winuser16.h"
17 #include "callback.h"
18 #include "dc.h"
19 #include "debug.h"
20 #include "gdi.h"
21 #include "options.h"
22 #include "winerror.h"
23 #include "xmalloc.h"
24
25 DEFAULT_DEBUG_CHANNEL(print)
26
27 /**********************************************************************
28  *           QueryAbort   (GDI.155)
29  *
30  *  Calls the app's AbortProc function if avail.
31  *
32  * RETURNS
33  * TRUE if no AbortProc avail or AbortProc wants to continue printing.
34  * FALSE if AbortProc wants to abort printing.
35  */
36 BOOL16 WINAPI QueryAbort16(HDC16 hdc, INT16 reserved)
37 {
38     DC *dc = DC_GetDCPtr( hdc );
39
40     if ((!dc) || (!dc->w.lpfnPrint))
41         return TRUE;
42     return Callbacks->CallDrvAbortProc(dc->w.lpfnPrint, hdc, 0);
43 }
44
45 /**********************************************************************
46  *           SetAbortProc16   (GDI.381)
47  *
48  */
49 INT16 WINAPI SetAbortProc16(HDC16 hdc, SEGPTR abrtprc)
50 {
51     return Escape16(hdc, SETABORTPROC, 0, abrtprc, (SEGPTR)0);
52
53
54 /**********************************************************************
55  *           SetAbortProc32   (GDI32.301)
56  *
57  */
58 INT WINAPI SetAbortProc(HDC hdc, ABORTPROC abrtprc)
59 {
60     FIXME(print, "stub\n");
61     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
62     return SP_ERROR;
63 }
64
65
66 /****************** misc. printer related functions */
67
68 /*
69  * The following function should implement a queing system
70  */
71 #ifndef HPQ 
72 #define HPQ WORD
73 #endif
74 struct hpq 
75 {
76     struct hpq  *next;
77     int          tag;
78     int          key;
79 };
80
81 static struct hpq *hpqueue;
82
83 /**********************************************************************
84  *           CreatePQ   (GDI.230)
85  *
86  */
87 HPQ WINAPI CreatePQ16(int size) 
88 {
89 #if 0
90     HGLOBAL16 hpq = 0;
91     WORD tmp_size;
92     LPWORD pPQ;
93
94     tmp_size = size << 2;
95     if (!(hpq = GlobalAlloc16(GMEM_SHARE|GMEM_MOVEABLE, tmp_size + 8)))
96        return 0xffff;
97     pPQ = GlobalLock16(hpq);
98     *pPQ++ = 0;
99     *pPQ++ = tmp_size;
100     *pPQ++ = 0;
101     *pPQ++ = 0;
102     GlobalUnlock16(hpq);
103
104     return (HPQ)hpq;
105 #else
106     FIXME(print, "(%d): stub\n",size);
107     return 1;
108 #endif
109 }
110
111 /**********************************************************************
112  *           DeletePQ   (GDI.235)
113  *
114  */
115 int WINAPI DeletePQ16(HPQ hPQ) 
116 {
117     return GlobalFree16((HGLOBAL16)hPQ);
118 }
119
120 /**********************************************************************
121  *           ExtractPQ   (GDI.232)
122  *
123  */
124 int WINAPI ExtractPQ16(HPQ hPQ) 
125
126     struct hpq *queue, *prev, *current, *currentPrev;
127     int key = 0, tag = -1;
128     currentPrev = prev = NULL;
129     queue = current = hpqueue;
130     if (current)
131         key = current->key;
132     
133     while (current)
134     {
135         currentPrev = current;
136         current = current->next;
137         if (current)
138         {
139             if (current->key < key)
140             {
141                 queue = current;
142                 prev = currentPrev;
143             }
144         }
145     }
146     if (queue)
147     {
148         tag = queue->tag;
149         
150         if (prev)
151             prev->next = queue->next;
152         else
153             hpqueue = queue->next;
154         free(queue);
155     }
156     
157     TRACE(print, "%x got tag %d key %d\n", hPQ, tag, key); 
158
159     return tag;
160 }
161
162 /**********************************************************************
163  *           InsertPQ   (GDI.233)
164  *
165  */
166 int WINAPI InsertPQ16(HPQ hPQ, int tag, int key) 
167 {
168     struct hpq *queueItem = xmalloc(sizeof(struct hpq));
169     queueItem->next = hpqueue;
170     hpqueue = queueItem;
171     queueItem->key = key;
172     queueItem->tag = tag;
173     
174     FIXME(print, "(%x %d %d): stub???\n", hPQ, tag, key);
175     return TRUE;
176 }
177
178 /**********************************************************************
179  *           MinPQ   (GDI.231)
180  *
181  */
182 int WINAPI MinPQ16(HPQ hPQ) 
183 {
184     FIXME(print, "(%x): stub\n", hPQ); 
185     return 0;
186 }
187
188 /**********************************************************************
189  *           SizePQ   (GDI.234)
190  *
191  */
192 int WINAPI SizePQ16(HPQ hPQ, int sizechange) 
193 {  
194     FIXME(print, "(%x %d): stub\n", hPQ, sizechange); 
195     return -1; 
196 }
197
198
199
200 /* 
201  * The following functions implement part of the spooling process to 
202  * print manager.  I would like to see wine have a version of print managers
203  * that used LPR/LPD.  For simplicity print jobs will be sent to a file for
204  * now.
205  */
206 typedef struct PRINTJOB
207 {
208     char        *pszOutput;
209     char        *pszTitle;
210     HDC16       hDC;
211     HANDLE16    hHandle;
212     int         nIndex;
213     int         fd;
214 } PRINTJOB, *PPRINTJOB;
215
216 #define MAX_PRINT_JOBS 1
217 #define SP_OK 1
218
219 PPRINTJOB gPrintJobsTable[MAX_PRINT_JOBS];
220
221
222 static PPRINTJOB FindPrintJobFromHandle(HANDLE16 hHandle)
223 {
224     return gPrintJobsTable[0];
225 }
226
227 /* TTD Need to do some DOS->UNIX file conversion here */
228 static int CreateSpoolFile(LPSTR pszOutput)
229 {
230     int fd=-1;
231     char psCmd[1024];
232     char *psCmdP = psCmd;
233
234     /* TTD convert the 'output device' into a spool file name */
235
236     if (pszOutput == NULL || *pszOutput == '\0')
237       return -1;
238
239     PROFILE_GetWineIniString( "spooler", pszOutput, "", psCmd, sizeof(psCmd) );
240     TRACE(print, "Got printerSpoolCommand '%s' for output device '%s'\n",
241           psCmd, pszOutput);
242     if (!*psCmd)
243         psCmdP = pszOutput;
244     else
245     {
246         while (*psCmdP && isspace(*psCmdP))
247         {
248             psCmdP++;
249         };
250         if (!*psCmdP)
251             return -1;
252     }
253     if (*psCmdP == '|')
254     {
255         int fds[2];
256         if (pipe(fds))
257             return -1;
258         if (fork() == 0)
259         {
260             psCmdP++;
261
262             TRACE(print, "In child need to exec %s\n",psCmdP);
263             close(0);
264             dup2(fds[0],0);
265             close (fds[1]);
266             system(psCmdP);
267             exit(0);
268             
269         }
270         close (fds[0]);
271         fd = fds[1];
272         TRACE(print,"Need to execute a cmnd and pipe the output to it\n");
273     }
274     else
275     {
276         TRACE(print, "Just assume its a file\n");
277
278         if ((fd = open(psCmdP, O_CREAT | O_TRUNC | O_WRONLY , 0600)) < 0)
279         {
280             ERR(print, "Failed to create spool file %s, errno = %d\n", 
281                 psCmdP, errno);
282         }
283     }
284     return fd;
285 }
286
287 static int FreePrintJob(HANDLE16 hJob)
288 {
289     int nRet = SP_ERROR;
290     PPRINTJOB pPrintJob;
291
292     pPrintJob = FindPrintJobFromHandle(hJob);
293     if (pPrintJob != NULL)
294     {
295         gPrintJobsTable[pPrintJob->nIndex] = NULL;
296         free(pPrintJob->pszOutput);
297         free(pPrintJob->pszTitle);
298         if (pPrintJob->fd >= 0) close(pPrintJob->fd);
299         free(pPrintJob);
300         nRet = SP_OK;
301     }
302     return nRet;
303 }
304
305 /**********************************************************************
306  *           OpenJob   (GDI.240)
307  *
308  */
309 HANDLE16 WINAPI OpenJob16(LPSTR lpOutput, LPSTR lpTitle, HDC16 hDC)
310 {
311     HANDLE16 hHandle = (HANDLE16)SP_ERROR;
312     PPRINTJOB pPrintJob;
313
314     TRACE(print, "'%s' '%s' %04x\n", lpOutput, lpTitle, hDC);
315
316     pPrintJob = gPrintJobsTable[0];
317     if (pPrintJob == NULL)
318     {
319         int fd;
320
321         /* Try an create a spool file */
322         fd = CreateSpoolFile(lpOutput);
323         if (fd >= 0)
324         {
325             hHandle = 1;
326
327             pPrintJob = xmalloc(sizeof(PRINTJOB));
328             memset(pPrintJob, 0, sizeof(PRINTJOB));
329
330             pPrintJob->pszOutput = strdup(lpOutput);
331             if(lpTitle)
332                 pPrintJob->pszTitle = strdup(lpTitle);
333             pPrintJob->hDC = hDC;
334             pPrintJob->fd = fd;
335             pPrintJob->nIndex = 0;
336             pPrintJob->hHandle = hHandle; 
337             gPrintJobsTable[pPrintJob->nIndex] = pPrintJob; 
338         }
339     }
340     TRACE(print, "return %04x\n", hHandle);
341     return hHandle;
342 }
343
344 /**********************************************************************
345  *           CloseJob   (GDI.243)
346  *
347  */
348 int WINAPI CloseJob16(HANDLE16 hJob)
349 {
350     int nRet = SP_ERROR;
351     PPRINTJOB pPrintJob = NULL;
352
353     TRACE(print, "%04x\n", hJob);
354
355     pPrintJob = FindPrintJobFromHandle(hJob);
356     if (pPrintJob != NULL)
357     {
358         /* Close the spool file */
359         close(pPrintJob->fd);
360         FreePrintJob(hJob);
361         nRet  = 1;
362     }
363     return nRet;
364 }
365
366 /**********************************************************************
367  *           WriteSpool   (GDI.241)
368  *
369  */
370 int WINAPI WriteSpool16(HANDLE16 hJob, LPSTR lpData, WORD cch)
371 {
372     int nRet = SP_ERROR;
373     PPRINTJOB pPrintJob = NULL;
374
375     TRACE(print, "%04x %08lx %04x\n", hJob, (DWORD)lpData, cch);
376
377     pPrintJob = FindPrintJobFromHandle(hJob);
378     if (pPrintJob != NULL && pPrintJob->fd >= 0 && cch)
379     {
380         if (write(pPrintJob->fd, lpData, cch) != cch)
381           nRet = SP_OUTOFDISK;
382         else
383           nRet = cch;
384         if (pPrintJob->hDC == 0) {
385             TRACE(print, "hDC == 0 so no QueryAbort\n");
386         }
387         else if (!(QueryAbort16(pPrintJob->hDC, (nRet == SP_OUTOFDISK) ? nRet : 0 )))
388         {
389             CloseJob16(hJob); /* printing aborted */
390             nRet = SP_APPABORT;
391         }
392     }
393     return nRet;
394 }
395
396 /**********************************************************************
397  *           WriteDialog   (GDI.242)
398  *
399  */
400 int WINAPI WriteDialog16(HANDLE16 hJob, LPSTR lpMsg, WORD cchMsg)
401 {
402     int nRet = 0;
403
404     TRACE(print, "%04x %04x '%s'\n", hJob,  cchMsg, lpMsg);
405
406     nRet = MessageBox16(0, lpMsg, "Printing Error", MB_OKCANCEL);
407     return nRet;
408 }
409
410
411 /**********************************************************************
412  *           DeleteJob  (GDI.244)
413  *
414  */
415 int WINAPI DeleteJob16(HANDLE16 hJob, WORD wNotUsed)
416 {
417     int nRet;
418
419     TRACE(print, "%04x\n", hJob);
420
421     nRet = FreePrintJob(hJob);
422     return nRet;
423 }
424
425 /* 
426  * The following two function would allow a page to be sent to the printer
427  * when it has been processed.  For simplicity they havn't been implemented.
428  * This means a whole job has to be processed before it is sent to the printer.
429  */
430
431 /**********************************************************************
432  *           StartSpoolPage   (GDI.246)
433  *
434  */
435 int WINAPI StartSpoolPage16(HANDLE16 hJob)
436 {
437     FIXME(print, "StartSpoolPage GDI.246 unimplemented\n");
438     return 1;
439
440 }
441
442
443 /**********************************************************************
444  *           EndSpoolPage   (GDI.247)
445  *
446  */
447 int WINAPI EndSpoolPage16(HANDLE16 hJob)
448 {
449     FIXME(print, "EndSpoolPage GDI.247 unimplemented\n");
450     return 1;
451 }
452
453
454 /**********************************************************************
455  *           GetSpoolJob   (GDI.245)
456  *
457  */
458 DWORD WINAPI GetSpoolJob16(int nOption, LONG param)
459 {
460     DWORD retval = 0;
461     TRACE(print, "In GetSpoolJob param 0x%lx noption %d\n",param, nOption);
462     return retval;
463 }