Rename queue_t to jobqueue_t to avoid collision with library symbol
[wine] / dlls / winspool / info.c
1 /*
2  * WINSPOOL functions
3  *
4  * Copyright 1996 John Harvey
5  * Copyright 1998 Andreas Mohr
6  * Copyright 1999 Klaas van Gend
7  * Copyright 1999, 2000 Huw D M Davies
8  * Copyright 2001 Marcus Meissner
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 #include "config.h"
26 #include "wine/port.h"
27
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <stddef.h>
34 #ifdef HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
37 #include <signal.h>
38 #ifdef HAVE_CUPS_CUPS_H
39 # include <cups/cups.h>
40 # ifndef SONAME_LIBCUPS
41 #  define SONAME_LIBCUPS "libcups.so"
42 # endif
43 #endif
44
45 #define NONAMELESSUNION
46 #define NONAMELESSSTRUCT
47 #include "wine/library.h"
48 #include "windef.h"
49 #include "winbase.h"
50 #include "winuser.h"
51 #include "winerror.h"
52 #include "winreg.h"
53 #include "wingdi.h"
54 #include "winspool.h"
55 #include "winternl.h"
56 #include "wine/windef16.h"
57 #include "wine/unicode.h"
58 #include "wine/debug.h"
59 #include "wine/list.h"
60 #include "heap.h"
61 #include "winnls.h"
62
63 #include "wspool.h"
64
65 WINE_DEFAULT_DEBUG_CHANNEL(winspool);
66
67 static CRITICAL_SECTION printer_handles_cs;
68 static CRITICAL_SECTION_DEBUG printer_handles_cs_debug = 
69 {
70     0, 0, &printer_handles_cs,
71     { &printer_handles_cs_debug.ProcessLocksList, &printer_handles_cs_debug.ProcessLocksList },
72       0, 0, { (DWORD_PTR)(__FILE__ ": printer_handles_cs") }
73 };
74 static CRITICAL_SECTION printer_handles_cs = { &printer_handles_cs_debug, -1, 0, 0, 0, 0 };
75
76 typedef struct {
77     DWORD job_id;
78     HANDLE hf;
79 } started_doc_t;
80
81 typedef struct {
82     struct list jobs;
83     LONG ref;
84 } jobqueue_t;
85
86 typedef struct {
87     LPWSTR name;
88     jobqueue_t *queue;
89     started_doc_t *doc;
90 } opened_printer_t;
91
92 typedef struct {
93     struct list entry;
94     DWORD job_id;
95     WCHAR *filename;
96     WCHAR *document_title;
97 } job_t;
98
99 static opened_printer_t **printer_handles;
100 static int nb_printer_handles;
101 static LONG next_job_id = 1;
102
103 static DWORD (WINAPI *GDI_CallDeviceCapabilities16)( LPCSTR lpszDevice, LPCSTR lpszPort,
104                                                      WORD fwCapability, LPSTR lpszOutput,
105                                                      LPDEVMODEA lpdm );
106 static INT (WINAPI *GDI_CallExtDeviceMode16)( HWND hwnd, LPDEVMODEA lpdmOutput,
107                                               LPSTR lpszDevice, LPSTR lpszPort,
108                                               LPDEVMODEA lpdmInput, LPSTR lpszProfile,
109                                               DWORD fwMode );
110
111 static const char Printers[] =
112 "System\\CurrentControlSet\\control\\Print\\Printers\\";
113
114 static const WCHAR DriversW[] = { 'S','y','s','t','e','m','\\',
115                                   'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
116                                   'c','o','n','t','r','o','l','\\',
117                                   'P','r','i','n','t','\\',
118                                   'E','n','v','i','r','o','n','m','e','n','t','s','\\',
119                                   '%','s','\\','D','r','i','v','e','r','s','\\',0 };
120
121 static const WCHAR user_default_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
122                                               'M','i','c','r','o','s','o','f','t','\\',
123                                               'W','i','n','d','o','w','s',' ','N','T','\\',
124                                               'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
125                                               'W','i','n','d','o','w','s',0};
126
127 static const WCHAR user_printers_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
128                                                'M','i','c','r','o','s','o','f','t','\\',
129                                                'W','i','n','d','o','w','s',' ','N','T','\\',
130                                                'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
131                                                'D','e','v','i','c','e','s',0};
132
133 static const WCHAR DefaultEnvironmentW[] = {'W','i','n','e',0};
134
135 static const WCHAR Configuration_FileW[] = {'C','o','n','f','i','g','u','r','a','t',
136                                       'i','o','n',' ','F','i','l','e',0};
137 static const WCHAR DatatypeW[] = {'D','a','t','a','t','y','p','e',0};
138 static const WCHAR Data_FileW[] = {'D','a','t','a',' ','F','i','l','e',0};
139 static const WCHAR Default_DevModeW[] = {'D','e','f','a','u','l','t',' ','D','e','v',
140                                    'M','o','d','e',0};
141 static const WCHAR Dependent_FilesW[] = {'D','e','p','e','n','d','e','n','t',' ','F',
142                                    'i','l','e','s',0};
143 static const WCHAR DescriptionW[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
144 static const WCHAR DriverW[] = {'D','r','i','v','e','r',0};
145 static const WCHAR Help_FileW[] = {'H','e','l','p',' ','F','i','l','e',0};
146 static const WCHAR LocationW[] = {'L','o','c','a','t','i','o','n',0};
147 static const WCHAR MonitorW[] = {'M','o','n','i','t','o','r',0};
148 static const WCHAR NameW[] = {'N','a','m','e',0};
149 static const WCHAR ParametersW[] = {'P','a','r','a','m','e','t','e','r','s',0};
150 static const WCHAR PortW[] = {'P','o','r','t',0};
151 static const WCHAR Print_ProcessorW[] = {'P','r','i','n','t',' ','P','r','o','c','e',
152                                    's','s','o','r',0};
153 static const WCHAR Printer_DriverW[] = {'P','r','i','n','t','e','r',' ','D','r','i',
154                                   'v','e','r',0};
155 static const WCHAR PrinterDriverDataW[] = {'P','r','i','n','t','e','r','D','r','i',
156                                      'v','e','r','D','a','t','a',0};
157 static const WCHAR Separator_FileW[] = {'S','e','p','a','r','a','t','o','r',' ','F',
158                                   'i','l','e',0};
159 static const WCHAR Share_NameW[] = {'S','h','a','r','e',' ','N','a','m','e',0};
160 static const WCHAR WinPrintW[] = {'W','i','n','P','r','i','n','t',0};
161 static const WCHAR deviceW[]  = {'d','e','v','i','c','e',0};
162 static const WCHAR devicesW[] = {'d','e','v','i','c','e','s',0};
163 static const WCHAR windowsW[] = {'w','i','n','d','o','w','s',0};
164 static const WCHAR emptyStringW[] = {0};
165
166 static const WCHAR May_Delete_Value[] = {'W','i','n','e','M','a','y','D','e','l','e','t','e','M','e',0};
167
168 static const WCHAR CUPS_Port[] = {'C','U','P','S',':',0};
169 static const WCHAR FILE_Port[] = {'F','I','L','E',':',0};
170 static const WCHAR LPR_Port[] = {'L','P','R',':',0};
171
172 static const WCHAR default_doc_title[] = {'L','o','c','a','l',' ','D','o','w','n','l','e','v','e','l',' ',
173                                           'D','o','c','u','m','e','n','t',0};
174
175 static HKEY WINSPOOL_OpenDriverReg( LPVOID pEnvironment, BOOL unicode);
176 static BOOL WINSPOOL_GetPrinterDriver(HANDLE hPrinter, LPWSTR pEnvironment,
177                                       DWORD Level, LPBYTE pDriverInfo,
178                                       DWORD cbBuf, LPDWORD pcbNeeded,
179                                       BOOL unicode);
180 static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey);
181
182 /* RtlCreateUnicodeStringFromAsciiz will return an empty string in the buffer
183    if passed a NULL string. This returns NULLs to the result. 
184 */
185 static inline PWSTR asciitounicode( UNICODE_STRING * usBufferPtr, LPCSTR src )
186 {
187     if ( (src) )
188     {
189         RtlCreateUnicodeStringFromAsciiz(usBufferPtr, src);
190         return usBufferPtr->Buffer;
191     }
192     usBufferPtr->Buffer = NULL; /* so that RtlFreeUnicodeString won't barf */
193     return NULL;
194 }
195             
196 static LPWSTR strdupW(LPCWSTR p)
197 {
198     LPWSTR ret;
199     DWORD len;
200
201     if(!p) return NULL;
202     len = (strlenW(p) + 1) * sizeof(WCHAR);
203     ret = HeapAlloc(GetProcessHeap(), 0, len);
204     memcpy(ret, p, len);
205     return ret;
206 }
207
208 static void
209 WINSPOOL_SetDefaultPrinter(const char *devname, const char *name,BOOL force) {
210     char qbuf[200];
211
212     /* If forcing, or no profile string entry for device yet, set the entry
213      *
214      * The always change entry if not WINEPS yet is discussable.
215      */
216     if (force                                                           ||
217         !GetProfileStringA("windows","device","*",qbuf,sizeof(qbuf))    ||
218         !strcmp(qbuf,"*")                                               ||
219         !strstr(qbuf,"WINEPS.DRV")
220     ) {
221         char *buf = HeapAlloc(GetProcessHeap(),0,strlen(name)+strlen(devname)+strlen(",WINEPS.DRV,LPR:")+1);
222         HKEY hkey;
223
224         sprintf(buf,"%s,WINEPS.DRV,LPR:%s",devname,name);
225         WriteProfileStringA("windows","device",buf);
226         if(RegCreateKeyW(HKEY_CURRENT_USER, user_default_reg_key, &hkey) == ERROR_SUCCESS) {
227             RegSetValueExA(hkey, "Device", 0, REG_SZ, (LPBYTE)buf, strlen(buf) + 1);
228             RegCloseKey(hkey);
229         }
230         HeapFree(GetProcessHeap(),0,buf);
231     }
232 }
233
234 #ifdef HAVE_CUPS_CUPS_H
235 static typeof(cupsGetDests)  *pcupsGetDests;
236 static typeof(cupsGetPPD)    *pcupsGetPPD;
237 static typeof(cupsPrintFile) *pcupsPrintFile;
238 static void *cupshandle;
239
240 static BOOL CUPS_LoadPrinters(void)
241 {
242     int                   i, nrofdests;
243     BOOL                  hadprinter = FALSE;
244     cups_dest_t          *dests;
245     PRINTER_INFO_2A       pinfo2a;
246     char   *port,*devline;
247     HKEY hkeyPrinter, hkeyPrinters, hkey;
248
249     cupshandle = wine_dlopen(SONAME_LIBCUPS, RTLD_NOW, NULL, 0);
250     if (!cupshandle) 
251         return FALSE;
252     TRACE("loaded %s\n", SONAME_LIBCUPS);
253
254 #define DYNCUPS(x)                                      \
255         p##x = wine_dlsym(cupshandle, #x, NULL,0);      \
256         if (!p##x) return FALSE;
257
258     DYNCUPS(cupsGetPPD);
259     DYNCUPS(cupsGetDests);
260     DYNCUPS(cupsPrintFile);
261 #undef DYNCUPS
262
263     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
264        ERROR_SUCCESS) {
265         ERR("Can't create Printers key\n");
266         return FALSE;
267     }
268
269     nrofdests = pcupsGetDests(&dests);
270     TRACE("Found %d CUPS %s:\n", nrofdests, (nrofdests == 1) ? "printer" : "printers");
271     for (i=0;i<nrofdests;i++) {
272         port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(dests[i].name)+1);
273         sprintf(port,"LPR:%s",dests[i].name);
274         devline=HeapAlloc(GetProcessHeap(),0,sizeof("WINEPS.DRV,")+strlen(port));
275         sprintf(devline,"WINEPS.DRV,%s",port);
276         WriteProfileStringA("devices",dests[i].name,devline);
277         if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
278             RegSetValueExA(hkey, dests[i].name, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
279             RegCloseKey(hkey);
280         }
281         HeapFree(GetProcessHeap(),0,devline);
282
283         TRACE("Printer %d: %s\n", i, dests[i].name);
284         if(RegOpenKeyA(hkeyPrinters, dests[i].name, &hkeyPrinter) == ERROR_SUCCESS) {
285             /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
286                and continue */
287             TRACE("Printer already exists\n");
288             RegDeleteValueW(hkeyPrinter, May_Delete_Value);
289             RegCloseKey(hkeyPrinter);
290         } else {
291             memset(&pinfo2a,0,sizeof(pinfo2a));
292             pinfo2a.pPrinterName        = dests[i].name;
293             pinfo2a.pDatatype   = "RAW";
294             pinfo2a.pPrintProcessor     = "WinPrint";
295             pinfo2a.pDriverName = "PS Driver";
296             pinfo2a.pComment    = "WINEPS Printer using CUPS";
297             pinfo2a.pLocation   = "<physical location of printer>";
298             pinfo2a.pPortName   = port;
299             pinfo2a.pParameters = "<parameters?>";
300             pinfo2a.pShareName  = "<share name?>";
301             pinfo2a.pSepFile    = "<sep file?>";
302
303             if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
304                 if (GetLastError() != ERROR_PRINTER_ALREADY_EXISTS)
305                     ERR("printer '%s' not added by AddPrinterA (error %ld)\n",dests[i].name,GetLastError());
306             }
307         }
308         HeapFree(GetProcessHeap(),0,port);
309
310         hadprinter = TRUE;
311         if (dests[i].is_default)
312             WINSPOOL_SetDefaultPrinter(dests[i].name, dests[i].name, TRUE);
313     }
314     RegCloseKey(hkeyPrinters);
315     return hadprinter;
316 }
317 #endif
318
319 static BOOL
320 PRINTCAP_ParseEntry(char *pent,BOOL isfirst) {
321     PRINTER_INFO_2A     pinfo2a;
322     char                *e,*s,*name,*prettyname,*devname;
323     BOOL                ret = FALSE, set_default = FALSE;
324     char                *port,*devline,*env_default;
325     HKEY                hkeyPrinter, hkeyPrinters, hkey;
326
327     while (isspace(*pent)) pent++;
328     s = strchr(pent,':');
329     if(s) *s='\0';
330     name = HeapAlloc(GetProcessHeap(), 0, strlen(pent) + 1);
331     strcpy(name,pent);
332     if(s) {
333         *s=':';
334         pent = s;
335     } else
336         pent = "";
337
338     TRACE("name=%s entry=%s\n",name, pent);
339
340     if(ispunct(*name)) { /* a tc entry, not a real printer */
341         TRACE("skipping tc entry\n");
342         goto end;
343     }
344
345     if(strstr(pent,":server")) { /* server only version so skip */
346         TRACE("skipping server entry\n");
347         goto end;
348     }
349
350     /* Determine whether this is a postscript printer. */
351
352     ret = TRUE;
353     env_default = getenv("PRINTER");
354     prettyname = name;
355     /* Get longest name, usually the one at the right for later display. */
356     while((s=strchr(prettyname,'|'))) {
357         *s = '\0';
358         e = s;
359         while(isspace(*--e)) *e = '\0';
360         TRACE("\t%s\n", debugstr_a(prettyname));
361         if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
362         for(prettyname = s+1; isspace(*prettyname); prettyname++)
363             ;
364     }
365     e = prettyname + strlen(prettyname);
366     while(isspace(*--e)) *e = '\0';
367     TRACE("\t%s\n", debugstr_a(prettyname));
368     if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
369
370     /* prettyname must fit into the dmDeviceName member of DEVMODE struct,
371      * if it is too long, we use it as comment below. */
372     devname = prettyname;
373     if (strlen(devname)>=CCHDEVICENAME-1)
374          devname = name;
375     if (strlen(devname)>=CCHDEVICENAME-1) {
376         ret = FALSE;
377         goto end;
378     }
379
380     port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(name)+1);
381     sprintf(port,"LPR:%s",name);
382
383     devline=HeapAlloc(GetProcessHeap(),0,sizeof("WINEPS.DRV,")+strlen(port));
384     sprintf(devline,"WINEPS.DRV,%s",port);
385     WriteProfileStringA("devices",devname,devline);
386     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
387         RegSetValueExA(hkey, devname, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
388         RegCloseKey(hkey);
389     }
390     HeapFree(GetProcessHeap(),0,devline);
391     
392     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
393        ERROR_SUCCESS) {
394         ERR("Can't create Printers key\n");
395         ret = FALSE;
396         goto end;
397     }
398     if(RegOpenKeyA(hkeyPrinters, devname, &hkeyPrinter) == ERROR_SUCCESS) {
399         /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
400            and continue */
401         TRACE("Printer already exists\n");
402         RegDeleteValueW(hkeyPrinter, May_Delete_Value);
403         RegCloseKey(hkeyPrinter);
404     } else {
405         memset(&pinfo2a,0,sizeof(pinfo2a));
406         pinfo2a.pPrinterName            = devname;
407         pinfo2a.pDatatype               = "RAW";
408         pinfo2a.pPrintProcessor         = "WinPrint";
409         pinfo2a.pDriverName             = "PS Driver";
410         pinfo2a.pComment                = "WINEPS Printer using LPR";
411         pinfo2a.pLocation               = prettyname;
412         pinfo2a.pPortName               = port;
413         pinfo2a.pParameters             = "<parameters?>";
414         pinfo2a.pShareName              = "<share name?>";
415         pinfo2a.pSepFile                = "<sep file?>";
416
417         if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
418             if (GetLastError()!=ERROR_PRINTER_ALREADY_EXISTS)
419                 ERR("%s not added by AddPrinterA (%ld)\n",name,GetLastError());
420         }
421     }
422     RegCloseKey(hkeyPrinters);
423
424     if (isfirst || set_default)
425         WINSPOOL_SetDefaultPrinter(devname,name,TRUE);
426
427     HeapFree(GetProcessHeap(), 0, port);
428  end:
429     HeapFree(GetProcessHeap(), 0, name);
430     return ret;
431 }
432
433 static BOOL
434 PRINTCAP_LoadPrinters(void) {
435     BOOL                hadprinter = FALSE;
436     char                buf[200];
437     FILE                *f;
438     char *pent = NULL;
439     BOOL had_bash = FALSE;
440
441     f = fopen("/etc/printcap","r");
442     if (!f)
443         return FALSE;
444
445     while(fgets(buf,sizeof(buf),f)) {
446         char *start, *end;
447
448         end=strchr(buf,'\n');
449         if (end) *end='\0';
450     
451         start = buf;
452         while(isspace(*start)) start++;
453         if(*start == '#' || *start == '\0')
454             continue;
455
456         if(pent && !had_bash && *start != ':' && *start != '|') { /* start of new entry, parse the previous one */
457             hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
458             HeapFree(GetProcessHeap(),0,pent);
459             pent = NULL;
460         }
461
462         if (end && *--end == '\\') {
463             *end = '\0';
464             had_bash = TRUE;
465         } else
466             had_bash = FALSE;
467
468         if (pent) {
469             pent=HeapReAlloc(GetProcessHeap(),0,pent,strlen(pent)+strlen(start)+1);
470             strcat(pent,start);
471         } else {
472             pent=HeapAlloc(GetProcessHeap(),0,strlen(start)+1);
473             strcpy(pent,start);
474         }
475
476     }
477     if(pent) {
478         hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
479         HeapFree(GetProcessHeap(),0,pent);
480     }
481     fclose(f);
482     return hadprinter;
483 }
484
485 static inline DWORD set_reg_szW(HKEY hkey, const WCHAR *keyname, const WCHAR *value)
486 {
487     if (value)
488         return RegSetValueExW(hkey, keyname, 0, REG_SZ, (const BYTE*)value,
489                    lstrlenW(value) * sizeof(WCHAR));
490     else
491         return ERROR_FILE_NOT_FOUND;
492 }
493
494 void WINSPOOL_LoadSystemPrinters(void)
495 {
496     HKEY                hkey, hkeyPrinters;
497     DRIVER_INFO_3A      di3a;
498     HANDLE              hprn;
499     DWORD               needed, num, i;
500     WCHAR               PrinterName[256];
501     BOOL                done = FALSE;
502
503     di3a.cVersion = 0x400;
504     di3a.pName = "PS Driver";
505     di3a.pEnvironment = NULL;   /* NULL means auto */
506     di3a.pDriverPath = "wineps16";
507     di3a.pDataFile = "<datafile?>";
508     di3a.pConfigFile = "wineps16";
509     di3a.pHelpFile = "<helpfile?>";
510     di3a.pDependentFiles = "<dependend files?>";
511     di3a.pMonitorName = "<monitor name?>";
512     di3a.pDefaultDataType = "RAW";
513
514     if (!AddPrinterDriverA(NULL,3,(LPBYTE)&di3a)) {
515         ERR("Failed adding PS Driver (%ld)\n",GetLastError());
516         return;
517     }
518
519     /* This ensures that all printer entries have a valid Name value.  If causes
520        problems later if they don't.  If one is found to be missed we create one
521        and set it equal to the name of the key */
522     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) == ERROR_SUCCESS) {
523         if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &num, NULL, NULL,
524                             NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
525             for(i = 0; i < num; i++) {
526                 if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)) == ERROR_SUCCESS) {
527                     if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkey) == ERROR_SUCCESS) {
528                         if(RegQueryValueExW(hkey, NameW, 0, 0, 0, &needed) == ERROR_FILE_NOT_FOUND) {
529                             set_reg_szW(hkey, NameW, PrinterName);
530                         }
531                         RegCloseKey(hkey);
532                     }
533                 }
534             }
535         }
536         RegCloseKey(hkeyPrinters);
537     }
538
539     /* We want to avoid calling AddPrinter on printers as much as
540        possible, because on cups printers this will (eventually) lead
541        to a call to cupsGetPPD which takes forever, even with non-cups
542        printers AddPrinter takes a while.  So we'll tag all printers that
543        were automatically added last time around, if they still exist
544        we'll leave them be otherwise we'll delete them. */
545     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
546     if(needed) {
547         PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
548         if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
549             for(i = 0; i < num; i++) {
550                 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
551                     if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
552                         if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
553                             DWORD dw = 1;
554                             RegSetValueExW(hkey, May_Delete_Value, 0, REG_DWORD, (LPBYTE)&dw, sizeof(dw));
555                             RegCloseKey(hkey);
556                         }
557                         ClosePrinter(hprn);
558                     }
559                 }
560             }
561         }
562         HeapFree(GetProcessHeap(), 0, pi);
563     }
564
565
566 #ifdef HAVE_CUPS_CUPS_H
567     done = CUPS_LoadPrinters();
568 #endif
569
570     if(!done) { /* If we have any CUPS based printers, skip looking for printcap printers */
571         /* Check for [ppd] section in config file before parsing /etc/printcap */
572         /* @@ Wine registry key: HKCU\Software\Wine\Printing\PPD Files */
573         if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Printing\\PPD Files",
574                         &hkey) == ERROR_SUCCESS) {
575             RegCloseKey(hkey);
576             PRINTCAP_LoadPrinters();
577         }
578     }
579
580     /* Now enumerate the list again and delete any printers that a still tagged */
581     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
582     if(needed) {
583         PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
584         if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
585             for(i = 0; i < num; i++) {
586                 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
587                     if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
588                         if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
589                             DWORD dw, type, size = sizeof(dw);
590                             if(RegQueryValueExW(hkey, May_Delete_Value, NULL, &type, (LPBYTE)&dw, &size) == ERROR_SUCCESS) {
591                                 TRACE("Deleting old printer %s\n", pi[i].pPrinterName);
592                                 DeletePrinter(hprn);
593                             }
594                             RegCloseKey(hkey);
595                         }
596                         ClosePrinter(hprn);
597                     }
598                 }
599             }
600         }
601         HeapFree(GetProcessHeap(), 0, pi);
602     }
603
604     return;
605
606 }
607
608
609 /******************************************************************
610  *  get_opened_printer_entry
611  *  Get the first place empty in the opened printer table
612  */
613 static HANDLE get_opened_printer_entry( LPCWSTR name )
614 {
615     UINT_PTR handle = nb_printer_handles, i;
616     jobqueue_t *queue = NULL;
617     opened_printer_t *printer;
618
619     EnterCriticalSection(&printer_handles_cs);
620
621     for (i = 0; i < nb_printer_handles; i++)
622     {
623         if (!printer_handles[i])
624         {
625             if(handle == nb_printer_handles)
626                 handle = i;
627         }
628         else if(!queue && !strcmpW(name, printer_handles[i]->name))
629             queue = printer_handles[i]->queue;
630     }
631
632     if (handle >= nb_printer_handles)
633     {
634         opened_printer_t **new_array;
635         if (printer_handles)
636             new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, printer_handles,
637                                      (nb_printer_handles + 16) * sizeof(*new_array) );
638         else
639             new_array = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
640                                    (nb_printer_handles + 16) * sizeof(*new_array) );
641
642         if (!new_array)
643         {
644             handle = 0;
645             goto end;
646         }
647         printer_handles = new_array;
648         nb_printer_handles += 16;
649     }
650
651     if (!(printer = HeapAlloc(GetProcessHeap(), 0, sizeof(*printer))))
652     {
653         handle = 0;
654         goto end;
655     }
656
657     printer->name = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
658     strcpyW(printer->name, name);
659     if(queue)
660         printer->queue = queue;
661     else
662     {
663         printer->queue = HeapAlloc(GetProcessHeap(), 0, sizeof(*queue));
664         list_init(&printer->queue->jobs);
665         printer->queue->ref = 0;
666     }
667     InterlockedIncrement(&printer->queue->ref);
668     printer->doc = NULL;
669
670     printer_handles[handle] = printer;
671     handle++;
672 end:
673     LeaveCriticalSection(&printer_handles_cs);
674
675     return (HANDLE)handle;
676 }
677
678 /******************************************************************
679  *  get_opened_printer
680  *  Get the pointer to the opened printer referred by the handle
681  */
682 static opened_printer_t *get_opened_printer(HANDLE hprn)
683 {
684     UINT_PTR idx = (UINT_PTR)hprn;
685     opened_printer_t *ret = NULL;
686
687     EnterCriticalSection(&printer_handles_cs);
688
689     if ((idx <= 0) || (idx > nb_printer_handles))
690         goto end;
691
692     ret = printer_handles[idx - 1];
693 end:
694     LeaveCriticalSection(&printer_handles_cs);
695     return ret;
696 }
697
698 /******************************************************************
699  *  get_opened_printer_name
700  *  Get the pointer to the opened printer name referred by the handle
701  */
702 static LPCWSTR get_opened_printer_name(HANDLE hprn)
703 {
704     opened_printer_t *printer = get_opened_printer(hprn);
705     if(!printer) return NULL;
706     return printer->name;
707 }
708
709 /******************************************************************
710  *  WINSPOOL_GetOpenedPrinterRegKey
711  *
712  */
713 static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey)
714 {
715     LPCWSTR name = get_opened_printer_name(hPrinter);
716     DWORD ret;
717     HKEY hkeyPrinters;
718
719     if(!name) return ERROR_INVALID_HANDLE;
720
721     if((ret = RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters)) !=
722        ERROR_SUCCESS)
723         return ret;
724
725     if(RegOpenKeyW(hkeyPrinters, name, phkey) != ERROR_SUCCESS)
726     {
727         ERR("Can't find opened printer %s in registry\n",
728             debugstr_w(name));
729         RegCloseKey(hkeyPrinters);
730         return ERROR_INVALID_PRINTER_NAME; /* ? */
731     }
732     RegCloseKey(hkeyPrinters);
733     return ERROR_SUCCESS;
734 }
735
736 /******************************************************************
737  *                  get_job
738  *
739  *  Get the pointer to the specified job.
740  *  Should hold the printer_handles_cs before calling.
741  */
742 static job_t *get_job(HANDLE hprn, DWORD JobId)
743 {
744     opened_printer_t *printer = get_opened_printer(hprn);
745     job_t *job;
746
747     if(!printer) return NULL;
748     LIST_FOR_EACH_ENTRY(job, &printer->queue->jobs, job_t, entry)
749     {
750         if(job->job_id == JobId)
751             return job;
752     }
753     return NULL;
754 }
755
756 /***********************************************************
757  *      DEVMODEcpyAtoW
758  */
759 static LPDEVMODEW DEVMODEcpyAtoW(DEVMODEW *dmW, const DEVMODEA *dmA)
760 {
761     BOOL Formname;
762     ptrdiff_t off_formname = (const char *)dmA->dmFormName - (const char *)dmA;
763     DWORD size;
764
765     Formname = (dmA->dmSize > off_formname);
766     size = dmA->dmSize + CCHDEVICENAME + (Formname ? CCHFORMNAME : 0);
767     MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmDeviceName, -1,
768                         dmW->dmDeviceName, CCHDEVICENAME);
769     if(!Formname) {
770       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
771              dmA->dmSize - CCHDEVICENAME);
772     } else {
773       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
774              off_formname - CCHDEVICENAME);
775       MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmFormName, -1,
776                           dmW->dmFormName, CCHFORMNAME);
777       memcpy(&dmW->dmLogPixels, &dmA->dmLogPixels, dmA->dmSize -
778              (off_formname + CCHFORMNAME));
779     }
780     dmW->dmSize = size;
781     memcpy((char *)dmW + dmW->dmSize, (const char *)dmA + dmA->dmSize,
782            dmA->dmDriverExtra);
783     return dmW;
784 }
785
786 /***********************************************************
787  *      DEVMODEdupWtoA
788  * Creates an ascii copy of supplied devmode on heap
789  */
790 static LPDEVMODEA DEVMODEdupWtoA(HANDLE heap, const DEVMODEW *dmW)
791 {
792     LPDEVMODEA dmA;
793     DWORD size;
794     BOOL Formname;
795     ptrdiff_t off_formname = (const char *)dmW->dmFormName - (const char *)dmW;
796
797     if(!dmW) return NULL;
798     Formname = (dmW->dmSize > off_formname);
799     size = dmW->dmSize - CCHDEVICENAME - (Formname ? CCHFORMNAME : 0);
800     dmA = HeapAlloc(heap, HEAP_ZERO_MEMORY, size + dmW->dmDriverExtra);
801     WideCharToMultiByte(CP_ACP, 0, dmW->dmDeviceName, -1,
802                         (LPSTR)dmA->dmDeviceName, CCHDEVICENAME, NULL, NULL);
803     if(!Formname) {
804       memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
805              dmW->dmSize - CCHDEVICENAME * sizeof(WCHAR));
806     } else {
807       memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
808              off_formname - CCHDEVICENAME * sizeof(WCHAR));
809       WideCharToMultiByte(CP_ACP, 0, dmW->dmFormName, -1,
810                           (LPSTR)dmA->dmFormName, CCHFORMNAME, NULL, NULL);
811       memcpy(&dmA->dmLogPixels, &dmW->dmLogPixels, dmW->dmSize -
812              (off_formname + CCHFORMNAME * sizeof(WCHAR)));
813     }
814     dmA->dmSize = size;
815     memcpy((char *)dmA + dmA->dmSize, (const char *)dmW + dmW->dmSize,
816            dmW->dmDriverExtra);
817     return dmA;
818 }
819
820 /***********************************************************
821  *             PRINTER_INFO_2AtoW
822  * Creates a unicode copy of PRINTER_INFO_2A on heap
823  */
824 static LPPRINTER_INFO_2W PRINTER_INFO_2AtoW(HANDLE heap, LPPRINTER_INFO_2A piA)
825 {
826     LPPRINTER_INFO_2W piW;
827     UNICODE_STRING usBuffer;
828
829     if(!piA) return NULL;
830     piW = HeapAlloc(heap, 0, sizeof(*piW));
831     memcpy(piW, piA, sizeof(*piW)); /* copy everything first */
832     
833     piW->pServerName = asciitounicode(&usBuffer,piA->pServerName);
834     piW->pPrinterName = asciitounicode(&usBuffer,piA->pPrinterName);
835     piW->pShareName = asciitounicode(&usBuffer,piA->pShareName);
836     piW->pPortName = asciitounicode(&usBuffer,piA->pPortName);
837     piW->pDriverName = asciitounicode(&usBuffer,piA->pDriverName);
838     piW->pComment = asciitounicode(&usBuffer,piA->pComment);
839     piW->pLocation = asciitounicode(&usBuffer,piA->pLocation);
840     piW->pDevMode = piA->pDevMode ? GdiConvertToDevmodeW(piA->pDevMode) : NULL;
841     piW->pSepFile = asciitounicode(&usBuffer,piA->pSepFile);
842     piW->pPrintProcessor = asciitounicode(&usBuffer,piA->pPrintProcessor);
843     piW->pDatatype = asciitounicode(&usBuffer,piA->pDatatype);
844     piW->pParameters = asciitounicode(&usBuffer,piA->pParameters);
845     return piW;
846 }
847
848 /***********************************************************
849  *       FREE_PRINTER_INFO_2W
850  * Free PRINTER_INFO_2W and all strings
851  */
852 static void FREE_PRINTER_INFO_2W(HANDLE heap, LPPRINTER_INFO_2W piW)
853 {
854     if(!piW) return;
855
856     HeapFree(heap,0,piW->pServerName);
857     HeapFree(heap,0,piW->pPrinterName);
858     HeapFree(heap,0,piW->pShareName);
859     HeapFree(heap,0,piW->pPortName);
860     HeapFree(heap,0,piW->pDriverName);
861     HeapFree(heap,0,piW->pComment);
862     HeapFree(heap,0,piW->pLocation);
863     HeapFree(heap,0,piW->pDevMode);
864     HeapFree(heap,0,piW->pSepFile);
865     HeapFree(heap,0,piW->pPrintProcessor);
866     HeapFree(heap,0,piW->pDatatype);
867     HeapFree(heap,0,piW->pParameters);
868     HeapFree(heap,0,piW);
869     return;
870 }
871
872 /******************************************************************
873  *              DeviceCapabilities     [WINSPOOL.@]
874  *              DeviceCapabilitiesA    [WINSPOOL.@]
875  *
876  */
877 INT WINAPI DeviceCapabilitiesA(LPCSTR pDevice,LPCSTR pPort, WORD cap,
878                                LPSTR pOutput, LPDEVMODEA lpdm)
879 {
880     INT ret;
881
882     if (!GDI_CallDeviceCapabilities16)
883     {
884         GDI_CallDeviceCapabilities16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
885                                                               (LPCSTR)104 );
886         if (!GDI_CallDeviceCapabilities16) return -1;
887     }
888     ret = GDI_CallDeviceCapabilities16(pDevice, pPort, cap, pOutput, lpdm);
889
890     /* If DC_PAPERSIZE map POINT16s to POINTs */
891     if(ret != -1 && cap == DC_PAPERSIZE && pOutput) {
892         POINT16 *tmp = HeapAlloc( GetProcessHeap(), 0, ret * sizeof(POINT16) );
893         POINT *pt = (POINT *)pOutput;
894         INT i;
895         memcpy(tmp, pOutput, ret * sizeof(POINT16));
896         for(i = 0; i < ret; i++, pt++)
897         {
898             pt->x = tmp[i].x;
899             pt->y = tmp[i].y;
900         }
901         HeapFree( GetProcessHeap(), 0, tmp );
902     }
903     return ret;
904 }
905
906
907 /*****************************************************************************
908  *          DeviceCapabilitiesW        [WINSPOOL.@]
909  *
910  * Call DeviceCapabilitiesA since we later call 16bit stuff anyway
911  *
912  */
913 INT WINAPI DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort,
914                                WORD fwCapability, LPWSTR pOutput,
915                                const DEVMODEW *pDevMode)
916 {
917     LPDEVMODEA dmA = DEVMODEdupWtoA(GetProcessHeap(), pDevMode);
918     LPSTR pDeviceA = HEAP_strdupWtoA(GetProcessHeap(),0,pDevice);
919     LPSTR pPortA = HEAP_strdupWtoA(GetProcessHeap(),0,pPort);
920     INT ret;
921
922     if(pOutput && (fwCapability == DC_BINNAMES ||
923                    fwCapability == DC_FILEDEPENDENCIES ||
924                    fwCapability == DC_PAPERNAMES)) {
925       /* These need A -> W translation */
926         INT size = 0, i;
927         LPSTR pOutputA;
928         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, NULL,
929                                   dmA);
930         if(ret == -1)
931             return ret;
932         switch(fwCapability) {
933         case DC_BINNAMES:
934             size = 24;
935             break;
936         case DC_PAPERNAMES:
937         case DC_FILEDEPENDENCIES:
938             size = 64;
939             break;
940         }
941         pOutputA = HeapAlloc(GetProcessHeap(), 0, size * ret);
942         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, pOutputA,
943                                   dmA);
944         for(i = 0; i < ret; i++)
945             MultiByteToWideChar(CP_ACP, 0, pOutputA + (i * size), -1,
946                                 pOutput + (i * size), size);
947         HeapFree(GetProcessHeap(), 0, pOutputA);
948     } else {
949         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability,
950                                   (LPSTR)pOutput, dmA);
951     }
952     HeapFree(GetProcessHeap(),0,pPortA);
953     HeapFree(GetProcessHeap(),0,pDeviceA);
954     HeapFree(GetProcessHeap(),0,dmA);
955     return ret;
956 }
957
958 /******************************************************************
959  *              DocumentPropertiesA   [WINSPOOL.@]
960  *
961  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
962  */
963 LONG WINAPI DocumentPropertiesA(HWND hWnd,HANDLE hPrinter,
964                                 LPSTR pDeviceName, LPDEVMODEA pDevModeOutput,
965                                 LPDEVMODEA pDevModeInput,DWORD fMode )
966 {
967     LPSTR lpName = pDeviceName;
968     LONG ret;
969
970     TRACE("(%p,%p,%s,%p,%p,%ld)\n",
971         hWnd,hPrinter,pDeviceName,pDevModeOutput,pDevModeInput,fMode
972     );
973
974     if(!pDeviceName) {
975         LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
976         if(!lpNameW) {
977                 ERR("no name from hPrinter?\n");
978                 SetLastError(ERROR_INVALID_HANDLE);
979                 return -1;
980         }
981         lpName = HEAP_strdupWtoA(GetProcessHeap(),0,lpNameW);
982     }
983
984     if (!GDI_CallExtDeviceMode16)
985     {
986         GDI_CallExtDeviceMode16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
987                                                          (LPCSTR)102 );
988         if (!GDI_CallExtDeviceMode16) {
989                 ERR("No CallExtDeviceMode16?\n");
990                 return -1;
991         }
992     }
993     ret = GDI_CallExtDeviceMode16(hWnd, pDevModeOutput, lpName, "LPT1:",
994                                   pDevModeInput, NULL, fMode);
995
996     if(!pDeviceName)
997         HeapFree(GetProcessHeap(),0,lpName);
998     return ret;
999 }
1000
1001
1002 /*****************************************************************************
1003  *          DocumentPropertiesW (WINSPOOL.@)
1004  *
1005  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1006  */
1007 LONG WINAPI DocumentPropertiesW(HWND hWnd, HANDLE hPrinter,
1008                                 LPWSTR pDeviceName,
1009                                 LPDEVMODEW pDevModeOutput,
1010                                 LPDEVMODEW pDevModeInput, DWORD fMode)
1011 {
1012
1013     LPSTR pDeviceNameA = HEAP_strdupWtoA(GetProcessHeap(),0,pDeviceName);
1014     LPDEVMODEA pDevModeInputA = DEVMODEdupWtoA(GetProcessHeap(),pDevModeInput);
1015     LPDEVMODEA pDevModeOutputA = NULL;
1016     LONG ret;
1017
1018     TRACE("(%p,%p,%s,%p,%p,%ld)\n",
1019           hWnd,hPrinter,debugstr_w(pDeviceName),pDevModeOutput,pDevModeInput,
1020           fMode);
1021     if(pDevModeOutput) {
1022         ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, NULL, NULL, 0);
1023         if(ret < 0) return ret;
1024         pDevModeOutputA = HeapAlloc(GetProcessHeap(), 0, ret);
1025     }
1026     ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, pDevModeOutputA,
1027                               pDevModeInputA, fMode);
1028     if(pDevModeOutput) {
1029         DEVMODEcpyAtoW(pDevModeOutput, pDevModeOutputA);
1030         HeapFree(GetProcessHeap(),0,pDevModeOutputA);
1031     }
1032     if(fMode == 0 && ret > 0)
1033         ret += (CCHDEVICENAME + CCHFORMNAME);
1034     HeapFree(GetProcessHeap(),0,pDevModeInputA);
1035     HeapFree(GetProcessHeap(),0,pDeviceNameA);
1036     return ret;
1037 }
1038
1039 /******************************************************************
1040  *              OpenPrinterA        [WINSPOOL.@]
1041  *
1042  */
1043 BOOL WINAPI OpenPrinterA(LPSTR lpPrinterName,HANDLE *phPrinter,
1044                          LPPRINTER_DEFAULTSA pDefault)
1045 {
1046     UNICODE_STRING lpPrinterNameW;
1047     UNICODE_STRING usBuffer;
1048     PRINTER_DEFAULTSW DefaultW, *pDefaultW = NULL;
1049     PWSTR pwstrPrinterNameW;
1050     BOOL ret;
1051
1052     pwstrPrinterNameW = asciitounicode(&lpPrinterNameW,lpPrinterName);
1053
1054     if(pDefault) {
1055         DefaultW.pDatatype = asciitounicode(&usBuffer,pDefault->pDatatype);
1056         DefaultW.pDevMode = pDefault->pDevMode ? GdiConvertToDevmodeW(pDefault->pDevMode) : NULL;
1057         DefaultW.DesiredAccess = pDefault->DesiredAccess;
1058         pDefaultW = &DefaultW;
1059     }
1060     ret = OpenPrinterW(pwstrPrinterNameW, phPrinter, pDefaultW);
1061     if(pDefault) {
1062         RtlFreeUnicodeString(&usBuffer);
1063         HeapFree(GetProcessHeap(), 0, DefaultW.pDevMode);
1064     }
1065     RtlFreeUnicodeString(&lpPrinterNameW);
1066     return ret;
1067 }
1068
1069 /******************************************************************
1070  *              OpenPrinterW        [WINSPOOL.@]
1071  *
1072  */
1073 BOOL WINAPI OpenPrinterW(LPWSTR lpPrinterName,HANDLE *phPrinter,
1074                          LPPRINTER_DEFAULTSW pDefault)
1075 {
1076     HKEY hkeyPrinters, hkeyPrinter;
1077
1078     if (!lpPrinterName) {
1079        FIXME("(printerName: NULL, pDefault %p Ret: False\n", pDefault);
1080        SetLastError(ERROR_INVALID_PARAMETER);
1081        return FALSE;
1082     }
1083
1084     TRACE("(printerName: %s, pDefault %p)\n", debugstr_w(lpPrinterName),
1085           pDefault);
1086
1087     /* Check Printer exists */
1088     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
1089        ERROR_SUCCESS) {
1090         ERR("Can't create Printers key\n");
1091         SetLastError(ERROR_FILE_NOT_FOUND); /* ?? */
1092         return FALSE;
1093     }
1094
1095     if(lpPrinterName[0] == '\0' || /* explicitly exclude "" */
1096        RegOpenKeyW(hkeyPrinters, lpPrinterName, &hkeyPrinter)
1097        != ERROR_SUCCESS) {
1098         TRACE("Can't find printer %s in registry\n",
1099               debugstr_w(lpPrinterName));
1100         RegCloseKey(hkeyPrinters);
1101         SetLastError(ERROR_INVALID_PRINTER_NAME);
1102         return FALSE;
1103     }
1104     RegCloseKey(hkeyPrinter);
1105     RegCloseKey(hkeyPrinters);
1106
1107     if(!phPrinter) /* This seems to be what win95 does anyway */
1108         return TRUE;
1109
1110     /* Get the unique handle of the printer*/
1111     *phPrinter = get_opened_printer_entry( lpPrinterName );
1112
1113     if (pDefault != NULL)
1114         FIXME("Not handling pDefault\n");
1115
1116     return TRUE;
1117 }
1118
1119 /******************************************************************
1120  *              AddMonitorA        [WINSPOOL.@]
1121  *
1122  */
1123 BOOL WINAPI AddMonitorA(LPSTR pName, DWORD Level, LPBYTE pMonitors)
1124 {
1125     FIXME("(%s,0x%08lx,%p), stub!\n", debugstr_a(pName), Level, pMonitors);
1126     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1127     return FALSE;
1128 }
1129
1130 /******************************************************************************
1131  *              AddMonitorW        [WINSPOOL.@]
1132  */
1133 BOOL WINAPI AddMonitorW(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
1134 {
1135     FIXME("(%s,0x%08lx,%p), stub!\n",debugstr_w(pName), Level, pMonitors);
1136     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1137     return FALSE;
1138 }
1139
1140 /******************************************************************
1141  *              DeletePrinterDriverA        [WINSPOOL.@]
1142  *
1143  */
1144 BOOL WINAPI
1145 DeletePrinterDriverA (LPSTR pName, LPSTR pEnvironment, LPSTR pDriverName)
1146 {
1147     FIXME("(%s,%s,%s):stub\n",debugstr_a(pName),debugstr_a(pEnvironment),
1148           debugstr_a(pDriverName));
1149     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1150     return FALSE;
1151 }
1152
1153 /******************************************************************
1154  *              DeletePrinterDriverW        [WINSPOOL.@]
1155  *
1156  */
1157 BOOL WINAPI
1158 DeletePrinterDriverW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pDriverName)
1159 {
1160     FIXME("(%s,%s,%s):stub\n",debugstr_w(pName),debugstr_w(pEnvironment),
1161           debugstr_w(pDriverName));
1162     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1163     return FALSE;
1164 }
1165
1166 /******************************************************************
1167  *              DeleteMonitorA        [WINSPOOL.@]
1168  *
1169  */
1170 BOOL WINAPI
1171 DeleteMonitorA (LPSTR pName, LPSTR pEnvironment, LPSTR pMonitorName)
1172 {
1173     FIXME("(%s,%s,%s):stub\n",debugstr_a(pName),debugstr_a(pEnvironment),
1174           debugstr_a(pMonitorName));
1175     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1176     return FALSE;
1177 }
1178
1179 /******************************************************************
1180  *              DeleteMonitorW        [WINSPOOL.@]
1181  *
1182  */
1183 BOOL WINAPI
1184 DeleteMonitorW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
1185 {
1186     FIXME("(%s,%s,%s):stub\n",debugstr_w(pName),debugstr_w(pEnvironment),
1187           debugstr_w(pMonitorName));
1188     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1189     return FALSE;
1190 }
1191
1192 /******************************************************************
1193  *              DeletePortA        [WINSPOOL.@]
1194  *
1195  */
1196 BOOL WINAPI
1197 DeletePortA (LPSTR pName, HWND hWnd, LPSTR pPortName)
1198 {
1199     FIXME("(%s,%p,%s):stub\n",debugstr_a(pName),hWnd,
1200           debugstr_a(pPortName));
1201     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1202     return FALSE;
1203 }
1204
1205 /******************************************************************
1206  *              DeletePortW        [WINSPOOL.@]
1207  *
1208  */
1209 BOOL WINAPI
1210 DeletePortW (LPWSTR pName, HWND hWnd, LPWSTR pPortName)
1211 {
1212     FIXME("(%s,%p,%s):stub\n",debugstr_w(pName),hWnd,
1213           debugstr_w(pPortName));
1214     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1215     return FALSE;
1216 }
1217
1218 /******************************************************************************
1219  *    SetPrinterW  [WINSPOOL.@]
1220  */
1221 BOOL WINAPI
1222 SetPrinterW(
1223   HANDLE  hPrinter,
1224   DWORD     Level,
1225   LPBYTE    pPrinter,
1226   DWORD     Command) {
1227
1228     FIXME("():stub\n");
1229     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1230     return FALSE;
1231 }
1232
1233 /******************************************************************************
1234  *    WritePrinter  [WINSPOOL.@]
1235  */
1236 BOOL WINAPI WritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
1237 {
1238     opened_printer_t *printer;
1239     BOOL ret = FALSE;
1240
1241     TRACE("(%p, %p, %ld, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
1242
1243     EnterCriticalSection(&printer_handles_cs);
1244     printer = get_opened_printer(hPrinter);
1245     if(!printer)
1246     {
1247         SetLastError(ERROR_INVALID_HANDLE);
1248         goto end;
1249     }
1250
1251     if(!printer->doc)
1252     {
1253         SetLastError(ERROR_SPL_NO_STARTDOC);
1254         goto end;
1255     }
1256
1257     ret = WriteFile(printer->doc->hf, pBuf, cbBuf, pcWritten, NULL);
1258 end:
1259     LeaveCriticalSection(&printer_handles_cs);
1260     return ret;
1261 }
1262
1263 /*****************************************************************************
1264  *          AddFormA  [WINSPOOL.@]
1265  */
1266 BOOL WINAPI AddFormA(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
1267 {
1268     FIXME("(%p,%ld,%p): stub\n", hPrinter, Level, pForm);
1269     return 1;
1270 }
1271
1272 /*****************************************************************************
1273  *          AddFormW  [WINSPOOL.@]
1274  */
1275 BOOL WINAPI AddFormW(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
1276 {
1277     FIXME("(%p,%ld,%p): stub\n", hPrinter, Level, pForm);
1278     return 1;
1279 }
1280
1281 /*****************************************************************************
1282  *          AddJobA  [WINSPOOL.@]
1283  */
1284 BOOL WINAPI AddJobA(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
1285 {
1286     BOOL ret;
1287     BYTE buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
1288     DWORD needed;
1289
1290     if(Level != 1) {
1291         SetLastError(ERROR_INVALID_LEVEL);
1292         return FALSE;
1293     }
1294
1295     ret = AddJobW(hPrinter, Level, buf, sizeof(buf), &needed);
1296
1297     if(ret) {
1298         ADDJOB_INFO_1W *addjobW = (ADDJOB_INFO_1W*)buf;
1299         DWORD len = WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, NULL, 0, NULL, NULL);
1300         *pcbNeeded = len + sizeof(ADDJOB_INFO_1A);
1301         if(*pcbNeeded > cbBuf) {
1302             SetLastError(ERROR_INSUFFICIENT_BUFFER);
1303             ret = FALSE;
1304         } else {
1305             ADDJOB_INFO_1A *addjobA = (ADDJOB_INFO_1A*)pData;
1306             addjobA->JobId = addjobW->JobId;
1307             addjobA->Path = (char *)(addjobA + 1);
1308             WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, addjobA->Path, len, NULL, NULL);
1309         }
1310     }
1311     return ret;
1312 }
1313
1314 /*****************************************************************************
1315  *          AddJobW  [WINSPOOL.@]
1316  */
1317 BOOL WINAPI AddJobW(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
1318 {
1319     opened_printer_t *printer;
1320     job_t *job;
1321     BOOL ret = FALSE;
1322     static const WCHAR spool_path[] = {'s','p','o','o','l','\\','P','R','I','N','T','E','R','S','\\',0};
1323     static const WCHAR fmtW[] = {'%','s','%','0','5','d','.','S','P','L',0};
1324     WCHAR path[MAX_PATH], filename[MAX_PATH];
1325     DWORD len;
1326     ADDJOB_INFO_1W *addjob;
1327
1328     TRACE("(%p,%ld,%p,%ld,%p)\n", hPrinter, Level, pData, cbBuf, pcbNeeded);
1329     
1330     EnterCriticalSection(&printer_handles_cs);
1331
1332     printer = get_opened_printer(hPrinter);
1333
1334     if(!printer) {
1335         SetLastError(ERROR_INVALID_HANDLE);
1336         goto end;
1337     }
1338
1339     if(Level != 1) {
1340         SetLastError(ERROR_INVALID_LEVEL);
1341         goto end;
1342     }
1343
1344     job = HeapAlloc(GetProcessHeap(), 0, sizeof(*job));
1345     if(!job)
1346         goto end;
1347
1348     job->job_id = InterlockedIncrement(&next_job_id);
1349
1350     len = GetSystemDirectoryW(path, sizeof(path) / sizeof(WCHAR));
1351     if(path[len - 1] != '\\')
1352         path[len++] = '\\';
1353     memcpy(path + len, spool_path, sizeof(spool_path));    
1354     sprintfW(filename, fmtW, path, job->job_id);
1355
1356     len = strlenW(filename);
1357     job->filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1358     memcpy(job->filename, filename, (len + 1) * sizeof(WCHAR));
1359     job->document_title = strdupW(default_doc_title);
1360     list_add_tail(&printer->queue->jobs, &job->entry);
1361
1362     *pcbNeeded = (len + 1) * sizeof(WCHAR) + sizeof(*addjob);
1363     if(*pcbNeeded <= cbBuf) {
1364         addjob = (ADDJOB_INFO_1W*)pData;
1365         addjob->JobId = job->job_id;
1366         addjob->Path = (WCHAR *)(addjob + 1);
1367         memcpy(addjob->Path, filename, (len + 1) * sizeof(WCHAR));
1368         ret = TRUE;
1369     } else 
1370         SetLastError(ERROR_INSUFFICIENT_BUFFER);
1371
1372 end:
1373     LeaveCriticalSection(&printer_handles_cs);
1374     return ret;
1375 }
1376
1377 /*****************************************************************************
1378  *          GetPrintProcessorDirectoryA  [WINSPOOL.@]
1379  */
1380 BOOL WINAPI GetPrintProcessorDirectoryA(LPSTR server, LPSTR env,
1381                                         DWORD level,  LPBYTE Info,
1382                                         DWORD cbBuf, LPDWORD needed)
1383 {
1384     FIXME("(%s,%s,%ld,%p,0x%08lx): stub\n", debugstr_a(server), debugstr_a(env),
1385           level, Info, cbBuf);
1386     return 0;
1387 }
1388
1389 /*****************************************************************************
1390  *          GetPrintProcessorDirectoryW  [WINSPOOL.@]
1391  */
1392 BOOL WINAPI GetPrintProcessorDirectoryW(LPWSTR server, LPWSTR env,
1393                                         DWORD level,  LPBYTE Info,
1394                                         DWORD cbBuf, LPDWORD needed)
1395 {
1396     FIXME("(%s,%s,%ld,%p,0x%08lx): stub\n", debugstr_w(server), debugstr_w(env),
1397           level, Info, cbBuf);
1398     return 0;
1399 }
1400
1401 /*****************************************************************************
1402  *          WINSPOOL_OpenDriverReg [internal]
1403  *
1404  * opens the registry for the printer drivers depending on the given input
1405  * variable pEnvironment
1406  *
1407  * RETURNS:
1408  *    the opened hkey on success
1409  *    NULL on error
1410  */
1411 static HKEY WINSPOOL_OpenDriverReg( LPVOID pEnvironment, BOOL unicode)
1412 {   
1413     static const WCHAR WinNTW[] = { 'W','i','n','d','o','w','s',' ','N','T',' ','x','8','6',0 };
1414     static const WCHAR Win40W[] = { 'W','i','n','d','o','w','s',' ','4','.','0',0 };
1415     HKEY  retval;
1416     LPWSTR lpKey, buffer = NULL;
1417     LPCWSTR pEnvW;
1418
1419     TRACE("%s\n",
1420           (unicode) ? debugstr_w(pEnvironment) : debugstr_a(pEnvironment));
1421
1422     if(pEnvironment) {
1423         if (unicode) {
1424             pEnvW = pEnvironment;
1425         } else {
1426             INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, NULL, 0);
1427             buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1428             if (buffer) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, buffer, len);
1429             pEnvW = buffer;
1430         }
1431     } else {
1432         OSVERSIONINFOW ver;
1433         ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
1434
1435         if(!GetVersionExW( &ver))
1436             return 0;
1437
1438         switch (ver.dwPlatformId) {
1439              case VER_PLATFORM_WIN32s:
1440                   ERR("win32 style printing used with 16 bits app, try specifying 'win95' Windows version\n");
1441                   return 0;
1442              case VER_PLATFORM_WIN32_NT:
1443                   pEnvW = WinNTW;
1444                   break;
1445              default:
1446                   pEnvW = Win40W;
1447                   break;
1448         }
1449         TRACE("set environment to %s\n", debugstr_w(pEnvW));
1450     }
1451
1452     lpKey = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
1453                        (strlenW(pEnvW) + strlenW(DriversW) + 1) * sizeof(WCHAR));
1454     wsprintfW( lpKey, DriversW, pEnvW);
1455
1456     TRACE("%s\n", debugstr_w(lpKey));
1457
1458     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, lpKey, &retval) != ERROR_SUCCESS)
1459        retval = 0;
1460
1461     HeapFree( GetProcessHeap(), 0, buffer);
1462     HeapFree( GetProcessHeap(), 0, lpKey);
1463
1464     return retval;
1465 }
1466
1467 /*****************************************************************************
1468  *          AddPrinterW  [WINSPOOL.@]
1469  */
1470 HANDLE WINAPI AddPrinterW(LPWSTR pName, DWORD Level, LPBYTE pPrinter)
1471 {
1472     PRINTER_INFO_2W *pi = (PRINTER_INFO_2W *) pPrinter;
1473     LPDEVMODEA dmA;
1474     LPDEVMODEW dmW;
1475     HANDLE retval;
1476     HKEY hkeyPrinter, hkeyPrinters, hkeyDriver, hkeyDrivers;
1477     LONG size;
1478
1479     TRACE("(%s,%ld,%p)\n", debugstr_w(pName), Level, pPrinter);
1480
1481     if(pName != NULL) {
1482         ERR("pName = %s - unsupported\n", debugstr_w(pName));
1483         SetLastError(ERROR_INVALID_PARAMETER);
1484         return 0;
1485     }
1486     if(Level != 2) {
1487         ERR("Level = %ld, unsupported!\n", Level);
1488         SetLastError(ERROR_INVALID_LEVEL);
1489         return 0;
1490     }
1491     if (strlenW(pi->pPrinterName) >= CCHDEVICENAME) {
1492         ERR("Printername %s must not exceed length of DEVMODE.dmDeviceName !\n",
1493                 debugstr_w(pi->pPrinterName)
1494         );
1495         SetLastError(ERROR_INVALID_LEVEL);
1496         return 0;
1497     }
1498     if(!pPrinter) {
1499         SetLastError(ERROR_INVALID_PARAMETER);
1500         return 0;
1501     }
1502     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
1503        ERROR_SUCCESS) {
1504         ERR("Can't create Printers key\n");
1505         return 0;
1506     }
1507     if(!RegOpenKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter)) {
1508         if (!RegQueryValueA(hkeyPrinter,"Attributes",NULL,NULL)) {
1509             SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
1510             RegCloseKey(hkeyPrinter);
1511             RegCloseKey(hkeyPrinters);
1512             return 0;
1513         }
1514         RegCloseKey(hkeyPrinter);
1515     }
1516     hkeyDrivers = WINSPOOL_OpenDriverReg( NULL, TRUE);
1517     if(!hkeyDrivers) {
1518         ERR("Can't create Drivers key\n");
1519         RegCloseKey(hkeyPrinters);
1520         return 0;
1521     }
1522     if(RegOpenKeyW(hkeyDrivers, pi->pDriverName, &hkeyDriver) !=
1523        ERROR_SUCCESS) {
1524         WARN("Can't find driver %s\n", debugstr_w(pi->pDriverName));
1525         RegCloseKey(hkeyPrinters);
1526         RegCloseKey(hkeyDrivers);
1527         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER);
1528         return 0;
1529     }
1530     RegCloseKey(hkeyDriver);
1531     RegCloseKey(hkeyDrivers);
1532
1533     if(lstrcmpiW(pi->pPrintProcessor, WinPrintW)) {  /* FIXME */
1534         FIXME("Can't find processor %s\n", debugstr_w(pi->pPrintProcessor));
1535         SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR);
1536         RegCloseKey(hkeyPrinters);
1537         return 0;
1538     }
1539
1540     if(RegCreateKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter) !=
1541        ERROR_SUCCESS) {
1542         FIXME("Can't create printer %s\n", debugstr_w(pi->pPrinterName));
1543         SetLastError(ERROR_INVALID_PRINTER_NAME);
1544         RegCloseKey(hkeyPrinters);
1545         return 0;
1546     }
1547     RegSetValueExA(hkeyPrinter, "Attributes", 0, REG_DWORD,
1548                    (LPBYTE)&pi->Attributes, sizeof(DWORD));
1549     set_reg_szW(hkeyPrinter, DatatypeW, pi->pDatatype);
1550
1551     /* See if we can load the driver.  We may need the devmode structure anyway
1552      *
1553      * FIXME:
1554      * Note that DocumentPropertiesW will briefly try to open the printer we
1555      * just create to find a DEVMODEA struct (it will use the WINEPS default
1556      * one in case it is not there, so we are ok).
1557      */
1558     size = DocumentPropertiesW(0, 0, pi->pPrinterName, NULL, NULL, 0);
1559
1560     if(size < 0) {
1561         FIXME("DocumentPropertiesW on printer '%s' fails\n", debugstr_w(pi->pPrinterName));
1562         size = sizeof(DEVMODEW);
1563     }
1564     if(pi->pDevMode)
1565         dmW = pi->pDevMode;
1566     else 
1567     {
1568             dmW = HeapAlloc(GetProcessHeap(), 0, size);
1569         ZeroMemory(dmW,size);
1570             dmW->dmSize = size;
1571             if (0>DocumentPropertiesW(0,0,pi->pPrinterName,dmW,NULL,DM_OUT_BUFFER)) 
1572         {
1573                 WARN("DocumentPropertiesW on printer '%s' failed!\n", debugstr_w(pi->pPrinterName));
1574             HeapFree(GetProcessHeap(),0,dmW);
1575             dmW=NULL;
1576             }
1577         else
1578         {
1579                 /* set devmode to printer name */
1580                 strcpyW(dmW->dmDeviceName,pi->pPrinterName);
1581         }
1582     }
1583
1584     /* Write DEVMODEA not DEVMODEW into reg.  This is what win9x does
1585        and we support these drivers.  NT writes DEVMODEW so somehow
1586        we'll need to distinguish between these when we support NT
1587        drivers */
1588     if (dmW)
1589     {
1590         dmA = DEVMODEdupWtoA(GetProcessHeap(), dmW);
1591         RegSetValueExA(hkeyPrinter, "Default DevMode", 0, REG_BINARY, 
1592                        (LPBYTE)dmA, dmA->dmSize + dmA->dmDriverExtra);
1593         HeapFree(GetProcessHeap(), 0, dmA);
1594         if(!pi->pDevMode)
1595             HeapFree(GetProcessHeap(), 0, dmW);
1596     }
1597     set_reg_szW(hkeyPrinter, DescriptionW, pi->pComment);
1598     set_reg_szW(hkeyPrinter, LocationW, pi->pLocation);
1599     set_reg_szW(hkeyPrinter, NameW, pi->pPrinterName);
1600     set_reg_szW(hkeyPrinter, ParametersW, pi->pParameters);
1601
1602     set_reg_szW(hkeyPrinter, PortW, pi->pPortName);
1603     set_reg_szW(hkeyPrinter, Print_ProcessorW, pi->pPrintProcessor);
1604     set_reg_szW(hkeyPrinter, Printer_DriverW, pi->pDriverName);
1605     RegSetValueExA(hkeyPrinter, "Priority", 0, REG_DWORD,
1606                    (LPBYTE)&pi->Priority, sizeof(DWORD));
1607     set_reg_szW(hkeyPrinter, Separator_FileW, pi->pSepFile);
1608     set_reg_szW(hkeyPrinter, Share_NameW, pi->pShareName);
1609     RegSetValueExA(hkeyPrinter, "StartTime", 0, REG_DWORD,
1610                    (LPBYTE)&pi->StartTime, sizeof(DWORD));
1611     RegSetValueExA(hkeyPrinter, "Status", 0, REG_DWORD,
1612                    (LPBYTE)&pi->Status, sizeof(DWORD));
1613     RegSetValueExA(hkeyPrinter, "UntilTime", 0, REG_DWORD,
1614                    (LPBYTE)&pi->UntilTime, sizeof(DWORD));
1615
1616     RegCloseKey(hkeyPrinter);
1617     RegCloseKey(hkeyPrinters);
1618     if(!OpenPrinterW(pi->pPrinterName, &retval, NULL)) {
1619         ERR("OpenPrinter failing\n");
1620         return 0;
1621     }
1622     return retval;
1623 }
1624
1625 /*****************************************************************************
1626  *          AddPrinterA  [WINSPOOL.@]
1627  */
1628 HANDLE WINAPI AddPrinterA(LPSTR pName, DWORD Level, LPBYTE pPrinter)
1629 {
1630     UNICODE_STRING pNameW;
1631     PWSTR pwstrNameW;
1632     PRINTER_INFO_2W *piW;
1633     PRINTER_INFO_2A *piA = (PRINTER_INFO_2A*)pPrinter;
1634     HANDLE ret;
1635
1636     TRACE("(%s,%ld,%p): stub\n", debugstr_a(pName), Level, pPrinter);
1637     if(Level != 2) {
1638         ERR("Level = %ld, unsupported!\n", Level);
1639         SetLastError(ERROR_INVALID_LEVEL);
1640         return 0;
1641     }
1642     pwstrNameW = asciitounicode(&pNameW,pName);
1643     piW = PRINTER_INFO_2AtoW(GetProcessHeap(), piA);
1644
1645     ret = AddPrinterW(pwstrNameW, Level, (LPBYTE)piW);
1646
1647     FREE_PRINTER_INFO_2W(GetProcessHeap(), piW);
1648     RtlFreeUnicodeString(&pNameW);
1649     return ret;
1650 }
1651
1652
1653 /*****************************************************************************
1654  *          ClosePrinter  [WINSPOOL.@]
1655  */
1656 BOOL WINAPI ClosePrinter(HANDLE hPrinter)
1657 {
1658     UINT_PTR i = (UINT_PTR)hPrinter;
1659     opened_printer_t *printer = NULL;
1660     BOOL ret = FALSE;
1661
1662     TRACE("Handle %p\n", hPrinter);
1663
1664     EnterCriticalSection(&printer_handles_cs);
1665
1666     if ((i > 0) && (i <= nb_printer_handles))
1667         printer = printer_handles[i - 1];
1668
1669     if(printer)
1670     {
1671         struct list *cursor, *cursor2;
1672
1673         if(printer->doc)
1674             EndDocPrinter(hPrinter);
1675
1676         if(InterlockedDecrement(&printer->queue->ref) == 0)
1677         {
1678             LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
1679             {
1680                 job_t *job = LIST_ENTRY(cursor, job_t, entry);
1681                 ScheduleJob(hPrinter, job->job_id);
1682             }
1683             HeapFree(GetProcessHeap(), 0, printer->queue);
1684         }
1685         HeapFree(GetProcessHeap(), 0, printer->name);
1686         HeapFree(GetProcessHeap(), 0, printer);
1687         printer_handles[i - 1] = NULL;
1688         ret = TRUE;
1689     }
1690     LeaveCriticalSection(&printer_handles_cs);
1691     return ret;
1692 }
1693
1694 /*****************************************************************************
1695  *          DeleteFormA  [WINSPOOL.@]
1696  */
1697 BOOL WINAPI DeleteFormA(HANDLE hPrinter, LPSTR pFormName)
1698 {
1699     FIXME("(%p,%s): stub\n", hPrinter, pFormName);
1700     return 1;
1701 }
1702
1703 /*****************************************************************************
1704  *          DeleteFormW  [WINSPOOL.@]
1705  */
1706 BOOL WINAPI DeleteFormW(HANDLE hPrinter, LPWSTR pFormName)
1707 {
1708     FIXME("(%p,%s): stub\n", hPrinter, debugstr_w(pFormName));
1709     return 1;
1710 }
1711
1712 /*****************************************************************************
1713  *   WINSPOOL_SHRegDeleteKey
1714  *
1715  *   Recursively delete subkeys.
1716  *   Cut & paste from shlwapi.
1717  * 
1718  */
1719 static DWORD WINSPOOL_SHDeleteKeyW(HKEY hKey, LPCWSTR lpszSubKey)
1720 {
1721   DWORD dwRet, dwKeyCount = 0, dwMaxSubkeyLen = 0, dwSize, i;
1722   WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
1723   HKEY hSubKey = 0;
1724
1725   dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
1726   if(!dwRet)
1727   {
1728     /* Find how many subkeys there are */
1729     dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwKeyCount,
1730                              &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
1731     if(!dwRet)
1732     {
1733       dwMaxSubkeyLen++;
1734       if (dwMaxSubkeyLen > sizeof(szNameBuf)/sizeof(WCHAR))
1735         /* Name too big: alloc a buffer for it */
1736         lpszName = HeapAlloc(GetProcessHeap(), 0, dwMaxSubkeyLen*sizeof(WCHAR));
1737
1738       if(!lpszName)
1739         dwRet = ERROR_NOT_ENOUGH_MEMORY;
1740       else
1741       {
1742         /* Recursively delete all the subkeys */
1743         for(i = 0; i < dwKeyCount && !dwRet; i++)
1744         {
1745           dwSize = dwMaxSubkeyLen;
1746           dwRet = RegEnumKeyExW(hSubKey, i, lpszName, &dwSize, NULL, NULL, NULL, NULL);
1747           if(!dwRet)
1748             dwRet = WINSPOOL_SHDeleteKeyW(hSubKey, lpszName);
1749         }
1750
1751         if (lpszName != szNameBuf)
1752           HeapFree(GetProcessHeap(), 0, lpszName); /* Free buffer if allocated */
1753       }
1754     }
1755
1756     RegCloseKey(hSubKey);
1757     if(!dwRet)
1758       dwRet = RegDeleteKeyW(hKey, lpszSubKey);
1759   }
1760   return dwRet;
1761 }
1762
1763 /*****************************************************************************
1764  *          DeletePrinter  [WINSPOOL.@]
1765  */
1766 BOOL WINAPI DeletePrinter(HANDLE hPrinter)
1767 {
1768     LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
1769     HKEY hkeyPrinters, hkey;
1770
1771     if(!lpNameW) {
1772         SetLastError(ERROR_INVALID_HANDLE);
1773         return FALSE;
1774     }
1775     if(RegOpenKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) == ERROR_SUCCESS) {
1776         WINSPOOL_SHDeleteKeyW(hkeyPrinters, lpNameW);
1777         RegCloseKey(hkeyPrinters);
1778     }
1779     WriteProfileStringW(devicesW, lpNameW, NULL);
1780     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
1781         RegDeleteValueW(hkey, lpNameW);
1782         RegCloseKey(hkey);
1783     }
1784     return TRUE;
1785 }
1786
1787 /*****************************************************************************
1788  *          SetPrinterA  [WINSPOOL.@]
1789  */
1790 BOOL WINAPI SetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
1791                            DWORD Command)
1792 {
1793     FIXME("(%p,%ld,%p,%ld): stub\n",hPrinter,Level,pPrinter,Command);
1794     return FALSE;
1795 }
1796
1797 /*****************************************************************************
1798  *          SetJobA  [WINSPOOL.@]
1799  */
1800 BOOL WINAPI SetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level,
1801                     LPBYTE pJob, DWORD Command)
1802 {
1803     BOOL ret;
1804     LPBYTE JobW;
1805     UNICODE_STRING usBuffer;
1806
1807     TRACE("(%p, %ld, %ld, %p, %ld)\n",hPrinter, JobId, Level, pJob, Command);
1808
1809     /* JobId, pPrinterName, pMachineName, pDriverName, Size, Submitted, Time and TotalPages
1810        are all ignored by SetJob, so we don't bother copying them */
1811     switch(Level)
1812     {
1813     case 0:
1814         JobW = NULL;
1815         break;
1816     case 1:
1817       {
1818         JOB_INFO_1W *info1W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info1W));
1819         JOB_INFO_1A *info1A = (JOB_INFO_1A*)pJob;
1820
1821         JobW = (LPBYTE)info1W;
1822         info1W->pUserName = asciitounicode(&usBuffer, info1A->pUserName);
1823         info1W->pDocument = asciitounicode(&usBuffer, info1A->pDocument);
1824         info1W->pDatatype = asciitounicode(&usBuffer, info1A->pDatatype);
1825         info1W->pStatus = asciitounicode(&usBuffer, info1A->pStatus);
1826         info1W->Status = info1A->Status;
1827         info1W->Priority = info1A->Priority;
1828         info1W->Position = info1A->Position;
1829         info1W->PagesPrinted = info1A->PagesPrinted;
1830         break;
1831       }
1832     case 2:
1833       {
1834         JOB_INFO_2W *info2W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info2W));
1835         JOB_INFO_2A *info2A = (JOB_INFO_2A*)pJob;
1836
1837         JobW = (LPBYTE)info2W;
1838         info2W->pUserName = asciitounicode(&usBuffer, info2A->pUserName);
1839         info2W->pDocument = asciitounicode(&usBuffer, info2A->pDocument);
1840         info2W->pNotifyName = asciitounicode(&usBuffer, info2A->pNotifyName);
1841         info2W->pDatatype = asciitounicode(&usBuffer, info2A->pDatatype);
1842         info2W->pPrintProcessor = asciitounicode(&usBuffer, info2A->pPrintProcessor);
1843         info2W->pParameters = asciitounicode(&usBuffer, info2A->pParameters);
1844         info2W->pDevMode = info2A->pDevMode ? GdiConvertToDevmodeW(info2A->pDevMode) : NULL;
1845         info2W->pStatus = asciitounicode(&usBuffer, info2A->pStatus);
1846         info2W->pSecurityDescriptor = info2A->pSecurityDescriptor;
1847         info2W->Status = info2A->Status;
1848         info2W->Priority = info2A->Priority;
1849         info2W->Position = info2A->Position;
1850         info2W->StartTime = info2A->StartTime;
1851         info2W->UntilTime = info2A->UntilTime;
1852         info2W->PagesPrinted = info2A->PagesPrinted;
1853         break;
1854       }
1855     case 3:
1856         JobW = HeapAlloc(GetProcessHeap(), 0, sizeof(JOB_INFO_3));
1857         memcpy(JobW, pJob, sizeof(JOB_INFO_3));
1858         break;
1859     default:
1860         SetLastError(ERROR_INVALID_LEVEL);
1861         return FALSE;
1862     }
1863
1864     ret = SetJobW(hPrinter, JobId, Level, JobW, Command);
1865
1866     switch(Level)
1867     {
1868     case 1:
1869       {
1870         JOB_INFO_1W *info1W = (JOB_INFO_1W*)JobW;
1871         HeapFree(GetProcessHeap(), 0, info1W->pUserName);
1872         HeapFree(GetProcessHeap(), 0, info1W->pDocument); 
1873         HeapFree(GetProcessHeap(), 0, info1W->pDatatype);
1874         HeapFree(GetProcessHeap(), 0, info1W->pStatus);
1875         break;
1876       }
1877     case 2:
1878       {
1879         JOB_INFO_2W *info2W = (JOB_INFO_2W*)JobW;
1880         HeapFree(GetProcessHeap(), 0, info2W->pUserName);
1881         HeapFree(GetProcessHeap(), 0, info2W->pDocument); 
1882         HeapFree(GetProcessHeap(), 0, info2W->pNotifyName);
1883         HeapFree(GetProcessHeap(), 0, info2W->pDatatype);
1884         HeapFree(GetProcessHeap(), 0, info2W->pPrintProcessor);
1885         HeapFree(GetProcessHeap(), 0, info2W->pParameters);
1886         HeapFree(GetProcessHeap(), 0, info2W->pDevMode);
1887         HeapFree(GetProcessHeap(), 0, info2W->pStatus);
1888         break;
1889       }
1890     }
1891     HeapFree(GetProcessHeap(), 0, JobW);
1892
1893     return ret;
1894 }
1895
1896 /*****************************************************************************
1897  *          SetJobW  [WINSPOOL.@]
1898  */
1899 BOOL WINAPI SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level,
1900                     LPBYTE pJob, DWORD Command)
1901 {
1902     BOOL ret = FALSE;
1903     job_t *job;
1904
1905     TRACE("(%p, %ld, %ld, %p, %ld)\n", hPrinter, JobId, Level, pJob, Command);
1906     FIXME("Ignoring everything other than document title\n");
1907
1908     EnterCriticalSection(&printer_handles_cs);
1909     job = get_job(hPrinter, JobId);
1910     if(!job)
1911         goto end;
1912
1913     switch(Level)
1914     {
1915     case 0:
1916         break;
1917     case 1:
1918       {
1919         JOB_INFO_1W *info1 = (JOB_INFO_1W*)pJob;
1920         HeapFree(GetProcessHeap(), 0, job->document_title);
1921         job->document_title = strdupW(info1->pDocument);
1922         break;
1923       }
1924     case 2:
1925       {
1926         JOB_INFO_2W *info2 = (JOB_INFO_2W*)pJob;
1927         HeapFree(GetProcessHeap(), 0, job->document_title);
1928         job->document_title = strdupW(info2->pDocument);
1929         break;
1930       }
1931     case 3:
1932         break;
1933     default:
1934         SetLastError(ERROR_INVALID_LEVEL);
1935         goto end;
1936     }
1937     ret = TRUE;
1938 end:
1939     LeaveCriticalSection(&printer_handles_cs);
1940     return ret;
1941 }
1942
1943 /*****************************************************************************
1944  *          EndDocPrinter  [WINSPOOL.@]
1945  */
1946 BOOL WINAPI EndDocPrinter(HANDLE hPrinter)
1947 {
1948     opened_printer_t *printer;
1949     BOOL ret = FALSE;
1950     TRACE("(%p)\n", hPrinter);
1951
1952     EnterCriticalSection(&printer_handles_cs);
1953
1954     printer = get_opened_printer(hPrinter);
1955     if(!printer)
1956     {
1957         SetLastError(ERROR_INVALID_HANDLE);
1958         goto end;
1959     }
1960
1961     if(!printer->doc)    
1962     {
1963         SetLastError(ERROR_SPL_NO_STARTDOC);
1964         goto end;
1965     }
1966
1967     CloseHandle(printer->doc->hf);
1968     ScheduleJob(hPrinter, printer->doc->job_id);
1969     HeapFree(GetProcessHeap(), 0, printer->doc);
1970     printer->doc = NULL;
1971     ret = TRUE;
1972 end:
1973     LeaveCriticalSection(&printer_handles_cs);
1974     return ret;
1975 }
1976
1977 /*****************************************************************************
1978  *          EndPagePrinter  [WINSPOOL.@]
1979  */
1980 BOOL WINAPI EndPagePrinter(HANDLE hPrinter)
1981 {
1982     FIXME("(%p): stub\n", hPrinter);
1983     return TRUE;
1984 }
1985
1986 /*****************************************************************************
1987  *          StartDocPrinterA  [WINSPOOL.@]
1988  */
1989 DWORD WINAPI StartDocPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
1990 {
1991     UNICODE_STRING usBuffer;
1992     DOC_INFO_2W doc2W;
1993     DOC_INFO_2A *doc2 = (DOC_INFO_2A*)pDocInfo;
1994     DWORD ret;
1995
1996     /* DOC_INFO_1, 2 and 3 all have the strings in the same place with either two (DOC_INFO_2)
1997        or one (DOC_INFO_3) extra DWORDs */
1998
1999     switch(Level) {
2000     case 2:
2001         doc2W.JobId = doc2->JobId;
2002         /* fall through */
2003     case 3:
2004         doc2W.dwMode = doc2->dwMode;
2005         /* fall through */
2006     case 1:
2007         doc2W.pDocName = asciitounicode(&usBuffer, doc2->pDocName);
2008         doc2W.pOutputFile = asciitounicode(&usBuffer, doc2->pOutputFile);
2009         doc2W.pDatatype = asciitounicode(&usBuffer, doc2->pDatatype);
2010         break;
2011
2012     default:
2013         SetLastError(ERROR_INVALID_LEVEL);
2014         return FALSE;
2015     }
2016
2017     ret = StartDocPrinterW(hPrinter, Level, (LPBYTE)&doc2W);
2018
2019     HeapFree(GetProcessHeap(), 0, doc2W.pDatatype);
2020     HeapFree(GetProcessHeap(), 0, doc2W.pOutputFile);
2021     HeapFree(GetProcessHeap(), 0, doc2W.pDocName);
2022
2023     return ret;
2024 }
2025
2026 /*****************************************************************************
2027  *          StartDocPrinterW  [WINSPOOL.@]
2028  */
2029 DWORD WINAPI StartDocPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
2030 {
2031     DOC_INFO_2W *doc = (DOC_INFO_2W *)pDocInfo;
2032     opened_printer_t *printer;
2033     BYTE addjob_buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
2034     ADDJOB_INFO_1W *addjob = (ADDJOB_INFO_1W*) addjob_buf;
2035     JOB_INFO_1W job_info;
2036     DWORD needed, ret = 0;
2037     HANDLE hf;
2038     WCHAR *filename;
2039
2040     TRACE("(hPrinter = %p, Level = %ld, pDocInfo = %p {pDocName = %s, pOutputFile = %s, pDatatype = %s}):\n",
2041           hPrinter, Level, doc, debugstr_w(doc->pDocName), debugstr_w(doc->pOutputFile),
2042           debugstr_w(doc->pDatatype));
2043
2044     if(Level < 1 || Level > 3)
2045     {
2046         SetLastError(ERROR_INVALID_LEVEL);
2047         return 0;
2048     }
2049
2050     EnterCriticalSection(&printer_handles_cs);
2051     printer = get_opened_printer(hPrinter);
2052     if(!printer)
2053     {
2054         SetLastError(ERROR_INVALID_HANDLE);
2055         goto end;
2056     }
2057
2058     if(printer->doc)
2059     {
2060         SetLastError(ERROR_INVALID_PRINTER_STATE);
2061         goto end;
2062     }
2063
2064     /* Even if we're printing to a file we still add a print job, we'll
2065        just ignore the spool file name */
2066
2067     if(!AddJobW(hPrinter, 1, addjob_buf, sizeof(addjob_buf), &needed))
2068     {
2069         ERR("AddJob failed gle %08lx\n", GetLastError());
2070         goto end;
2071     }
2072
2073     if(doc->pOutputFile)
2074         filename = doc->pOutputFile;
2075     else
2076         filename = addjob->Path;
2077
2078     hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2079     if(hf == INVALID_HANDLE_VALUE)
2080         goto end;
2081
2082     memset(&job_info, 0, sizeof(job_info));
2083     job_info.pDocument = doc->pDocName;
2084     SetJobW(hPrinter, addjob->JobId, 1, (LPBYTE)&job_info, 0);
2085
2086     printer->doc = HeapAlloc(GetProcessHeap(), 0, sizeof(*printer->doc));
2087     printer->doc->hf = hf;
2088     ret = printer->doc->job_id = addjob->JobId;
2089 end:
2090     LeaveCriticalSection(&printer_handles_cs);
2091
2092     return ret;
2093 }
2094
2095 /*****************************************************************************
2096  *          StartPagePrinter  [WINSPOOL.@]
2097  */
2098 BOOL WINAPI StartPagePrinter(HANDLE hPrinter)
2099 {
2100     FIXME("(%p): stub\n", hPrinter);
2101     return TRUE;
2102 }
2103
2104 /*****************************************************************************
2105  *          GetFormA  [WINSPOOL.@]
2106  */
2107 BOOL WINAPI GetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
2108                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
2109 {
2110     FIXME("(%p,%s,%ld,%p,%ld,%p): stub\n",hPrinter,pFormName,
2111          Level,pForm,cbBuf,pcbNeeded);
2112     return FALSE;
2113 }
2114
2115 /*****************************************************************************
2116  *          GetFormW  [WINSPOOL.@]
2117  */
2118 BOOL WINAPI GetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
2119                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
2120 {
2121     FIXME("(%p,%s,%ld,%p,%ld,%p): stub\n",hPrinter,
2122           debugstr_w(pFormName),Level,pForm,cbBuf,pcbNeeded);
2123     return FALSE;
2124 }
2125
2126 /*****************************************************************************
2127  *          SetFormA  [WINSPOOL.@]
2128  */
2129 BOOL WINAPI SetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
2130                         LPBYTE pForm)
2131 {
2132     FIXME("(%p,%s,%ld,%p): stub\n",hPrinter,pFormName,Level,pForm);
2133     return FALSE;
2134 }
2135
2136 /*****************************************************************************
2137  *          SetFormW  [WINSPOOL.@]
2138  */
2139 BOOL WINAPI SetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
2140                         LPBYTE pForm)
2141 {
2142     FIXME("(%p,%p,%ld,%p): stub\n",hPrinter,pFormName,Level,pForm);
2143     return FALSE;
2144 }
2145
2146 /*****************************************************************************
2147  *          ReadPrinter  [WINSPOOL.@]
2148  */
2149 BOOL WINAPI ReadPrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf,
2150                            LPDWORD pNoBytesRead)
2151 {
2152     FIXME("(%p,%p,%ld,%p): stub\n",hPrinter,pBuf,cbBuf,pNoBytesRead);
2153     return FALSE;
2154 }
2155
2156 /*****************************************************************************
2157  *          ResetPrinterA  [WINSPOOL.@]
2158  */
2159 BOOL WINAPI ResetPrinterA(HANDLE hPrinter, LPPRINTER_DEFAULTSA pDefault)
2160 {
2161     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
2162     return FALSE;
2163 }
2164
2165 /*****************************************************************************
2166  *          ResetPrinterW  [WINSPOOL.@]
2167  */
2168 BOOL WINAPI ResetPrinterW(HANDLE hPrinter, LPPRINTER_DEFAULTSW pDefault)
2169 {
2170     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
2171     return FALSE;
2172 }
2173
2174 /*****************************************************************************
2175  *    WINSPOOL_GetDWORDFromReg
2176  *
2177  * Return DWORD associated with ValueName from hkey.
2178  */
2179 static DWORD WINSPOOL_GetDWORDFromReg(HKEY hkey, LPCSTR ValueName)
2180 {
2181     DWORD sz = sizeof(DWORD), type, value = 0;
2182     LONG ret;
2183
2184     ret = RegQueryValueExA(hkey, ValueName, 0, &type, (LPBYTE)&value, &sz);
2185
2186     if(ret != ERROR_SUCCESS) {
2187         WARN("Got ret = %ld on name %s\n", ret, ValueName);
2188         return 0;
2189     }
2190     if(type != REG_DWORD) {
2191         ERR("Got type %ld\n", type);
2192         return 0;
2193     }
2194     return value;
2195 }
2196
2197 /*****************************************************************************
2198  *    WINSPOOL_GetStringFromReg
2199  *
2200  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
2201  * String is stored either as unicode or ascii.
2202  * Bit of a hack here to get the ValueName if we want ascii.
2203  */
2204 static BOOL WINSPOOL_GetStringFromReg(HKEY hkey, LPCWSTR ValueName, LPBYTE ptr,
2205                                       DWORD buflen, DWORD *needed,
2206                                       BOOL unicode)
2207 {
2208     DWORD sz = buflen, type;
2209     LONG ret;
2210
2211     if(unicode)
2212         ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
2213     else {
2214         LPSTR ValueNameA = HEAP_strdupWtoA(GetProcessHeap(),0,ValueName);
2215         ret = RegQueryValueExA(hkey, ValueNameA, 0, &type, ptr, &sz);
2216         HeapFree(GetProcessHeap(),0,ValueNameA);
2217     }
2218     if(ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) {
2219         WARN("Got ret = %ld\n", ret);
2220         *needed = 0;
2221         return FALSE;
2222     }
2223     *needed = sz;
2224     return TRUE;
2225 }
2226
2227 /*****************************************************************************
2228  *    WINSPOOL_GetDefaultDevMode
2229  *
2230  * Get a default DevMode values for wineps.
2231  * FIXME - use ppd.
2232  */
2233
2234 static void WINSPOOL_GetDefaultDevMode(
2235         LPBYTE ptr,
2236         DWORD buflen, DWORD *needed,
2237         BOOL unicode)
2238 {
2239     DEVMODEA    dm;
2240     static const char szwps[] = "wineps.drv";
2241
2242         /* fill default DEVMODE - should be read from ppd... */
2243         ZeroMemory( &dm, sizeof(dm) );
2244         memcpy(dm.dmDeviceName,szwps,sizeof szwps);
2245         dm.dmSpecVersion = DM_SPECVERSION;
2246         dm.dmDriverVersion = 1;
2247         dm.dmSize = sizeof(DEVMODEA);
2248         dm.dmDriverExtra = 0;
2249         dm.dmFields =
2250                 DM_ORIENTATION | DM_PAPERSIZE |
2251                 DM_PAPERLENGTH | DM_PAPERWIDTH |
2252                 DM_SCALE |
2253                 DM_COPIES |
2254                 DM_DEFAULTSOURCE | DM_PRINTQUALITY |
2255                 DM_YRESOLUTION | DM_TTOPTION;
2256
2257         dm.u1.s1.dmOrientation = DMORIENT_PORTRAIT;
2258         dm.u1.s1.dmPaperSize = DMPAPER_A4;
2259         dm.u1.s1.dmPaperLength = 2970;
2260         dm.u1.s1.dmPaperWidth = 2100;
2261
2262         dm.dmScale = 100;
2263         dm.dmCopies = 1;
2264         dm.dmDefaultSource = DMBIN_AUTO;
2265         dm.dmPrintQuality = DMRES_MEDIUM;
2266         /* dm.dmColor */
2267         /* dm.dmDuplex */
2268         dm.dmYResolution = 300; /* 300dpi */
2269         dm.dmTTOption = DMTT_BITMAP;
2270         /* dm.dmCollate */
2271         /* dm.dmFormName */
2272         /* dm.dmLogPixels */
2273         /* dm.dmBitsPerPel */
2274         /* dm.dmPelsWidth */
2275         /* dm.dmPelsHeight */
2276         /* dm.dmDisplayFlags */
2277         /* dm.dmDisplayFrequency */
2278         /* dm.dmICMMethod */
2279         /* dm.dmICMIntent */
2280         /* dm.dmMediaType */
2281         /* dm.dmDitherType */
2282         /* dm.dmReserved1 */
2283         /* dm.dmReserved2 */
2284         /* dm.dmPanningWidth */
2285         /* dm.dmPanningHeight */
2286
2287     if(unicode) {
2288         if(buflen >= sizeof(DEVMODEW)) {
2289             DEVMODEW *pdmW = GdiConvertToDevmodeW(&dm);
2290             memcpy(ptr, pdmW, sizeof(DEVMODEW));
2291             HeapFree(GetProcessHeap(),0,pdmW);
2292         }
2293         *needed = sizeof(DEVMODEW);
2294     }
2295     else
2296     {
2297         if(buflen >= sizeof(DEVMODEA)) {
2298             memcpy(ptr, &dm, sizeof(DEVMODEA));
2299         }
2300         *needed = sizeof(DEVMODEA);
2301     }
2302 }
2303
2304 /*****************************************************************************
2305  *    WINSPOOL_GetDevModeFromReg
2306  *
2307  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
2308  * DevMode is stored either as unicode or ascii.
2309  */
2310 static BOOL WINSPOOL_GetDevModeFromReg(HKEY hkey, LPCWSTR ValueName,
2311                                        LPBYTE ptr,
2312                                        DWORD buflen, DWORD *needed,
2313                                        BOOL unicode)
2314 {
2315     DWORD sz = buflen, type;
2316     LONG ret;
2317
2318     if (ptr && buflen>=sizeof(DEVMODEA)) memset(ptr, 0, sizeof(DEVMODEA));
2319     ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
2320     if ((ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA)) sz = 0;
2321     if (sz < sizeof(DEVMODEA))
2322     {
2323         TRACE("corrupted registry for %s ( size %ld)\n",debugstr_w(ValueName),sz);
2324         return FALSE;
2325     }
2326     /* ensures that dmSize is not erratically bogus if registry is invalid */
2327     if (ptr && ((DEVMODEA*)ptr)->dmSize < sizeof(DEVMODEA))
2328         ((DEVMODEA*)ptr)->dmSize = sizeof(DEVMODEA);
2329     if(unicode) {
2330         sz += (CCHDEVICENAME + CCHFORMNAME);
2331         if(buflen >= sz) {
2332             DEVMODEW *dmW = GdiConvertToDevmodeW((DEVMODEA*)ptr);
2333             memcpy(ptr, dmW, sz);
2334             HeapFree(GetProcessHeap(),0,dmW);
2335         }
2336     }
2337     *needed = sz;
2338     return TRUE;
2339 }
2340
2341 /*********************************************************************
2342  *    WINSPOOL_GetPrinter_2
2343  *
2344  * Fills out a PRINTER_INFO_2A|W struct storing the strings in buf.
2345  * The strings are either stored as unicode or ascii.
2346  */
2347 static BOOL WINSPOOL_GetPrinter_2(HKEY hkeyPrinter, PRINTER_INFO_2W *pi2,
2348                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
2349                                   BOOL unicode)
2350 {
2351     DWORD size, left = cbBuf;
2352     BOOL space = (cbBuf > 0);
2353     LPBYTE ptr = buf;
2354
2355     *pcbNeeded = 0;
2356
2357     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
2358                                  unicode)) {
2359         if(space && size <= left) {
2360             pi2->pPrinterName = (LPWSTR)ptr;
2361             ptr += size;
2362             left -= size;
2363         } else
2364             space = FALSE;
2365         *pcbNeeded += size;
2366     }
2367     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Share_NameW, ptr, left, &size,
2368                                  unicode)) {
2369         if(space && size <= left) {
2370             pi2->pShareName = (LPWSTR)ptr;
2371             ptr += size;
2372             left -= size;
2373         } else
2374             space = FALSE;
2375         *pcbNeeded += size;
2376     }
2377     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
2378                                  unicode)) {
2379         if(space && size <= left) {
2380             pi2->pPortName = (LPWSTR)ptr;
2381             ptr += size;
2382             left -= size;
2383         } else
2384             space = FALSE;
2385         *pcbNeeded += size;
2386     }
2387     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Printer_DriverW, ptr, left,
2388                                  &size, unicode)) {
2389         if(space && size <= left) {
2390             pi2->pDriverName = (LPWSTR)ptr;
2391             ptr += size;
2392             left -= size;
2393         } else
2394             space = FALSE;
2395         *pcbNeeded += size;
2396     }
2397     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size,
2398                                  unicode)) {
2399         if(space && size <= left) {
2400             pi2->pComment = (LPWSTR)ptr;
2401             ptr += size;
2402             left -= size;
2403         } else
2404             space = FALSE;
2405         *pcbNeeded += size;
2406     }
2407     if(WINSPOOL_GetStringFromReg(hkeyPrinter, LocationW, ptr, left, &size,
2408                                  unicode)) {
2409         if(space && size <= left) {
2410             pi2->pLocation = (LPWSTR)ptr;
2411             ptr += size;
2412             left -= size;
2413         } else
2414             space = FALSE;
2415         *pcbNeeded += size;
2416     }
2417     if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, ptr, left,
2418                                   &size, unicode)) {
2419         if(space && size <= left) {
2420             pi2->pDevMode = (LPDEVMODEW)ptr;
2421             ptr += size;
2422             left -= size;
2423         } else
2424             space = FALSE;
2425         *pcbNeeded += size;
2426     }
2427     else
2428     {
2429         WINSPOOL_GetDefaultDevMode(ptr, left, &size, unicode);
2430         if(space && size <= left) {
2431             pi2->pDevMode = (LPDEVMODEW)ptr;
2432             ptr += size;
2433             left -= size;
2434         } else
2435             space = FALSE;
2436         *pcbNeeded += size;
2437     }
2438     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Separator_FileW, ptr, left,
2439                                  &size, unicode)) {
2440         if(space && size <= left) {
2441             pi2->pSepFile = (LPWSTR)ptr;
2442             ptr += size;
2443             left -= size;
2444         } else
2445             space = FALSE;
2446         *pcbNeeded += size;
2447     }
2448     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Print_ProcessorW, ptr, left,
2449                                  &size, unicode)) {
2450         if(space && size <= left) {
2451             pi2->pPrintProcessor = (LPWSTR)ptr;
2452             ptr += size;
2453             left -= size;
2454         } else
2455             space = FALSE;
2456         *pcbNeeded += size;
2457     }
2458     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DatatypeW, ptr, left,
2459                                  &size, unicode)) {
2460         if(space && size <= left) {
2461             pi2->pDatatype = (LPWSTR)ptr;
2462             ptr += size;
2463             left -= size;
2464         } else
2465             space = FALSE;
2466         *pcbNeeded += size;
2467     }
2468     if(WINSPOOL_GetStringFromReg(hkeyPrinter, ParametersW, ptr, left,
2469                                  &size, unicode)) {
2470         if(space && size <= left) {
2471             pi2->pParameters = (LPWSTR)ptr;
2472             ptr += size;
2473             left -= size;
2474         } else
2475             space = FALSE;
2476         *pcbNeeded += size;
2477     }
2478     if(pi2) {
2479         pi2->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
2480         pi2->Priority = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Priority");
2481         pi2->DefaultPriority = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
2482                                                         "Default Priority");
2483         pi2->StartTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "StartTime");
2484         pi2->UntilTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "UntilTime");
2485     }
2486
2487     if(!space && pi2) /* zero out pi2 if we can't completely fill buf */
2488         memset(pi2, 0, sizeof(*pi2));
2489
2490     return space;
2491 }
2492
2493 /*********************************************************************
2494  *    WINSPOOL_GetPrinter_4
2495  *
2496  * Fills out a PRINTER_INFO_4 struct storing the strings in buf.
2497  */
2498 static BOOL WINSPOOL_GetPrinter_4(HKEY hkeyPrinter, PRINTER_INFO_4W *pi4,
2499                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
2500                                   BOOL unicode)
2501 {
2502     DWORD size, left = cbBuf;
2503     BOOL space = (cbBuf > 0);
2504     LPBYTE ptr = buf;
2505
2506     *pcbNeeded = 0;
2507
2508     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
2509                                  unicode)) {
2510         if(space && size <= left) {
2511             pi4->pPrinterName = (LPWSTR)ptr;
2512             ptr += size;
2513             left -= size;
2514         } else
2515             space = FALSE;
2516         *pcbNeeded += size;
2517     }
2518     if(pi4) {
2519         pi4->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
2520     }
2521
2522     if(!space && pi4) /* zero out pi4 if we can't completely fill buf */
2523         memset(pi4, 0, sizeof(*pi4));
2524
2525     return space;
2526 }
2527
2528 /*********************************************************************
2529  *    WINSPOOL_GetPrinter_5
2530  *
2531  * Fills out a PRINTER_INFO_5 struct storing the strings in buf.
2532  */
2533 static BOOL WINSPOOL_GetPrinter_5(HKEY hkeyPrinter, PRINTER_INFO_5W *pi5,
2534                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
2535                                   BOOL unicode)
2536 {
2537     DWORD size, left = cbBuf;
2538     BOOL space = (cbBuf > 0);
2539     LPBYTE ptr = buf;
2540
2541     *pcbNeeded = 0;
2542
2543     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
2544                                  unicode)) {
2545         if(space && size <= left) {
2546             pi5->pPrinterName = (LPWSTR)ptr;
2547             ptr += size;
2548             left -= size;
2549         } else
2550             space = FALSE;
2551         *pcbNeeded += size;
2552     }
2553     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
2554                                  unicode)) {
2555         if(space && size <= left) {
2556             pi5->pPortName = (LPWSTR)ptr;
2557             ptr += size;
2558             left -= size;
2559         } else
2560             space = FALSE;
2561         *pcbNeeded += size;
2562     }
2563     if(pi5) {
2564         pi5->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
2565         pi5->DeviceNotSelectedTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
2566                                                                 "dnsTimeout");
2567         pi5->TransmissionRetryTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
2568                                                                  "txTimeout");
2569     }
2570
2571     if(!space && pi5) /* zero out pi5 if we can't completely fill buf */
2572         memset(pi5, 0, sizeof(*pi5));
2573
2574     return space;
2575 }
2576
2577 /*****************************************************************************
2578  *          WINSPOOL_GetPrinter
2579  *
2580  *    Implementation of GetPrinterA|W.  Relies on PRINTER_INFO_*W being
2581  *    essentially the same as PRINTER_INFO_*A. i.e. the structure itself is
2582  *    just a collection of pointers to strings.
2583  */
2584 static BOOL WINSPOOL_GetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
2585                                 DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
2586 {
2587     LPCWSTR name;
2588     DWORD size, needed = 0;
2589     LPBYTE ptr = NULL;
2590     HKEY hkeyPrinter, hkeyPrinters;
2591     BOOL ret;
2592
2593     TRACE("(%p,%ld,%p,%ld,%p)\n",hPrinter,Level,pPrinter,cbBuf, pcbNeeded);
2594
2595     if (!(name = get_opened_printer_name(hPrinter))) {
2596         SetLastError(ERROR_INVALID_HANDLE);
2597         return FALSE;
2598     }
2599
2600     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
2601        ERROR_SUCCESS) {
2602         ERR("Can't create Printers key\n");
2603         return FALSE;
2604     }
2605     if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter) != ERROR_SUCCESS)
2606     {
2607         ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
2608         RegCloseKey(hkeyPrinters);
2609         SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
2610         return FALSE;
2611     }
2612
2613     switch(Level) {
2614     case 2:
2615       {
2616         PRINTER_INFO_2W *pi2 = (PRINTER_INFO_2W *)pPrinter;
2617
2618         size = sizeof(PRINTER_INFO_2W);
2619         if(size <= cbBuf) {
2620             ptr = pPrinter + size;
2621             cbBuf -= size;
2622             memset(pPrinter, 0, size);
2623         } else {
2624             pi2 = NULL;
2625             cbBuf = 0;
2626         }
2627         ret = WINSPOOL_GetPrinter_2(hkeyPrinter, pi2, ptr, cbBuf, &needed,
2628                                     unicode);
2629         needed += size;
2630         break;
2631       }
2632
2633     case 4:
2634       {
2635         PRINTER_INFO_4W *pi4 = (PRINTER_INFO_4W *)pPrinter;
2636
2637         size = sizeof(PRINTER_INFO_4W);
2638         if(size <= cbBuf) {
2639             ptr = pPrinter + size;
2640             cbBuf -= size;
2641             memset(pPrinter, 0, size);
2642         } else {
2643             pi4 = NULL;
2644             cbBuf = 0;
2645         }
2646         ret = WINSPOOL_GetPrinter_4(hkeyPrinter, pi4, ptr, cbBuf, &needed,
2647                                     unicode);
2648         needed += size;
2649         break;
2650       }
2651
2652
2653     case 5:
2654       {
2655         PRINTER_INFO_5W *pi5 = (PRINTER_INFO_5W *)pPrinter;
2656
2657         size = sizeof(PRINTER_INFO_5W);
2658         if(size <= cbBuf) {
2659             ptr = pPrinter + size;
2660             cbBuf -= size;
2661             memset(pPrinter, 0, size);
2662         } else {
2663             pi5 = NULL;
2664             cbBuf = 0;
2665         }
2666
2667         ret = WINSPOOL_GetPrinter_5(hkeyPrinter, pi5, ptr, cbBuf, &needed,
2668                                     unicode);
2669         needed += size;
2670         break;
2671       }
2672
2673     default:
2674         FIXME("Unimplemented level %ld\n", Level);
2675         SetLastError(ERROR_INVALID_LEVEL);
2676         RegCloseKey(hkeyPrinters);
2677         RegCloseKey(hkeyPrinter);
2678         return FALSE;
2679     }
2680
2681     RegCloseKey(hkeyPrinter);
2682     RegCloseKey(hkeyPrinters);
2683
2684     TRACE("returning %d needed = %ld\n", ret, needed);
2685     if(pcbNeeded) *pcbNeeded = needed;
2686     if(!ret)
2687         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2688     return ret;
2689 }
2690
2691 /*****************************************************************************
2692  *          GetPrinterW  [WINSPOOL.@]
2693  */
2694 BOOL WINAPI GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
2695                         DWORD cbBuf, LPDWORD pcbNeeded)
2696 {
2697     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
2698                                TRUE);
2699 }
2700
2701 /*****************************************************************************
2702  *          GetPrinterA  [WINSPOOL.@]
2703  */
2704 BOOL WINAPI GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
2705                     DWORD cbBuf, LPDWORD pcbNeeded)
2706 {
2707     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
2708                                FALSE);
2709 }
2710
2711 /*****************************************************************************
2712  *          WINSPOOL_EnumPrinters
2713  *
2714  *    Implementation of EnumPrintersA|W
2715  */
2716 static BOOL WINSPOOL_EnumPrinters(DWORD dwType, LPWSTR lpszName,
2717                                   DWORD dwLevel, LPBYTE lpbPrinters,
2718                                   DWORD cbBuf, LPDWORD lpdwNeeded,
2719                                   LPDWORD lpdwReturned, BOOL unicode)
2720
2721 {
2722     HKEY hkeyPrinters, hkeyPrinter;
2723     WCHAR PrinterName[255];
2724     DWORD needed = 0, number = 0;
2725     DWORD used, i, left;
2726     PBYTE pi, buf;
2727
2728     if(lpbPrinters)
2729         memset(lpbPrinters, 0, cbBuf);
2730     if(lpdwReturned)
2731         *lpdwReturned = 0;
2732     if(lpdwNeeded)
2733         *lpdwNeeded = 0;
2734
2735     /* PRINTER_ENUM_DEFAULT is only supported under win9x, we behave like NT */
2736     if(dwType == PRINTER_ENUM_DEFAULT)
2737         return TRUE;
2738
2739     if (dwType & PRINTER_ENUM_CONNECTIONS) {
2740         FIXME("We don't handle PRINTER_ENUM_CONNECTIONS\n");
2741         dwType &= ~PRINTER_ENUM_CONNECTIONS; /* we don't handle that */
2742         if(!dwType) return TRUE;
2743     }
2744
2745     if (!((dwType & PRINTER_ENUM_LOCAL) || (dwType & PRINTER_ENUM_NAME))) {
2746         FIXME("dwType = %08lx\n", dwType);
2747         SetLastError(ERROR_INVALID_FLAGS);
2748         return FALSE;
2749     }
2750
2751     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
2752        ERROR_SUCCESS) {
2753         ERR("Can't create Printers key\n");
2754         return FALSE;
2755     }
2756
2757     if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &number, NULL, NULL,
2758                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
2759         RegCloseKey(hkeyPrinters);
2760         ERR("Can't query Printers key\n");
2761         return FALSE;
2762     }
2763     TRACE("Found %ld printers\n", number);
2764
2765     switch(dwLevel) {
2766     case 1:
2767         RegCloseKey(hkeyPrinters);
2768         if (lpdwReturned)
2769             *lpdwReturned = number;
2770         return TRUE;
2771
2772     case 2:
2773         used = number * sizeof(PRINTER_INFO_2W);
2774         break;
2775     case 4:
2776         used = number * sizeof(PRINTER_INFO_4W);
2777         break;
2778     case 5:
2779         used = number * sizeof(PRINTER_INFO_5W);
2780         break;
2781
2782     default:
2783         SetLastError(ERROR_INVALID_LEVEL);
2784         RegCloseKey(hkeyPrinters);
2785         return FALSE;
2786     }
2787     pi = (used <= cbBuf) ? lpbPrinters : NULL;
2788
2789     for(i = 0; i < number; i++) {
2790         if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)) !=
2791            ERROR_SUCCESS) {
2792             ERR("Can't enum key number %ld\n", i);
2793             RegCloseKey(hkeyPrinters);
2794             return FALSE;
2795         }
2796         TRACE("Printer %ld is %s\n", i, debugstr_w(PrinterName));
2797         if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkeyPrinter) !=
2798            ERROR_SUCCESS) {
2799             ERR("Can't open key %s\n", debugstr_w(PrinterName));
2800             RegCloseKey(hkeyPrinters);
2801             return FALSE;
2802         }
2803
2804         if(cbBuf > used) {
2805             buf = lpbPrinters + used;
2806             left = cbBuf - used;
2807         } else {
2808             buf = NULL;
2809             left = 0;
2810         }
2811
2812         switch(dwLevel) {
2813         case 2:
2814             WINSPOOL_GetPrinter_2(hkeyPrinter, (PRINTER_INFO_2W *)pi, buf,
2815                                   left, &needed, unicode);
2816             used += needed;
2817             if(pi) pi += sizeof(PRINTER_INFO_2W);
2818             break;
2819         case 4:
2820             WINSPOOL_GetPrinter_4(hkeyPrinter, (PRINTER_INFO_4W *)pi, buf,
2821                                   left, &needed, unicode);
2822             used += needed;
2823             if(pi) pi += sizeof(PRINTER_INFO_4W);
2824             break;
2825         case 5:
2826             WINSPOOL_GetPrinter_5(hkeyPrinter, (PRINTER_INFO_5W *)pi, buf,
2827                                   left, &needed, unicode);
2828             used += needed;
2829             if(pi) pi += sizeof(PRINTER_INFO_5W);
2830             break;
2831         default:
2832             ERR("Shouldn't be here!\n");
2833             RegCloseKey(hkeyPrinter);
2834             RegCloseKey(hkeyPrinters);
2835             return FALSE;
2836         }
2837         RegCloseKey(hkeyPrinter);
2838     }
2839     RegCloseKey(hkeyPrinters);
2840
2841     if(lpdwNeeded)
2842         *lpdwNeeded = used;
2843
2844     if(used > cbBuf) {
2845         if(lpbPrinters)
2846             memset(lpbPrinters, 0, cbBuf);
2847         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2848         return FALSE;
2849     }
2850     if(lpdwReturned)
2851         *lpdwReturned = number;
2852     SetLastError(ERROR_SUCCESS);
2853     return TRUE;
2854 }
2855
2856
2857 /******************************************************************
2858  *              EnumPrintersW        [WINSPOOL.@]
2859  *
2860  *    Enumerates the available printers, print servers and print
2861  *    providers, depending on the specified flags, name and level.
2862  *
2863  * RETURNS:
2864  *
2865  *    If level is set to 1:
2866  *      Not implemented yet!
2867  *      Returns TRUE with an empty list.
2868  *
2869  *    If level is set to 2:
2870  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
2871  *      Returns an array of PRINTER_INFO_2 data structures in the
2872  *      lpbPrinters buffer. Note that according to MSDN also an
2873  *      OpenPrinter should be performed on every remote printer.
2874  *
2875  *    If level is set to 4 (officially WinNT only):
2876  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
2877  *      Fast: Only the registry is queried to retrieve printer names,
2878  *      no connection to the driver is made.
2879  *      Returns an array of PRINTER_INFO_4 data structures in the
2880  *      lpbPrinters buffer.
2881  *
2882  *    If level is set to 5 (officially WinNT4/Win9x only):
2883  *      Fast: Only the registry is queried to retrieve printer names,
2884  *      no connection to the driver is made.
2885  *      Returns an array of PRINTER_INFO_5 data structures in the
2886  *      lpbPrinters buffer.
2887  *
2888  *    If level set to 3 or 6+:
2889  *          returns zero (failure!)
2890  *
2891  *    Returns nonzero (TRUE) on success, or zero on failure, use GetLastError
2892  *    for information.
2893  *
2894  * BUGS:
2895  *    - Only PRINTER_ENUM_LOCAL and PRINTER_ENUM_NAME are implemented.
2896  *    - Only levels 2, 4 and 5 are implemented at the moment.
2897  *    - 16-bit printer drivers are not enumerated.
2898  *    - Returned amount of bytes used/needed does not match the real Windoze
2899  *      implementation (as in this implementation, all strings are part
2900  *      of the buffer, whereas Win32 keeps them somewhere else)
2901  *    - At level 2, EnumPrinters should also call OpenPrinter for remote printers.
2902  *
2903  * NOTE:
2904  *    - In a regular Wine installation, no registry settings for printers
2905  *      exist, which makes this function return an empty list.
2906  */
2907 BOOL  WINAPI EnumPrintersW(
2908                 DWORD dwType,        /* [in] Types of print objects to enumerate */
2909                 LPWSTR lpszName,     /* [in] name of objects to enumerate */
2910                 DWORD dwLevel,       /* [in] type of printer info structure */
2911                 LPBYTE lpbPrinters,  /* [out] buffer which receives info */
2912                 DWORD cbBuf,         /* [in] max size of buffer in bytes */
2913                 LPDWORD lpdwNeeded,  /* [out] pointer to var: # bytes used/needed */
2914                 LPDWORD lpdwReturned /* [out] number of entries returned */
2915                 )
2916 {
2917     return WINSPOOL_EnumPrinters(dwType, lpszName, dwLevel, lpbPrinters, cbBuf,
2918                                  lpdwNeeded, lpdwReturned, TRUE);
2919 }
2920
2921 /******************************************************************
2922  *              EnumPrintersA        [WINSPOOL.@]
2923  *
2924  */
2925 BOOL WINAPI EnumPrintersA(DWORD dwType, LPSTR lpszName,
2926                           DWORD dwLevel, LPBYTE lpbPrinters,
2927                           DWORD cbBuf, LPDWORD lpdwNeeded,
2928                           LPDWORD lpdwReturned)
2929 {
2930     BOOL ret;
2931     UNICODE_STRING lpszNameW;
2932     PWSTR pwstrNameW;
2933     
2934     pwstrNameW = asciitounicode(&lpszNameW,lpszName);
2935     ret = WINSPOOL_EnumPrinters(dwType, pwstrNameW, dwLevel, lpbPrinters, cbBuf,
2936                                 lpdwNeeded, lpdwReturned, FALSE);
2937     RtlFreeUnicodeString(&lpszNameW);
2938     return ret;
2939 }
2940
2941 /*****************************************************************************
2942  *          WINSPOOL_GetDriverInfoFromReg [internal]
2943  *
2944  *    Enters the information from the registry into the DRIVER_INFO struct
2945  *
2946  * RETURNS
2947  *    zero if the printer driver does not exist in the registry
2948  *    (only if Level > 1) otherwise nonzero
2949  */
2950 static BOOL WINSPOOL_GetDriverInfoFromReg(
2951                             HKEY    hkeyDrivers,
2952                             LPWSTR  DriverName,
2953                             LPWSTR  pEnvironment,
2954                             DWORD   Level,
2955                             LPBYTE  ptr,            /* DRIVER_INFO */
2956                             LPBYTE  pDriverStrings, /* strings buffer */
2957                             DWORD   cbBuf,          /* size of string buffer */
2958                             LPDWORD pcbNeeded,      /* space needed for str. */
2959                             BOOL    unicode)        /* type of strings */
2960 {   DWORD  dw, size, tmp, type;
2961     HKEY   hkeyDriver;
2962     LPBYTE strPtr = pDriverStrings;
2963
2964     TRACE("%s,%s,%ld,%p,%p,%ld,%d\n",
2965           debugstr_w(DriverName), debugstr_w(pEnvironment),
2966           Level, ptr, pDriverStrings, cbBuf, unicode);
2967
2968     if(unicode) {
2969         *pcbNeeded = (lstrlenW(DriverName) + 1) * sizeof(WCHAR);
2970             if (*pcbNeeded <= cbBuf)
2971                strcpyW((LPWSTR)strPtr, DriverName);
2972     } else {
2973         *pcbNeeded = WideCharToMultiByte(CP_ACP, 0, DriverName, -1, NULL, 0,
2974                                           NULL, NULL);
2975         if(*pcbNeeded <= cbBuf)
2976             WideCharToMultiByte(CP_ACP, 0, DriverName, -1,
2977                                 (LPSTR)strPtr, *pcbNeeded, NULL, NULL);
2978     }
2979     if(Level == 1) {
2980        if(ptr)
2981           ((PDRIVER_INFO_1W) ptr)->pName = (LPWSTR) strPtr;
2982        return TRUE;
2983     } else {
2984        if(ptr)
2985           ((PDRIVER_INFO_3W) ptr)->pName = (LPWSTR) strPtr;
2986        strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
2987     }
2988
2989     if(!DriverName[0] || RegOpenKeyW(hkeyDrivers, DriverName, &hkeyDriver) != ERROR_SUCCESS) {
2990         ERR("Can't find driver '%s' in registry\n", debugstr_w(DriverName));
2991         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER); /* ? */
2992         return FALSE;
2993     }
2994
2995     size = sizeof(dw);
2996     if(RegQueryValueExA(hkeyDriver, "Version", 0, &type, (PBYTE)&dw, &size) !=
2997         ERROR_SUCCESS)
2998          WARN("Can't get Version\n");
2999     else if(ptr)
3000          ((PDRIVER_INFO_3A) ptr)->cVersion = dw;
3001
3002     if(!pEnvironment)
3003         pEnvironment = (LPWSTR)DefaultEnvironmentW;
3004     if(unicode)
3005         size = (lstrlenW(pEnvironment) + 1) * sizeof(WCHAR);
3006     else
3007         size = WideCharToMultiByte(CP_ACP, 0, pEnvironment, -1, NULL, 0,
3008                                    NULL, NULL);
3009     *pcbNeeded += size;
3010     if(*pcbNeeded <= cbBuf) {
3011         if(unicode)
3012             strcpyW((LPWSTR)strPtr, pEnvironment);
3013         else
3014             WideCharToMultiByte(CP_ACP, 0, pEnvironment, -1,
3015                                 (LPSTR)strPtr, size, NULL, NULL);
3016         if(ptr)
3017             ((PDRIVER_INFO_3W) ptr)->pEnvironment = (LPWSTR)strPtr;
3018         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
3019     }
3020
3021     if(WINSPOOL_GetStringFromReg(hkeyDriver, DriverW, strPtr, 0, &size,
3022                                  unicode)) {
3023         *pcbNeeded += size;
3024         if(*pcbNeeded <= cbBuf)
3025             WINSPOOL_GetStringFromReg(hkeyDriver, DriverW, strPtr, size, &tmp,
3026                                       unicode);
3027         if(ptr)
3028             ((PDRIVER_INFO_3W) ptr)->pDriverPath = (LPWSTR)strPtr;
3029         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
3030     }
3031
3032     if(WINSPOOL_GetStringFromReg(hkeyDriver, Data_FileW, strPtr, 0, &size,
3033                                  unicode)) {
3034         *pcbNeeded += size;
3035         if(*pcbNeeded <= cbBuf)
3036             WINSPOOL_GetStringFromReg(hkeyDriver, Data_FileW, strPtr, size,
3037                                       &tmp, unicode);
3038         if(ptr)
3039             ((PDRIVER_INFO_3W) ptr)->pDataFile = (LPWSTR)strPtr;
3040         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3041     }
3042
3043     if(WINSPOOL_GetStringFromReg(hkeyDriver, Configuration_FileW, strPtr,
3044                                  0, &size, unicode)) {
3045         *pcbNeeded += size;
3046         if(*pcbNeeded <= cbBuf)
3047             WINSPOOL_GetStringFromReg(hkeyDriver, Configuration_FileW, strPtr,
3048                                       size, &tmp, unicode);
3049         if(ptr)
3050             ((PDRIVER_INFO_3W) ptr)->pConfigFile = (LPWSTR)strPtr;
3051         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3052     }
3053
3054     if(Level == 2 ) {
3055         RegCloseKey(hkeyDriver);
3056         TRACE("buffer space %ld required %ld\n", cbBuf, *pcbNeeded);
3057         return TRUE;
3058     }
3059
3060     if(WINSPOOL_GetStringFromReg(hkeyDriver, Help_FileW, strPtr, 0, &size,
3061                                  unicode)) {
3062         *pcbNeeded += size;
3063         if(*pcbNeeded <= cbBuf)
3064             WINSPOOL_GetStringFromReg(hkeyDriver, Help_FileW, strPtr,
3065                                       size, &tmp, unicode);
3066         if(ptr)
3067             ((PDRIVER_INFO_3W) ptr)->pHelpFile = (LPWSTR)strPtr;
3068         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3069     }
3070
3071     if(WINSPOOL_GetStringFromReg(hkeyDriver, Dependent_FilesW, strPtr, 0,
3072                              &size, unicode)) {
3073         *pcbNeeded += size;
3074         if(*pcbNeeded <= cbBuf)
3075             WINSPOOL_GetStringFromReg(hkeyDriver, Dependent_FilesW, strPtr,
3076                                       size, &tmp, unicode);
3077         if(ptr)
3078             ((PDRIVER_INFO_3W) ptr)->pDependentFiles = (LPWSTR)strPtr;
3079         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3080     }
3081
3082     if(WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, 0, &size,
3083                                  unicode)) {
3084         *pcbNeeded += size;
3085         if(*pcbNeeded <= cbBuf)
3086             WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr,
3087                                       size, &tmp, unicode);
3088         if(ptr)
3089             ((PDRIVER_INFO_3W) ptr)->pMonitorName = (LPWSTR)strPtr;
3090         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3091     }
3092
3093     if(WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, 0, &size,
3094                                  unicode)) {
3095         *pcbNeeded += size;
3096         if(*pcbNeeded <= cbBuf)
3097             WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr,
3098                                       size, &tmp, unicode);
3099         if(ptr)
3100             ((PDRIVER_INFO_3W) ptr)->pDefaultDataType = (LPWSTR)strPtr;
3101         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3102     }
3103
3104     TRACE("buffer space %ld required %ld\n", cbBuf, *pcbNeeded);
3105     RegCloseKey(hkeyDriver);
3106     return TRUE;
3107 }
3108
3109 /*****************************************************************************
3110  *          WINSPOOL_GetPrinterDriver
3111  */
3112 static BOOL WINSPOOL_GetPrinterDriver(HANDLE hPrinter, LPWSTR pEnvironment,
3113                                       DWORD Level, LPBYTE pDriverInfo,
3114                                       DWORD cbBuf, LPDWORD pcbNeeded,
3115                                       BOOL unicode)
3116 {
3117     LPCWSTR name;
3118     WCHAR DriverName[100];
3119     DWORD ret, type, size, needed = 0;
3120     LPBYTE ptr = NULL;
3121     HKEY hkeyPrinter, hkeyPrinters, hkeyDrivers;
3122
3123     TRACE("(%p,%s,%ld,%p,%ld,%p)\n",hPrinter,debugstr_w(pEnvironment),
3124           Level,pDriverInfo,cbBuf, pcbNeeded);
3125
3126     ZeroMemory(pDriverInfo, cbBuf);
3127
3128     if (!(name = get_opened_printer_name(hPrinter))) {
3129         SetLastError(ERROR_INVALID_HANDLE);
3130         return FALSE;
3131     }
3132     if(Level < 1 || Level > 3) {
3133         SetLastError(ERROR_INVALID_LEVEL);
3134         return FALSE;
3135     }
3136     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
3137        ERROR_SUCCESS) {
3138         ERR("Can't create Printers key\n");
3139         return FALSE;
3140     }
3141     if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter)
3142        != ERROR_SUCCESS) {
3143         ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
3144         RegCloseKey(hkeyPrinters);
3145         SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
3146         return FALSE;
3147     }
3148     size = sizeof(DriverName);
3149     DriverName[0] = 0;
3150     ret = RegQueryValueExW(hkeyPrinter, Printer_DriverW, 0, &type,
3151                            (LPBYTE)DriverName, &size);
3152     RegCloseKey(hkeyPrinter);
3153     RegCloseKey(hkeyPrinters);
3154     if(ret != ERROR_SUCCESS) {
3155         ERR("Can't get DriverName for printer %s\n", debugstr_w(name));
3156         return FALSE;
3157     }
3158
3159     hkeyDrivers = WINSPOOL_OpenDriverReg( pEnvironment, TRUE);
3160     if(!hkeyDrivers) {
3161         ERR("Can't create Drivers key\n");
3162         return FALSE;
3163     }
3164
3165     switch(Level) {
3166     case 1:
3167         size = sizeof(DRIVER_INFO_1W);
3168         break;
3169     case 2:
3170         size = sizeof(DRIVER_INFO_2W);
3171         break;
3172     case 3:
3173         size = sizeof(DRIVER_INFO_3W);
3174         break;
3175     default:
3176         ERR("Invalid level\n");
3177         return FALSE;
3178     }
3179
3180     if(size <= cbBuf)
3181         ptr = pDriverInfo + size;
3182
3183     if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverName,
3184                          pEnvironment, Level, pDriverInfo,
3185                          (cbBuf < size) ? NULL : ptr,
3186                          (cbBuf < size) ? 0 : cbBuf - size,
3187                          &needed, unicode)) {
3188             RegCloseKey(hkeyDrivers);
3189             return FALSE;
3190     }
3191
3192     RegCloseKey(hkeyDrivers);
3193
3194     if(pcbNeeded) *pcbNeeded = size + needed;
3195     TRACE("buffer space %ld required %ld\n", cbBuf, *pcbNeeded);
3196     if(cbBuf >= needed) return TRUE;
3197     SetLastError(ERROR_INSUFFICIENT_BUFFER);
3198     return FALSE;
3199 }
3200
3201 /*****************************************************************************
3202  *          GetPrinterDriverA  [WINSPOOL.@]
3203  */
3204 BOOL WINAPI GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment,
3205                               DWORD Level, LPBYTE pDriverInfo,
3206                               DWORD cbBuf, LPDWORD pcbNeeded)
3207 {
3208     BOOL ret;
3209     UNICODE_STRING pEnvW;
3210     PWSTR pwstrEnvW;
3211     
3212     pwstrEnvW = asciitounicode(&pEnvW, pEnvironment);
3213     ret = WINSPOOL_GetPrinterDriver(hPrinter, pwstrEnvW, Level, pDriverInfo,
3214                                     cbBuf, pcbNeeded, FALSE);
3215     RtlFreeUnicodeString(&pEnvW);
3216     return ret;
3217 }
3218 /*****************************************************************************
3219  *          GetPrinterDriverW  [WINSPOOL.@]
3220  */
3221 BOOL WINAPI GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment,
3222                                   DWORD Level, LPBYTE pDriverInfo,
3223                                   DWORD cbBuf, LPDWORD pcbNeeded)
3224 {
3225     return WINSPOOL_GetPrinterDriver(hPrinter, pEnvironment, Level,
3226                                      pDriverInfo, cbBuf, pcbNeeded, TRUE);
3227 }
3228
3229 /*****************************************************************************
3230  *       GetPrinterDriverDirectoryW  [WINSPOOL.@]
3231  */
3232 BOOL WINAPI GetPrinterDriverDirectoryW(LPWSTR pName, LPWSTR pEnvironment,
3233                                        DWORD Level, LPBYTE pDriverDirectory,
3234                                        DWORD cbBuf, LPDWORD pcbNeeded)
3235 {
3236     DWORD needed;
3237
3238     TRACE("(%s, %s, %ld, %p, %ld, %p)\n", debugstr_w(pName), 
3239           debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
3240     if(pName != NULL) {
3241         FIXME("pName = `%s' - unsupported\n", debugstr_w(pName));
3242         SetLastError(ERROR_INVALID_PARAMETER);
3243         return FALSE;
3244     }
3245     if(pEnvironment != NULL) {
3246         FIXME("pEnvironment = `%s' - unsupported\n", debugstr_w(pEnvironment));
3247         SetLastError(ERROR_INVALID_ENVIRONMENT);
3248         return FALSE;
3249     }
3250     if(Level != 1)  /* win95 ignores this so we just carry on */
3251         WARN("Level = %ld - assuming 1\n", Level);
3252
3253     /* FIXME should read from registry */
3254     needed = GetSystemDirectoryW( (LPWSTR)pDriverDirectory, cbBuf/sizeof(WCHAR));
3255     /* GetSystemDirectoryW returns number of TCHAR without '\0' 
3256      * adjust this now
3257      */
3258     needed++;
3259     needed*=sizeof(WCHAR);
3260
3261     if(pcbNeeded)
3262         *pcbNeeded = needed;
3263     TRACE("required <%08lx>\n", *pcbNeeded);
3264     if(needed > cbBuf) {
3265         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3266         return FALSE;
3267     }
3268     return TRUE;
3269 }
3270
3271
3272 /*****************************************************************************
3273  *       GetPrinterDriverDirectoryA  [WINSPOOL.@]
3274  */
3275 BOOL WINAPI GetPrinterDriverDirectoryA(LPSTR pName, LPSTR pEnvironment,
3276                                        DWORD Level, LPBYTE pDriverDirectory,
3277                                        DWORD cbBuf, LPDWORD pcbNeeded)
3278 {
3279     UNICODE_STRING nameW, environmentW;
3280     BOOL ret;
3281     DWORD pcbNeededW;
3282     INT len = cbBuf * sizeof(WCHAR)/sizeof(CHAR);
3283     WCHAR *driverDirectoryW = NULL;
3284
3285     if (len) driverDirectoryW = HeapAlloc( GetProcessHeap(), 0, len );
3286
3287     if(pName) RtlCreateUnicodeStringFromAsciiz(&nameW, pName);
3288     else nameW.Buffer = NULL;
3289     if(pEnvironment) RtlCreateUnicodeStringFromAsciiz(&environmentW, pEnvironment);
3290     else environmentW.Buffer = NULL;
3291
3292     ret = GetPrinterDriverDirectoryW( nameW.Buffer, environmentW.Buffer, Level,
3293                                       (LPBYTE)driverDirectoryW, len, &pcbNeededW );
3294     if (ret) {
3295         DWORD needed;
3296         needed = 1 + WideCharToMultiByte( CP_ACP, 0, driverDirectoryW, -1, 
3297                                    (LPSTR)pDriverDirectory, cbBuf, NULL, NULL);
3298         if(pcbNeeded)
3299             *pcbNeeded = needed;
3300         ret = (needed <= cbBuf) ? TRUE : FALSE;
3301     } else 
3302         if(pcbNeeded) *pcbNeeded = pcbNeededW * sizeof(CHAR)/sizeof(WCHAR);
3303
3304     TRACE("provided<%ld> required <%ld>\n", cbBuf, *pcbNeeded);
3305
3306     HeapFree( GetProcessHeap(), 0, driverDirectoryW );
3307     RtlFreeUnicodeString(&environmentW);
3308     RtlFreeUnicodeString(&nameW);
3309
3310     return ret;
3311 }
3312
3313 /*****************************************************************************
3314  *          AddPrinterDriverA  [WINSPOOL.@]
3315  */
3316 BOOL WINAPI AddPrinterDriverA(LPSTR pName, DWORD level, LPBYTE pDriverInfo)
3317 {
3318     DRIVER_INFO_3A di3;
3319     HKEY hkeyDrivers, hkeyName;
3320
3321     TRACE("(%s,%ld,%p)\n",debugstr_a(pName),level,pDriverInfo);
3322
3323     if(level != 2 && level != 3) {
3324         SetLastError(ERROR_INVALID_LEVEL);
3325         return FALSE;
3326     }
3327     if(pName != NULL) {
3328         FIXME("pName= %s - unsupported\n", debugstr_a(pName));
3329         SetLastError(ERROR_INVALID_PARAMETER);
3330         return FALSE;
3331     }
3332     if(!pDriverInfo) {
3333         WARN("pDriverInfo == NULL\n");
3334         SetLastError(ERROR_INVALID_PARAMETER);
3335         return FALSE;
3336     }
3337
3338     if(level == 3)
3339         di3 = *(DRIVER_INFO_3A *)pDriverInfo;
3340     else {
3341         memset(&di3, 0, sizeof(di3));
3342         memcpy(&di3, pDriverInfo, sizeof(DRIVER_INFO_2A));
3343     }
3344
3345     if(!di3.pName || !di3.pDriverPath || !di3.pConfigFile ||
3346        !di3.pDataFile) {
3347         SetLastError(ERROR_INVALID_PARAMETER);
3348         return FALSE;
3349     }
3350     if(!di3.pDefaultDataType) di3.pDefaultDataType = "";
3351     if(!di3.pDependentFiles) di3.pDependentFiles = "\0";
3352     if(!di3.pHelpFile) di3.pHelpFile = "";
3353     if(!di3.pMonitorName) di3.pMonitorName = "";
3354
3355     hkeyDrivers = WINSPOOL_OpenDriverReg(di3.pEnvironment, FALSE);
3356
3357     if(!hkeyDrivers) {
3358         ERR("Can't create Drivers key\n");
3359         return FALSE;
3360     }
3361
3362     if(level == 2) { /* apparently can't overwrite with level2 */
3363         if(RegOpenKeyA(hkeyDrivers, di3.pName, &hkeyName) == ERROR_SUCCESS) {
3364             RegCloseKey(hkeyName);
3365             RegCloseKey(hkeyDrivers);
3366             WARN("Trying to create existing printer driver %s\n", debugstr_a(di3.pName));
3367             SetLastError(ERROR_PRINTER_DRIVER_ALREADY_INSTALLED);
3368             return FALSE;
3369         }
3370     }
3371     if(RegCreateKeyA(hkeyDrivers, di3.pName, &hkeyName) != ERROR_SUCCESS) {
3372         RegCloseKey(hkeyDrivers);
3373         ERR("Can't create Name key\n");
3374         return FALSE;
3375     }
3376     RegSetValueExA(hkeyName, "Configuration File", 0, REG_SZ, (LPBYTE) di3.pConfigFile,
3377                    0);
3378     RegSetValueExA(hkeyName, "Data File", 0, REG_SZ, (LPBYTE) di3.pDataFile, 0);
3379     RegSetValueExA(hkeyName, "Driver", 0, REG_SZ, (LPBYTE) di3.pDriverPath, 0);
3380     RegSetValueExA(hkeyName, "Version", 0, REG_DWORD, (LPBYTE) &di3.cVersion,
3381                    sizeof(DWORD));
3382     RegSetValueExA(hkeyName, "Datatype", 0, REG_SZ, (LPBYTE) di3.pDefaultDataType, 0);
3383     RegSetValueExA(hkeyName, "Dependent Files", 0, REG_MULTI_SZ,
3384                    (LPBYTE) di3.pDependentFiles, 0);
3385     RegSetValueExA(hkeyName, "Help File", 0, REG_SZ, (LPBYTE) di3.pHelpFile, 0);
3386     RegSetValueExA(hkeyName, "Monitor", 0, REG_SZ, (LPBYTE) di3.pMonitorName, 0);
3387     RegCloseKey(hkeyName);
3388     RegCloseKey(hkeyDrivers);
3389
3390     return TRUE;
3391 }
3392
3393 /*****************************************************************************
3394  *          AddPrinterDriverW  [WINSPOOL.@]
3395  */
3396 BOOL WINAPI AddPrinterDriverW(LPWSTR printerName,DWORD level,
3397                                    LPBYTE pDriverInfo)
3398 {
3399     FIXME("(%s,%ld,%p): stub\n",debugstr_w(printerName),
3400           level,pDriverInfo);
3401     return FALSE;
3402 }
3403
3404 /*****************************************************************************
3405  *          AddPrintProcessorA  [WINSPOOL.@]
3406  */
3407 BOOL WINAPI AddPrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPathName,
3408                                LPSTR pPrintProcessorName)
3409 {
3410     FIXME("(%s,%s,%s,%s): stub\n", debugstr_a(pName), debugstr_a(pEnvironment),
3411           debugstr_a(pPathName), debugstr_a(pPrintProcessorName));
3412     return FALSE;
3413 }
3414
3415 /*****************************************************************************
3416  *          AddPrintProcessorW  [WINSPOOL.@]
3417  */
3418 BOOL WINAPI AddPrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPathName,
3419                                LPWSTR pPrintProcessorName)
3420 {
3421     FIXME("(%s,%s,%s,%s): stub\n", debugstr_w(pName), debugstr_w(pEnvironment),
3422           debugstr_w(pPathName), debugstr_w(pPrintProcessorName));
3423     return FALSE;
3424 }
3425
3426 /*****************************************************************************
3427  *          AddPrintProvidorA  [WINSPOOL.@]
3428  */
3429 BOOL WINAPI AddPrintProvidorA(LPSTR pName, DWORD Level, LPBYTE pProviderInfo)
3430 {
3431     FIXME("(%s,0x%08lx,%p): stub\n", debugstr_a(pName), Level, pProviderInfo);
3432     return FALSE;
3433 }
3434
3435 /*****************************************************************************
3436  *          AddPrintProvidorW  [WINSPOOL.@]
3437  */
3438 BOOL WINAPI AddPrintProvidorW(LPWSTR pName, DWORD Level, LPBYTE pProviderInfo)
3439 {
3440     FIXME("(%s,0x%08lx,%p): stub\n", debugstr_w(pName), Level, pProviderInfo);
3441     return FALSE;
3442 }
3443
3444 /*****************************************************************************
3445  *          AdvancedDocumentPropertiesA  [WINSPOOL.@]
3446  */
3447 LONG WINAPI AdvancedDocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName,
3448                                         PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput)
3449 {
3450     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_a(pDeviceName),
3451           pDevModeOutput, pDevModeInput);
3452     return 0;
3453 }
3454
3455 /*****************************************************************************
3456  *          AdvancedDocumentPropertiesW  [WINSPOOL.@]
3457  */
3458 LONG WINAPI AdvancedDocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName,
3459                                         PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput)
3460 {
3461     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_w(pDeviceName),
3462           pDevModeOutput, pDevModeInput);
3463     return 0;
3464 }
3465
3466 /*****************************************************************************
3467  *          PrinterProperties  [WINSPOOL.@]
3468  *
3469  *     Displays a dialog to set the properties of the printer.
3470  *
3471  * RETURNS
3472  *     nonzero on success or zero on failure
3473  *
3474  * BUGS
3475  *         implemented as stub only
3476  */
3477 BOOL WINAPI PrinterProperties(HWND hWnd,      /* [in] handle to parent window */
3478                               HANDLE hPrinter /* [in] handle to printer object */
3479 ){
3480     FIXME("(%p,%p): stub\n", hWnd, hPrinter);
3481     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3482     return FALSE;
3483 }
3484
3485 /*****************************************************************************
3486  *          EnumJobsA [WINSPOOL.@]
3487  *
3488  */
3489 BOOL WINAPI EnumJobsA(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
3490                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
3491                       LPDWORD pcReturned)
3492 {
3493     FIXME("(%p,first=%ld,no=%ld,level=%ld,job=%p,cb=%ld,%p,%p), stub!\n",
3494         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
3495     );
3496     if(pcbNeeded) *pcbNeeded = 0;
3497     if(pcReturned) *pcReturned = 0;
3498     return FALSE;
3499 }
3500
3501
3502 /*****************************************************************************
3503  *          EnumJobsW [WINSPOOL.@]
3504  *
3505  */
3506 BOOL WINAPI EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
3507                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
3508                       LPDWORD pcReturned)
3509 {
3510     FIXME("(%p,first=%ld,no=%ld,level=%ld,job=%p,cb=%ld,%p,%p), stub!\n",
3511         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
3512     );
3513     if(pcbNeeded) *pcbNeeded = 0;
3514     if(pcReturned) *pcReturned = 0;
3515     return FALSE;
3516 }
3517
3518 /*****************************************************************************
3519  *          WINSPOOL_EnumPrinterDrivers [internal]
3520  *
3521  *    Delivers information about all printer drivers installed on the
3522  *    localhost or a given server
3523  *
3524  * RETURNS
3525  *    nonzero on success or zero on failure. If the buffer for the returned
3526  *    information is too small the function will return an error
3527  *
3528  * BUGS
3529  *    - only implemented for localhost, foreign hosts will return an error
3530  */
3531 static BOOL WINSPOOL_EnumPrinterDrivers(LPWSTR pName, LPWSTR pEnvironment,
3532                                         DWORD Level, LPBYTE pDriverInfo,
3533                                         DWORD cbBuf, LPDWORD pcbNeeded,
3534                                         LPDWORD pcReturned, BOOL unicode)
3535
3536 {   HKEY  hkeyDrivers;
3537     DWORD i, needed, number = 0, size = 0;
3538     WCHAR DriverNameW[255];
3539     PBYTE ptr;
3540
3541     TRACE("%s,%s,%ld,%p,%ld,%d\n",
3542           debugstr_w(pName), debugstr_w(pEnvironment),
3543           Level, pDriverInfo, cbBuf, unicode);
3544
3545     /* check for local drivers */
3546     if(pName) {
3547         ERR("remote drivers unsupported! Current remote host is %s\n",
3548              debugstr_w(pName));
3549         return FALSE;
3550     }
3551
3552     /* check input parameter */
3553     if((Level < 1) || (Level > 3)) {
3554         ERR("unsupported level %ld\n", Level);
3555         SetLastError(ERROR_INVALID_LEVEL);
3556         return FALSE;
3557     }
3558
3559     /* initialize return values */
3560     if(pDriverInfo)
3561         memset( pDriverInfo, 0, cbBuf);
3562     *pcbNeeded  = 0;
3563     *pcReturned = 0;
3564
3565     hkeyDrivers = WINSPOOL_OpenDriverReg(pEnvironment, TRUE);
3566     if(!hkeyDrivers) {
3567         ERR("Can't open Drivers key\n");
3568         return FALSE;
3569     }
3570
3571     if(RegQueryInfoKeyA(hkeyDrivers, NULL, NULL, NULL, &number, NULL, NULL,
3572                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
3573         RegCloseKey(hkeyDrivers);
3574         ERR("Can't query Drivers key\n");
3575         return FALSE;
3576     }
3577     TRACE("Found %ld Drivers\n", number);
3578
3579     /* get size of single struct
3580      * unicode and ascii structure have the same size
3581      */
3582     switch (Level) {
3583         case 1:
3584             size = sizeof(DRIVER_INFO_1A);
3585             break;
3586         case 2:
3587             size = sizeof(DRIVER_INFO_2A);
3588             break;
3589         case 3:
3590             size = sizeof(DRIVER_INFO_3A);
3591             break;
3592     }
3593
3594     /* calculate required buffer size */
3595     *pcbNeeded = size * number;
3596
3597     for( i = 0,  ptr = (pDriverInfo && (cbBuf >= size)) ? pDriverInfo : NULL ;
3598          i < number;
3599          i++, ptr = (ptr && (cbBuf >= size * i)) ? ptr + size : NULL) {
3600         if(RegEnumKeyW(hkeyDrivers, i, DriverNameW, sizeof(DriverNameW))
3601                        != ERROR_SUCCESS) {
3602             ERR("Can't enum key number %ld\n", i);
3603             RegCloseKey(hkeyDrivers);
3604             return FALSE;
3605         }
3606         if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverNameW,
3607                          pEnvironment, Level, ptr,
3608                          (cbBuf < *pcbNeeded) ? NULL : pDriverInfo + *pcbNeeded,
3609                          (cbBuf < *pcbNeeded) ? 0 : cbBuf - *pcbNeeded,
3610                          &needed, unicode)) {
3611             RegCloseKey(hkeyDrivers);
3612             return FALSE;
3613         }
3614         (*pcbNeeded) += needed;
3615     }
3616
3617     RegCloseKey(hkeyDrivers);
3618
3619     if(cbBuf < *pcbNeeded){
3620         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3621         return FALSE;
3622     }
3623
3624     *pcReturned = number;
3625     return TRUE;
3626 }
3627
3628 /*****************************************************************************
3629  *          EnumPrinterDriversW  [WINSPOOL.@]
3630  *
3631  *    see function EnumPrinterDrivers for RETURNS, BUGS
3632  */
3633 BOOL WINAPI EnumPrinterDriversW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
3634                                 LPBYTE pDriverInfo, DWORD cbBuf,
3635                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
3636 {
3637     return WINSPOOL_EnumPrinterDrivers(pName, pEnvironment, Level, pDriverInfo,
3638                                        cbBuf, pcbNeeded, pcReturned, TRUE);
3639 }
3640
3641 /*****************************************************************************
3642  *          EnumPrinterDriversA  [WINSPOOL.@]
3643  *
3644  *    see function EnumPrinterDrivers for RETURNS, BUGS
3645  */
3646 BOOL WINAPI EnumPrinterDriversA(LPSTR pName, LPSTR pEnvironment, DWORD Level,
3647                                 LPBYTE pDriverInfo, DWORD cbBuf,
3648                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
3649 {   BOOL ret;
3650     UNICODE_STRING pNameW, pEnvironmentW;
3651     PWSTR pwstrNameW, pwstrEnvironmentW;
3652
3653     pwstrNameW = asciitounicode(&pNameW, pName);
3654     pwstrEnvironmentW = asciitounicode(&pEnvironmentW, pEnvironment);
3655
3656     ret = WINSPOOL_EnumPrinterDrivers(pwstrNameW, pwstrEnvironmentW,
3657                                       Level, pDriverInfo, cbBuf, pcbNeeded,
3658                                       pcReturned, FALSE);
3659     RtlFreeUnicodeString(&pNameW);
3660     RtlFreeUnicodeString(&pEnvironmentW);
3661
3662     return ret;
3663 }
3664
3665 static CHAR PortMonitor[] = "Wine Port Monitor";
3666 static CHAR PortDescription[] = "Wine Port";
3667
3668 static BOOL WINSPOOL_ComPortExists( LPCSTR name )
3669 {
3670     HANDLE handle;
3671
3672     handle = CreateFileA( name, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
3673                          NULL, OPEN_EXISTING, 0, NULL );
3674     if (handle == INVALID_HANDLE_VALUE)
3675         return FALSE;
3676     TRACE("Checking %s exists\n", name );
3677     CloseHandle( handle );
3678     return TRUE;
3679 }
3680
3681 static DWORD WINSPOOL_CountSerialPorts(void)
3682 {
3683     CHAR name[6];
3684     DWORD n = 0, i;
3685
3686     for (i=0; i<4; i++)
3687     {
3688         strcpy( name, "COMx:" );
3689         name[3] = '1' + i;
3690         if (WINSPOOL_ComPortExists( name ))
3691             n++;
3692     }
3693
3694     return n;
3695 }
3696
3697 /******************************************************************************
3698  *              EnumPortsA   (WINSPOOL.@)
3699  */
3700 BOOL WINAPI EnumPortsA(LPSTR name,DWORD level,LPBYTE buffer,DWORD bufsize,
3701                        LPDWORD bufneeded,LPDWORD bufreturned)
3702 {
3703     CHAR portname[10];
3704     DWORD info_size, ofs, i, printer_count, serial_count, count, n, r;
3705     const LPCSTR szPrinterPortKey = "Software\\Wine\\Wine\\Config\\spooler";
3706     HKEY hkey_printer;
3707     BOOL retval = TRUE;
3708
3709     TRACE("(%s,%ld,%p,%ld,%p,%p)\n",
3710           debugstr_a(name),level,buffer,bufsize,bufneeded,bufreturned);
3711
3712     switch( level )
3713     {
3714     case 1:
3715         info_size = sizeof (PORT_INFO_1A);
3716         break;
3717     case 2:
3718         info_size = sizeof (PORT_INFO_2A);
3719         break;
3720     default:
3721         SetLastError(ERROR_INVALID_LEVEL);
3722         return FALSE;
3723     }
3724     
3725     /* see how many exist */
3726
3727     hkey_printer = 0;
3728     serial_count = WINSPOOL_CountSerialPorts();
3729     printer_count = 0;
3730
3731     r = RegOpenKeyA( HKEY_LOCAL_MACHINE, szPrinterPortKey, &hkey_printer);
3732     if ( r == ERROR_SUCCESS )
3733     {
3734         RegQueryInfoKeyA( hkey_printer, NULL, NULL, NULL, NULL, NULL, NULL,
3735             &printer_count, NULL, NULL, NULL, NULL);
3736     }
3737     count = serial_count + printer_count;
3738
3739     /* then fill in the structure info structure once
3740        we know the offset to the first string */
3741
3742     memset( buffer, 0, bufsize );
3743     n = 0;
3744     ofs = info_size*count; 
3745     for ( i=0; i<count; i++)
3746     {
3747         DWORD vallen = sizeof(portname) - 1;
3748
3749         /* get the serial port values, then the printer values */
3750         if ( i < serial_count )
3751         {
3752             strcpy( portname, "COMx:" );
3753             portname[3] = '1' + i;
3754             if (!WINSPOOL_ComPortExists( portname ))
3755                 continue;
3756
3757             TRACE("Found %s\n", portname );
3758             vallen = strlen( portname );
3759         }
3760         else
3761         {
3762             r = RegEnumValueA( hkey_printer, i-serial_count, 
3763                      portname, &vallen, NULL, NULL, NULL, 0 );
3764             if ( r )
3765                 continue;
3766         }
3767
3768         /* add a colon if necessary, and make it upper case */
3769         CharUpperBuffA(portname,vallen);
3770         if (strcasecmp(portname,"nul")!=0)
3771             if (vallen && (portname[vallen-1] != ':') )
3772                 lstrcatA(portname,":");
3773
3774         /* add the port info structure if we can fit it */
3775         if ( info_size*(n+1) < bufsize )
3776         {
3777             if ( level == 1)
3778             {
3779                 PORT_INFO_1A *info = (PORT_INFO_1A*) &buffer[info_size*n];
3780                 info->pName = (LPSTR) &buffer[ofs];
3781             }
3782             else if ( level == 2)
3783             {
3784                 PORT_INFO_2A *info = (PORT_INFO_2A*) &buffer[info_size*n];
3785                 info->pPortName = (LPSTR) &buffer[ofs];
3786                 /* FIXME: fill in more stuff here */
3787                 info->pMonitorName = PortMonitor;
3788                 info->pDescription = PortDescription;
3789                 info->fPortType = PORT_TYPE_WRITE|PORT_TYPE_READ;
3790             }
3791
3792             /* add the name of the port if we can fit it */
3793             if ( ofs < bufsize )
3794                 lstrcpynA((LPSTR)&buffer[ofs],portname,bufsize - ofs);
3795
3796             n++;
3797         }
3798         else
3799             retval = FALSE;
3800         ofs += lstrlenA(portname)+1;
3801     }
3802
3803     RegCloseKey(hkey_printer);
3804
3805     if(bufneeded)
3806         *bufneeded = ofs;
3807
3808     if(bufreturned)
3809         *bufreturned = n;
3810
3811     return retval;
3812 }
3813
3814 /******************************************************************************
3815  *      EnumPortsW   (WINSPOOL.@)
3816  */
3817 BOOL WINAPI EnumPortsW(LPWSTR name,DWORD level,LPBYTE buffer,DWORD bufsize,
3818                        LPDWORD bufneeded,LPDWORD bufreturned)
3819 {
3820     FIXME("(%s,%ld,%p,%ld,%p,%p) - stub\n",
3821           debugstr_w(name),level,buffer,bufsize,bufneeded,bufreturned);
3822     return FALSE;
3823 }
3824
3825 /******************************************************************************
3826  *              GetDefaultPrinterW   (WINSPOOL.@)
3827  *
3828  * FIXME
3829  *      This function must read the value from data 'device' of key
3830  *      HCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows
3831  */
3832 BOOL WINAPI GetDefaultPrinterW(LPWSTR name, LPDWORD namesize)
3833 {
3834     BOOL  retval = TRUE;
3835     DWORD insize, len;
3836     WCHAR *buffer, *ptr;
3837
3838     if (!namesize)
3839     {
3840         SetLastError(ERROR_INVALID_PARAMETER);
3841         return FALSE;
3842     }
3843
3844     /* make the buffer big enough for the stuff from the profile/registry,
3845      * the content must fit into the local buffer to compute the correct
3846      * size even if the extern buffer is too small or not given.
3847      * (20 for ,driver,port) */
3848     insize = *namesize;
3849     len = max(100, (insize + 20));
3850     buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
3851
3852     if (!GetProfileStringW(windowsW, deviceW, emptyStringW, buffer, len))
3853     {
3854         SetLastError (ERROR_FILE_NOT_FOUND);
3855         retval = FALSE;
3856         goto end;
3857     }
3858     TRACE("%s\n", debugstr_w(buffer));
3859
3860     if ((ptr = strchrW(buffer, ',')) == NULL)
3861     {
3862         SetLastError(ERROR_INVALID_NAME);
3863         retval = FALSE;
3864         goto end;
3865     }
3866
3867     *ptr = 0;
3868     *namesize = strlenW(buffer) + 1;
3869     if(!name || (*namesize > insize))
3870     {
3871         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3872         retval = FALSE;
3873         goto end;
3874     }
3875     strcpyW(name, buffer);
3876
3877 end:
3878     HeapFree( GetProcessHeap(), 0, buffer);
3879     return retval;
3880 }
3881
3882
3883 /******************************************************************************
3884  *              GetDefaultPrinterA   (WINSPOOL.@)
3885  */
3886 BOOL WINAPI GetDefaultPrinterA(LPSTR name, LPDWORD namesize)
3887 {
3888     BOOL  retval = TRUE;
3889     DWORD insize = 0;
3890     WCHAR *bufferW = NULL;
3891
3892     if (!namesize)
3893     {
3894         SetLastError(ERROR_INVALID_PARAMETER);
3895         return FALSE;
3896     }
3897
3898     if(name && *namesize) {
3899         insize = *namesize;
3900         bufferW = HeapAlloc( GetProcessHeap(), 0, insize * sizeof(WCHAR));
3901     }
3902
3903     if(!GetDefaultPrinterW( bufferW, namesize)) {
3904         retval = FALSE;
3905         goto end;
3906     }
3907
3908     *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, name, insize,
3909                                     NULL, NULL);
3910     if (!*namesize)
3911     {
3912         *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
3913         retval = FALSE;
3914     }
3915     TRACE("0x%08lx/0x%08lx:%s\n", *namesize, insize, debugstr_w(bufferW));
3916
3917 end:
3918     HeapFree( GetProcessHeap(), 0, bufferW);
3919     return retval;
3920 }
3921
3922
3923 /******************************************************************************
3924  *              SetPrinterDataExA   (WINSPOOL.@)
3925  */
3926 DWORD WINAPI SetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
3927                                LPCSTR pValueName, DWORD Type,
3928                                LPBYTE pData, DWORD cbData)
3929 {
3930     HKEY hkeyPrinter, hkeySubkey;
3931     DWORD ret;
3932
3933     TRACE("(%p, %s, %s %08lx, %p, %08lx)\n", hPrinter, debugstr_a(pKeyName),
3934           debugstr_a(pValueName), Type, pData, cbData);
3935
3936     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
3937        != ERROR_SUCCESS)
3938         return ret;
3939
3940     if((ret = RegCreateKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
3941        != ERROR_SUCCESS) {
3942         ERR("Can't create subkey %s\n", debugstr_a(pKeyName));
3943         RegCloseKey(hkeyPrinter);
3944         return ret;
3945     }
3946     ret = RegSetValueExA(hkeySubkey, pValueName, 0, Type, pData, cbData);
3947     RegCloseKey(hkeySubkey);
3948     RegCloseKey(hkeyPrinter);
3949     return ret;
3950 }
3951
3952 /******************************************************************************
3953  *              SetPrinterDataExW   (WINSPOOL.@)
3954  */
3955 DWORD WINAPI SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
3956                                LPCWSTR pValueName, DWORD Type,
3957                                LPBYTE pData, DWORD cbData)
3958 {
3959     HKEY hkeyPrinter, hkeySubkey;
3960     DWORD ret;
3961
3962     TRACE("(%p, %s, %s %08lx, %p, %08lx)\n", hPrinter, debugstr_w(pKeyName),
3963           debugstr_w(pValueName), Type, pData, cbData);
3964
3965     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
3966        != ERROR_SUCCESS)
3967         return ret;
3968
3969     if((ret = RegCreateKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
3970        != ERROR_SUCCESS) {
3971         ERR("Can't create subkey %s\n", debugstr_w(pKeyName));
3972         RegCloseKey(hkeyPrinter);
3973         return ret;
3974     }
3975     ret = RegSetValueExW(hkeySubkey, pValueName, 0, Type, pData, cbData);
3976     RegCloseKey(hkeySubkey);
3977     RegCloseKey(hkeyPrinter);
3978     return ret;
3979 }
3980
3981 /******************************************************************************
3982  *              SetPrinterDataA   (WINSPOOL.@)
3983  */
3984 DWORD WINAPI SetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, DWORD Type,
3985                                LPBYTE pData, DWORD cbData)
3986 {
3987     return SetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, Type,
3988                              pData, cbData);
3989 }
3990
3991 /******************************************************************************
3992  *              SetPrinterDataW   (WINSPOOL.@)
3993  */
3994 DWORD WINAPI SetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, DWORD Type,
3995                              LPBYTE pData, DWORD cbData)
3996 {
3997     return SetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, Type,
3998                              pData, cbData);
3999 }
4000
4001 /******************************************************************************
4002  *              GetPrinterDataExA   (WINSPOOL.@)
4003  */
4004 DWORD WINAPI GetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
4005                                LPCSTR pValueName, LPDWORD pType,
4006                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
4007 {
4008     HKEY hkeyPrinter, hkeySubkey;
4009     DWORD ret;
4010
4011     TRACE("(%p, %s, %s %p, %p, %08lx, %p)\n", hPrinter,
4012           debugstr_a(pKeyName), debugstr_a(pValueName), pType, pData, nSize,
4013           pcbNeeded);
4014
4015     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
4016        != ERROR_SUCCESS)
4017         return ret;
4018
4019     if((ret = RegOpenKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
4020        != ERROR_SUCCESS) {
4021         WARN("Can't open subkey %s\n", debugstr_a(pKeyName));
4022         RegCloseKey(hkeyPrinter);
4023         return ret;
4024     }
4025     *pcbNeeded = nSize;
4026     ret = RegQueryValueExA(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
4027     RegCloseKey(hkeySubkey);
4028     RegCloseKey(hkeyPrinter);
4029     return ret;
4030 }
4031
4032 /******************************************************************************
4033  *              GetPrinterDataExW   (WINSPOOL.@)
4034  */
4035 DWORD WINAPI GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
4036                                LPCWSTR pValueName, LPDWORD pType,
4037                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
4038 {
4039     HKEY hkeyPrinter, hkeySubkey;
4040     DWORD ret;
4041
4042     TRACE("(%p, %s, %s %p, %p, %08lx, %p)\n", hPrinter,
4043           debugstr_w(pKeyName), debugstr_w(pValueName), pType, pData, nSize,
4044           pcbNeeded);
4045
4046     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
4047        != ERROR_SUCCESS)
4048         return ret;
4049
4050     if((ret = RegOpenKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
4051        != ERROR_SUCCESS) {
4052         WARN("Can't open subkey %s\n", debugstr_w(pKeyName));
4053         RegCloseKey(hkeyPrinter);
4054         return ret;
4055     }
4056     *pcbNeeded = nSize;
4057     ret = RegQueryValueExW(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
4058     RegCloseKey(hkeySubkey);
4059     RegCloseKey(hkeyPrinter);
4060     return ret;
4061 }
4062
4063 /******************************************************************************
4064  *              GetPrinterDataA   (WINSPOOL.@)
4065  */
4066 DWORD WINAPI GetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, LPDWORD pType,
4067                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
4068 {
4069     return GetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, pType,
4070                              pData, nSize, pcbNeeded);
4071 }
4072
4073 /******************************************************************************
4074  *              GetPrinterDataW   (WINSPOOL.@)
4075  */
4076 DWORD WINAPI GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType,
4077                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
4078 {
4079     return GetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, pType,
4080                              pData, nSize, pcbNeeded);
4081 }
4082
4083 /*******************************************************************************
4084  *              EnumPrinterDataExW      [WINSPOOL.@]
4085  */
4086 DWORD WINAPI EnumPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
4087                                 LPBYTE pEnumValues, DWORD cbEnumValues,
4088                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
4089 {
4090     HKEY                    hkPrinter, hkSubKey;
4091     DWORD                   r, ret, dwIndex, cValues, cbMaxValueNameLen,
4092                             cbValueNameLen, cbMaxValueLen, cbValueLen,
4093                             cbBufSize, dwType;
4094     LPWSTR                  lpValueName;
4095     HANDLE                  hHeap;
4096     PBYTE                   lpValue;
4097     PPRINTER_ENUM_VALUESW   ppev;
4098
4099     TRACE ("%p %s\n", hPrinter, debugstr_w (pKeyName));
4100
4101     if (pKeyName == NULL || *pKeyName == 0)
4102         return ERROR_INVALID_PARAMETER;
4103
4104     ret = WINSPOOL_GetOpenedPrinterRegKey (hPrinter, &hkPrinter);
4105     if (ret != ERROR_SUCCESS)
4106     {
4107         TRACE ("WINSPOOL_GetOpenedPrinterRegKey (%p) returned %li\n",
4108                 hPrinter, ret);
4109         return ret;
4110     }
4111
4112     ret = RegOpenKeyExW (hkPrinter, pKeyName, 0, KEY_READ, &hkSubKey);
4113     if (ret != ERROR_SUCCESS)
4114     {
4115         r = RegCloseKey (hkPrinter);
4116         if (r != ERROR_SUCCESS)
4117             WARN ("RegCloseKey returned %li\n", r);
4118         TRACE ("RegOpenKeyExW (%p, %s) returned %li\n", hPrinter,
4119                 debugstr_w (pKeyName), ret);
4120         return ret;
4121     }
4122
4123     ret = RegCloseKey (hkPrinter);
4124     if (ret != ERROR_SUCCESS)
4125     {
4126         ERR ("RegCloseKey returned %li\n", ret);
4127         r = RegCloseKey (hkSubKey);
4128         if (r != ERROR_SUCCESS)
4129             WARN ("RegCloseKey returned %li\n", r);
4130         return ret;
4131     }
4132
4133     ret = RegQueryInfoKeyW (hkSubKey, NULL, NULL, NULL, NULL, NULL, NULL,
4134             &cValues, &cbMaxValueNameLen, &cbMaxValueLen, NULL, NULL);
4135     if (ret != ERROR_SUCCESS)
4136     {
4137         r = RegCloseKey (hkSubKey);
4138         if (r != ERROR_SUCCESS)
4139             WARN ("RegCloseKey returned %li\n", r);
4140         TRACE ("RegQueryInfoKeyW (%p) returned %li\n", hkSubKey, ret);
4141         return ret;
4142     }
4143
4144     TRACE ("RegQueryInfoKeyW returned cValues = %li, cbMaxValueNameLen = %li, "
4145             "cbMaxValueLen = %li\n", cValues, cbMaxValueNameLen, cbMaxValueLen);
4146
4147     if (cValues == 0)                   /* empty key */
4148     {
4149         r = RegCloseKey (hkSubKey);
4150         if (r != ERROR_SUCCESS)
4151             WARN ("RegCloseKey returned %li\n", r);
4152         *pcbEnumValues = *pnEnumValues = 0;
4153         return ERROR_SUCCESS;
4154     }
4155
4156     ++cbMaxValueNameLen;                        /* allow for trailing '\0' */
4157
4158     hHeap = GetProcessHeap ();
4159     if (hHeap == NULL)
4160     {
4161         ERR ("GetProcessHeap failed\n");
4162         r = RegCloseKey (hkSubKey);
4163         if (r != ERROR_SUCCESS)
4164             WARN ("RegCloseKey returned %li\n", r);
4165         return ERROR_OUTOFMEMORY;
4166     }
4167
4168     lpValueName = HeapAlloc (hHeap, 0, cbMaxValueNameLen * sizeof (WCHAR));
4169     if (lpValueName == NULL)
4170     {
4171         ERR ("Failed to allocate %li bytes from process heap\n",
4172                 cbMaxValueNameLen * sizeof (WCHAR));
4173         r = RegCloseKey (hkSubKey);
4174         if (r != ERROR_SUCCESS)
4175             WARN ("RegCloseKey returned %li\n", r);
4176         return ERROR_OUTOFMEMORY;
4177     }
4178
4179     lpValue = HeapAlloc (hHeap, 0, cbMaxValueLen);
4180     if (lpValue == NULL)
4181     {
4182         ERR ("Failed to allocate %li bytes from process heap\n", cbMaxValueLen);
4183         if (HeapFree (hHeap, 0, lpValueName) == 0)
4184             WARN ("HeapFree failed with code %li\n", GetLastError ());
4185         r = RegCloseKey (hkSubKey);
4186         if (r != ERROR_SUCCESS)
4187             WARN ("RegCloseKey returned %li\n", r);
4188         return ERROR_OUTOFMEMORY;
4189     }
4190
4191     TRACE ("pass 1: calculating buffer required for all names and values\n");
4192
4193     cbBufSize = cValues * sizeof (PRINTER_ENUM_VALUESW);
4194
4195     TRACE ("%li bytes required for %li headers\n", cbBufSize, cValues);
4196
4197     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
4198     {
4199         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
4200         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
4201                 NULL, NULL, lpValue, &cbValueLen);
4202         if (ret != ERROR_SUCCESS)
4203         {
4204             if (HeapFree (hHeap, 0, lpValue) == 0)
4205                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4206             if (HeapFree (hHeap, 0, lpValueName) == 0)
4207                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4208             r = RegCloseKey (hkSubKey);
4209             if (r != ERROR_SUCCESS)
4210                 WARN ("RegCloseKey returned %li\n", r);
4211             TRACE ("RegEnumValueW (%li) returned %li\n", dwIndex, ret);
4212             return ret;
4213         }
4214
4215         TRACE ("%s [%li]: name needs %li bytes, data needs %li bytes\n",
4216                 debugstr_w (lpValueName), dwIndex,
4217                 (cbValueNameLen + 1) * sizeof (WCHAR), cbValueLen);
4218
4219         cbBufSize += (cbValueNameLen + 1) * sizeof (WCHAR);
4220         cbBufSize += cbValueLen;
4221     }
4222
4223     TRACE ("%li bytes required for all %li values\n", cbBufSize, cValues);
4224
4225     *pcbEnumValues = cbBufSize;
4226     *pnEnumValues = cValues;
4227
4228     if (cbEnumValues < cbBufSize)       /* buffer too small */
4229     {
4230         if (HeapFree (hHeap, 0, lpValue) == 0)
4231             WARN ("HeapFree failed with code %li\n", GetLastError ());
4232         if (HeapFree (hHeap, 0, lpValueName) == 0)
4233             WARN ("HeapFree failed with code %li\n", GetLastError ());
4234         r = RegCloseKey (hkSubKey);
4235         if (r != ERROR_SUCCESS)
4236             WARN ("RegCloseKey returned %li\n", r);
4237         TRACE ("%li byte buffer is not large enough\n", cbEnumValues);
4238         return ERROR_MORE_DATA;
4239     }
4240
4241     TRACE ("pass 2: copying all names and values to buffer\n");
4242
4243     ppev = (PPRINTER_ENUM_VALUESW) pEnumValues;         /* array of structs */
4244     pEnumValues += cValues * sizeof (PRINTER_ENUM_VALUESW);
4245
4246     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
4247     {
4248         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
4249         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
4250                 NULL, &dwType, lpValue, &cbValueLen);
4251         if (ret != ERROR_SUCCESS)
4252         {
4253             if (HeapFree (hHeap, 0, lpValue) == 0)
4254                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4255             if (HeapFree (hHeap, 0, lpValueName) == 0)
4256                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4257             r = RegCloseKey (hkSubKey);
4258             if (r != ERROR_SUCCESS)
4259                 WARN ("RegCloseKey returned %li\n", r);
4260             TRACE ("RegEnumValueW (%li) returned %li\n", dwIndex, ret);
4261             return ret;
4262         }
4263
4264         cbValueNameLen = (cbValueNameLen + 1) * sizeof (WCHAR);
4265         memcpy (pEnumValues, lpValueName, cbValueNameLen);
4266         ppev[dwIndex].pValueName = (LPWSTR) pEnumValues;
4267         pEnumValues += cbValueNameLen;
4268
4269         /* return # of *bytes* (including trailing \0), not # of chars */
4270         ppev[dwIndex].cbValueName = cbValueNameLen;
4271
4272         ppev[dwIndex].dwType = dwType;
4273
4274         memcpy (pEnumValues, lpValue, cbValueLen);
4275         ppev[dwIndex].pData = pEnumValues;
4276         pEnumValues += cbValueLen;
4277
4278         ppev[dwIndex].cbData = cbValueLen;
4279
4280         TRACE ("%s [%li]: copied name (%li bytes) and data (%li bytes)\n",
4281                 debugstr_w (lpValueName), dwIndex, cbValueNameLen, cbValueLen);
4282     }
4283
4284     if (HeapFree (hHeap, 0, lpValue) == 0)
4285     {
4286         ret = GetLastError ();
4287         ERR ("HeapFree failed with code %li\n", ret);
4288         if (HeapFree (hHeap, 0, lpValueName) == 0)
4289             WARN ("HeapFree failed with code %li\n", GetLastError ());
4290         r = RegCloseKey (hkSubKey);
4291         if (r != ERROR_SUCCESS)
4292             WARN ("RegCloseKey returned %li\n", r);
4293         return ret;
4294     }
4295
4296     if (HeapFree (hHeap, 0, lpValueName) == 0)
4297     {
4298         ret = GetLastError ();
4299         ERR ("HeapFree failed with code %li\n", ret);
4300         r = RegCloseKey (hkSubKey);
4301         if (r != ERROR_SUCCESS)
4302             WARN ("RegCloseKey returned %li\n", r);
4303         return ret;
4304     }
4305
4306     ret = RegCloseKey (hkSubKey);
4307     if (ret != ERROR_SUCCESS)
4308     {
4309         ERR ("RegCloseKey returned %li\n", ret);
4310         return ret;
4311     }
4312
4313     return ERROR_SUCCESS;
4314 }
4315
4316 /*******************************************************************************
4317  *              EnumPrinterDataExA      [WINSPOOL.@]
4318  *
4319  * This functions returns value names and REG_SZ, REG_EXPAND_SZ, and
4320  * REG_MULTI_SZ values as ASCII strings in Unicode-sized buffers.  This is
4321  * what Windows 2000 SP1 does.
4322  *
4323  */
4324 DWORD WINAPI EnumPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
4325                                 LPBYTE pEnumValues, DWORD cbEnumValues,
4326                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
4327 {
4328     INT     len;
4329     LPWSTR  pKeyNameW;
4330     DWORD   ret, dwIndex, dwBufSize;
4331     HANDLE  hHeap;
4332     LPSTR   pBuffer;
4333
4334     TRACE ("%p %s\n", hPrinter, pKeyName);
4335
4336     if (pKeyName == NULL || *pKeyName == 0)
4337         return ERROR_INVALID_PARAMETER;
4338
4339     len = MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, NULL, 0);
4340     if (len == 0)
4341     {
4342         ret = GetLastError ();
4343         ERR ("MultiByteToWideChar failed with code %li\n", ret);
4344         return ret;
4345     }
4346
4347     hHeap = GetProcessHeap ();
4348     if (hHeap == NULL)
4349     {
4350         ERR ("GetProcessHeap failed\n");
4351         return ERROR_OUTOFMEMORY;
4352     }
4353
4354     pKeyNameW = HeapAlloc (hHeap, 0, len * sizeof (WCHAR));
4355     if (pKeyNameW == NULL)
4356     {
4357         ERR ("Failed to allocate %li bytes from process heap\n",
4358                 (LONG) len * sizeof (WCHAR));
4359         return ERROR_OUTOFMEMORY;
4360     }
4361
4362     if (MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, pKeyNameW, len) == 0)
4363     {
4364         ret = GetLastError ();
4365         ERR ("MultiByteToWideChar failed with code %li\n", ret);
4366         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
4367             WARN ("HeapFree failed with code %li\n", GetLastError ());
4368         return ret;
4369     }
4370
4371     ret = EnumPrinterDataExW (hPrinter, pKeyNameW, pEnumValues, cbEnumValues,
4372             pcbEnumValues, pnEnumValues);
4373     if (ret != ERROR_SUCCESS)
4374     {
4375         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
4376             WARN ("HeapFree failed with code %li\n", GetLastError ());
4377         TRACE ("EnumPrinterDataExW returned %li\n", ret);
4378         return ret;
4379     }
4380
4381     if (HeapFree (hHeap, 0, pKeyNameW) == 0)
4382     {
4383         ret = GetLastError ();
4384         ERR ("HeapFree failed with code %li\n", ret);
4385         return ret;
4386     }
4387
4388     if (*pnEnumValues == 0)     /* empty key */
4389         return ERROR_SUCCESS;
4390
4391     dwBufSize = 0;
4392     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
4393     {
4394         PPRINTER_ENUM_VALUESW ppev =
4395                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
4396
4397         if (dwBufSize < ppev->cbValueName)
4398             dwBufSize = ppev->cbValueName;
4399
4400         if (dwBufSize < ppev->cbData && (ppev->dwType == REG_SZ ||
4401                 ppev->dwType == REG_EXPAND_SZ || ppev->dwType == REG_MULTI_SZ))
4402             dwBufSize = ppev->cbData;
4403     }
4404
4405     TRACE ("Largest Unicode name or value is %li bytes\n", dwBufSize);
4406
4407     pBuffer = HeapAlloc (hHeap, 0, dwBufSize);
4408     if (pBuffer == NULL)
4409     {
4410         ERR ("Failed to allocate %li bytes from process heap\n", dwBufSize);
4411         return ERROR_OUTOFMEMORY;
4412     }
4413
4414     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
4415     {
4416         PPRINTER_ENUM_VALUESW ppev =
4417                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
4418
4419         len = WideCharToMultiByte (CP_ACP, 0, ppev->pValueName,
4420                 ppev->cbValueName / sizeof (WCHAR), pBuffer, dwBufSize, NULL,
4421                 NULL);
4422         if (len == 0)
4423         {
4424             ret = GetLastError ();
4425             ERR ("WideCharToMultiByte failed with code %li\n", ret);
4426             if (HeapFree (hHeap, 0, pBuffer) == 0)
4427                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4428             return ret;
4429         }
4430
4431         memcpy (ppev->pValueName, pBuffer, len);
4432
4433         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
4434
4435         if (ppev->dwType != REG_SZ && ppev->dwType != REG_EXPAND_SZ &&
4436                 ppev->dwType != REG_MULTI_SZ)
4437             continue;
4438
4439         len = WideCharToMultiByte (CP_ACP, 0, (LPWSTR) ppev->pData,
4440                 ppev->cbData / sizeof (WCHAR), pBuffer, dwBufSize, NULL, NULL);
4441         if (len == 0)
4442         {
4443             ret = GetLastError ();
4444             ERR ("WideCharToMultiByte failed with code %li\n", ret);
4445             if (HeapFree (hHeap, 0, pBuffer) == 0)
4446                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4447             return ret;
4448         }
4449
4450         memcpy (ppev->pData, pBuffer, len);
4451
4452         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
4453         TRACE ("  (only first string of REG_MULTI_SZ printed)\n");
4454     }
4455
4456     if (HeapFree (hHeap, 0, pBuffer) == 0)
4457     {
4458         ret = GetLastError ();
4459         ERR ("HeapFree failed with code %li\n", ret);
4460         return ret;
4461     }
4462
4463     return ERROR_SUCCESS;
4464 }
4465
4466 /******************************************************************************
4467  *      AbortPrinter (WINSPOOL.@)
4468  */
4469 BOOL WINAPI AbortPrinter( HANDLE hPrinter )
4470 {
4471     FIXME("(%p), stub!\n", hPrinter);
4472     return TRUE;
4473 }
4474
4475 /******************************************************************************
4476  *              AddPortA (WINSPOOL.@)
4477  */
4478 BOOL WINAPI AddPortA(LPSTR pName, HWND hWnd, LPSTR pMonitorName)
4479 {
4480     FIXME("(%s, %p, %s), stub!\n",debugstr_a(pName),hWnd,debugstr_a(pMonitorName));
4481     return FALSE;
4482 }
4483
4484 /******************************************************************************
4485  *      AddPortW (WINSPOOL.@)
4486  */
4487 BOOL WINAPI AddPortW(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
4488 {
4489     FIXME("(%s, %p, %s), stub!\n",debugstr_w(pName),hWnd,debugstr_w(pMonitorName));
4490     return FALSE;
4491 }
4492
4493 /******************************************************************************
4494  *             AddPortExA (WINSPOOL.@)
4495  *
4496  * Adds a print spooler port without presenting a user interface.
4497  */
4498 BOOL WINAPI AddPortExA(HANDLE hMonitor, LPSTR pName, DWORD Level, LPBYTE lpBuffer, LPSTR lpMonitorName)
4499 {
4500     FIXME("(%p, %s, %ld, %p, %s), stub!\n",hMonitor, debugstr_a(pName), Level,
4501           lpBuffer, debugstr_a(lpMonitorName));
4502     return FALSE;
4503 }
4504
4505 /******************************************************************************
4506  *             AddPortExW (WINSPOOL.@)
4507  *
4508  * See AddPortExW.
4509  */
4510 BOOL WINAPI AddPortExW(HANDLE hMonitor, LPWSTR pName, DWORD Level, LPBYTE lpBuffer, LPWSTR lpMonitorName)
4511 {
4512     FIXME("(%p, %s, %ld, %p, %s), stub!\n", hMonitor, debugstr_w(pName), Level,
4513           lpBuffer, debugstr_w(lpMonitorName));
4514     return FALSE;
4515 }
4516
4517 /******************************************************************************
4518  *      AddPrinterConnectionA (WINSPOOL.@)
4519  */
4520 BOOL WINAPI AddPrinterConnectionA( LPSTR pName )
4521 {
4522     FIXME("%s\n", debugstr_a(pName));
4523     return FALSE;
4524 }
4525
4526 /******************************************************************************
4527  *      AddPrinterConnectionW (WINSPOOL.@)
4528  */
4529 BOOL WINAPI AddPrinterConnectionW( LPWSTR pName )
4530 {
4531     FIXME("%s\n", debugstr_w(pName));
4532     return FALSE;
4533 }
4534
4535 /******************************************************************************
4536  *              AddPrinterDriverExW (WINSPOOL.@)
4537  */
4538 BOOL WINAPI AddPrinterDriverExW( LPWSTR pName, DWORD Level,
4539     LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
4540 {
4541     FIXME("%s %ld %p %ld\n", debugstr_w(pName),
4542            Level, pDriverInfo, dwFileCopyFlags);
4543     SetLastError(ERROR_PRINTER_DRIVER_BLOCKED);
4544     return FALSE;
4545 }
4546
4547 /******************************************************************************
4548  *              AddPrinterDriverExA (WINSPOOL.@)
4549  */
4550 BOOL WINAPI AddPrinterDriverExA( LPSTR pName, DWORD Level,
4551     LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
4552 {
4553     FIXME("%s %ld %p %ld\n", debugstr_a(pName),
4554            Level, pDriverInfo, dwFileCopyFlags);
4555     SetLastError(ERROR_PRINTER_DRIVER_BLOCKED);
4556     return FALSE;
4557 }
4558
4559 /******************************************************************************
4560  *      ConfigurePortA (WINSPOOL.@)
4561  */
4562 BOOL WINAPI ConfigurePortA(LPSTR pName, HWND hWnd, LPSTR pPortName)
4563 {
4564     FIXME("%s %p %s\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
4565     return FALSE;
4566 }
4567
4568 /******************************************************************************
4569  *      ConfigurePortW (WINSPOOL.@)
4570  */
4571 BOOL WINAPI ConfigurePortW(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
4572 {
4573     FIXME("%s %p %s\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
4574     return FALSE;
4575 }
4576
4577 /******************************************************************************
4578  *      ConnectToPrinterDlg (WINSPOOL.@)
4579  */
4580 HANDLE WINAPI ConnectToPrinterDlg( HWND hWnd, DWORD Flags )
4581 {
4582     FIXME("%p %lx\n", hWnd, Flags);
4583     return NULL;
4584 }
4585
4586 /******************************************************************************
4587  *      DeletePrinterConnectionA (WINSPOOL.@)
4588  */
4589 BOOL WINAPI DeletePrinterConnectionA( LPSTR pName )
4590 {
4591     FIXME("%s\n", debugstr_a(pName));
4592     return TRUE;
4593 }
4594
4595 /******************************************************************************
4596  *      DeletePrinterConnectionW (WINSPOOL.@)
4597  */
4598 BOOL WINAPI DeletePrinterConnectionW( LPWSTR pName )
4599 {
4600     FIXME("%s\n", debugstr_w(pName));
4601     return TRUE;
4602 }
4603
4604 /******************************************************************************
4605  *              DeletePrinterDriverExW (WINSPOOL.@)
4606  */
4607 BOOL WINAPI DeletePrinterDriverExW( LPWSTR pName, LPWSTR pEnvironment,
4608     LPWSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
4609 {
4610     FIXME("%s %s %s %lx %lx\n", debugstr_w(pName), debugstr_w(pEnvironment),
4611           debugstr_w(pDriverName), dwDeleteFlag, dwVersionFlag);
4612     return TRUE;
4613 }
4614
4615 /******************************************************************************
4616  *              DeletePrinterDriverExA (WINSPOOL.@)
4617  */
4618 BOOL WINAPI DeletePrinterDriverExA( LPSTR pName, LPSTR pEnvironment,
4619     LPSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
4620 {
4621     FIXME("%s %s %s %lx %lx\n", debugstr_a(pName), debugstr_a(pEnvironment),
4622           debugstr_a(pDriverName), dwDeleteFlag, dwVersionFlag);
4623     return TRUE;
4624 }
4625
4626 /******************************************************************************
4627  *              DeletePrinterDataExW (WINSPOOL.@)
4628  */
4629 DWORD WINAPI DeletePrinterDataExW( HANDLE hPrinter, LPCWSTR pKeyName,
4630                                   LPCWSTR pValueName)
4631 {
4632     FIXME("%p %s %s\n", hPrinter, 
4633           debugstr_w(pKeyName), debugstr_w(pValueName));
4634     return ERROR_INVALID_PARAMETER;
4635 }
4636
4637 /******************************************************************************
4638  *              DeletePrinterDataExA (WINSPOOL.@)
4639  */
4640 DWORD WINAPI DeletePrinterDataExA( HANDLE hPrinter, LPCSTR pKeyName,
4641                                   LPCSTR pValueName)
4642 {
4643     FIXME("%p %s %s\n", hPrinter, 
4644           debugstr_a(pKeyName), debugstr_a(pValueName));
4645     return ERROR_INVALID_PARAMETER;
4646 }
4647
4648 /******************************************************************************
4649  *      DeletePrintProcessorA (WINSPOOL.@)
4650  */
4651 BOOL WINAPI DeletePrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProcessorName)
4652 {
4653     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
4654           debugstr_a(pPrintProcessorName));
4655     return TRUE;
4656 }
4657
4658 /******************************************************************************
4659  *      DeletePrintProcessorW (WINSPOOL.@)
4660  */
4661 BOOL WINAPI DeletePrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProcessorName)
4662 {
4663     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
4664           debugstr_w(pPrintProcessorName));
4665     return TRUE;
4666 }
4667
4668 /******************************************************************************
4669  *      DeletePrintProvidorA (WINSPOOL.@)
4670  */
4671 BOOL WINAPI DeletePrintProvidorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProviderName)
4672 {
4673     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
4674           debugstr_a(pPrintProviderName));
4675     return TRUE;
4676 }
4677
4678 /******************************************************************************
4679  *      DeletePrintProvidorW (WINSPOOL.@)
4680  */
4681 BOOL WINAPI DeletePrintProvidorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProviderName)
4682 {
4683     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
4684           debugstr_w(pPrintProviderName));
4685     return TRUE;
4686 }
4687
4688 /******************************************************************************
4689  *      EnumFormsA (WINSPOOL.@)
4690  */
4691 BOOL WINAPI EnumFormsA( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
4692     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
4693 {
4694     FIXME("%p %lx %p %lx %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
4695     return FALSE;
4696 }
4697
4698 /******************************************************************************
4699  *      EnumFormsW (WINSPOOL.@)
4700  */
4701 BOOL WINAPI EnumFormsW( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
4702     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
4703 {
4704     FIXME("%p %lx %p %lx %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
4705     return FALSE;
4706 }
4707
4708 /*****************************************************************************
4709  *          EnumMonitorsA [WINSPOOL.@]
4710  *
4711  */
4712 BOOL WINAPI EnumMonitorsA(LPSTR pName, DWORD Level, LPBYTE pMonitors,
4713                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
4714 {
4715     FIXME("%s,%ld,%p,%ld,%p,%p\n", debugstr_a(pName), Level, pMonitors,
4716           cbBuf, pcbNeeded, pcReturned);
4717     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4718     return 0;
4719 }
4720
4721 /*****************************************************************************
4722  *          EnumMonitorsW [WINSPOOL.@]
4723  *
4724  */
4725 BOOL WINAPI EnumMonitorsW(LPWSTR pName, DWORD Level, LPBYTE pMonitors,
4726                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
4727 {
4728     FIXME("%s,%ld,%p,%ld,%p,%p\n", debugstr_w(pName), Level, pMonitors,
4729           cbBuf, pcbNeeded, pcReturned);
4730     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4731     return 0;
4732 }
4733
4734 /******************************************************************************
4735  *              XcvDataW (WINSPOOL.@)
4736  *
4737  * Notes:
4738  *  There doesn't seem to be an A version...
4739  */
4740 BOOL WINAPI XcvDataW( HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData,
4741     DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData,
4742     PDWORD pcbOutputNeeded, PDWORD pdwStatus)
4743 {
4744     FIXME("%p %s %p %ld %p %ld %p %p\n", hXcv, debugstr_w(pszDataName), 
4745           pInputData, cbInputData, pOutputData,
4746           cbOutputData, pcbOutputNeeded, pdwStatus);
4747     return FALSE;
4748 }
4749
4750 /*****************************************************************************
4751  *          EnumPrinterDataA [WINSPOOL.@]
4752  *
4753  */
4754 DWORD WINAPI EnumPrinterDataA( HANDLE hPrinter, DWORD dwIndex, LPSTR pValueName,
4755     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
4756     DWORD cbData, LPDWORD pcbData )
4757 {
4758     FIXME("%p %lx %p %lx %p %p %p %lx %p\n", hPrinter, dwIndex, pValueName,
4759           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
4760     return ERROR_NO_MORE_ITEMS;
4761 }
4762
4763 /*****************************************************************************
4764  *          EnumPrinterDataW [WINSPOOL.@]
4765  *
4766  */
4767 DWORD WINAPI EnumPrinterDataW( HANDLE hPrinter, DWORD dwIndex, LPWSTR pValueName,
4768     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
4769     DWORD cbData, LPDWORD pcbData )
4770 {
4771     FIXME("%p %lx %p %lx %p %p %p %lx %p\n", hPrinter, dwIndex, pValueName,
4772           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
4773     return ERROR_NO_MORE_ITEMS;
4774 }
4775
4776 /*****************************************************************************
4777  *          EnumPrintProcessorDatatypesA [WINSPOOL.@]
4778  *
4779  */
4780 BOOL WINAPI EnumPrintProcessorDatatypesA(LPSTR pName, LPSTR pPrintProcessorName,
4781                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
4782                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
4783 {
4784     FIXME("Stub: %s %s %ld %p %ld %p %p\n", debugstr_a(pName),
4785           debugstr_a(pPrintProcessorName), Level, pDatatypes, cbBuf,
4786           pcbNeeded, pcReturned);
4787     return FALSE;
4788 }
4789
4790 /*****************************************************************************
4791  *          EnumPrintProcessorDatatypesW [WINSPOOL.@]
4792  *
4793  */
4794 BOOL WINAPI EnumPrintProcessorDatatypesW(LPWSTR pName, LPWSTR pPrintProcessorName,
4795                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
4796                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
4797 {
4798     FIXME("Stub: %s %s %ld %p %ld %p %p\n", debugstr_w(pName),
4799           debugstr_w(pPrintProcessorName), Level, pDatatypes, cbBuf,
4800           pcbNeeded, pcReturned);
4801     return FALSE;
4802 }
4803
4804 /*****************************************************************************
4805  *          EnumPrintProcessorsA [WINSPOOL.@]
4806  *
4807  */
4808 BOOL WINAPI EnumPrintProcessorsA(LPSTR pName, LPSTR pEnvironment, DWORD Level, 
4809     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
4810 {
4811     FIXME("Stub: %s %s %ld %p %ld %p %p\n", pName, pEnvironment, Level,
4812         pPrintProcessorInfo, cbBuf, pcbNeeded, pcbReturned);
4813     return FALSE;
4814 }
4815
4816 /*****************************************************************************
4817  *          EnumPrintProcessorsW [WINSPOOL.@]
4818  *
4819  */
4820 BOOL WINAPI EnumPrintProcessorsW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
4821     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
4822 {
4823     FIXME("Stub: %s %s %ld %p %ld %p %p\n", debugstr_w(pName),
4824         debugstr_w(pEnvironment), Level, pPrintProcessorInfo,
4825         cbBuf, pcbNeeded, pcbReturned);
4826     return FALSE;
4827 }
4828
4829 /*****************************************************************************
4830  *          ExtDeviceMode [WINSPOOL.@]
4831  *
4832  */
4833 LONG WINAPI ExtDeviceMode( HWND hWnd, HANDLE hInst, LPDEVMODEA pDevModeOutput,
4834     LPSTR pDeviceName, LPSTR pPort, LPDEVMODEA pDevModeInput, LPSTR pProfile,
4835     DWORD fMode)
4836 {
4837     FIXME("Stub: %p %p %p %s %s %p %s %lx\n", hWnd, hInst, pDevModeOutput,
4838           debugstr_a(pDeviceName), debugstr_a(pPort), pDevModeInput,
4839           debugstr_a(pProfile), fMode);
4840     return -1;
4841 }
4842
4843 /*****************************************************************************
4844  *          FindClosePrinterChangeNotification [WINSPOOL.@]
4845  *
4846  */
4847 BOOL WINAPI FindClosePrinterChangeNotification( HANDLE hChange )
4848 {
4849     FIXME("Stub: %p\n", hChange);
4850     return TRUE;
4851 }
4852
4853 /*****************************************************************************
4854  *          FindFirstPrinterChangeNotification [WINSPOOL.@]
4855  *
4856  */
4857 HANDLE WINAPI FindFirstPrinterChangeNotification( HANDLE hPrinter,
4858     DWORD fdwFlags, DWORD fdwOptions, LPVOID pPrinterNotifyOptions )
4859 {
4860     FIXME("Stub: %p %lx %lx %p\n",
4861           hPrinter, fdwFlags, fdwOptions, pPrinterNotifyOptions);
4862     return INVALID_HANDLE_VALUE;
4863 }
4864
4865 /*****************************************************************************
4866  *          FindNextPrinterChangeNotification [WINSPOOL.@]
4867  *
4868  */
4869 BOOL WINAPI FindNextPrinterChangeNotification( HANDLE hChange, PDWORD pdwChange,
4870     LPVOID pPrinterNotifyOptions, LPVOID *ppPrinterNotifyInfo )
4871 {
4872     FIXME("Stub: %p %p %p %p\n",
4873           hChange, pdwChange, pPrinterNotifyOptions, ppPrinterNotifyInfo);
4874     return FALSE;
4875 }
4876
4877 /*****************************************************************************
4878  *          FreePrinterNotifyInfo [WINSPOOL.@]
4879  *
4880  */
4881 BOOL WINAPI FreePrinterNotifyInfo( PPRINTER_NOTIFY_INFO pPrinterNotifyInfo )
4882 {
4883     FIXME("Stub: %p\n", pPrinterNotifyInfo);
4884     return TRUE;
4885 }
4886
4887 /*****************************************************************************
4888  *          string_to_buf
4889  *
4890  * Copies a unicode string into a buffer.  The buffer will either contain unicode or
4891  * ansi depending on the unicode parameter.
4892  */
4893 static BOOL string_to_buf(LPCWSTR str, LPBYTE ptr, DWORD cb, DWORD *size, BOOL unicode)
4894 {
4895     if(!str)
4896     {
4897         *size = 0;
4898         return TRUE;
4899     }
4900
4901     if(unicode)
4902     {
4903         *size = (strlenW(str) + 1) * sizeof(WCHAR);
4904         if(*size <= cb)
4905         {
4906             memcpy(ptr, str, *size);
4907             return TRUE;
4908         }
4909         return FALSE;
4910     }
4911     else
4912     {
4913         *size = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
4914         if(*size <= cb)
4915         {
4916             WideCharToMultiByte(CP_ACP, 0, str, -1, (LPSTR)ptr, *size, NULL, NULL);
4917             return TRUE;
4918         }
4919         return FALSE;
4920     }
4921 }
4922
4923 /*****************************************************************************
4924  *          get_job_info_1
4925  */
4926 static BOOL get_job_info_1(job_t *job, JOB_INFO_1W *ji1, LPBYTE buf, DWORD cbBuf,
4927                            LPDWORD pcbNeeded, BOOL unicode)
4928 {
4929     DWORD size, left = cbBuf;
4930     BOOL space = (cbBuf > 0);
4931     LPBYTE ptr = buf;
4932
4933     *pcbNeeded = 0;
4934
4935     if(space)
4936     {
4937         ji1->JobId = job->job_id;
4938     }
4939
4940     string_to_buf(job->document_title, ptr, left, &size, unicode);
4941     if(space && size <= left)
4942     {
4943         ji1->pDocument = (LPWSTR)ptr;
4944         ptr += size;
4945         left -= size;
4946     }
4947     else
4948         space = FALSE;
4949     *pcbNeeded += size;
4950
4951     return space;
4952 }
4953
4954 /*****************************************************************************
4955  *          get_job_info_2
4956  */
4957 static BOOL get_job_info_2(job_t *job, JOB_INFO_2W *ji2, LPBYTE buf, DWORD cbBuf,
4958                            LPDWORD pcbNeeded, BOOL unicode)
4959 {
4960     DWORD size, left = cbBuf;
4961     BOOL space = (cbBuf > 0);
4962     LPBYTE ptr = buf;
4963
4964     *pcbNeeded = 0;
4965
4966     if(space)
4967     {
4968         ji2->JobId = job->job_id;
4969     }
4970
4971     string_to_buf(job->document_title, ptr, left, &size, unicode);
4972     if(space && size <= left)
4973     {
4974         ji2->pDocument = (LPWSTR)ptr;
4975         ptr += size;
4976         left -= size;
4977     }
4978     else
4979         space = FALSE;
4980     *pcbNeeded += size;
4981
4982     return space;
4983 }
4984
4985 /*****************************************************************************
4986  *          get_job_info
4987  */
4988 static BOOL get_job_info(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
4989                          DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
4990 {
4991     BOOL ret = FALSE;
4992     DWORD needed = 0, size;
4993     job_t *job;
4994     LPBYTE ptr = pJob;
4995
4996     TRACE("%p %ld %ld %p %ld %p\n", hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
4997
4998     EnterCriticalSection(&printer_handles_cs);
4999     job = get_job(hPrinter, JobId);
5000     if(!job)
5001         goto end;
5002
5003     switch(Level)
5004     {
5005     case 1:
5006         size = sizeof(JOB_INFO_1W);
5007         if(cbBuf >= size)
5008         {
5009             cbBuf -= size;
5010             ptr += size;
5011             memset(pJob, 0, size);
5012         }
5013         else
5014             cbBuf = 0;
5015         ret = get_job_info_1(job, (JOB_INFO_1W *)pJob, ptr, cbBuf, &needed, unicode);
5016         needed += size;
5017         break;
5018
5019     case 2:
5020         size = sizeof(JOB_INFO_2W);
5021         if(cbBuf >= size)
5022         {
5023             cbBuf -= size;
5024             ptr += size;
5025             memset(pJob, 0, size);
5026         }
5027         else
5028             cbBuf = 0;
5029         ret = get_job_info_2(job, (JOB_INFO_2W *)pJob, ptr, cbBuf, &needed, unicode);
5030         needed += size;
5031         break;
5032
5033     case 3:
5034         size = sizeof(JOB_INFO_3);
5035         if(cbBuf >= size)
5036         {
5037             cbBuf -= size;
5038             memset(pJob, 0, size);
5039             ret = TRUE;
5040         }
5041         else
5042             cbBuf = 0;
5043         needed = size;
5044         break;
5045
5046     default:
5047         SetLastError(ERROR_INVALID_LEVEL);
5048         goto end;
5049     }
5050     if(pcbNeeded)
5051         *pcbNeeded = needed;
5052 end:
5053     LeaveCriticalSection(&printer_handles_cs);
5054     return ret;
5055 }
5056
5057 /*****************************************************************************
5058  *          GetJobA [WINSPOOL.@]
5059  *
5060  */
5061 BOOL WINAPI GetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
5062                     DWORD cbBuf, LPDWORD pcbNeeded)
5063 {
5064     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, FALSE);
5065 }
5066
5067 /*****************************************************************************
5068  *          GetJobW [WINSPOOL.@]
5069  *
5070  */
5071 BOOL WINAPI GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
5072                     DWORD cbBuf, LPDWORD pcbNeeded)
5073 {
5074     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, TRUE);
5075 }
5076
5077 /*****************************************************************************
5078  *          schedule_lpr
5079  */
5080 static BOOL schedule_lpr(LPCWSTR printer_name, LPCWSTR filename)
5081 {
5082     char *unixname, *queue, *cmd;
5083     char fmt[] = "lpr -P%s %s";
5084     DWORD len;
5085
5086     if(!(unixname = wine_get_unix_file_name(filename)))
5087         return FALSE;
5088
5089     len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
5090     queue = HeapAlloc(GetProcessHeap(), 0, len);
5091     WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
5092
5093     cmd = HeapAlloc(GetProcessHeap(), 0, strlen(unixname) + len + sizeof(fmt) - 5);
5094     sprintf(cmd, fmt, queue, unixname);
5095
5096     TRACE("printing with: %s\n", cmd);
5097     system(cmd);
5098
5099     HeapFree(GetProcessHeap(), 0, cmd);
5100     HeapFree(GetProcessHeap(), 0, queue);
5101     HeapFree(GetProcessHeap(), 0, unixname);
5102     return TRUE;
5103 }
5104
5105 /*****************************************************************************
5106  *          schedule_cups
5107  */
5108 static BOOL schedule_cups(LPCWSTR printer_name, LPCWSTR filename, LPCWSTR document_title)
5109 {
5110 #if HAVE_CUPS_CUPS_H
5111     if(pcupsPrintFile)
5112     {
5113         char *unixname, *queue, *doc_titleA;
5114         DWORD len;
5115         BOOL ret;
5116
5117         if(!(unixname = wine_get_unix_file_name(filename)))
5118             return FALSE;
5119
5120         len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
5121         queue = HeapAlloc(GetProcessHeap(), 0, len);
5122         WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
5123
5124         len = WideCharToMultiByte(CP_ACP, 0, document_title, -1, NULL, 0, NULL, NULL);
5125         doc_titleA = HeapAlloc(GetProcessHeap(), 0, len);
5126         WideCharToMultiByte(CP_ACP, 0, document_title, -1, doc_titleA, len, NULL, NULL);
5127
5128         TRACE("printing via cups\n");
5129         ret = pcupsPrintFile(queue, unixname, doc_titleA, 0, NULL);
5130         HeapFree(GetProcessHeap(), 0, doc_titleA);
5131         HeapFree(GetProcessHeap(), 0, queue);
5132         HeapFree(GetProcessHeap(), 0, unixname);
5133         return ret;
5134     }
5135     else
5136 #endif
5137     {
5138         return schedule_lpr(printer_name, filename);
5139     }
5140 }
5141
5142 INT_PTR CALLBACK file_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
5143 {
5144     LPWSTR filename;
5145
5146     switch(msg)
5147     {
5148     case WM_INITDIALOG:
5149         SetWindowLongPtrW(hwnd, DWLP_USER, lparam);
5150         return TRUE;
5151
5152     case WM_COMMAND:
5153         if(HIWORD(wparam) == BN_CLICKED)
5154         {
5155             if(LOWORD(wparam) == IDOK)
5156             {
5157                 HANDLE hf;
5158                 DWORD len = SendDlgItemMessageW(hwnd, EDITBOX, WM_GETTEXTLENGTH, 0, 0);
5159                 LPWSTR *output;
5160
5161                 filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
5162                 GetDlgItemTextW(hwnd, EDITBOX, filename, len + 1);
5163
5164                 if(GetFileAttributesW(filename) != INVALID_FILE_ATTRIBUTES)
5165                 {
5166                     WCHAR caption[200], message[200];
5167                     int mb_ret;
5168
5169                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
5170                     LoadStringW(WINSPOOL_hInstance, IDS_FILE_EXISTS, message, sizeof(message) / sizeof(WCHAR));
5171                     mb_ret = MessageBoxW(hwnd, message, caption, MB_OKCANCEL | MB_ICONEXCLAMATION);
5172                     if(mb_ret == IDCANCEL)
5173                     {
5174                         HeapFree(GetProcessHeap(), 0, filename);
5175                         return TRUE;
5176                     }
5177                 }
5178                 hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
5179                 if(hf == INVALID_HANDLE_VALUE)
5180                 {
5181                     WCHAR caption[200], message[200];
5182
5183                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
5184                     LoadStringW(WINSPOOL_hInstance, IDS_CANNOT_OPEN, message, sizeof(message) / sizeof(WCHAR));
5185                     MessageBoxW(hwnd, message, caption, MB_OK | MB_ICONEXCLAMATION);
5186                     HeapFree(GetProcessHeap(), 0, filename);
5187                     return TRUE;
5188                 }
5189                 CloseHandle(hf);
5190                 DeleteFileW(filename);
5191                 output = (LPWSTR *)GetWindowLongPtrW(hwnd, DWLP_USER);
5192                 *output = filename;
5193                 EndDialog(hwnd, IDOK);
5194                 return TRUE;
5195             }
5196             if(LOWORD(wparam) == IDCANCEL)
5197             {
5198                 EndDialog(hwnd, IDCANCEL);
5199                 return TRUE;
5200             }
5201         }
5202         return FALSE;
5203     }
5204     return FALSE;
5205 }
5206
5207 /*****************************************************************************
5208  *          get_filename
5209  */
5210 static BOOL get_filename(LPWSTR *filename)
5211 {
5212     return DialogBoxParamW(WINSPOOL_hInstance, MAKEINTRESOURCEW(FILENAME_DIALOG), GetForegroundWindow(),
5213                            file_dlg_proc, (LPARAM)filename) == IDOK;
5214 }
5215
5216 /*****************************************************************************
5217  *          schedule_file
5218  */
5219 static BOOL schedule_file(LPCWSTR filename)
5220 {
5221     LPWSTR output = NULL;
5222
5223     if(get_filename(&output))
5224     {
5225         TRACE("copy to %s\n", debugstr_w(output));
5226         CopyFileW(filename, output, FALSE);
5227         HeapFree(GetProcessHeap(), 0, output);
5228         return TRUE;
5229     }
5230     return FALSE;
5231 }
5232
5233 /*****************************************************************************
5234  *          schedule_pipe
5235  */
5236 static BOOL schedule_pipe(LPCWSTR cmd, LPCWSTR filename)
5237 {
5238 #ifdef HAVE_FORK
5239     char *unixname, *cmdA;
5240     DWORD len;
5241     int fds[2] = {-1, -1}, file_fd = -1, no_read;
5242     BOOL ret = FALSE;
5243     char buf[1024];
5244
5245     if(!(unixname = wine_get_unix_file_name(filename)))
5246         return FALSE;
5247
5248     len = WideCharToMultiByte(CP_ACP, 0, cmd, -1, NULL, 0, NULL, NULL);
5249     cmdA = HeapAlloc(GetProcessHeap(), 0, len);
5250     WideCharToMultiByte(CP_ACP, 0, cmd, -1, cmdA, len, NULL, NULL);
5251
5252     TRACE("printing with: %s\n", cmdA);
5253
5254     if((file_fd = open(unixname, O_RDONLY)) == -1)
5255         goto end;
5256
5257     if (pipe(fds))
5258     {
5259         ERR("pipe() failed!\n"); 
5260         goto end;
5261     }
5262
5263     if (fork() == 0)
5264     {
5265         close(0);
5266         dup2(fds[0], 0);
5267         close(fds[1]);
5268
5269         /* reset signals that we previously set to SIG_IGN */
5270         signal(SIGPIPE, SIG_DFL);
5271         signal(SIGCHLD, SIG_DFL);
5272
5273         system(cmdA);
5274         exit(0);
5275     }
5276
5277     while((no_read = read(file_fd, buf, sizeof(buf))))
5278         write(fds[1], buf, no_read);
5279
5280     ret = TRUE;
5281
5282 end:
5283     if(file_fd != -1) close(file_fd);
5284     if(fds[0] != -1) close(fds[0]);
5285     if(fds[1] != -1) close(fds[1]);
5286
5287     HeapFree(GetProcessHeap(), 0, cmdA);
5288     HeapFree(GetProcessHeap(), 0, unixname);
5289     return ret;
5290 #else
5291     return FALSE;
5292 #endif
5293 }
5294
5295 /*****************************************************************************
5296  *          schedule_unixfile
5297  */
5298 static BOOL schedule_unixfile(LPCWSTR output, LPCWSTR filename)
5299 {
5300     int in_fd, out_fd, no_read;
5301     char buf[1024];
5302     BOOL ret = FALSE;
5303     char *unixname, *outputA;
5304     DWORD len;
5305
5306     if(!(unixname = wine_get_unix_file_name(filename)))
5307         return FALSE;
5308
5309     len = WideCharToMultiByte(CP_ACP, 0, output, -1, NULL, 0, NULL, NULL);
5310     outputA = HeapAlloc(GetProcessHeap(), 0, len);
5311     WideCharToMultiByte(CP_ACP, 0, output, -1, outputA, len, NULL, NULL);
5312     
5313     out_fd = open(outputA, O_CREAT | O_TRUNC | O_WRONLY, 0666);
5314     in_fd = open(unixname, O_RDONLY);
5315     if(out_fd == -1 || in_fd == -1)
5316         goto end;
5317
5318     while((no_read = read(in_fd, buf, sizeof(buf))))
5319         write(out_fd, buf, no_read);
5320
5321     ret = TRUE;
5322 end:
5323     if(in_fd != -1) close(in_fd);
5324     if(out_fd != -1) close(out_fd);
5325     HeapFree(GetProcessHeap(), 0, outputA);
5326     HeapFree(GetProcessHeap(), 0, unixname);
5327     return ret;
5328 }
5329
5330 /*****************************************************************************
5331  *          ScheduleJob [WINSPOOL.@]
5332  *
5333  */
5334 BOOL WINAPI ScheduleJob( HANDLE hPrinter, DWORD dwJobID )
5335 {
5336     opened_printer_t *printer;
5337     BOOL ret = FALSE;
5338     struct list *cursor, *cursor2;
5339
5340     TRACE("(%p, %lx)\n", hPrinter, dwJobID);
5341     EnterCriticalSection(&printer_handles_cs);
5342     printer = get_opened_printer(hPrinter);
5343     if(!printer)
5344         goto end;
5345
5346     LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
5347     {
5348         job_t *job = LIST_ENTRY(cursor, job_t, entry);
5349         HANDLE hf;
5350
5351         if(job->job_id != dwJobID) continue;
5352
5353         hf = CreateFileW(job->filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
5354         if(hf != INVALID_HANDLE_VALUE)
5355         {
5356             PRINTER_INFO_5W *pi5;
5357             DWORD needed;
5358             HKEY hkey;
5359             WCHAR output[1024];
5360             static const WCHAR spooler_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
5361                                                 'P','r','i','n','t','i','n','g','\\','S','p','o','o','l','e','r',0};
5362
5363             GetPrinterW(hPrinter, 5, NULL, 0, &needed);
5364             pi5 = HeapAlloc(GetProcessHeap(), 0, needed);
5365             GetPrinterW(hPrinter, 5, (LPBYTE)pi5, needed, &needed);
5366             TRACE("need to schedule job %ld filename %s to port %s\n", job->job_id, debugstr_w(job->filename),
5367                   debugstr_w(pi5->pPortName));
5368             
5369             output[0] = 0;
5370
5371             /* @@ Wine registry key: HKCU\Software\Wine\Printing\Spooler */
5372             if(RegOpenKeyW(HKEY_CURRENT_USER, spooler_key, &hkey) == ERROR_SUCCESS)
5373             {
5374                 DWORD type, count = sizeof(output);
5375                 RegQueryValueExW(hkey, pi5->pPortName, NULL, &type, (LPBYTE)output, &count);
5376                 RegCloseKey(hkey);
5377             }
5378             if(output[0] == '|')
5379             {
5380                 schedule_pipe(output + 1, job->filename);
5381             }
5382             else if(output[0])
5383             {
5384                 schedule_unixfile(output, job->filename);
5385             }
5386             else if(!strncmpW(pi5->pPortName, LPR_Port, strlenW(LPR_Port)))
5387             {
5388                 schedule_lpr(pi5->pPortName + strlenW(LPR_Port), job->filename);
5389             }
5390             else if(!strncmpW(pi5->pPortName, CUPS_Port, strlenW(CUPS_Port)))
5391             {
5392                 schedule_cups(pi5->pPortName + strlenW(CUPS_Port), job->filename, job->document_title);
5393             }
5394             else if(!strncmpW(pi5->pPortName, FILE_Port, strlenW(FILE_Port)))
5395             {
5396                 schedule_file(job->filename);
5397             }
5398             else
5399             {
5400                 FIXME("can't schedule to port %s\n", debugstr_w(pi5->pPortName));
5401             }
5402             HeapFree(GetProcessHeap(), 0, pi5);
5403             CloseHandle(hf);
5404             DeleteFileW(job->filename);
5405         }
5406         list_remove(cursor);
5407         HeapFree(GetProcessHeap(), 0, job->document_title);
5408         HeapFree(GetProcessHeap(), 0, job->filename);
5409         HeapFree(GetProcessHeap(), 0, job);
5410         ret = TRUE;
5411         break;
5412     }
5413 end:
5414     LeaveCriticalSection(&printer_handles_cs);
5415     return ret;
5416 }
5417
5418 /*****************************************************************************
5419  *          StartDocDlgA [WINSPOOL.@]
5420  */
5421  LPSTR WINAPI StartDocDlgA( HANDLE hPrinter, DOCINFOA *doc )
5422 {
5423     UNICODE_STRING usBuffer;
5424     DOCINFOW docW;
5425     LPWSTR retW;
5426     LPSTR ret = NULL;
5427
5428     docW.cbSize = sizeof(docW);
5429     docW.lpszDocName = asciitounicode(&usBuffer, doc->lpszDocName);
5430     docW.lpszOutput = asciitounicode(&usBuffer, doc->lpszOutput);
5431     docW.lpszDatatype = asciitounicode(&usBuffer, doc->lpszDatatype);
5432     docW.fwType = doc->fwType;
5433
5434     retW = StartDocDlgW(hPrinter, &docW);
5435
5436     if(retW)
5437     {
5438         DWORD len = WideCharToMultiByte(CP_ACP, 0, retW, -1, NULL, 0, NULL, NULL);
5439         ret = HeapAlloc(GetProcessHeap(), 0, len);
5440         WideCharToMultiByte(CP_ACP, 0, retW, -1, ret, len, NULL, NULL);
5441         HeapFree(GetProcessHeap(), 0, retW);
5442     }
5443
5444     HeapFree(GetProcessHeap(), 0, (LPWSTR)docW.lpszDatatype);
5445     HeapFree(GetProcessHeap(), 0, (LPWSTR)docW.lpszOutput);
5446     HeapFree(GetProcessHeap(), 0, (LPWSTR)docW.lpszDocName);
5447
5448     return ret;
5449 }
5450
5451 /*****************************************************************************
5452  *          StartDocDlgW [WINSPOOL.@]
5453  *
5454  * Undocumented: Apparently used by gdi32:StartDocW() to popup the file dialog
5455  * when lpszOutput is "FILE:" or if lpszOutput is NULL and the default printer
5456  * port is "FILE:". Also returns the full path if passed a relative path.
5457  *
5458  * The caller should free the returned string from the process heap.
5459  */
5460 LPWSTR WINAPI StartDocDlgW( HANDLE hPrinter, DOCINFOW *doc )
5461 {
5462     LPWSTR ret = NULL;
5463     DWORD len, attr;
5464
5465     if(doc->lpszOutput == NULL) /* Check whether default port is FILE: */
5466     {
5467         PRINTER_INFO_5W *pi5;
5468         GetPrinterW(hPrinter, 5, NULL, 0, &len);
5469         if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
5470             return NULL;
5471         pi5 = HeapAlloc(GetProcessHeap(), 0, len);
5472         GetPrinterW(hPrinter, 5, (LPBYTE)pi5, len, &len);
5473         if(!pi5->pPortName || strcmpW(pi5->pPortName, FILE_Port))
5474         {
5475             HeapFree(GetProcessHeap(), 0, pi5);
5476             return NULL;
5477         }
5478         HeapFree(GetProcessHeap(), 0, pi5);
5479     }
5480
5481     if(doc->lpszOutput == NULL || !strcmpW(doc->lpszOutput, FILE_Port))
5482     {
5483         LPWSTR name;
5484         get_filename(&name);
5485         if(name)
5486         {
5487             if(!(len = GetFullPathNameW(name, 0, NULL, NULL)))
5488             {
5489                 HeapFree(GetProcessHeap(), 0, name);
5490                 return NULL;
5491             }
5492             ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5493             GetFullPathNameW(name, len, ret, NULL);
5494             HeapFree(GetProcessHeap(), 0, name);
5495         }
5496         return ret;
5497     }
5498
5499     if(!(len = GetFullPathNameW(doc->lpszOutput, 0, NULL, NULL)))
5500         return NULL;
5501
5502     ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5503     GetFullPathNameW(doc->lpszOutput, len, ret, NULL);
5504         
5505     attr = GetFileAttributesW(ret);
5506     if(attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY))
5507     {
5508         HeapFree(GetProcessHeap(), 0, ret);
5509         ret = NULL;
5510     }
5511     return ret;
5512 }