When including 'wine/port.h', include it first.
[wine] / dlls / gdi / printdrv.c
1 /* 
2  * Implementation of some printer driver bits
3  * 
4  * Copyright 1996 John Harvey
5  * Copyright 1998 Huw Davies
6  * Copyright 1998 Andreas Mohr
7  * Copyright 1999 Klaas van Gend
8  */
9
10 #include "config.h"
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <ctype.h>
16 #include <errno.h>
17 #include <unistd.h>
18 #include <fcntl.h>
19 #include "winbase.h"
20 #include "wine/winbase16.h"
21 #include "wine/wingdi16.h"
22 #include "winspool.h"
23 #include "winerror.h"
24 #include "winreg.h"
25 #include "debugtools.h"
26 #include "gdi.h"
27 #include "callback.h"
28 #include "heap.h"
29 #include "file.h"
30
31 DEFAULT_DEBUG_CHANNEL(print);
32
33 static char PrinterModel[]      = "Printer Model";
34 static char DefaultDevMode[]    = "Default DevMode";
35 static char PrinterDriverData[] = "PrinterDriverData";
36 static char Printers[]          = "System\\CurrentControlSet\\Control\\Print\\Printers\\";
37
38 /******************************************************************
39  *                  StartDoc  [GDI.377]
40  *
41  */
42 INT16 WINAPI StartDoc16( HDC16 hdc, const DOCINFO16 *lpdoc )
43 {
44     DOCINFOA docA;
45
46     docA.cbSize = lpdoc->cbSize;
47     docA.lpszDocName = MapSL(lpdoc->lpszDocName);
48     docA.lpszOutput = MapSL(lpdoc->lpszOutput);
49
50     if(lpdoc->cbSize >= 14)
51         docA.lpszDatatype = MapSL(lpdoc->lpszDatatype);
52     else
53         docA.lpszDatatype = NULL;
54
55     if(lpdoc->cbSize >= 18)
56         docA.fwType = lpdoc->fwType;
57     else
58         docA.fwType = 0;
59
60     return StartDocA(hdc, &docA);
61 }
62
63 /******************************************************************
64  *                  StartDocA  [GDI32.@]
65  *
66  * StartDoc calls the STARTDOC Escape with the input data pointing to DocName
67  * and the output data (which is used as a second input parameter).pointing at
68  * the whole docinfo structure.  This seems to be an undocumented feature of
69  * the STARTDOC Escape. 
70  *
71  * Note: we now do it the other way, with the STARTDOC Escape calling StartDoc.
72  */
73 INT WINAPI StartDocA(HDC hdc, const DOCINFOA* doc)
74 {
75     INT ret = 0;
76     DC *dc = DC_GetDCPtr( hdc );
77
78     TRACE("DocName = '%s' Output = '%s' Datatype = '%s'\n",
79           doc->lpszDocName, doc->lpszOutput, doc->lpszDatatype);
80
81     if(!dc) return SP_ERROR;
82
83     if (dc->funcs->pStartDoc) ret = dc->funcs->pStartDoc( dc, doc );
84     GDI_ReleaseObj( hdc );
85     return ret;
86 }
87
88 /*************************************************************************
89  *                  StartDocW [GDI32.@]
90  * 
91  */
92 INT WINAPI StartDocW(HDC hdc, const DOCINFOW* doc)
93 {
94     DOCINFOA docA;
95     INT ret;
96
97     docA.cbSize = doc->cbSize;
98     docA.lpszDocName = doc->lpszDocName ? 
99       HEAP_strdupWtoA( GetProcessHeap(), 0, doc->lpszDocName ) : NULL;
100     docA.lpszOutput = doc->lpszOutput ?
101       HEAP_strdupWtoA( GetProcessHeap(), 0, doc->lpszOutput ) : NULL;
102     docA.lpszDatatype = doc->lpszDatatype ?
103       HEAP_strdupWtoA( GetProcessHeap(), 0, doc->lpszDatatype ) : NULL;
104     docA.fwType = doc->fwType;
105
106     ret = StartDocA(hdc, &docA);
107
108     if(docA.lpszDocName)
109         HeapFree( GetProcessHeap(), 0, (LPSTR)docA.lpszDocName );
110     if(docA.lpszOutput)
111         HeapFree( GetProcessHeap(), 0, (LPSTR)docA.lpszOutput );
112     if(docA.lpszDatatype)
113         HeapFree( GetProcessHeap(), 0, (LPSTR)docA.lpszDatatype );
114
115     return ret;
116 }
117
118 /******************************************************************
119  *                  EndDoc  [GDI.378]
120  *
121  */
122 INT16 WINAPI EndDoc16(HDC16 hdc)
123 {
124     return EndDoc(hdc);
125 }
126
127 /******************************************************************
128  *                  EndDoc  [GDI32.@]
129  *
130  */
131 INT WINAPI EndDoc(HDC hdc)
132 {
133     INT ret = 0;
134     DC *dc = DC_GetDCPtr( hdc );
135     if(!dc) return SP_ERROR;
136
137     if (dc->funcs->pEndDoc) ret = dc->funcs->pEndDoc( dc );
138     GDI_ReleaseObj( hdc );
139     return ret;
140 }
141
142 /******************************************************************
143  *                  StartPage  [GDI.379]
144  *
145  */
146 INT16 WINAPI StartPage16(HDC16 hdc)
147 {
148     return StartPage(hdc);
149 }
150
151 /******************************************************************
152  *                  StartPage  [GDI32.@]
153  *
154  */
155 INT WINAPI StartPage(HDC hdc)
156 {
157     INT ret = 1;
158     DC *dc = DC_GetDCPtr( hdc );
159     if(!dc) return SP_ERROR;
160
161     if(dc->funcs->pStartPage)
162         ret = dc->funcs->pStartPage( dc );
163     else
164         FIXME("stub\n");
165     GDI_ReleaseObj( hdc );
166     return ret;
167 }
168
169 /******************************************************************
170  *                  EndPage  [GDI.380]
171  *
172  */
173 INT16 WINAPI EndPage16( HDC16 hdc )
174 {
175     return EndPage(hdc);
176 }
177
178 /******************************************************************
179  *                  EndPage  [GDI32.@]
180  *
181  */
182 INT WINAPI EndPage(HDC hdc)
183 {
184     INT ret = 0;
185     DC *dc = DC_GetDCPtr( hdc );
186     if(!dc) return SP_ERROR;
187
188     if (dc->funcs->pEndPage) ret = dc->funcs->pEndPage( dc );
189     GDI_ReleaseObj( hdc );
190     if (!QueryAbort16( hdc, 0 ))
191     {
192         EndDoc( hdc );
193         ret = 0;
194     }
195     return ret;
196 }
197
198 /******************************************************************************
199  *                 AbortDoc  [GDI.382]
200  */
201 INT16 WINAPI AbortDoc16(HDC16 hdc)
202 {
203     return AbortDoc(hdc);
204 }
205
206 /******************************************************************************
207  *                 AbortDoc  [GDI32.@]
208  */
209 INT WINAPI AbortDoc(HDC hdc)
210 {
211     INT ret = 0;
212     DC *dc = DC_GetDCPtr( hdc );
213     if(!dc) return SP_ERROR;
214
215     if (dc->funcs->pAbortDoc) ret = dc->funcs->pAbortDoc( dc );
216     GDI_ReleaseObj( hdc );
217     return ret;
218 }
219
220 /**********************************************************************
221  *           QueryAbort   (GDI.155)
222  *
223  *  Calls the app's AbortProc function if avail.
224  *
225  * RETURNS
226  * TRUE if no AbortProc avail or AbortProc wants to continue printing.
227  * FALSE if AbortProc wants to abort printing.
228  */
229 BOOL16 WINAPI QueryAbort16(HDC16 hdc, INT16 reserved)
230 {
231     BOOL ret = TRUE;
232     DC *dc = DC_GetDCPtr( hdc );
233     ABORTPROC abproc;
234
235     if(!dc) {
236         ERR("Invalid hdc %04x\n", hdc);
237         return FALSE;
238     }
239
240     abproc = dc->pAbortProc;
241     GDI_ReleaseObj( hdc );
242
243     if (abproc)
244         ret = abproc(hdc, 0);
245     return ret;
246 }
247
248 /* ### start build ### */
249 extern WORD CALLBACK PRTDRV_CallTo16_word_ww(FARPROC16,WORD,WORD);
250 /* ### stop build ### */
251
252 /**********************************************************************
253  *           SetAbortProc   (GDI.381)
254  *
255  */
256 INT16 WINAPI SetAbortProc16(HDC16 hdc, SEGPTR abrtprc)
257 {
258     ABORTPROC proc32 = (ABORTPROC)THUNK_Alloc((FARPROC16)abrtprc,
259                                               (RELAY)PRTDRV_CallTo16_word_ww);
260     return SetAbortProc(hdc, proc32);
261 }
262
263 /**********************************************************************
264  *           SetAbortProc   (GDI32.@)
265  *
266  */
267 INT WINAPI SetAbortProc(HDC hdc, ABORTPROC abrtprc)
268 {
269     DC *dc = DC_GetDCPtr( hdc );
270
271     if(dc->pAbortProc) THUNK_Free((FARPROC)dc->pAbortProc);
272     dc->pAbortProc = abrtprc;
273     GDI_ReleaseObj( hdc );
274     return TRUE;
275 }
276
277
278 /****************** misc. printer related functions */
279
280 /*
281  * The following function should implement a queing system
282  */
283 struct hpq 
284 {
285     struct hpq  *next;
286     int          tag;
287     int          key;
288 };
289
290 static struct hpq *hpqueue;
291
292 /**********************************************************************
293  *           CreatePQ   (GDI.230)
294  *
295  */
296 HPQ16 WINAPI CreatePQ16(INT16 size) 
297 {
298 #if 0
299     HGLOBAL16 hpq = 0;
300     WORD tmp_size;
301     LPWORD pPQ;
302
303     tmp_size = size << 2;
304     if (!(hpq = GlobalAlloc16(GMEM_SHARE|GMEM_MOVEABLE, tmp_size + 8)))
305        return 0xffff;
306     pPQ = GlobalLock16(hpq);
307     *pPQ++ = 0;
308     *pPQ++ = tmp_size;
309     *pPQ++ = 0;
310     *pPQ++ = 0;
311     GlobalUnlock16(hpq);
312
313     return (HPQ16)hpq;
314 #else
315     FIXME("(%d): stub\n",size);
316     return 1;
317 #endif
318 }
319
320 /**********************************************************************
321  *           DeletePQ   (GDI.235)
322  *
323  */
324 INT16 WINAPI DeletePQ16(HPQ16 hPQ) 
325 {
326     return GlobalFree16((HGLOBAL16)hPQ);
327 }
328
329 /**********************************************************************
330  *           ExtractPQ   (GDI.232)
331  *
332  */
333 INT16 WINAPI ExtractPQ16(HPQ16 hPQ) 
334
335     struct hpq *queue, *prev, *current, *currentPrev;
336     int key = 0, tag = -1;
337     currentPrev = prev = NULL;
338     queue = current = hpqueue;
339     if (current)
340         key = current->key;
341     
342     while (current)
343     {
344         currentPrev = current;
345         current = current->next;
346         if (current)
347         {
348             if (current->key < key)
349             {
350                 queue = current;
351                 prev = currentPrev;
352             }
353         }
354     }
355     if (queue)
356     {
357         tag = queue->tag;
358         
359         if (prev)
360             prev->next = queue->next;
361         else
362             hpqueue = queue->next;
363         HeapFree(GetProcessHeap(), 0, queue);
364     }
365     
366     TRACE("%x got tag %d key %d\n", hPQ, tag, key); 
367
368     return tag;
369 }
370
371 /**********************************************************************
372  *           InsertPQ   (GDI.233)
373  *
374  */
375 INT16 WINAPI InsertPQ16(HPQ16 hPQ, INT16 tag, INT16 key) 
376 {
377     struct hpq *queueItem = HeapAlloc(GetProcessHeap(), 0, sizeof(struct hpq));
378     if(queueItem == NULL) {
379         ERR("Memory exausted!\n");
380         return FALSE;
381     }
382     queueItem->next = hpqueue;
383     hpqueue = queueItem;
384     queueItem->key = key;
385     queueItem->tag = tag;
386     
387     FIXME("(%x %d %d): stub???\n", hPQ, tag, key);
388     return TRUE;
389 }
390
391 /**********************************************************************
392  *           MinPQ   (GDI.231)
393  *
394  */
395 INT16 WINAPI MinPQ16(HPQ16 hPQ) 
396 {
397     FIXME("(%x): stub\n", hPQ); 
398     return 0;
399 }
400
401 /**********************************************************************
402  *           SizePQ   (GDI.234)
403  *
404  */
405 INT16 WINAPI SizePQ16(HPQ16 hPQ, INT16 sizechange) 
406 {  
407     FIXME("(%x %d): stub\n", hPQ, sizechange); 
408     return -1; 
409 }
410
411
412
413 /* 
414  * The following functions implement part of the spooling process to 
415  * print manager.  I would like to see wine have a version of print managers
416  * that used LPR/LPD.  For simplicity print jobs will be sent to a file for
417  * now.
418  */
419 typedef struct PRINTJOB
420 {
421     char        *pszOutput;
422     char        *pszTitle;
423     HDC16       hDC;
424     HANDLE16    hHandle;
425     int         nIndex;
426     int         fd;
427 } PRINTJOB, *PPRINTJOB;
428
429 #define MAX_PRINT_JOBS 1
430 #define SP_OK 1
431
432 PPRINTJOB gPrintJobsTable[MAX_PRINT_JOBS];
433
434
435 static PPRINTJOB FindPrintJobFromHandle(HANDLE16 hHandle)
436 {
437     return gPrintJobsTable[0];
438 }
439
440 static int CreateSpoolFile(LPCSTR pszOutput)
441 {
442     int fd=-1;
443     char psCmd[1024];
444     char *psCmdP = psCmd;
445
446     /* TTD convert the 'output device' into a spool file name */
447
448     if (pszOutput == NULL || *pszOutput == '\0')
449       return -1;
450
451     if (!strncmp("LPR:",pszOutput,4))
452       sprintf(psCmd,"|lpr -P%s",pszOutput+4);
453     else
454     {
455         HKEY hkey;
456         /* default value */
457         psCmd[0] = 0;
458         if(!RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\spooler", &hkey))
459         {
460             DWORD type, count = sizeof(psCmd);
461             RegQueryValueExA(hkey, pszOutput, 0, &type, psCmd, &count);
462             RegCloseKey(hkey);
463         }
464     }
465     TRACE("Got printerSpoolCommand '%s' for output device '%s'\n",
466           psCmd, pszOutput);
467     if (!*psCmd)
468         psCmdP = (char *)pszOutput;
469     else
470     {
471         while (*psCmdP && isspace(*psCmdP))
472         {
473             psCmdP++;
474         };
475         if (!*psCmdP)
476             return -1;
477     }
478     if (*psCmdP == '|')
479     {
480         int fds[2];
481         if (pipe(fds))
482             return -1;
483         if (fork() == 0)
484         {
485             psCmdP++;
486
487             TRACE("In child need to exec %s\n",psCmdP);
488             close(0);
489             dup2(fds[0],0);
490             close (fds[1]);
491             system(psCmdP);
492             exit(0);
493             
494         }
495         close (fds[0]);
496         fd = fds[1];
497         TRACE("Need to execute a cmnd and pipe the output to it\n");
498     }
499     else
500     {
501         DOS_FULL_NAME fullName;
502
503         TRACE("Just assume it's a file\n");
504
505         /**
506          * The file name can be dos based, we have to find its
507          * Unix correspondant file name
508          */
509         DOSFS_GetFullName(psCmdP, FALSE, &fullName);
510
511         if ((fd = open(fullName.long_name, O_CREAT | O_TRUNC | O_WRONLY , 0600)) < 0)
512         {
513             ERR("Failed to create spool file %s (%s)\n", 
514                 fullName.long_name, strerror(errno));
515         }
516     }
517     return fd;
518 }
519
520 static int FreePrintJob(HANDLE16 hJob)
521 {
522     int nRet = SP_ERROR;
523     PPRINTJOB pPrintJob;
524
525     pPrintJob = FindPrintJobFromHandle(hJob);
526     if (pPrintJob != NULL)
527     {
528         gPrintJobsTable[pPrintJob->nIndex] = NULL;
529         HeapFree(GetProcessHeap(), 0, pPrintJob->pszOutput);
530         HeapFree(GetProcessHeap(), 0, pPrintJob->pszTitle);
531         if (pPrintJob->fd >= 0) close(pPrintJob->fd);
532         HeapFree(GetProcessHeap(), 0, pPrintJob);
533         nRet = SP_OK;
534     }
535     return nRet;
536 }
537
538 /**********************************************************************
539  *           OpenJob   (GDI.240)
540  *
541  */
542 HPJOB16 WINAPI OpenJob16(LPCSTR lpOutput, LPCSTR lpTitle, HDC16 hDC)
543 {
544     HPJOB16 hHandle = (HPJOB16)SP_ERROR;
545     PPRINTJOB pPrintJob;
546
547     TRACE("'%s' '%s' %04x\n", lpOutput, lpTitle, hDC);
548
549     pPrintJob = gPrintJobsTable[0];
550     if (pPrintJob == NULL)
551     {
552         int fd;
553
554         /* Try an create a spool file */
555         fd = CreateSpoolFile(lpOutput);
556         if (fd >= 0)
557         {
558             pPrintJob = HeapAlloc(GetProcessHeap(), 0, sizeof(PRINTJOB));
559             if(pPrintJob == NULL) {
560                 WARN("Memory exausted!\n");
561                 return hHandle;
562             }
563             
564             hHandle = 1;
565
566             pPrintJob->pszOutput = HeapAlloc(GetProcessHeap(), 0, strlen(lpOutput)+1);
567             strcpy( pPrintJob->pszOutput, lpOutput );
568             if(lpTitle)
569             {
570                 pPrintJob->pszTitle = HeapAlloc(GetProcessHeap(), 0, strlen(lpTitle)+1);
571                 strcpy( pPrintJob->pszTitle, lpTitle );
572             }
573             pPrintJob->hDC = hDC;
574             pPrintJob->fd = fd;
575             pPrintJob->nIndex = 0;
576             pPrintJob->hHandle = hHandle; 
577             gPrintJobsTable[pPrintJob->nIndex] = pPrintJob; 
578         }
579     }
580     TRACE("return %04x\n", hHandle);
581     return hHandle;
582 }
583
584 /**********************************************************************
585  *           CloseJob   (GDI.243)
586  *
587  */
588 INT16 WINAPI CloseJob16(HPJOB16 hJob)
589 {
590     int nRet = SP_ERROR;
591     PPRINTJOB pPrintJob = NULL;
592
593     TRACE("%04x\n", hJob);
594
595     pPrintJob = FindPrintJobFromHandle(hJob);
596     if (pPrintJob != NULL)
597     {
598         /* Close the spool file */
599         close(pPrintJob->fd);
600         FreePrintJob(hJob);
601         nRet  = 1;
602     }
603     return nRet;
604 }
605
606 /**********************************************************************
607  *           WriteSpool   (GDI.241)
608  *
609  */
610 INT16 WINAPI WriteSpool16(HPJOB16 hJob, LPSTR lpData, INT16 cch)
611 {
612     int nRet = SP_ERROR;
613     PPRINTJOB pPrintJob = NULL;
614
615     TRACE("%04x %08lx %04x\n", hJob, (DWORD)lpData, cch);
616
617     pPrintJob = FindPrintJobFromHandle(hJob);
618     if (pPrintJob != NULL && pPrintJob->fd >= 0 && cch)
619     {
620         if (write(pPrintJob->fd, lpData, cch) != cch)
621           nRet = SP_OUTOFDISK;
622         else
623           nRet = cch;
624 #if 0
625         /* FIXME: We just cannot call 16 bit functions from here, since we
626          * have acquired several locks (DC). And we do not really need to.
627          */
628         if (pPrintJob->hDC == 0) {
629             TRACE("hDC == 0 so no QueryAbort\n");
630         }
631         else if (!(QueryAbort16(pPrintJob->hDC, (nRet == SP_OUTOFDISK) ? nRet : 0 )))
632         {
633             CloseJob16(hJob); /* printing aborted */
634             nRet = SP_APPABORT;
635         }
636 #endif
637     }
638     return nRet;
639 }
640
641 typedef INT WINAPI (*MSGBOX_PROC)( HWND, LPCSTR, LPCSTR, UINT );
642
643 /**********************************************************************
644  *           WriteDialog   (GDI.242)
645  *
646  */
647 INT16 WINAPI WriteDialog16(HPJOB16 hJob, LPSTR lpMsg, INT16 cchMsg)
648 {
649     HMODULE mod;
650     MSGBOX_PROC pMessageBoxA;
651     INT16 ret = 0;
652
653     TRACE("%04x %04x '%s'\n", hJob,  cchMsg, lpMsg);
654
655     if ((mod = GetModuleHandleA("user32.dll")))
656     {
657         if ((pMessageBoxA = (MSGBOX_PROC)GetProcAddress( mod, "MessageBoxA" )))
658             ret = pMessageBoxA(0, lpMsg, "Printing Error", MB_OKCANCEL);
659     }
660     return ret;
661 }
662
663
664 /**********************************************************************
665  *           DeleteJob  (GDI.244)
666  *
667  */
668 INT16 WINAPI DeleteJob16(HPJOB16 hJob, INT16 nNotUsed)
669 {
670     int nRet;
671
672     TRACE("%04x\n", hJob);
673
674     nRet = FreePrintJob(hJob);
675     return nRet;
676 }
677
678 /* 
679  * The following two function would allow a page to be sent to the printer
680  * when it has been processed.  For simplicity they havn't been implemented.
681  * This means a whole job has to be processed before it is sent to the printer.
682  */
683
684 /**********************************************************************
685  *           StartSpoolPage   (GDI.246)
686  *
687  */
688 INT16 WINAPI StartSpoolPage16(HPJOB16 hJob)
689 {
690     FIXME("StartSpoolPage GDI.246 unimplemented\n");
691     return 1;
692
693 }
694
695
696 /**********************************************************************
697  *           EndSpoolPage   (GDI.247)
698  *
699  */
700 INT16 WINAPI EndSpoolPage16(HPJOB16 hJob)
701 {
702     FIXME("EndSpoolPage GDI.247 unimplemented\n");
703     return 1;
704 }
705
706
707 /**********************************************************************
708  *           GetSpoolJob   (GDI.245)
709  *
710  */
711 DWORD WINAPI GetSpoolJob16(int nOption, LONG param)
712 {
713     DWORD retval = 0;
714     TRACE("In GetSpoolJob param 0x%lx noption %d\n",param, nOption);
715     return retval;
716 }
717
718
719 /******************************************************************
720  *                  DrvGetPrinterDataInternal
721  *
722  * Helper for DrvGetPrinterData
723  */
724 static DWORD DrvGetPrinterDataInternal(LPSTR RegStr_Printer,
725 LPBYTE lpPrinterData, int cbData, int what)
726 {
727     DWORD res = -1;
728     HKEY hkey;
729     DWORD dwType, cbQueryData;
730
731     if (!(RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey))) {
732         if (what == INT_PD_DEFAULT_DEVMODE) { /* "Default DevMode" */
733             if (!(RegQueryValueExA(hkey, DefaultDevMode, 0, &dwType, 0, &cbQueryData))) {
734                 if (!lpPrinterData)
735                     res = cbQueryData;
736                 else if ((cbQueryData) && (cbQueryData <= cbData)) {
737                     cbQueryData = cbData;
738                     if (RegQueryValueExA(hkey, DefaultDevMode, 0,
739                                 &dwType, lpPrinterData, &cbQueryData))
740                         res = cbQueryData;
741                 }
742             }
743         } else { /* "Printer Driver" */
744             cbQueryData = 32;
745             RegQueryValueExA(hkey, "Printer Driver", 0,
746                         &dwType, lpPrinterData, &cbQueryData);
747             res = cbQueryData;
748         }
749     }
750     if (hkey) RegCloseKey(hkey);
751     return res;
752 }
753
754 /******************************************************************
755  *                DrvGetPrinterData     [GDI.282]
756  *
757  */
758 DWORD WINAPI DrvGetPrinterData16(LPSTR lpPrinter, LPSTR lpProfile,
759                                LPDWORD lpType, LPBYTE lpPrinterData,
760                                int cbData, LPDWORD lpNeeded)
761 {
762     LPSTR RegStr_Printer;
763     HKEY hkey = 0, hkey2 = 0;
764     DWORD res = 0;
765     DWORD dwType, PrinterAttr, cbPrinterAttr, SetData, size;
766
767     if (HIWORD(lpPrinter))
768             TRACE("printer %s\n",lpPrinter);
769     else
770             TRACE("printer %p\n",lpPrinter);
771     if (HIWORD(lpProfile))
772             TRACE("profile %s\n",lpProfile);
773     else
774             TRACE("profile %p\n",lpProfile);
775     TRACE("lpType %p\n",lpType);
776
777     if ((!lpPrinter) || (!lpProfile) || (!lpNeeded))
778         return ERROR_INVALID_PARAMETER;
779
780     RegStr_Printer = HeapAlloc(GetProcessHeap(), 0,
781                                strlen(Printers) + strlen(lpPrinter) + 2);
782     strcpy(RegStr_Printer, Printers);
783     strcat(RegStr_Printer, lpPrinter);
784
785     if (((DWORD)lpProfile == INT_PD_DEFAULT_DEVMODE) || (HIWORD(lpProfile) &&
786     (!strcmp(lpProfile, DefaultDevMode)))) {
787         size = DrvGetPrinterDataInternal(RegStr_Printer, lpPrinterData, cbData,
788                                          INT_PD_DEFAULT_DEVMODE);
789         if (size+1) {
790             *lpNeeded = size;
791             if ((lpPrinterData) && (*lpNeeded > cbData))
792                 res = ERROR_MORE_DATA;
793         }
794         else res = ERROR_INVALID_PRINTER_NAME;
795     }
796     else
797     if (((DWORD)lpProfile == INT_PD_DEFAULT_MODEL) || (HIWORD(lpProfile) &&
798     (!strcmp(lpProfile, PrinterModel)))) {
799         *lpNeeded = 32;
800         if (!lpPrinterData) goto failed;
801         if (cbData < 32) {
802             res = ERROR_MORE_DATA;
803             goto failed;
804         }
805         size = DrvGetPrinterDataInternal(RegStr_Printer, lpPrinterData, cbData,
806                                          INT_PD_DEFAULT_MODEL);
807         if ((size+1) && (lpType))
808             *lpType = REG_SZ;
809         else
810             res = ERROR_INVALID_PRINTER_NAME;
811     }
812     else
813     {
814         if ((res = RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey)))
815             goto failed;
816         cbPrinterAttr = 4;
817         if ((res = RegQueryValueExA(hkey, "Attributes", 0,
818                         &dwType, (LPBYTE)&PrinterAttr, &cbPrinterAttr)))
819             goto failed;
820         if ((res = RegOpenKeyA(hkey, PrinterDriverData, &hkey2)))
821             goto failed;
822         *lpNeeded = cbData;
823         res = RegQueryValueExA(hkey2, lpProfile, 0,
824                 lpType, lpPrinterData, lpNeeded);
825         if ((res != ERROR_CANTREAD) &&
826          ((PrinterAttr &
827         (PRINTER_ATTRIBUTE_ENABLE_BIDI|PRINTER_ATTRIBUTE_NETWORK))
828         == PRINTER_ATTRIBUTE_NETWORK))
829         {
830             if (!(res) && (*lpType == REG_DWORD) && (*(LPDWORD)lpPrinterData == -1))
831                 res = ERROR_INVALID_DATA;
832         }
833         else
834         {
835             SetData = -1;
836             RegSetValueExA(hkey2, lpProfile, 0, REG_DWORD, (LPBYTE)&SetData, 4); /* no result returned */
837         }
838     }
839         
840 failed:
841     if (hkey2) RegCloseKey(hkey2);
842     if (hkey) RegCloseKey(hkey);
843     HeapFree(GetProcessHeap(), 0, RegStr_Printer);
844     return res;
845 }
846
847
848 /******************************************************************
849  *                 DrvSetPrinterData     [GDI.281]
850  *
851  */
852 DWORD WINAPI DrvSetPrinterData16(LPSTR lpPrinter, LPSTR lpProfile,
853                                DWORD lpType, LPBYTE lpPrinterData,
854                                DWORD dwSize)
855 {
856     LPSTR RegStr_Printer;
857     HKEY hkey = 0;
858     DWORD res = 0;
859
860     if (HIWORD(lpPrinter))
861             TRACE("printer %s\n",lpPrinter);
862     else
863             TRACE("printer %p\n",lpPrinter);
864     if (HIWORD(lpProfile))
865             TRACE("profile %s\n",lpProfile);
866     else
867             TRACE("profile %p\n",lpProfile);
868     TRACE("lpType %08lx\n",lpType);
869
870     if ((!lpPrinter) || (!lpProfile) ||
871     ((DWORD)lpProfile == INT_PD_DEFAULT_MODEL) || (HIWORD(lpProfile) &&
872     (!strcmp(lpProfile, PrinterModel))))
873         return ERROR_INVALID_PARAMETER;
874
875     RegStr_Printer = HeapAlloc(GetProcessHeap(), 0,
876                         strlen(Printers) + strlen(lpPrinter) + 2);
877     strcpy(RegStr_Printer, Printers);
878     strcat(RegStr_Printer, lpPrinter);
879
880     if (((DWORD)lpProfile == INT_PD_DEFAULT_DEVMODE) || (HIWORD(lpProfile) &&
881     (!strcmp(lpProfile, DefaultDevMode)))) {
882         if ( RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey) 
883              != ERROR_SUCCESS ||
884              RegSetValueExA(hkey, DefaultDevMode, 0, REG_BINARY, 
885                               lpPrinterData, dwSize) != ERROR_SUCCESS )
886                 res = ERROR_INVALID_PRINTER_NAME;
887     }
888     else
889     {
890         strcat(RegStr_Printer, "\\");
891
892         if( (res = RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey)) ==
893             ERROR_SUCCESS ) {
894
895             if (!lpPrinterData) 
896                 res = RegDeleteValueA(hkey, lpProfile);
897             else
898                 res = RegSetValueExA(hkey, lpProfile, 0, lpType,
899                                        lpPrinterData, dwSize);
900         }
901     }
902
903     if (hkey) RegCloseKey(hkey);
904     HeapFree(GetProcessHeap(), 0, RegStr_Printer);
905     return res;
906 }