msvcrt/tests: Test _dup2 for failure when second arg is negative.
[wine] / dlls / wineps.drv / escape.c
1 /*
2  *      PostScript driver Escape function
3  *
4  *      Copyright 1998  Huw D M Davies
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <signal.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34
35 #include "windef.h"
36 #include "winbase.h"
37 #include "wingdi.h"
38 #include "wine/wingdi16.h"
39 #include "winreg.h"
40 #include "psdrv.h"
41 #include "wine/debug.h"
42 #include "winspool.h"
43
44 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
45
46 static const char psbegindocument[] =
47 "%%BeginDocument: Wine passthrough\n";
48
49 /* FIXME: should use winspool functions instead */
50 static DWORD create_job(LPCSTR pszOutput)
51 {
52     int fd = -1;
53     char psCmd[1024];
54     const char *psCmdP = psCmd;
55     HKEY hkey;
56
57     /* TTD convert the 'output device' into a spool file name */
58
59     if (pszOutput == NULL || *pszOutput == '\0') return 0;
60
61     psCmd[0] = 0;
62     /* @@ Wine registry key: HKCU\Software\Wine\Printing\Spooler */
63     if(!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Printing\\Spooler", &hkey))
64     {
65         DWORD type, count = sizeof(psCmd);
66         RegQueryValueExA(hkey, pszOutput, 0, &type, (LPBYTE)psCmd, &count);
67         RegCloseKey(hkey);
68     }
69     if (!psCmd[0] && !strncmp("LPR:",pszOutput,4))
70         sprintf(psCmd,"|lpr -P'%s'",pszOutput+4);
71
72     TRACE("Got printerSpoolCommand '%s' for output device '%s'\n",
73           psCmd, pszOutput);
74     if (!*psCmd)
75         psCmdP = pszOutput;
76     else
77     {
78         while (*psCmdP && isspace(*psCmdP))
79         {
80             psCmdP++;
81         }
82         if (!*psCmdP) return 0;
83     }
84     TRACE("command: '%s'\n", psCmdP);
85 #ifdef HAVE_FORK
86     if (*psCmdP == '|')
87     {
88         int fds[2];
89         if (pipe(fds)) {
90             ERR("pipe() failed!\n");
91             return 0;
92         }
93         if (fork() == 0)
94         {
95             psCmdP++;
96
97             TRACE("In child need to exec %s\n",psCmdP);
98             close(0);
99             dup2(fds[0],0);
100             close (fds[1]);
101
102             /* reset signals that we previously set to SIG_IGN */
103             signal( SIGPIPE, SIG_DFL );
104             signal( SIGCHLD, SIG_DFL );
105
106             execl("/bin/sh", "/bin/sh", "-c", psCmdP, NULL);
107             _exit(1);
108
109         }
110         close (fds[0]);
111         fd = fds[1];
112         TRACE("Need to execute a cmnd and pipe the output to it\n");
113     }
114     else
115 #endif
116     {
117         char *buffer;
118         WCHAR psCmdPW[MAX_PATH];
119
120         TRACE("Just assume it's a file\n");
121
122         /**
123          * The file name can be dos based, we have to find its
124          * corresponding Unix file name.
125          */
126         MultiByteToWideChar(CP_ACP, 0, psCmdP, -1, psCmdPW, MAX_PATH);
127         if ((buffer = wine_get_unix_file_name(psCmdPW)))
128         {
129             if ((fd = open(buffer, O_CREAT | O_TRUNC | O_WRONLY, 0666)) < 0)
130             {
131                 ERR("Failed to create spool file '%s' ('%s'). (error %s)\n",
132                     buffer, psCmdP, strerror(errno));
133             }
134             HeapFree(GetProcessHeap(), 0, buffer);
135         }
136     }
137     return fd + 1;
138 }
139
140 static int close_job( DWORD id )
141 {
142     int fd = id - 1;
143     close( fd );
144     return TRUE;
145 }
146
147 DWORD write_spool( PSDRV_PDEVICE *physDev, const void *data, DWORD num )
148 {
149     int fd = physDev->job.id - 1;
150     if (write( fd, data, num) != num) return SP_OUTOFDISK;
151     return num;
152 }
153
154 /**********************************************************************
155  *           ExtEscape  (WINEPS.@)
156  */
157 INT CDECL PSDRV_ExtEscape( PSDRV_PDEVICE *physDev, INT nEscape, INT cbInput, LPCVOID in_data,
158                            INT cbOutput, LPVOID out_data )
159 {
160     switch(nEscape)
161     {
162     case QUERYESCSUPPORT:
163         if(cbInput < sizeof(INT))
164         {
165             WARN("cbInput < sizeof(INT) (=%d) for QUERYESCSUPPORT\n", cbInput);
166             return 0;
167         } else {
168             UINT num = *(const UINT *)in_data;
169             TRACE("QUERYESCSUPPORT for %d\n", num);
170
171             switch(num) {
172             case NEXTBAND:
173             /*case BANDINFO:*/
174             case SETCOPYCOUNT:
175             case GETTECHNOLOGY:
176             case SETLINECAP:
177             case SETLINEJOIN:
178             case SETMITERLIMIT:
179             case SETCHARSET:
180             case EXT_DEVICE_CAPS:
181             case SET_BOUNDS:
182             case EPSPRINTING:
183             case POSTSCRIPT_DATA:
184             case PASSTHROUGH:
185             case POSTSCRIPT_PASSTHROUGH:
186             case POSTSCRIPT_IGNORE:
187             case BEGIN_PATH:
188             case CLIP_TO_PATH:
189             case END_PATH:
190             /*case DRAWPATTERNRECT:*/
191                 return TRUE;
192
193             default:
194                 FIXME("QUERYESCSUPPORT(%d) - not supported.\n", num);
195                 return FALSE;
196             }
197         }
198
199     case MFCOMMENT:
200     {
201         FIXME("MFCOMMENT(%p, %d)\n", in_data, cbInput);
202         return 1;
203     }
204     case DRAWPATTERNRECT:
205     {
206         DRAWPATRECT     *dpr = (DRAWPATRECT*)in_data;
207
208         FIXME("DRAWPATTERNRECT(pos (%d,%d), size %dx%d, style %d, pattern %x), stub!\n",
209                 dpr->ptPosition.x, dpr->ptPosition.y,
210                 dpr->ptSize.x, dpr->ptSize.y,
211                 dpr->wStyle, dpr->wPattern
212         );
213         return 1;
214     }
215     case BANDINFO:
216     {
217         BANDINFOSTRUCT  *ibi = (BANDINFOSTRUCT*)in_data;
218         BANDINFOSTRUCT  *obi = (BANDINFOSTRUCT*)out_data;
219
220         FIXME("BANDINFO(graphics %d, text %d, rect [%dx%d-%dx%d]), stub!\n",
221                 ibi->GraphicsFlag,
222                 ibi->TextFlag,
223                 ibi->GraphicsRect.top,
224                 ibi->GraphicsRect.bottom,
225                 ibi->GraphicsRect.left,
226                 ibi->GraphicsRect.right
227         );
228         *obi = *ibi;
229         return 1;
230     }
231     case NEXTBAND:
232     {
233         RECT *r = out_data;
234         if(!physDev->job.banding) {
235             physDev->job.banding = TRUE;
236             r->left   = 0;
237             r->top    = 0;
238             r->right  = physDev->horzRes;
239             r->bottom = physDev->vertRes;
240             TRACE("NEXTBAND returning %d,%d - %d,%d\n", r->left, r->top, r->right, r->bottom );
241             return 1;
242         }
243         r->left   = 0;
244         r->top    = 0;
245         r->right  = 0;
246         r->bottom = 0;
247         TRACE("NEXTBAND rect to 0,0 - 0,0\n" );
248         physDev->job.banding = FALSE;
249         return EndPage( physDev->hdc );
250     }
251
252     case SETCOPYCOUNT:
253         {
254             const INT *NumCopies = in_data;
255             INT *ActualCopies = out_data;
256             if(cbInput != sizeof(INT)) {
257                 WARN("cbInput != sizeof(INT) (=%d) for SETCOPYCOUNT\n", cbInput);
258                 return 0;
259             }
260             TRACE("SETCOPYCOUNT %d\n", *NumCopies);
261             *ActualCopies = 1;
262             return 1;
263         }
264
265     case GETTECHNOLOGY:
266         {
267             LPSTR p = out_data;
268             strcpy(p, "PostScript");
269             *(p + strlen(p) + 1) = '\0'; /* 2 '\0's at end of string */
270             return 1;
271         }
272
273     case SETLINECAP:
274         {
275             INT newCap = *(const INT *)in_data;
276             if(cbInput != sizeof(INT)) {
277                 WARN("cbInput != sizeof(INT) (=%d) for SETLINECAP\n", cbInput);
278                 return 0;
279             }
280             TRACE("SETLINECAP %d\n", newCap);
281             return 0;
282         }
283
284     case SETLINEJOIN:
285         {
286             INT newJoin = *(const INT *)in_data;
287             if(cbInput != sizeof(INT)) {
288                 WARN("cbInput != sizeof(INT) (=%d) for SETLINEJOIN\n", cbInput);
289                 return 0;
290             }
291             TRACE("SETLINEJOIN %d\n", newJoin);
292             return 0;
293         }
294
295     case SETMITERLIMIT:
296         {
297             INT newLimit = *(const INT *)in_data;
298             if(cbInput != sizeof(INT)) {
299                 WARN("cbInput != sizeof(INT) (=%d) for SETMITERLIMIT\n", cbInput);
300                 return 0;
301             }
302             TRACE("SETMITERLIMIT %d\n", newLimit);
303             return 0;
304         }
305
306     case SETCHARSET:
307       /* Undocumented escape used by winword6.
308          Switches between ANSI and a special charset.
309          If *lpInData == 1 we require that
310          0x91 is quoteleft
311          0x92 is quoteright
312          0x93 is quotedblleft
313          0x94 is quotedblright
314          0x95 is bullet
315          0x96 is endash
316          0x97 is emdash
317          0xa0 is non break space - yeah right.
318
319          If *lpInData == 0 we get ANSI.
320          Since there's nothing else there, let's just make these the default
321          anyway and see what happens...
322       */
323         return 1;
324
325     case EXT_DEVICE_CAPS:
326         {
327             UINT cap = *(const UINT *)in_data;
328             if(cbInput != sizeof(UINT)) {
329                 WARN("cbInput != sizeof(UINT) (=%d) for EXT_DEVICE_CAPS\n", cbInput);
330                 return 0;
331             }
332             TRACE("EXT_DEVICE_CAPS %d\n", cap);
333             return 0;
334         }
335
336     case SET_BOUNDS:
337         {
338             const RECT *r = in_data;
339             if(cbInput != sizeof(RECT)) {
340                 WARN("cbInput != sizeof(RECT) (=%d) for SET_BOUNDS\n", cbInput);
341                 return 0;
342             }
343             TRACE("SET_BOUNDS (%d,%d) - (%d,%d)\n", r->left, r->top,
344                   r->right, r->bottom);
345             return 0;
346         }
347
348     case EPSPRINTING:
349         {
350             UINT epsprint = *(const UINT*)in_data;
351             /* FIXME: In this mode we do not need to send page intros and page
352              * ends according to the doc. But I just ignore that detail
353              * for now.
354              */
355             TRACE("EPS Printing support %sable.\n",epsprint?"en":"dis");
356             return 1;
357         }
358
359     case POSTSCRIPT_DATA:
360     case PASSTHROUGH:
361     case POSTSCRIPT_PASSTHROUGH:
362         {
363             /* Write directly to spool file, bypassing normal PS driver
364              * processing that is done along with writing PostScript code
365              * to the spool.
366              * We have a WORD before the data counting the size, but
367              * cbInput is just this +2.
368              * However Photoshop 7 has a bug that sets cbInput to 2 less than the
369              * length of the string, rather than 2 more.  So we'll use the WORD at
370              * in_data[0] instead.
371              */
372             if(!physDev->job.in_passthrough) {
373                 write_spool(physDev, psbegindocument, sizeof(psbegindocument)-1);
374                 physDev->job.in_passthrough = TRUE;
375             }
376             return write_spool(physDev,((char*)in_data)+2,*(const WORD*)in_data);
377         }
378
379     case POSTSCRIPT_IGNORE:
380       {
381         BOOL ret = physDev->job.quiet;
382         TRACE("POSTSCRIPT_IGNORE %d\n", *(const short*)in_data);
383         physDev->job.quiet = *(const short*)in_data;
384         return ret;
385       }
386
387     case GETSETPRINTORIENT:
388         {
389             /* If lpInData is present, it is a 20 byte structure, first 32
390              * bit LONG value is the orientation. if lpInData is NULL, it
391              * returns the current orientation.
392              */
393             FIXME("GETSETPRINTORIENT not implemented (data %p)!\n",in_data);
394             return 1;
395         }
396     case BEGIN_PATH:
397         TRACE("BEGIN_PATH\n");
398         if(physDev->pathdepth)
399             FIXME("Nested paths not yet handled\n");
400         return ++physDev->pathdepth;
401
402     case END_PATH:
403       {
404         const struct PATH_INFO *info = (const struct PATH_INFO*)in_data;
405
406         TRACE("END_PATH\n");
407         if(!physDev->pathdepth) {
408             ERR("END_PATH called without a BEIGN_PATH\n");
409             return -1;
410         }
411         TRACE("RenderMode = %d, FillMode = %d, BkMode = %d\n",
412               info->RenderMode, info->FillMode, info->BkMode);
413         switch(info->RenderMode) {
414         case RENDERMODE_NO_DISPLAY:
415             PSDRV_WriteClosePath(physDev); /* not sure if this is necessary, but it can't hurt */
416             break;
417         case RENDERMODE_OPEN:
418         case RENDERMODE_CLOSED:
419         default:
420             FIXME("END_PATH: RenderMode %d, not yet supported\n", info->RenderMode);
421             break;
422         }
423         return --physDev->pathdepth;
424       }
425
426     case CLIP_TO_PATH:
427       {
428         WORD mode = *(const WORD*)in_data;
429
430         switch(mode) {
431         case CLIP_SAVE:
432             TRACE("CLIP_TO_PATH: CLIP_SAVE\n");
433             PSDRV_WriteGSave(physDev);
434             return 1;
435         case CLIP_RESTORE:
436             TRACE("CLIP_TO_PATH: CLIP_RESTORE\n");
437             PSDRV_WriteGRestore(physDev);
438             return 1;
439         case CLIP_INCLUSIVE:
440             TRACE("CLIP_TO_PATH: CLIP_INCLUSIVE\n");
441             /* FIXME to clip or eoclip ? (see PATH_INFO.FillMode) */
442             PSDRV_WriteClip(physDev);
443             PSDRV_WriteNewPath(physDev);
444             return 1;
445         case CLIP_EXCLUSIVE:
446             FIXME("CLIP_EXCLUSIVE: not implemented\n");
447             return 0;
448         default:
449             FIXME("Unknown CLIP_TO_PATH mode %d\n", mode);
450             return 0;
451         }
452       }
453     default:
454         FIXME("Unimplemented code 0x%x\n", nEscape);
455         return 0;
456     }
457 }
458
459 /************************************************************************
460  *           PSDRV_StartPage
461  */
462 INT CDECL PSDRV_StartPage( PSDRV_PDEVICE *physDev )
463 {
464     if(!physDev->job.OutOfPage) {
465         FIXME("Already started a page?\n");
466         return 1;
467     }
468
469     if(physDev->job.PageNo++ == 0) {
470         if(!PSDRV_WriteHeader( physDev, physDev->job.DocName ))
471             return 0;
472     }
473
474     if(!PSDRV_WriteNewPage( physDev ))
475         return 0;
476     physDev->job.OutOfPage = FALSE;
477     return 1;
478 }
479
480
481 /************************************************************************
482  *           PSDRV_EndPage
483  */
484 INT CDECL PSDRV_EndPage( PSDRV_PDEVICE *physDev )
485 {
486     if(physDev->job.OutOfPage) {
487         FIXME("Already ended a page?\n");
488         return 1;
489     }
490     if(!PSDRV_WriteEndPage( physDev ))
491         return 0;
492     PSDRV_EmptyDownloadList(physDev, FALSE);
493     physDev->job.OutOfPage = TRUE;
494     return 1;
495 }
496
497
498 /************************************************************************
499  *           PSDRV_StartDocA
500  */
501 static INT PSDRV_StartDocA( PSDRV_PDEVICE *physDev, const DOCINFOA *doc )
502 {
503     LPCSTR output = "LPT1:";
504     BYTE buf[300];
505     HANDLE hprn = INVALID_HANDLE_VALUE;
506     PRINTER_INFO_5A *pi5 = (PRINTER_INFO_5A*)buf;
507     DWORD needed;
508
509     if(physDev->job.id) {
510         FIXME("hJob != 0. Now what?\n");
511         return 0;
512     }
513
514     if(doc->lpszOutput)
515         output = doc->lpszOutput;
516     else if(physDev->job.output)
517         output = physDev->job.output;
518     else {
519         if(OpenPrinterA(physDev->pi->FriendlyName, &hprn, NULL) &&
520            GetPrinterA(hprn, 5, buf, sizeof(buf), &needed)) {
521             output = pi5->pPortName;
522         }
523         if(hprn != INVALID_HANDLE_VALUE)
524             ClosePrinter(hprn);
525     }
526
527     physDev->job.id = create_job( output );
528     if(!physDev->job.id) {
529         WARN("OpenJob failed\n");
530         return 0;
531     }
532     physDev->job.banding = FALSE;
533     physDev->job.OutOfPage = TRUE;
534     physDev->job.PageNo = 0;
535     physDev->job.quiet = FALSE;
536     physDev->job.in_passthrough = FALSE;
537     physDev->job.had_passthrough_rect = FALSE;
538     if(doc->lpszDocName) {
539         physDev->job.DocName = HeapAlloc(GetProcessHeap(), 0, strlen(doc->lpszDocName)+1);
540         strcpy(physDev->job.DocName, doc->lpszDocName);
541     } else
542         physDev->job.DocName = NULL;
543
544     return physDev->job.id;
545 }
546
547 /************************************************************************
548  *           PSDRV_StartDoc
549  */
550 INT CDECL PSDRV_StartDoc( PSDRV_PDEVICE *physDev, const DOCINFOW *doc )
551 {
552     DOCINFOA docA;
553     INT ret, len;
554     LPSTR docname = NULL, output = NULL, datatype = NULL;
555
556     docA.cbSize = doc->cbSize;
557     if (doc->lpszDocName)
558     {
559         len = WideCharToMultiByte( CP_ACP, 0, doc->lpszDocName, -1, NULL, 0, NULL, NULL );
560         if ((docname = HeapAlloc( GetProcessHeap(), 0, len )))
561             WideCharToMultiByte( CP_ACP, 0, doc->lpszDocName, -1, docname, len, NULL, NULL );
562     }
563     if (doc->lpszOutput)
564     {
565         len = WideCharToMultiByte( CP_ACP, 0, doc->lpszOutput, -1, NULL, 0, NULL, NULL );
566         if ((output = HeapAlloc( GetProcessHeap(), 0, len )))
567             WideCharToMultiByte( CP_ACP, 0, doc->lpszOutput, -1, output, len, NULL, NULL );
568     }
569     if (doc->lpszDatatype)
570     {
571         len = WideCharToMultiByte( CP_ACP, 0, doc->lpszDatatype, -1, NULL, 0, NULL, NULL );
572         if ((datatype = HeapAlloc( GetProcessHeap(), 0, len )))
573             WideCharToMultiByte( CP_ACP, 0, doc->lpszDatatype, -1, datatype, len, NULL, NULL );
574     }
575     docA.lpszDocName = docname;
576     docA.lpszOutput = output;
577     docA.lpszDatatype = datatype;
578     docA.fwType = doc->fwType;
579
580     ret = PSDRV_StartDocA(physDev, &docA);
581
582     HeapFree( GetProcessHeap(), 0, docname );
583     HeapFree( GetProcessHeap(), 0, output );
584     HeapFree( GetProcessHeap(), 0, datatype );
585
586     return ret;
587 }
588
589 /************************************************************************
590  *           PSDRV_EndDoc
591  */
592 INT CDECL PSDRV_EndDoc( PSDRV_PDEVICE *physDev )
593 {
594     INT ret = 1;
595     if(!physDev->job.id) {
596         FIXME("hJob == 0. Now what?\n");
597         return 0;
598     }
599
600     if(!physDev->job.OutOfPage) {
601         WARN("Somebody forgot an EndPage\n");
602         PSDRV_EndPage( physDev );
603     }
604     PSDRV_WriteFooter( physDev );
605
606     ret = close_job( physDev->job.id );
607     physDev->job.id = 0;
608     HeapFree(GetProcessHeap(), 0, physDev->job.DocName);
609     physDev->job.DocName = NULL;
610
611     return ret;
612 }