Implement asn.1 encoding/decoding of times, with tests.
[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_CUPS_CUPS_H
35 # include <cups/cups.h>
36 # ifndef SONAME_LIBCUPS
37 #  define SONAME_LIBCUPS "libcups.so"
38 # endif
39 #endif
40
41 #define NONAMELESSUNION
42 #define NONAMELESSSTRUCT
43 #include "wine/library.h"
44 #include "windef.h"
45 #include "winbase.h"
46 #include "winuser.h"
47 #include "winerror.h"
48 #include "winreg.h"
49 #include "wingdi.h"
50 #include "winspool.h"
51 #include "winternl.h"
52 #include "wine/windef16.h"
53 #include "wine/unicode.h"
54 #include "wine/debug.h"
55 #include "heap.h"
56 #include "winnls.h"
57
58 WINE_DEFAULT_DEBUG_CHANNEL(winspool);
59
60 static LPWSTR *printer_array;
61 static int nb_printers;
62
63 static DWORD (WINAPI *GDI_CallDeviceCapabilities16)( LPCSTR lpszDevice, LPCSTR lpszPort,
64                                                      WORD fwCapability, LPSTR lpszOutput,
65                                                      LPDEVMODEA lpdm );
66 static INT (WINAPI *GDI_CallExtDeviceMode16)( HWND hwnd, LPDEVMODEA lpdmOutput,
67                                               LPSTR lpszDevice, LPSTR lpszPort,
68                                               LPDEVMODEA lpdmInput, LPSTR lpszProfile,
69                                               DWORD fwMode );
70
71 static const char Printers[] =
72 "System\\CurrentControlSet\\control\\Print\\Printers\\";
73
74 static const WCHAR DriversW[] = { 'S','y','s','t','e','m','\\',
75                                   'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
76                                   'c','o','n','t','r','o','l','\\',
77                                   'P','r','i','n','t','\\',
78                                   'E','n','v','i','r','o','n','m','e','n','t','s','\\',
79                                   '%','s','\\','D','r','i','v','e','r','s','\\',0 };
80
81 static const WCHAR user_default_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
82                                               'M','i','c','r','o','s','o','f','t','\\',
83                                               'W','i','n','d','o','w','s',' ','N','T','\\',
84                                               'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
85                                               'W','i','n','d','o','w','s',0};
86
87 static const WCHAR user_printers_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
88                                                'M','i','c','r','o','s','o','f','t','\\',
89                                                'W','i','n','d','o','w','s',' ','N','T','\\',
90                                                'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
91                                                'D','e','v','i','c','e','s',0};
92
93 static const WCHAR DefaultEnvironmentW[] = {'W','i','n','e',0};
94
95 static const WCHAR Configuration_FileW[] = {'C','o','n','f','i','g','u','r','a','t',
96                                       'i','o','n',' ','F','i','l','e',0};
97 static const WCHAR DatatypeW[] = {'D','a','t','a','t','y','p','e',0};
98 static const WCHAR Data_FileW[] = {'D','a','t','a',' ','F','i','l','e',0};
99 static const WCHAR Default_DevModeW[] = {'D','e','f','a','u','l','t',' ','D','e','v',
100                                    'M','o','d','e',0};
101 static const WCHAR Dependent_FilesW[] = {'D','e','p','e','n','d','e','n','t',' ','F',
102                                    'i','l','e','s',0};
103 static const WCHAR DescriptionW[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
104 static const WCHAR DriverW[] = {'D','r','i','v','e','r',0};
105 static const WCHAR Help_FileW[] = {'H','e','l','p',' ','F','i','l','e',0};
106 static const WCHAR LocationW[] = {'L','o','c','a','t','i','o','n',0};
107 static const WCHAR MonitorW[] = {'M','o','n','i','t','o','r',0};
108 static const WCHAR NameW[] = {'N','a','m','e',0};
109 static const WCHAR ParametersW[] = {'P','a','r','a','m','e','t','e','r','s',0};
110 static const WCHAR PortW[] = {'P','o','r','t',0};
111 static const WCHAR Print_ProcessorW[] = {'P','r','i','n','t',' ','P','r','o','c','e',
112                                    's','s','o','r',0};
113 static const WCHAR Printer_DriverW[] = {'P','r','i','n','t','e','r',' ','D','r','i',
114                                   'v','e','r',0};
115 static const WCHAR PrinterDriverDataW[] = {'P','r','i','n','t','e','r','D','r','i',
116                                      'v','e','r','D','a','t','a',0};
117 static const WCHAR Separator_FileW[] = {'S','e','p','a','r','a','t','o','r',' ','F',
118                                   'i','l','e',0};
119 static const WCHAR Share_NameW[] = {'S','h','a','r','e',' ','N','a','m','e',0};
120 static const WCHAR WinPrintW[] = {'W','i','n','P','r','i','n','t',0};
121 static const WCHAR deviceW[]  = {'d','e','v','i','c','e',0};
122 static const WCHAR devicesW[] = {'d','e','v','i','c','e','s',0};
123 static const WCHAR windowsW[] = {'w','i','n','d','o','w','s',0};
124 static const WCHAR emptyStringW[] = {0};
125
126 static const WCHAR May_Delete_Value[] = {'W','i','n','e','M','a','y','D','e','l','e','t','e','M','e',0};
127
128 static HKEY WINSPOOL_OpenDriverReg( LPVOID pEnvironment, BOOL unicode);
129 static BOOL WINSPOOL_GetPrinterDriver(HANDLE hPrinter, LPWSTR pEnvironment,
130                                       DWORD Level, LPBYTE pDriverInfo,
131                                       DWORD cbBuf, LPDWORD pcbNeeded,
132                                       BOOL unicode);
133 static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey);
134
135 /* RtlCreateUnicodeStringFromAsciiz will return an empty string in the buffer
136    if passed a NULL string. This returns NULLs to the result. 
137 */
138 static inline PWSTR asciitounicode( UNICODE_STRING * usBufferPtr, LPCSTR src )
139 {
140     if ( (src) )
141     {
142         RtlCreateUnicodeStringFromAsciiz(usBufferPtr, src);
143         return usBufferPtr->Buffer;
144     }
145     usBufferPtr->Buffer = NULL; /* so that RtlFreeUnicodeString won't barf */
146     return NULL;
147 }
148             
149 static void
150 WINSPOOL_SetDefaultPrinter(const char *devname, const char *name,BOOL force) {
151     char qbuf[200];
152
153     /* If forcing, or no profile string entry for device yet, set the entry
154      *
155      * The always change entry if not WINEPS yet is discussable.
156      */
157     if (force                                                           ||
158         !GetProfileStringA("windows","device","*",qbuf,sizeof(qbuf))    ||
159         !strcmp(qbuf,"*")                                               ||
160         !strstr(qbuf,"WINEPS.DRV")
161     ) {
162         char *buf = HeapAlloc(GetProcessHeap(),0,strlen(name)+strlen(devname)+strlen(",WINEPS.DRV,LPR:")+1);
163         HKEY hkey;
164
165         sprintf(buf,"%s,WINEPS.DRV,LPR:%s",devname,name);
166         WriteProfileStringA("windows","device",buf);
167         if(RegCreateKeyW(HKEY_CURRENT_USER, user_default_reg_key, &hkey) == ERROR_SUCCESS) {
168             RegSetValueExA(hkey, "Device", 0, REG_SZ, buf, strlen(buf) + 1);
169             RegCloseKey(hkey);
170         }
171         HeapFree(GetProcessHeap(),0,buf);
172     }
173 }
174
175 #ifdef HAVE_CUPS_CUPS_H
176 static BOOL CUPS_LoadPrinters(void)
177 {
178     typeof(cupsGetDests) *pcupsGetDests = NULL;
179     typeof(cupsGetPPD)   *pcupsGetPPD = NULL;
180     int                   i, nrofdests;
181     BOOL                  hadprinter = FALSE;
182     cups_dest_t          *dests;
183     PRINTER_INFO_2A       pinfo2a;
184     void *cupshandle = NULL;
185     char   *port,*devline;
186     HKEY hkeyPrinter, hkeyPrinters, hkey;
187
188     cupshandle = wine_dlopen(SONAME_LIBCUPS, RTLD_NOW, NULL, 0);
189     if (!cupshandle) 
190         return FALSE;
191     TRACE("loaded %s\n", SONAME_LIBCUPS);
192
193 #define DYNCUPS(x)                                      \
194         p##x = wine_dlsym(cupshandle, #x, NULL,0);      \
195         if (!p##x) return FALSE;
196
197     DYNCUPS(cupsGetPPD);
198     DYNCUPS(cupsGetDests);
199 #undef DYNCUPS
200
201     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
202        ERROR_SUCCESS) {
203         ERR("Can't create Printers key\n");
204         return FALSE;
205     }
206
207     nrofdests = pcupsGetDests(&dests);
208     TRACE("Found %d CUPS %s:\n", nrofdests, (nrofdests == 1) ? "printer" : "printers");
209     for (i=0;i<nrofdests;i++) {
210         port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(dests[i].name)+1);
211         sprintf(port,"LPR:%s",dests[i].name);
212         devline=HeapAlloc(GetProcessHeap(),0,sizeof("WINEPS.DRV,")+strlen(port));
213         sprintf(devline,"WINEPS.DRV,%s",port);
214         WriteProfileStringA("devices",dests[i].name,devline);
215         if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
216             RegSetValueExA(hkey, dests[i].name, 0, REG_SZ, devline, strlen(devline) + 1);
217             RegCloseKey(hkey);
218         }
219         HeapFree(GetProcessHeap(),0,devline);
220
221         TRACE("Printer %d: %s\n", i, dests[i].name);
222         if(RegOpenKeyA(hkeyPrinters, dests[i].name, &hkeyPrinter) == ERROR_SUCCESS) {
223             /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
224                and continue */
225             TRACE("Printer already exists\n");
226             RegDeleteValueW(hkeyPrinter, May_Delete_Value);
227             RegCloseKey(hkeyPrinter);
228         } else {
229             memset(&pinfo2a,0,sizeof(pinfo2a));
230             pinfo2a.pPrinterName        = dests[i].name;
231             pinfo2a.pDatatype   = "RAW";
232             pinfo2a.pPrintProcessor     = "WinPrint";
233             pinfo2a.pDriverName = "PS Driver";
234             pinfo2a.pComment    = "WINEPS Printer using CUPS";
235             pinfo2a.pLocation   = "<physical location of printer>";
236             pinfo2a.pPortName   = port;
237             pinfo2a.pParameters = "<parameters?>";
238             pinfo2a.pShareName  = "<share name?>";
239             pinfo2a.pSepFile    = "<sep file?>";
240
241             if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
242                 if (GetLastError() != ERROR_PRINTER_ALREADY_EXISTS)
243                     ERR("printer '%s' not added by AddPrinterA (error %ld)\n",dests[i].name,GetLastError());
244             }
245         }
246         HeapFree(GetProcessHeap(),0,port);
247
248         hadprinter = TRUE;
249         if (dests[i].is_default)
250             WINSPOOL_SetDefaultPrinter(dests[i].name, dests[i].name, TRUE);
251     }
252     RegCloseKey(hkeyPrinters);
253     wine_dlclose(cupshandle, NULL, 0);
254     return hadprinter;
255 }
256 #endif
257
258 static BOOL
259 PRINTCAP_ParseEntry(char *pent,BOOL isfirst) {
260     PRINTER_INFO_2A     pinfo2a;
261     char                *e,*s,*name,*prettyname,*devname;
262     BOOL                ret = FALSE, set_default = FALSE;
263     char                *port,*devline,*env_default;
264     HKEY                hkeyPrinter, hkeyPrinters, hkey;
265
266     while (isspace(*pent)) pent++;
267     s = strchr(pent,':');
268     if(s) *s='\0';
269     name = HeapAlloc(GetProcessHeap(), 0, strlen(pent) + 1);
270     strcpy(name,pent);
271     if(s) {
272         *s=':';
273         pent = s;
274     } else
275         pent = "";
276
277     TRACE("name=%s entry=%s\n",name, pent);
278
279     if(ispunct(*name)) { /* a tc entry, not a real printer */
280         TRACE("skipping tc entry\n");
281         goto end;
282     }
283
284     if(strstr(pent,":server")) { /* server only version so skip */
285         TRACE("skipping server entry\n");
286         goto end;
287     }
288
289     /* Determine whether this is a postscript printer. */
290
291     ret = TRUE;
292     env_default = getenv("PRINTER");
293     prettyname = name;
294     /* Get longest name, usually the one at the right for later display. */
295     while((s=strchr(prettyname,'|'))) {
296         *s = '\0';
297         e = s;
298         while(isspace(*--e)) *e = '\0';
299         TRACE("\t%s\n", debugstr_a(prettyname));
300         if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
301         for(prettyname = s+1; isspace(*prettyname); prettyname++)
302             ;
303     }
304     e = prettyname + strlen(prettyname);
305     while(isspace(*--e)) *e = '\0';
306     TRACE("\t%s\n", debugstr_a(prettyname));
307     if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
308
309     /* prettyname must fit into the dmDeviceName member of DEVMODE struct,
310      * if it is too long, we use it as comment below. */
311     devname = prettyname;
312     if (strlen(devname)>=CCHDEVICENAME-1)
313          devname = name;
314     if (strlen(devname)>=CCHDEVICENAME-1) {
315         ret = FALSE;
316         goto end;
317     }
318
319     port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(name)+1);
320     sprintf(port,"LPR:%s",name);
321
322     devline=HeapAlloc(GetProcessHeap(),0,sizeof("WINEPS.DRV,")+strlen(port));
323     sprintf(devline,"WINEPS.DRV,%s",port);
324     WriteProfileStringA("devices",devname,devline);
325     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
326         RegSetValueExA(hkey, devname, 0, REG_SZ, devline, strlen(devline) + 1);
327         RegCloseKey(hkey);
328     }
329     HeapFree(GetProcessHeap(),0,devline);
330     
331     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
332        ERROR_SUCCESS) {
333         ERR("Can't create Printers key\n");
334         ret = FALSE;
335         goto end;
336     }
337     if(RegOpenKeyA(hkeyPrinters, devname, &hkeyPrinter) == ERROR_SUCCESS) {
338         /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
339            and continue */
340         TRACE("Printer already exists\n");
341         RegDeleteValueW(hkeyPrinter, May_Delete_Value);
342         RegCloseKey(hkeyPrinter);
343     } else {
344         memset(&pinfo2a,0,sizeof(pinfo2a));
345         pinfo2a.pPrinterName            = devname;
346         pinfo2a.pDatatype               = "RAW";
347         pinfo2a.pPrintProcessor         = "WinPrint";
348         pinfo2a.pDriverName             = "PS Driver";
349         pinfo2a.pComment                = "WINEPS Printer using LPR";
350         pinfo2a.pLocation               = prettyname;
351         pinfo2a.pPortName               = port;
352         pinfo2a.pParameters             = "<parameters?>";
353         pinfo2a.pShareName              = "<share name?>";
354         pinfo2a.pSepFile                = "<sep file?>";
355
356         if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
357             if (GetLastError()!=ERROR_PRINTER_ALREADY_EXISTS)
358                 ERR("%s not added by AddPrinterA (%ld)\n",name,GetLastError());
359         }
360     }
361     RegCloseKey(hkeyPrinters);
362
363     if (isfirst || set_default)
364         WINSPOOL_SetDefaultPrinter(devname,name,TRUE);
365
366     HeapFree(GetProcessHeap(), 0, port);
367  end:
368     HeapFree(GetProcessHeap(), 0, name);
369     return ret;
370 }
371
372 static BOOL
373 PRINTCAP_LoadPrinters(void) {
374     BOOL                hadprinter = FALSE;
375     char                buf[200];
376     FILE                *f;
377     char *pent = NULL;
378     BOOL had_bash = FALSE;
379
380     f = fopen("/etc/printcap","r");
381     if (!f)
382         return FALSE;
383
384     while(fgets(buf,sizeof(buf),f)) {
385         char *start, *end;
386
387         end=strchr(buf,'\n');
388         if (end) *end='\0';
389     
390         start = buf;
391         while(isspace(*start)) start++;
392         if(*start == '#' || *start == '\0')
393             continue;
394
395         if(pent && !had_bash && *start != ':' && *start != '|') { /* start of new entry, parse the previous one */
396             hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
397             HeapFree(GetProcessHeap(),0,pent);
398             pent = NULL;
399         }
400
401         if (end && *--end == '\\') {
402             *end = '\0';
403             had_bash = TRUE;
404         } else
405             had_bash = FALSE;
406
407         if (pent) {
408             pent=HeapReAlloc(GetProcessHeap(),0,pent,strlen(pent)+strlen(start)+1);
409             strcat(pent,start);
410         } else {
411             pent=HeapAlloc(GetProcessHeap(),0,strlen(start)+1);
412             strcpy(pent,start);
413         }
414
415     }
416     if(pent) {
417         hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
418         HeapFree(GetProcessHeap(),0,pent);
419     }
420     fclose(f);
421     return hadprinter;
422 }
423
424 static inline DWORD set_reg_szW(HKEY hkey, const WCHAR *keyname, const WCHAR *value)
425 {
426     if (value)
427         return RegSetValueExW(hkey, keyname, 0, REG_SZ, (const BYTE*)value,
428                    lstrlenW(value) * sizeof(WCHAR));
429     else
430         return ERROR_FILE_NOT_FOUND;
431 }
432
433 void WINSPOOL_LoadSystemPrinters(void)
434 {
435     HKEY                hkey, hkeyPrinters;
436     DRIVER_INFO_3A      di3a;
437     HANDLE              hprn;
438     DWORD               needed, num, i;
439     WCHAR               PrinterName[256];
440     BOOL                done = FALSE;
441
442     di3a.cVersion = 0x400;
443     di3a.pName = "PS Driver";
444     di3a.pEnvironment = NULL;   /* NULL means auto */
445     di3a.pDriverPath = "wineps16";
446     di3a.pDataFile = "<datafile?>";
447     di3a.pConfigFile = "wineps16";
448     di3a.pHelpFile = "<helpfile?>";
449     di3a.pDependentFiles = "<dependend files?>";
450     di3a.pMonitorName = "<monitor name?>";
451     di3a.pDefaultDataType = "RAW";
452
453     if (!AddPrinterDriverA(NULL,3,(LPBYTE)&di3a)) {
454         ERR("Failed adding PS Driver (%ld)\n",GetLastError());
455         return;
456     }
457
458     /* This ensures that all printer entries have a valid Name value.  If causes
459        problems later if they don't.  If one is found to be missed we create one
460        and set it equal to the name of the key */
461     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) == ERROR_SUCCESS) {
462         if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &num, NULL, NULL,
463                             NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
464             for(i = 0; i < num; i++) {
465                 if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)) == ERROR_SUCCESS) {
466                     if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkey) == ERROR_SUCCESS) {
467                         if(RegQueryValueExW(hkey, NameW, 0, 0, 0, &needed) == ERROR_FILE_NOT_FOUND) {
468                             set_reg_szW(hkey, NameW, PrinterName);
469                         }
470                         RegCloseKey(hkey);
471                     }
472                 }
473             }
474         }
475         RegCloseKey(hkeyPrinters);
476     }
477
478     /* We want to avoid calling AddPrinter on printers as much as
479        possible, because on cups printers this will (eventually) lead
480        to a call to cupsGetPPD which takes forever, even with non-cups
481        printers AddPrinter takes a while.  So we'll tag all printers that
482        were automatically added last time around, if they still exist
483        we'll leave them be otherwise we'll delete them. */
484     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
485     if(needed) {
486         PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
487         if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
488             for(i = 0; i < num; i++) {
489                 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
490                     if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
491                         if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
492                             DWORD dw = 1;
493                             RegSetValueExW(hkey, May_Delete_Value, 0, REG_DWORD, (LPBYTE)&dw, sizeof(dw));
494                             RegCloseKey(hkey);
495                         }
496                         ClosePrinter(hprn);
497                     }
498                 }
499             }
500         }
501         HeapFree(GetProcessHeap(), 0, pi);
502     }
503
504
505 #ifdef HAVE_CUPS_CUPS_H
506     done = CUPS_LoadPrinters();
507 #endif
508
509     if(!done) { /* If we have any CUPS based printers, skip looking for printcap printers */
510         /* Check for [ppd] section in config file before parsing /etc/printcap */
511         if (RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\ppd",
512                         &hkey) == ERROR_SUCCESS) {
513             RegCloseKey(hkey);
514             PRINTCAP_LoadPrinters();
515         }
516     }
517
518     /* Now enumerate the list again and delete any printers that a still tagged */
519     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
520     if(needed) {
521         PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
522         if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
523             for(i = 0; i < num; i++) {
524                 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
525                     if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
526                         if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
527                             DWORD dw, type, size = sizeof(dw);
528                             if(RegQueryValueExW(hkey, May_Delete_Value, NULL, &type, (LPBYTE)&dw, &size) == ERROR_SUCCESS) {
529                                 TRACE("Deleting old printer %s\n", pi[i].pPrinterName);
530                                 DeletePrinter(hprn);
531                             }
532                             RegCloseKey(hkey);
533                         }
534                         ClosePrinter(hprn);
535                     }
536                 }
537             }
538         }
539         HeapFree(GetProcessHeap(), 0, pi);
540     }
541
542     return;
543
544 }
545
546
547 /******************************************************************
548  *  WINSPOOL_GetOpenedPrinterEntry
549  *  Get the first place empty in the opened printer table
550  */
551 static HANDLE WINSPOOL_GetOpenedPrinterEntry( LPCWSTR name )
552 {
553     int i;
554
555     for (i = 0; i < nb_printers; i++) if (!printer_array[i]) break;
556
557     if (i >= nb_printers)
558     {
559         LPWSTR *new_array;
560         if (printer_array) 
561             new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, printer_array,
562                                          (nb_printers + 16) * sizeof(*new_array) );
563         else 
564             new_array = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
565                                          (nb_printers + 16) * sizeof(*new_array) );
566
567         if (!new_array) return 0;
568         printer_array = new_array;
569         nb_printers += 16;
570     }
571
572     if ((printer_array[i] = HeapAlloc( GetProcessHeap(), 0, (strlenW(name)+1)*sizeof(WCHAR) )))
573     {
574         strcpyW( printer_array[i], name );
575         return (HANDLE)(i + 1);
576     }
577     return 0;
578 }
579
580 /******************************************************************
581  *  WINSPOOL_GetOpenedPrinter
582  *  Get the pointer to the opened printer referred by the handle
583  */
584 static LPCWSTR WINSPOOL_GetOpenedPrinter(HANDLE printerHandle)
585 {
586     int idx = (int)printerHandle;
587     if ((idx <= 0) || (idx > nb_printers))
588     {
589         SetLastError(ERROR_INVALID_HANDLE);
590         return NULL;
591     }
592     return printer_array[idx - 1];
593 }
594
595 /******************************************************************
596  *  WINSPOOL_GetOpenedPrinterRegKey
597  *
598  */
599 static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey)
600 {
601     LPCWSTR name = WINSPOOL_GetOpenedPrinter(hPrinter);
602     DWORD ret;
603     HKEY hkeyPrinters;
604
605     if(!name) return ERROR_INVALID_HANDLE;
606
607     if((ret = RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters)) !=
608        ERROR_SUCCESS)
609         return ret;
610
611     if(RegOpenKeyW(hkeyPrinters, name, phkey) != ERROR_SUCCESS)
612     {
613         ERR("Can't find opened printer %s in registry\n",
614             debugstr_w(name));
615         RegCloseKey(hkeyPrinters);
616         return ERROR_INVALID_PRINTER_NAME; /* ? */
617     }
618     RegCloseKey(hkeyPrinters);
619     return ERROR_SUCCESS;
620 }
621
622 /***********************************************************
623  *      DEVMODEcpyAtoW
624  */
625 static LPDEVMODEW DEVMODEcpyAtoW(DEVMODEW *dmW, const DEVMODEA *dmA)
626 {
627     BOOL Formname;
628     ptrdiff_t off_formname = (const char *)dmA->dmFormName - (const char *)dmA;
629     DWORD size;
630
631     Formname = (dmA->dmSize > off_formname);
632     size = dmA->dmSize + CCHDEVICENAME + (Formname ? CCHFORMNAME : 0);
633     MultiByteToWideChar(CP_ACP, 0, dmA->dmDeviceName, -1, dmW->dmDeviceName,
634                         CCHDEVICENAME);
635     if(!Formname) {
636       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
637              dmA->dmSize - CCHDEVICENAME);
638     } else {
639       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
640              off_formname - CCHDEVICENAME);
641       MultiByteToWideChar(CP_ACP, 0, dmA->dmFormName, -1, dmW->dmFormName,
642                           CCHFORMNAME);
643       memcpy(&dmW->dmLogPixels, &dmA->dmLogPixels, dmA->dmSize -
644              (off_formname + CCHFORMNAME));
645     }
646     dmW->dmSize = size;
647     memcpy((char *)dmW + dmW->dmSize, (const char *)dmA + dmA->dmSize,
648            dmA->dmDriverExtra);
649     return dmW;
650 }
651
652 /***********************************************************
653  *      DEVMODEdupWtoA
654  * Creates an ascii copy of supplied devmode on heap
655  */
656 static LPDEVMODEA DEVMODEdupWtoA(HANDLE heap, const DEVMODEW *dmW)
657 {
658     LPDEVMODEA dmA;
659     DWORD size;
660     BOOL Formname;
661     ptrdiff_t off_formname = (const char *)dmW->dmFormName - (const char *)dmW;
662
663     if(!dmW) return NULL;
664     Formname = (dmW->dmSize > off_formname);
665     size = dmW->dmSize - CCHDEVICENAME - (Formname ? CCHFORMNAME : 0);
666     dmA = HeapAlloc(heap, HEAP_ZERO_MEMORY, size + dmW->dmDriverExtra);
667     WideCharToMultiByte(CP_ACP, 0, dmW->dmDeviceName, -1, dmA->dmDeviceName,
668                         CCHDEVICENAME, NULL, NULL);
669     if(!Formname) {
670       memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
671              dmW->dmSize - CCHDEVICENAME * sizeof(WCHAR));
672     } else {
673       memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
674              off_formname - CCHDEVICENAME * sizeof(WCHAR));
675       WideCharToMultiByte(CP_ACP, 0, dmW->dmFormName, -1, dmA->dmFormName,
676                           CCHFORMNAME, NULL, NULL);
677       memcpy(&dmA->dmLogPixels, &dmW->dmLogPixels, dmW->dmSize -
678              (off_formname + CCHFORMNAME * sizeof(WCHAR)));
679     }
680     dmA->dmSize = size;
681     memcpy((char *)dmA + dmA->dmSize, (const char *)dmW + dmW->dmSize,
682            dmW->dmDriverExtra);
683     return dmA;
684 }
685
686 /***********************************************************
687  *             PRINTER_INFO_2AtoW
688  * Creates a unicode copy of PRINTER_INFO_2A on heap
689  */
690 static LPPRINTER_INFO_2W PRINTER_INFO_2AtoW(HANDLE heap, LPPRINTER_INFO_2A piA)
691 {
692     LPPRINTER_INFO_2W piW;
693     UNICODE_STRING usBuffer;
694
695     if(!piA) return NULL;
696     piW = HeapAlloc(heap, 0, sizeof(*piW));
697     memcpy(piW, piA, sizeof(*piW)); /* copy everything first */
698     
699     piW->pServerName = asciitounicode(&usBuffer,piA->pServerName);
700     piW->pPrinterName = asciitounicode(&usBuffer,piA->pPrinterName);
701     piW->pShareName = asciitounicode(&usBuffer,piA->pShareName);
702     piW->pPortName = asciitounicode(&usBuffer,piA->pPortName);
703     piW->pDriverName = asciitounicode(&usBuffer,piA->pDriverName);
704     piW->pComment = asciitounicode(&usBuffer,piA->pComment);
705     piW->pLocation = asciitounicode(&usBuffer,piA->pLocation);
706     piW->pDevMode = piA->pDevMode ? GdiConvertToDevmodeW(piA->pDevMode) : NULL;
707     piW->pSepFile = asciitounicode(&usBuffer,piA->pSepFile);
708     piW->pPrintProcessor = asciitounicode(&usBuffer,piA->pPrintProcessor);
709     piW->pDatatype = asciitounicode(&usBuffer,piA->pDatatype);
710     piW->pParameters = asciitounicode(&usBuffer,piA->pParameters);
711     return piW;
712 }
713
714 /***********************************************************
715  *       FREE_PRINTER_INFO_2W
716  * Free PRINTER_INFO_2W and all strings
717  */
718 static void FREE_PRINTER_INFO_2W(HANDLE heap, LPPRINTER_INFO_2W piW)
719 {
720     if(!piW) return;
721
722     HeapFree(heap,0,piW->pServerName);
723     HeapFree(heap,0,piW->pPrinterName);
724     HeapFree(heap,0,piW->pShareName);
725     HeapFree(heap,0,piW->pPortName);
726     HeapFree(heap,0,piW->pDriverName);
727     HeapFree(heap,0,piW->pComment);
728     HeapFree(heap,0,piW->pLocation);
729     HeapFree(heap,0,piW->pDevMode);
730     HeapFree(heap,0,piW->pSepFile);
731     HeapFree(heap,0,piW->pPrintProcessor);
732     HeapFree(heap,0,piW->pDatatype);
733     HeapFree(heap,0,piW->pParameters);
734     HeapFree(heap,0,piW);
735     return;
736 }
737
738 /******************************************************************
739  *              DeviceCapabilities     [WINSPOOL.@]
740  *              DeviceCapabilitiesA    [WINSPOOL.@]
741  *
742  */
743 INT WINAPI DeviceCapabilitiesA(LPCSTR pDevice,LPCSTR pPort, WORD cap,
744                                LPSTR pOutput, LPDEVMODEA lpdm)
745 {
746     INT ret;
747
748     if (!GDI_CallDeviceCapabilities16)
749     {
750         GDI_CallDeviceCapabilities16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
751                                                               (LPCSTR)104 );
752         if (!GDI_CallDeviceCapabilities16) return -1;
753     }
754     ret = GDI_CallDeviceCapabilities16(pDevice, pPort, cap, pOutput, lpdm);
755
756     /* If DC_PAPERSIZE map POINT16s to POINTs */
757     if(ret != -1 && cap == DC_PAPERSIZE && pOutput) {
758         POINT16 *tmp = HeapAlloc( GetProcessHeap(), 0, ret * sizeof(POINT16) );
759         POINT *pt = (POINT *)pOutput;
760         INT i;
761         memcpy(tmp, pOutput, ret * sizeof(POINT16));
762         for(i = 0; i < ret; i++, pt++)
763         {
764             pt->x = tmp[i].x;
765             pt->y = tmp[i].y;
766         }
767         HeapFree( GetProcessHeap(), 0, tmp );
768     }
769     return ret;
770 }
771
772
773 /*****************************************************************************
774  *          DeviceCapabilitiesW        [WINSPOOL.@]
775  *
776  * Call DeviceCapabilitiesA since we later call 16bit stuff anyway
777  *
778  */
779 INT WINAPI DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort,
780                                WORD fwCapability, LPWSTR pOutput,
781                                const DEVMODEW *pDevMode)
782 {
783     LPDEVMODEA dmA = DEVMODEdupWtoA(GetProcessHeap(), pDevMode);
784     LPSTR pDeviceA = HEAP_strdupWtoA(GetProcessHeap(),0,pDevice);
785     LPSTR pPortA = HEAP_strdupWtoA(GetProcessHeap(),0,pPort);
786     INT ret;
787
788     if(pOutput && (fwCapability == DC_BINNAMES ||
789                    fwCapability == DC_FILEDEPENDENCIES ||
790                    fwCapability == DC_PAPERNAMES)) {
791       /* These need A -> W translation */
792         INT size = 0, i;
793         LPSTR pOutputA;
794         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, NULL,
795                                   dmA);
796         if(ret == -1)
797             return ret;
798         switch(fwCapability) {
799         case DC_BINNAMES:
800             size = 24;
801             break;
802         case DC_PAPERNAMES:
803         case DC_FILEDEPENDENCIES:
804             size = 64;
805             break;
806         }
807         pOutputA = HeapAlloc(GetProcessHeap(), 0, size * ret);
808         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, pOutputA,
809                                   dmA);
810         for(i = 0; i < ret; i++)
811             MultiByteToWideChar(CP_ACP, 0, pOutputA + (i * size), -1,
812                                 pOutput + (i * size), size);
813         HeapFree(GetProcessHeap(), 0, pOutputA);
814     } else {
815         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability,
816                                   (LPSTR)pOutput, dmA);
817     }
818     HeapFree(GetProcessHeap(),0,pPortA);
819     HeapFree(GetProcessHeap(),0,pDeviceA);
820     HeapFree(GetProcessHeap(),0,dmA);
821     return ret;
822 }
823
824 /******************************************************************
825  *              DocumentPropertiesA   [WINSPOOL.@]
826  *
827  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
828  */
829 LONG WINAPI DocumentPropertiesA(HWND hWnd,HANDLE hPrinter,
830                                 LPSTR pDeviceName, LPDEVMODEA pDevModeOutput,
831                                 LPDEVMODEA pDevModeInput,DWORD fMode )
832 {
833     LPSTR lpName = pDeviceName;
834     LONG ret;
835
836     TRACE("(%p,%p,%s,%p,%p,%ld)\n",
837         hWnd,hPrinter,pDeviceName,pDevModeOutput,pDevModeInput,fMode
838     );
839
840     if(!pDeviceName) {
841         LPCWSTR lpNameW = WINSPOOL_GetOpenedPrinter(hPrinter);
842         if(!lpNameW) {
843                 ERR("no name from hPrinter?\n");
844                 return -1;
845         }
846         lpName = HEAP_strdupWtoA(GetProcessHeap(),0,lpNameW);
847     }
848
849     if (!GDI_CallExtDeviceMode16)
850     {
851         GDI_CallExtDeviceMode16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
852                                                          (LPCSTR)102 );
853         if (!GDI_CallExtDeviceMode16) {
854                 ERR("No CallExtDeviceMode16?\n");
855                 return -1;
856         }
857     }
858     ret = GDI_CallExtDeviceMode16(hWnd, pDevModeOutput, lpName, "LPT1:",
859                                   pDevModeInput, NULL, fMode);
860
861     if(!pDeviceName)
862         HeapFree(GetProcessHeap(),0,lpName);
863     return ret;
864 }
865
866
867 /*****************************************************************************
868  *          DocumentPropertiesW (WINSPOOL.@)
869  *
870  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
871  */
872 LONG WINAPI DocumentPropertiesW(HWND hWnd, HANDLE hPrinter,
873                                 LPWSTR pDeviceName,
874                                 LPDEVMODEW pDevModeOutput,
875                                 LPDEVMODEW pDevModeInput, DWORD fMode)
876 {
877
878     LPSTR pDeviceNameA = HEAP_strdupWtoA(GetProcessHeap(),0,pDeviceName);
879     LPDEVMODEA pDevModeInputA = DEVMODEdupWtoA(GetProcessHeap(),pDevModeInput);
880     LPDEVMODEA pDevModeOutputA = NULL;
881     LONG ret;
882
883     TRACE("(%p,%p,%s,%p,%p,%ld)\n",
884           hWnd,hPrinter,debugstr_w(pDeviceName),pDevModeOutput,pDevModeInput,
885           fMode);
886     if(pDevModeOutput) {
887         ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, NULL, NULL, 0);
888         if(ret < 0) return ret;
889         pDevModeOutputA = HeapAlloc(GetProcessHeap(), 0, ret);
890     }
891     ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, pDevModeOutputA,
892                               pDevModeInputA, fMode);
893     if(pDevModeOutput) {
894         DEVMODEcpyAtoW(pDevModeOutput, pDevModeOutputA);
895         HeapFree(GetProcessHeap(),0,pDevModeOutputA);
896     }
897     if(fMode == 0 && ret > 0)
898         ret += (CCHDEVICENAME + CCHFORMNAME);
899     HeapFree(GetProcessHeap(),0,pDevModeInputA);
900     HeapFree(GetProcessHeap(),0,pDeviceNameA);
901     return ret;
902 }
903
904 /******************************************************************
905  *              OpenPrinterA        [WINSPOOL.@]
906  *
907  */
908 BOOL WINAPI OpenPrinterA(LPSTR lpPrinterName,HANDLE *phPrinter,
909                          LPPRINTER_DEFAULTSA pDefault)
910 {
911     UNICODE_STRING lpPrinterNameW;
912     UNICODE_STRING usBuffer;
913     PRINTER_DEFAULTSW DefaultW, *pDefaultW = NULL;
914     PWSTR pwstrPrinterNameW;
915     BOOL ret;
916
917     pwstrPrinterNameW = asciitounicode(&lpPrinterNameW,lpPrinterName);
918
919     if(pDefault) {
920         DefaultW.pDatatype = asciitounicode(&usBuffer,pDefault->pDatatype);
921         DefaultW.pDevMode = pDefault->pDevMode ? GdiConvertToDevmodeW(pDefault->pDevMode) : NULL;
922         DefaultW.DesiredAccess = pDefault->DesiredAccess;
923         pDefaultW = &DefaultW;
924     }
925     ret = OpenPrinterW(pwstrPrinterNameW, phPrinter, pDefaultW);
926     if(pDefault) {
927         RtlFreeUnicodeString(&usBuffer);
928         HeapFree(GetProcessHeap(), 0, DefaultW.pDevMode);
929     }
930     RtlFreeUnicodeString(&lpPrinterNameW);
931     return ret;
932 }
933
934 /******************************************************************
935  *              OpenPrinterW        [WINSPOOL.@]
936  *
937  */
938 BOOL WINAPI OpenPrinterW(LPWSTR lpPrinterName,HANDLE *phPrinter,
939                          LPPRINTER_DEFAULTSW pDefault)
940 {
941     HKEY hkeyPrinters, hkeyPrinter;
942
943     if (!lpPrinterName) {
944        FIXME("(printerName: NULL, pDefault %p Ret: False\n", pDefault);
945        SetLastError(ERROR_INVALID_PARAMETER);
946        return FALSE;
947     }
948
949     TRACE("(printerName: %s, pDefault %p)\n", debugstr_w(lpPrinterName),
950           pDefault);
951
952     /* Check Printer exists */
953     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
954        ERROR_SUCCESS) {
955         ERR("Can't create Printers key\n");
956         SetLastError(ERROR_FILE_NOT_FOUND); /* ?? */
957         return FALSE;
958     }
959
960     if(lpPrinterName[0] == '\0' || /* explicitly exclude "" */
961        RegOpenKeyW(hkeyPrinters, lpPrinterName, &hkeyPrinter)
962        != ERROR_SUCCESS) {
963         TRACE("Can't find printer %s in registry\n",
964               debugstr_w(lpPrinterName));
965         RegCloseKey(hkeyPrinters);
966         SetLastError(ERROR_INVALID_PRINTER_NAME);
967         return FALSE;
968     }
969     RegCloseKey(hkeyPrinter);
970     RegCloseKey(hkeyPrinters);
971
972     if(!phPrinter) /* This seems to be what win95 does anyway */
973         return TRUE;
974
975     /* Get the unique handle of the printer*/
976     *phPrinter = WINSPOOL_GetOpenedPrinterEntry( lpPrinterName );
977
978     if (pDefault != NULL)
979         FIXME("Not handling pDefault\n");
980
981     return TRUE;
982 }
983
984 /******************************************************************
985  *              AddMonitorA        [WINSPOOL.@]
986  *
987  */
988 BOOL WINAPI AddMonitorA(LPSTR pName, DWORD Level, LPBYTE pMonitors)
989 {
990     FIXME("(%s,0x%08lx,%p), stub!\n", debugstr_a(pName), Level, pMonitors);
991     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
992     return FALSE;
993 }
994
995 /******************************************************************************
996  *              AddMonitorW        [WINSPOOL.@]
997  */
998 BOOL WINAPI AddMonitorW(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
999 {
1000     FIXME("(%s,0x%08lx,%p), stub!\n",debugstr_w(pName), Level, pMonitors);
1001     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1002     return FALSE;
1003 }
1004
1005 /******************************************************************
1006  *              DeletePrinterDriverA        [WINSPOOL.@]
1007  *
1008  */
1009 BOOL WINAPI
1010 DeletePrinterDriverA (LPSTR pName, LPSTR pEnvironment, LPSTR pDriverName)
1011 {
1012     FIXME("(%s,%s,%s):stub\n",debugstr_a(pName),debugstr_a(pEnvironment),
1013           debugstr_a(pDriverName));
1014     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1015     return FALSE;
1016 }
1017
1018 /******************************************************************
1019  *              DeletePrinterDriverW        [WINSPOOL.@]
1020  *
1021  */
1022 BOOL WINAPI
1023 DeletePrinterDriverW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pDriverName)
1024 {
1025     FIXME("(%s,%s,%s):stub\n",debugstr_w(pName),debugstr_w(pEnvironment),
1026           debugstr_w(pDriverName));
1027     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1028     return FALSE;
1029 }
1030
1031 /******************************************************************
1032  *              DeleteMonitorA        [WINSPOOL.@]
1033  *
1034  */
1035 BOOL WINAPI
1036 DeleteMonitorA (LPSTR pName, LPSTR pEnvironment, LPSTR pMonitorName)
1037 {
1038     FIXME("(%s,%s,%s):stub\n",debugstr_a(pName),debugstr_a(pEnvironment),
1039           debugstr_a(pMonitorName));
1040     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1041     return FALSE;
1042 }
1043
1044 /******************************************************************
1045  *              DeleteMonitorW        [WINSPOOL.@]
1046  *
1047  */
1048 BOOL WINAPI
1049 DeleteMonitorW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
1050 {
1051     FIXME("(%s,%s,%s):stub\n",debugstr_w(pName),debugstr_w(pEnvironment),
1052           debugstr_w(pMonitorName));
1053     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1054     return FALSE;
1055 }
1056
1057 /******************************************************************
1058  *              DeletePortA        [WINSPOOL.@]
1059  *
1060  */
1061 BOOL WINAPI
1062 DeletePortA (LPSTR pName, HWND hWnd, LPSTR pPortName)
1063 {
1064     FIXME("(%s,%p,%s):stub\n",debugstr_a(pName),hWnd,
1065           debugstr_a(pPortName));
1066     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1067     return FALSE;
1068 }
1069
1070 /******************************************************************
1071  *              DeletePortW        [WINSPOOL.@]
1072  *
1073  */
1074 BOOL WINAPI
1075 DeletePortW (LPWSTR pName, HWND hWnd, LPWSTR pPortName)
1076 {
1077     FIXME("(%s,%p,%s):stub\n",debugstr_w(pName),hWnd,
1078           debugstr_w(pPortName));
1079     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1080     return FALSE;
1081 }
1082
1083 /******************************************************************************
1084  *    SetPrinterW  [WINSPOOL.@]
1085  */
1086 BOOL WINAPI
1087 SetPrinterW(
1088   HANDLE  hPrinter,
1089   DWORD     Level,
1090   LPBYTE    pPrinter,
1091   DWORD     Command) {
1092
1093     FIXME("():stub\n");
1094     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1095     return FALSE;
1096 }
1097
1098 /******************************************************************************
1099  *    WritePrinter  [WINSPOOL.@]
1100  */
1101 BOOL WINAPI
1102 WritePrinter(
1103   HANDLE  hPrinter,
1104   LPVOID  pBuf,
1105   DWORD   cbBuf,
1106   LPDWORD pcWritten) {
1107
1108     FIXME("():stub\n");
1109     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1110     return FALSE;
1111 }
1112
1113 /*****************************************************************************
1114  *          AddFormA  [WINSPOOL.@]
1115  */
1116 BOOL WINAPI AddFormA(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
1117 {
1118     FIXME("(%p,%ld,%p): stub\n", hPrinter, Level, pForm);
1119     return 1;
1120 }
1121
1122 /*****************************************************************************
1123  *          AddFormW  [WINSPOOL.@]
1124  */
1125 BOOL WINAPI AddFormW(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
1126 {
1127     FIXME("(%p,%ld,%p): stub\n", hPrinter, Level, pForm);
1128     return 1;
1129 }
1130
1131 /*****************************************************************************
1132  *          AddJobA  [WINSPOOL.@]
1133  */
1134 BOOL WINAPI AddJobA(HANDLE hPrinter, DWORD Level, LPBYTE pData,
1135                         DWORD cbBuf, LPDWORD pcbNeeded)
1136 {
1137     FIXME("(%p,%ld,%p,%ld,%p): stub\n", hPrinter, Level, pData, cbBuf,
1138           pcbNeeded);
1139     return 1;
1140 }
1141
1142 /*****************************************************************************
1143  *          AddJobW  [WINSPOOL.@]
1144  */
1145 BOOL WINAPI AddJobW(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf,
1146                         LPDWORD pcbNeeded)
1147 {
1148     FIXME("(%p,%ld,%p,%ld,%p): stub\n", hPrinter, Level, pData, cbBuf,
1149           pcbNeeded);
1150     return 1;
1151 }
1152
1153 /*****************************************************************************
1154  *          GetPrintProcessorDirectoryA  [WINSPOOL.@]
1155  */
1156 BOOL WINAPI GetPrintProcessorDirectoryA(LPSTR server, LPSTR env,
1157                                         DWORD level,  LPBYTE Info,
1158                                         DWORD cbBuf, LPDWORD needed)
1159 {
1160     FIXME("(%s,%s,%ld,%p,0x%08lx): stub\n", debugstr_a(server), debugstr_a(env),
1161           level, Info, cbBuf);
1162     return 0;
1163 }
1164
1165 /*****************************************************************************
1166  *          GetPrintProcessorDirectoryW  [WINSPOOL.@]
1167  */
1168 BOOL WINAPI GetPrintProcessorDirectoryW(LPWSTR server, LPWSTR env,
1169                                         DWORD level,  LPBYTE Info,
1170                                         DWORD cbBuf, LPDWORD needed)
1171 {
1172     FIXME("(%s,%s,%ld,%p,0x%08lx): stub\n", debugstr_w(server), debugstr_w(env),
1173           level, Info, cbBuf);
1174     return 0;
1175 }
1176
1177 /*****************************************************************************
1178  *          WINSPOOL_OpenDriverReg [internal]
1179  *
1180  * opens the registry for the printer drivers depending on the given input
1181  * variable pEnvironment
1182  *
1183  * RETURNS:
1184  *    the opened hkey on success
1185  *    NULL on error
1186  */
1187 static HKEY WINSPOOL_OpenDriverReg( LPVOID pEnvironment, BOOL unicode)
1188 {   
1189     static const WCHAR WinNTW[] = { 'W','i','n','d','o','w','s',' ','N','T',' ','x','8','6',0 };
1190     static const WCHAR Win40W[] = { 'W','i','n','d','o','w','s',' ','4','.','0',0 };
1191     HKEY  retval;
1192     LPWSTR lpKey, buffer = NULL;
1193     LPCWSTR pEnvW;
1194
1195     TRACE("%s\n",
1196           (unicode) ? debugstr_w(pEnvironment) : debugstr_a(pEnvironment));
1197
1198     if(pEnvironment) {
1199         if (unicode) {
1200             pEnvW = pEnvironment;
1201         } else {
1202             INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, NULL, 0);
1203             buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1204             if (buffer) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, buffer, len);
1205             pEnvW = buffer;
1206         }
1207     } else {
1208         OSVERSIONINFOW ver;
1209         ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
1210
1211         if(!GetVersionExW( &ver))
1212             return 0;
1213
1214         switch (ver.dwPlatformId) {
1215              case VER_PLATFORM_WIN32s:
1216                   ERR("win32 style printing used with 16 bits app, try specifying 'win95' Windows version\n");
1217                   return 0;
1218              case VER_PLATFORM_WIN32_NT:
1219                   pEnvW = WinNTW;
1220                   break;
1221              default:
1222                   pEnvW = Win40W;
1223                   break;
1224         }
1225         TRACE("set environment to %s\n", debugstr_w(pEnvW));
1226     }
1227
1228     lpKey = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
1229                        (strlenW(pEnvW) + strlenW(DriversW) + 1) * sizeof(WCHAR));
1230     wsprintfW( lpKey, DriversW, pEnvW);
1231
1232     TRACE("%s\n", debugstr_w(lpKey));
1233
1234     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, lpKey, &retval) != ERROR_SUCCESS)
1235        retval = 0;
1236
1237     HeapFree( GetProcessHeap(), 0, buffer);
1238     HeapFree( GetProcessHeap(), 0, lpKey);
1239
1240     return retval;
1241 }
1242
1243 /*****************************************************************************
1244  *          AddPrinterW  [WINSPOOL.@]
1245  */
1246 HANDLE WINAPI AddPrinterW(LPWSTR pName, DWORD Level, LPBYTE pPrinter)
1247 {
1248     PRINTER_INFO_2W *pi = (PRINTER_INFO_2W *) pPrinter;
1249     LPDEVMODEA dmA;
1250     LPDEVMODEW dmW;
1251     HANDLE retval;
1252     HKEY hkeyPrinter, hkeyPrinters, hkeyDriver, hkeyDrivers;
1253     LONG size;
1254
1255     TRACE("(%s,%ld,%p)\n", debugstr_w(pName), Level, pPrinter);
1256
1257     if(pName != NULL) {
1258         ERR("pName = %s - unsupported\n", debugstr_w(pName));
1259         SetLastError(ERROR_INVALID_PARAMETER);
1260         return 0;
1261     }
1262     if(Level != 2) {
1263         ERR("Level = %ld, unsupported!\n", Level);
1264         SetLastError(ERROR_INVALID_LEVEL);
1265         return 0;
1266     }
1267     if (strlenW(pi->pPrinterName) >= CCHDEVICENAME) {
1268         ERR("Printername %s must not exceed length of DEVMODE.dmDeviceName !\n",
1269                 debugstr_w(pi->pPrinterName)
1270         );
1271         SetLastError(ERROR_INVALID_LEVEL);
1272         return 0;
1273     }
1274     if(!pPrinter) {
1275         SetLastError(ERROR_INVALID_PARAMETER);
1276         return 0;
1277     }
1278     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
1279        ERROR_SUCCESS) {
1280         ERR("Can't create Printers key\n");
1281         return 0;
1282     }
1283     if(!RegOpenKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter)) {
1284         if (!RegQueryValueA(hkeyPrinter,"Attributes",NULL,NULL)) {
1285             SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
1286             RegCloseKey(hkeyPrinter);
1287             RegCloseKey(hkeyPrinters);
1288             return 0;
1289         }
1290         RegCloseKey(hkeyPrinter);
1291     }
1292     hkeyDrivers = WINSPOOL_OpenDriverReg( NULL, TRUE);
1293     if(!hkeyDrivers) {
1294         ERR("Can't create Drivers key\n");
1295         RegCloseKey(hkeyPrinters);
1296         return 0;
1297     }
1298     if(RegOpenKeyW(hkeyDrivers, pi->pDriverName, &hkeyDriver) !=
1299        ERROR_SUCCESS) {
1300         WARN("Can't find driver %s\n", debugstr_w(pi->pDriverName));
1301         RegCloseKey(hkeyPrinters);
1302         RegCloseKey(hkeyDrivers);
1303         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER);
1304         return 0;
1305     }
1306     RegCloseKey(hkeyDriver);
1307     RegCloseKey(hkeyDrivers);
1308
1309     if(lstrcmpiW(pi->pPrintProcessor, WinPrintW)) {  /* FIXME */
1310         FIXME("Can't find processor %s\n", debugstr_w(pi->pPrintProcessor));
1311         SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR);
1312         RegCloseKey(hkeyPrinters);
1313         return 0;
1314     }
1315
1316     if(RegCreateKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter) !=
1317        ERROR_SUCCESS) {
1318         FIXME("Can't create printer %s\n", debugstr_w(pi->pPrinterName));
1319         SetLastError(ERROR_INVALID_PRINTER_NAME);
1320         RegCloseKey(hkeyPrinters);
1321         return 0;
1322     }
1323     RegSetValueExA(hkeyPrinter, "Attributes", 0, REG_DWORD,
1324                    (LPBYTE)&pi->Attributes, sizeof(DWORD));
1325     set_reg_szW(hkeyPrinter, DatatypeW, pi->pDatatype);
1326
1327     /* See if we can load the driver.  We may need the devmode structure anyway
1328      *
1329      * FIXME:
1330      * Note that DocumentPropertiesW will briefly try to open the printer we
1331      * just create to find a DEVMODEA struct (it will use the WINEPS default
1332      * one in case it is not there, so we are ok).
1333      */
1334     size = DocumentPropertiesW(0, 0, pi->pPrinterName, NULL, NULL, 0);
1335
1336     if(size < 0) {
1337         FIXME("DocumentPropertiesW on printer '%s' fails\n", debugstr_w(pi->pPrinterName));
1338         size = sizeof(DEVMODEW);
1339     }
1340     if(pi->pDevMode)
1341         dmW = pi->pDevMode;
1342     else 
1343     {
1344             dmW = HeapAlloc(GetProcessHeap(), 0, size);
1345         ZeroMemory(dmW,size);
1346             dmW->dmSize = size;
1347             if (0>DocumentPropertiesW(0,0,pi->pPrinterName,dmW,NULL,DM_OUT_BUFFER)) 
1348         {
1349                 WARN("DocumentPropertiesW on printer '%s' failed!\n", debugstr_w(pi->pPrinterName));
1350             HeapFree(GetProcessHeap(),0,dmW);
1351             dmW=NULL;
1352             }
1353         else
1354         {
1355                 /* set devmode to printer name */
1356                 strcpyW(dmW->dmDeviceName,pi->pPrinterName);
1357         }
1358     }
1359
1360     /* Write DEVMODEA not DEVMODEW into reg.  This is what win9x does
1361        and we support these drivers.  NT writes DEVMODEW so somehow
1362        we'll need to distinguish between these when we support NT
1363        drivers */
1364     if (dmW)
1365     {
1366         dmA = DEVMODEdupWtoA(GetProcessHeap(), dmW);
1367         RegSetValueExA(hkeyPrinter, "Default DevMode", 0, REG_BINARY, 
1368                        (LPBYTE)dmA, dmA->dmSize + dmA->dmDriverExtra);
1369         HeapFree(GetProcessHeap(), 0, dmA);
1370         if(!pi->pDevMode)
1371             HeapFree(GetProcessHeap(), 0, dmW);
1372     }
1373     set_reg_szW(hkeyPrinter, DescriptionW, pi->pComment);
1374     set_reg_szW(hkeyPrinter, LocationW, pi->pLocation);
1375     set_reg_szW(hkeyPrinter, NameW, pi->pPrinterName);
1376     set_reg_szW(hkeyPrinter, ParametersW, pi->pParameters);
1377
1378     set_reg_szW(hkeyPrinter, PortW, pi->pPortName);
1379     set_reg_szW(hkeyPrinter, Print_ProcessorW, pi->pPrintProcessor);
1380     set_reg_szW(hkeyPrinter, Printer_DriverW, pi->pDriverName);
1381     RegSetValueExA(hkeyPrinter, "Priority", 0, REG_DWORD,
1382                    (LPBYTE)&pi->Priority, sizeof(DWORD));
1383     set_reg_szW(hkeyPrinter, Separator_FileW, pi->pSepFile);
1384     set_reg_szW(hkeyPrinter, Share_NameW, pi->pShareName);
1385     RegSetValueExA(hkeyPrinter, "StartTime", 0, REG_DWORD,
1386                    (LPBYTE)&pi->StartTime, sizeof(DWORD));
1387     RegSetValueExA(hkeyPrinter, "Status", 0, REG_DWORD,
1388                    (LPBYTE)&pi->Status, sizeof(DWORD));
1389     RegSetValueExA(hkeyPrinter, "UntilTime", 0, REG_DWORD,
1390                    (LPBYTE)&pi->UntilTime, sizeof(DWORD));
1391
1392     RegCloseKey(hkeyPrinter);
1393     RegCloseKey(hkeyPrinters);
1394     if(!OpenPrinterW(pi->pPrinterName, &retval, NULL)) {
1395         ERR("OpenPrinter failing\n");
1396         return 0;
1397     }
1398     return retval;
1399 }
1400
1401 /*****************************************************************************
1402  *          AddPrinterA  [WINSPOOL.@]
1403  */
1404 HANDLE WINAPI AddPrinterA(LPSTR pName, DWORD Level, LPBYTE pPrinter)
1405 {
1406     UNICODE_STRING pNameW;
1407     PWSTR pwstrNameW;
1408     PRINTER_INFO_2W *piW;
1409     PRINTER_INFO_2A *piA = (PRINTER_INFO_2A*)pPrinter;
1410     HANDLE ret;
1411
1412     TRACE("(%s,%ld,%p): stub\n", debugstr_a(pName), Level, pPrinter);
1413     if(Level != 2) {
1414         ERR("Level = %ld, unsupported!\n", Level);
1415         SetLastError(ERROR_INVALID_LEVEL);
1416         return 0;
1417     }
1418     pwstrNameW = asciitounicode(&pNameW,pName);
1419     piW = PRINTER_INFO_2AtoW(GetProcessHeap(), piA);
1420
1421     ret = AddPrinterW(pwstrNameW, Level, (LPBYTE)piW);
1422
1423     FREE_PRINTER_INFO_2W(GetProcessHeap(), piW);
1424     RtlFreeUnicodeString(&pNameW);
1425     return ret;
1426 }
1427
1428
1429 /*****************************************************************************
1430  *          ClosePrinter  [WINSPOOL.@]
1431  */
1432 BOOL WINAPI ClosePrinter(HANDLE hPrinter)
1433 {
1434     int i = (int)hPrinter;
1435
1436     TRACE("Handle %p\n", hPrinter);
1437
1438     if ((i <= 0) || (i > nb_printers)) return FALSE;
1439     HeapFree( GetProcessHeap(), 0, printer_array[i - 1] );
1440     printer_array[i - 1] = NULL;
1441     return TRUE;
1442 }
1443
1444 /*****************************************************************************
1445  *          DeleteFormA  [WINSPOOL.@]
1446  */
1447 BOOL WINAPI DeleteFormA(HANDLE hPrinter, LPSTR pFormName)
1448 {
1449     FIXME("(%p,%s): stub\n", hPrinter, pFormName);
1450     return 1;
1451 }
1452
1453 /*****************************************************************************
1454  *          DeleteFormW  [WINSPOOL.@]
1455  */
1456 BOOL WINAPI DeleteFormW(HANDLE hPrinter, LPWSTR pFormName)
1457 {
1458     FIXME("(%p,%s): stub\n", hPrinter, debugstr_w(pFormName));
1459     return 1;
1460 }
1461
1462 /*****************************************************************************
1463  *   WINSPOOL_SHRegDeleteKey
1464  *
1465  *   Recursively delete subkeys.
1466  *   Cut & paste from shlwapi.
1467  * 
1468  */
1469 static DWORD WINSPOOL_SHDeleteKeyW(HKEY hKey, LPCWSTR lpszSubKey)
1470 {
1471   DWORD dwRet, dwKeyCount = 0, dwMaxSubkeyLen = 0, dwSize, i;
1472   WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
1473   HKEY hSubKey = 0;
1474
1475   dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
1476   if(!dwRet)
1477   {
1478     /* Find how many subkeys there are */
1479     dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwKeyCount,
1480                              &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
1481     if(!dwRet)
1482     {
1483       dwMaxSubkeyLen++;
1484       if (dwMaxSubkeyLen > sizeof(szNameBuf)/sizeof(WCHAR))
1485         /* Name too big: alloc a buffer for it */
1486         lpszName = HeapAlloc(GetProcessHeap(), 0, dwMaxSubkeyLen*sizeof(WCHAR));
1487
1488       if(!lpszName)
1489         dwRet = ERROR_NOT_ENOUGH_MEMORY;
1490       else
1491       {
1492         /* Recursively delete all the subkeys */
1493         for(i = 0; i < dwKeyCount && !dwRet; i++)
1494         {
1495           dwSize = dwMaxSubkeyLen;
1496           dwRet = RegEnumKeyExW(hSubKey, i, lpszName, &dwSize, NULL, NULL, NULL, NULL);
1497           if(!dwRet)
1498             dwRet = WINSPOOL_SHDeleteKeyW(hSubKey, lpszName);
1499         }
1500
1501         if (lpszName != szNameBuf)
1502           HeapFree(GetProcessHeap(), 0, lpszName); /* Free buffer if allocated */
1503       }
1504     }
1505
1506     RegCloseKey(hSubKey);
1507     if(!dwRet)
1508       dwRet = RegDeleteKeyW(hKey, lpszSubKey);
1509   }
1510   return dwRet;
1511 }
1512
1513 /*****************************************************************************
1514  *          DeletePrinter  [WINSPOOL.@]
1515  */
1516 BOOL WINAPI DeletePrinter(HANDLE hPrinter)
1517 {
1518     LPCWSTR lpNameW = WINSPOOL_GetOpenedPrinter(hPrinter);
1519     HKEY hkeyPrinters, hkey;
1520
1521     if(!lpNameW) return FALSE;
1522     if(RegOpenKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) == ERROR_SUCCESS) {
1523         WINSPOOL_SHDeleteKeyW(hkeyPrinters, lpNameW);
1524         RegCloseKey(hkeyPrinters);
1525     }
1526     WriteProfileStringW(devicesW, lpNameW, NULL);
1527     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
1528         RegDeleteValueW(hkey, lpNameW);
1529         RegCloseKey(hkey);
1530     }
1531     return TRUE;
1532 }
1533
1534 /*****************************************************************************
1535  *          SetPrinterA  [WINSPOOL.@]
1536  */
1537 BOOL WINAPI SetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
1538                            DWORD Command)
1539 {
1540     FIXME("(%p,%ld,%p,%ld): stub\n",hPrinter,Level,pPrinter,Command);
1541     return FALSE;
1542 }
1543
1544 /*****************************************************************************
1545  *          SetJobA  [WINSPOOL.@]
1546  */
1547 BOOL WINAPI SetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level,
1548                        LPBYTE pJob, DWORD Command)
1549 {
1550     FIXME("(%p,%ld,%ld,%p,%ld): stub\n",hPrinter,JobId,Level,pJob,
1551          Command);
1552     return FALSE;
1553 }
1554
1555 /*****************************************************************************
1556  *          SetJobW  [WINSPOOL.@]
1557  */
1558 BOOL WINAPI SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level,
1559                        LPBYTE pJob, DWORD Command)
1560 {
1561     FIXME("(%p,%ld,%ld,%p,%ld): stub\n",hPrinter,JobId,Level,pJob,
1562          Command);
1563     return FALSE;
1564 }
1565
1566 /*****************************************************************************
1567  *          EndDocPrinter  [WINSPOOL.@]
1568  */
1569 BOOL WINAPI EndDocPrinter(HANDLE hPrinter)
1570 {
1571     FIXME("(hPrinter=%p): stub\n", hPrinter);
1572     return FALSE;
1573 }
1574
1575 /*****************************************************************************
1576  *          EndPagePrinter  [WINSPOOL.@]
1577  */
1578 BOOL WINAPI EndPagePrinter(HANDLE hPrinter)
1579 {
1580     FIXME("(hPrinter=%p): stub\n", hPrinter);
1581     return FALSE;
1582 }
1583
1584 /*****************************************************************************
1585  *          StartDocPrinterA  [WINSPOOL.@]
1586  */
1587 DWORD WINAPI StartDocPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
1588 {
1589     FIXME("(hPrinter=%p, Level=0x%lx, pDocInfo=%p): stub\n", hPrinter, Level, pDocInfo);
1590     return FALSE;
1591 }
1592
1593 /*****************************************************************************
1594  *          StartDocPrinterW  [WINSPOOL.@]
1595  */
1596 DWORD WINAPI StartDocPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
1597 {
1598     FIXME("(hPrinter=%p, Level=0x%lx, pDocInfo=%p): stub\n", hPrinter, Level, pDocInfo);
1599     return FALSE;
1600 }
1601
1602 /*****************************************************************************
1603  *          StartPagePrinter  [WINSPOOL.@]
1604  */
1605 BOOL WINAPI StartPagePrinter(HANDLE hPrinter)
1606 {
1607     FIXME("(hPrinter=%p): stub\n", hPrinter);
1608     return FALSE;
1609 }
1610
1611 /*****************************************************************************
1612  *          GetFormA  [WINSPOOL.@]
1613  */
1614 BOOL WINAPI GetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
1615                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
1616 {
1617     FIXME("(%p,%s,%ld,%p,%ld,%p): stub\n",hPrinter,pFormName,
1618          Level,pForm,cbBuf,pcbNeeded);
1619     return FALSE;
1620 }
1621
1622 /*****************************************************************************
1623  *          GetFormW  [WINSPOOL.@]
1624  */
1625 BOOL WINAPI GetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
1626                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
1627 {
1628     FIXME("(%p,%s,%ld,%p,%ld,%p): stub\n",hPrinter,
1629           debugstr_w(pFormName),Level,pForm,cbBuf,pcbNeeded);
1630     return FALSE;
1631 }
1632
1633 /*****************************************************************************
1634  *          SetFormA  [WINSPOOL.@]
1635  */
1636 BOOL WINAPI SetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
1637                         LPBYTE pForm)
1638 {
1639     FIXME("(%p,%s,%ld,%p): stub\n",hPrinter,pFormName,Level,pForm);
1640     return FALSE;
1641 }
1642
1643 /*****************************************************************************
1644  *          SetFormW  [WINSPOOL.@]
1645  */
1646 BOOL WINAPI SetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
1647                         LPBYTE pForm)
1648 {
1649     FIXME("(%p,%p,%ld,%p): stub\n",hPrinter,pFormName,Level,pForm);
1650     return FALSE;
1651 }
1652
1653 /*****************************************************************************
1654  *          ReadPrinter  [WINSPOOL.@]
1655  */
1656 BOOL WINAPI ReadPrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf,
1657                            LPDWORD pNoBytesRead)
1658 {
1659     FIXME("(%p,%p,%ld,%p): stub\n",hPrinter,pBuf,cbBuf,pNoBytesRead);
1660     return FALSE;
1661 }
1662
1663 /*****************************************************************************
1664  *          ResetPrinterA  [WINSPOOL.@]
1665  */
1666 BOOL WINAPI ResetPrinterA(HANDLE hPrinter, LPPRINTER_DEFAULTSA pDefault)
1667 {
1668     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
1669     return FALSE;
1670 }
1671
1672 /*****************************************************************************
1673  *          ResetPrinterW  [WINSPOOL.@]
1674  */
1675 BOOL WINAPI ResetPrinterW(HANDLE hPrinter, LPPRINTER_DEFAULTSW pDefault)
1676 {
1677     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
1678     return FALSE;
1679 }
1680
1681 /*****************************************************************************
1682  *    WINSPOOL_GetDWORDFromReg
1683  *
1684  * Return DWORD associated with ValueName from hkey.
1685  */
1686 static DWORD WINSPOOL_GetDWORDFromReg(HKEY hkey, LPCSTR ValueName)
1687 {
1688     DWORD sz = sizeof(DWORD), type, value = 0;
1689     LONG ret;
1690
1691     ret = RegQueryValueExA(hkey, ValueName, 0, &type, (LPBYTE)&value, &sz);
1692
1693     if(ret != ERROR_SUCCESS) {
1694         WARN("Got ret = %ld on name %s\n", ret, ValueName);
1695         return 0;
1696     }
1697     if(type != REG_DWORD) {
1698         ERR("Got type %ld\n", type);
1699         return 0;
1700     }
1701     return value;
1702 }
1703
1704 /*****************************************************************************
1705  *    WINSPOOL_GetStringFromReg
1706  *
1707  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
1708  * String is stored either as unicode or ascii.
1709  * Bit of a hack here to get the ValueName if we want ascii.
1710  */
1711 static BOOL WINSPOOL_GetStringFromReg(HKEY hkey, LPCWSTR ValueName, LPBYTE ptr,
1712                                       DWORD buflen, DWORD *needed,
1713                                       BOOL unicode)
1714 {
1715     DWORD sz = buflen, type;
1716     LONG ret;
1717
1718     if(unicode)
1719         ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
1720     else {
1721         LPSTR ValueNameA = HEAP_strdupWtoA(GetProcessHeap(),0,ValueName);
1722         ret = RegQueryValueExA(hkey, ValueNameA, 0, &type, ptr, &sz);
1723         HeapFree(GetProcessHeap(),0,ValueNameA);
1724     }
1725     if(ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) {
1726         WARN("Got ret = %ld\n", ret);
1727         *needed = 0;
1728         return FALSE;
1729     }
1730     *needed = sz;
1731     return TRUE;
1732 }
1733
1734 /*****************************************************************************
1735  *    WINSPOOL_GetDefaultDevMode
1736  *
1737  * Get a default DevMode values for wineps.
1738  * FIXME - use ppd.
1739  */
1740
1741 static void WINSPOOL_GetDefaultDevMode(
1742         LPBYTE ptr,
1743         DWORD buflen, DWORD *needed,
1744         BOOL unicode)
1745 {
1746     DEVMODEA    dm;
1747
1748         /* fill default DEVMODE - should be read from ppd... */
1749         ZeroMemory( &dm, sizeof(dm) );
1750         strcpy(dm.dmDeviceName,"wineps.drv");
1751         dm.dmSpecVersion = DM_SPECVERSION;
1752         dm.dmDriverVersion = 1;
1753         dm.dmSize = sizeof(DEVMODEA);
1754         dm.dmDriverExtra = 0;
1755         dm.dmFields =
1756                 DM_ORIENTATION | DM_PAPERSIZE |
1757                 DM_PAPERLENGTH | DM_PAPERWIDTH |
1758                 DM_SCALE |
1759                 DM_COPIES |
1760                 DM_DEFAULTSOURCE | DM_PRINTQUALITY |
1761                 DM_YRESOLUTION | DM_TTOPTION;
1762
1763         dm.u1.s1.dmOrientation = DMORIENT_PORTRAIT;
1764         dm.u1.s1.dmPaperSize = DMPAPER_A4;
1765         dm.u1.s1.dmPaperLength = 2970;
1766         dm.u1.s1.dmPaperWidth = 2100;
1767
1768         dm.dmScale = 100;
1769         dm.dmCopies = 1;
1770         dm.dmDefaultSource = DMBIN_AUTO;
1771         dm.dmPrintQuality = DMRES_MEDIUM;
1772         /* dm.dmColor */
1773         /* dm.dmDuplex */
1774         dm.dmYResolution = 300; /* 300dpi */
1775         dm.dmTTOption = DMTT_BITMAP;
1776         /* dm.dmCollate */
1777         /* dm.dmFormName */
1778         /* dm.dmLogPixels */
1779         /* dm.dmBitsPerPel */
1780         /* dm.dmPelsWidth */
1781         /* dm.dmPelsHeight */
1782         /* dm.dmDisplayFlags */
1783         /* dm.dmDisplayFrequency */
1784         /* dm.dmICMMethod */
1785         /* dm.dmICMIntent */
1786         /* dm.dmMediaType */
1787         /* dm.dmDitherType */
1788         /* dm.dmReserved1 */
1789         /* dm.dmReserved2 */
1790         /* dm.dmPanningWidth */
1791         /* dm.dmPanningHeight */
1792
1793     if(unicode) {
1794         if(buflen >= sizeof(DEVMODEW)) {
1795             DEVMODEW *pdmW = GdiConvertToDevmodeW(&dm);
1796             memcpy(ptr, pdmW, sizeof(DEVMODEW));
1797             HeapFree(GetProcessHeap(),0,pdmW);
1798         }
1799         *needed = sizeof(DEVMODEW);
1800     }
1801     else
1802     {
1803         if(buflen >= sizeof(DEVMODEA)) {
1804             memcpy(ptr, &dm, sizeof(DEVMODEA));
1805         }
1806         *needed = sizeof(DEVMODEA);
1807     }
1808 }
1809
1810 /*****************************************************************************
1811  *    WINSPOOL_GetDevModeFromReg
1812  *
1813  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
1814  * DevMode is stored either as unicode or ascii.
1815  */
1816 static BOOL WINSPOOL_GetDevModeFromReg(HKEY hkey, LPCWSTR ValueName,
1817                                        LPBYTE ptr,
1818                                        DWORD buflen, DWORD *needed,
1819                                        BOOL unicode)
1820 {
1821     DWORD sz = buflen, type;
1822     LONG ret;
1823
1824     if (ptr && buflen>=sizeof(DEVMODEA)) memset(ptr, 0, sizeof(DEVMODEA));
1825     ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
1826     if ((ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA)) sz = 0;
1827     if (sz < sizeof(DEVMODEA))
1828     {
1829         TRACE("corrupted registry for %s ( size %ld)\n",debugstr_w(ValueName),sz);
1830         return FALSE;
1831     }
1832     /* ensures that dmSize is not erratically bogus if registry is invalid */
1833     if (ptr && ((DEVMODEA*)ptr)->dmSize < sizeof(DEVMODEA))
1834         ((DEVMODEA*)ptr)->dmSize = sizeof(DEVMODEA);
1835     if(unicode) {
1836         sz += (CCHDEVICENAME + CCHFORMNAME);
1837         if(buflen >= sz) {
1838             DEVMODEW *dmW = GdiConvertToDevmodeW((DEVMODEA*)ptr);
1839             memcpy(ptr, dmW, sz);
1840             HeapFree(GetProcessHeap(),0,dmW);
1841         }
1842     }
1843     *needed = sz;
1844     return TRUE;
1845 }
1846
1847 /*********************************************************************
1848  *    WINSPOOL_GetPrinter_2
1849  *
1850  * Fills out a PRINTER_INFO_2A|W struct storing the strings in buf.
1851  * The strings are either stored as unicode or ascii.
1852  */
1853 static BOOL WINSPOOL_GetPrinter_2(HKEY hkeyPrinter, PRINTER_INFO_2W *pi2,
1854                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
1855                                   BOOL unicode)
1856 {
1857     DWORD size, left = cbBuf;
1858     BOOL space = (cbBuf > 0);
1859     LPBYTE ptr = buf;
1860
1861     *pcbNeeded = 0;
1862
1863     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
1864                                  unicode)) {
1865         if(space && size <= left) {
1866             pi2->pPrinterName = (LPWSTR)ptr;
1867             ptr += size;
1868             left -= size;
1869         } else
1870             space = FALSE;
1871         *pcbNeeded += size;
1872     }
1873     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Share_NameW, ptr, left, &size,
1874                                  unicode)) {
1875         if(space && size <= left) {
1876             pi2->pShareName = (LPWSTR)ptr;
1877             ptr += size;
1878             left -= size;
1879         } else
1880             space = FALSE;
1881         *pcbNeeded += size;
1882     }
1883     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
1884                                  unicode)) {
1885         if(space && size <= left) {
1886             pi2->pPortName = (LPWSTR)ptr;
1887             ptr += size;
1888             left -= size;
1889         } else
1890             space = FALSE;
1891         *pcbNeeded += size;
1892     }
1893     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Printer_DriverW, ptr, left,
1894                                  &size, unicode)) {
1895         if(space && size <= left) {
1896             pi2->pDriverName = (LPWSTR)ptr;
1897             ptr += size;
1898             left -= size;
1899         } else
1900             space = FALSE;
1901         *pcbNeeded += size;
1902     }
1903     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size,
1904                                  unicode)) {
1905         if(space && size <= left) {
1906             pi2->pComment = (LPWSTR)ptr;
1907             ptr += size;
1908             left -= size;
1909         } else
1910             space = FALSE;
1911         *pcbNeeded += size;
1912     }
1913     if(WINSPOOL_GetStringFromReg(hkeyPrinter, LocationW, ptr, left, &size,
1914                                  unicode)) {
1915         if(space && size <= left) {
1916             pi2->pLocation = (LPWSTR)ptr;
1917             ptr += size;
1918             left -= size;
1919         } else
1920             space = FALSE;
1921         *pcbNeeded += size;
1922     }
1923     if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, ptr, left,
1924                                   &size, unicode)) {
1925         if(space && size <= left) {
1926             pi2->pDevMode = (LPDEVMODEW)ptr;
1927             ptr += size;
1928             left -= size;
1929         } else
1930             space = FALSE;
1931         *pcbNeeded += size;
1932     }
1933     else
1934     {
1935         WINSPOOL_GetDefaultDevMode(ptr, left, &size, unicode);
1936         if(space && size <= left) {
1937             pi2->pDevMode = (LPDEVMODEW)ptr;
1938             ptr += size;
1939             left -= size;
1940         } else
1941             space = FALSE;
1942         *pcbNeeded += size;
1943     }
1944     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Separator_FileW, ptr, left,
1945                                  &size, unicode)) {
1946         if(space && size <= left) {
1947             pi2->pSepFile = (LPWSTR)ptr;
1948             ptr += size;
1949             left -= size;
1950         } else
1951             space = FALSE;
1952         *pcbNeeded += size;
1953     }
1954     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Print_ProcessorW, ptr, left,
1955                                  &size, unicode)) {
1956         if(space && size <= left) {
1957             pi2->pPrintProcessor = (LPWSTR)ptr;
1958             ptr += size;
1959             left -= size;
1960         } else
1961             space = FALSE;
1962         *pcbNeeded += size;
1963     }
1964     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DatatypeW, ptr, left,
1965                                  &size, unicode)) {
1966         if(space && size <= left) {
1967             pi2->pDatatype = (LPWSTR)ptr;
1968             ptr += size;
1969             left -= size;
1970         } else
1971             space = FALSE;
1972         *pcbNeeded += size;
1973     }
1974     if(WINSPOOL_GetStringFromReg(hkeyPrinter, ParametersW, ptr, left,
1975                                  &size, unicode)) {
1976         if(space && size <= left) {
1977             pi2->pParameters = (LPWSTR)ptr;
1978             ptr += size;
1979             left -= size;
1980         } else
1981             space = FALSE;
1982         *pcbNeeded += size;
1983     }
1984     if(pi2) {
1985         pi2->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
1986         pi2->Priority = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Priority");
1987         pi2->DefaultPriority = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
1988                                                         "Default Priority");
1989         pi2->StartTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "StartTime");
1990         pi2->UntilTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "UntilTime");
1991     }
1992
1993     if(!space && pi2) /* zero out pi2 if we can't completely fill buf */
1994         memset(pi2, 0, sizeof(*pi2));
1995
1996     return space;
1997 }
1998
1999 /*********************************************************************
2000  *    WINSPOOL_GetPrinter_4
2001  *
2002  * Fills out a PRINTER_INFO_4 struct storing the strings in buf.
2003  */
2004 static BOOL WINSPOOL_GetPrinter_4(HKEY hkeyPrinter, PRINTER_INFO_4W *pi4,
2005                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
2006                                   BOOL unicode)
2007 {
2008     DWORD size, left = cbBuf;
2009     BOOL space = (cbBuf > 0);
2010     LPBYTE ptr = buf;
2011
2012     *pcbNeeded = 0;
2013
2014     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
2015                                  unicode)) {
2016         if(space && size <= left) {
2017             pi4->pPrinterName = (LPWSTR)ptr;
2018             ptr += size;
2019             left -= size;
2020         } else
2021             space = FALSE;
2022         *pcbNeeded += size;
2023     }
2024     if(pi4) {
2025         pi4->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
2026     }
2027
2028     if(!space && pi4) /* zero out pi4 if we can't completely fill buf */
2029         memset(pi4, 0, sizeof(*pi4));
2030
2031     return space;
2032 }
2033
2034 /*********************************************************************
2035  *    WINSPOOL_GetPrinter_5
2036  *
2037  * Fills out a PRINTER_INFO_5 struct storing the strings in buf.
2038  */
2039 static BOOL WINSPOOL_GetPrinter_5(HKEY hkeyPrinter, PRINTER_INFO_5W *pi5,
2040                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
2041                                   BOOL unicode)
2042 {
2043     DWORD size, left = cbBuf;
2044     BOOL space = (cbBuf > 0);
2045     LPBYTE ptr = buf;
2046
2047     *pcbNeeded = 0;
2048
2049     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
2050                                  unicode)) {
2051         if(space && size <= left) {
2052             pi5->pPrinterName = (LPWSTR)ptr;
2053             ptr += size;
2054             left -= size;
2055         } else
2056             space = FALSE;
2057         *pcbNeeded += size;
2058     }
2059     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
2060                                  unicode)) {
2061         if(space && size <= left) {
2062             pi5->pPortName = (LPWSTR)ptr;
2063             ptr += size;
2064             left -= size;
2065         } else
2066             space = FALSE;
2067         *pcbNeeded += size;
2068     }
2069     if(pi5) {
2070         pi5->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
2071         pi5->DeviceNotSelectedTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
2072                                                                 "dnsTimeout");
2073         pi5->TransmissionRetryTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
2074                                                                  "txTimeout");
2075     }
2076
2077     if(!space && pi5) /* zero out pi5 if we can't completely fill buf */
2078         memset(pi5, 0, sizeof(*pi5));
2079
2080     return space;
2081 }
2082
2083 /*****************************************************************************
2084  *          WINSPOOL_GetPrinter
2085  *
2086  *    Implementation of GetPrinterA|W.  Relies on PRINTER_INFO_*W being
2087  *    essentially the same as PRINTER_INFO_*A. i.e. the structure itself is
2088  *    just a collection of pointers to strings.
2089  */
2090 static BOOL WINSPOOL_GetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
2091                                 DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
2092 {
2093     LPCWSTR name;
2094     DWORD size, needed = 0;
2095     LPBYTE ptr = NULL;
2096     HKEY hkeyPrinter, hkeyPrinters;
2097     BOOL ret;
2098
2099     TRACE("(%p,%ld,%p,%ld,%p)\n",hPrinter,Level,pPrinter,cbBuf, pcbNeeded);
2100
2101     if (!(name = WINSPOOL_GetOpenedPrinter(hPrinter))) return FALSE;
2102
2103     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
2104        ERROR_SUCCESS) {
2105         ERR("Can't create Printers key\n");
2106         return FALSE;
2107     }
2108     if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter) != ERROR_SUCCESS)
2109     {
2110         ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
2111         RegCloseKey(hkeyPrinters);
2112         SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
2113         return FALSE;
2114     }
2115
2116     switch(Level) {
2117     case 2:
2118       {
2119         PRINTER_INFO_2W *pi2 = (PRINTER_INFO_2W *)pPrinter;
2120
2121         size = sizeof(PRINTER_INFO_2W);
2122         if(size <= cbBuf) {
2123             ptr = pPrinter + size;
2124             cbBuf -= size;
2125             memset(pPrinter, 0, size);
2126         } else {
2127             pi2 = NULL;
2128             cbBuf = 0;
2129         }
2130         ret = WINSPOOL_GetPrinter_2(hkeyPrinter, pi2, ptr, cbBuf, &needed,
2131                                     unicode);
2132         needed += size;
2133         break;
2134       }
2135
2136     case 4:
2137       {
2138         PRINTER_INFO_4W *pi4 = (PRINTER_INFO_4W *)pPrinter;
2139
2140         size = sizeof(PRINTER_INFO_4W);
2141         if(size <= cbBuf) {
2142             ptr = pPrinter + size;
2143             cbBuf -= size;
2144             memset(pPrinter, 0, size);
2145         } else {
2146             pi4 = NULL;
2147             cbBuf = 0;
2148         }
2149         ret = WINSPOOL_GetPrinter_4(hkeyPrinter, pi4, ptr, cbBuf, &needed,
2150                                     unicode);
2151         needed += size;
2152         break;
2153       }
2154
2155
2156     case 5:
2157       {
2158         PRINTER_INFO_5W *pi5 = (PRINTER_INFO_5W *)pPrinter;
2159
2160         size = sizeof(PRINTER_INFO_5W);
2161         if(size <= cbBuf) {
2162             ptr = pPrinter + size;
2163             cbBuf -= size;
2164             memset(pPrinter, 0, size);
2165         } else {
2166             pi5 = NULL;
2167             cbBuf = 0;
2168         }
2169
2170         ret = WINSPOOL_GetPrinter_5(hkeyPrinter, pi5, ptr, cbBuf, &needed,
2171                                     unicode);
2172         needed += size;
2173         break;
2174       }
2175
2176     default:
2177         FIXME("Unimplemented level %ld\n", Level);
2178         SetLastError(ERROR_INVALID_LEVEL);
2179         RegCloseKey(hkeyPrinters);
2180         RegCloseKey(hkeyPrinter);
2181         return FALSE;
2182     }
2183
2184     RegCloseKey(hkeyPrinter);
2185     RegCloseKey(hkeyPrinters);
2186
2187     TRACE("returning %d needed = %ld\n", ret, needed);
2188     if(pcbNeeded) *pcbNeeded = needed;
2189     if(!ret)
2190         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2191     return ret;
2192 }
2193
2194 /*****************************************************************************
2195  *          GetPrinterW  [WINSPOOL.@]
2196  */
2197 BOOL WINAPI GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
2198                         DWORD cbBuf, LPDWORD pcbNeeded)
2199 {
2200     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
2201                                TRUE);
2202 }
2203
2204 /*****************************************************************************
2205  *          GetPrinterA  [WINSPOOL.@]
2206  */
2207 BOOL WINAPI GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
2208                     DWORD cbBuf, LPDWORD pcbNeeded)
2209 {
2210     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
2211                                FALSE);
2212 }
2213
2214 /*****************************************************************************
2215  *          WINSPOOL_EnumPrinters
2216  *
2217  *    Implementation of EnumPrintersA|W
2218  */
2219 static BOOL WINSPOOL_EnumPrinters(DWORD dwType, LPWSTR lpszName,
2220                                   DWORD dwLevel, LPBYTE lpbPrinters,
2221                                   DWORD cbBuf, LPDWORD lpdwNeeded,
2222                                   LPDWORD lpdwReturned, BOOL unicode)
2223
2224 {
2225     HKEY hkeyPrinters, hkeyPrinter;
2226     WCHAR PrinterName[255];
2227     DWORD needed = 0, number = 0;
2228     DWORD used, i, left;
2229     PBYTE pi, buf;
2230
2231     if(lpbPrinters)
2232         memset(lpbPrinters, 0, cbBuf);
2233     if(lpdwReturned)
2234         *lpdwReturned = 0;
2235     if(lpdwNeeded)
2236         *lpdwNeeded = 0;
2237
2238     /* PRINTER_ENUM_DEFAULT is only supported under win9x, we behave like NT */
2239     if(dwType == PRINTER_ENUM_DEFAULT)
2240         return TRUE;
2241
2242     if (dwType & PRINTER_ENUM_CONNECTIONS) {
2243         FIXME("We don't handle PRINTER_ENUM_CONNECTIONS\n");
2244         dwType &= ~PRINTER_ENUM_CONNECTIONS; /* we don't handle that */
2245         if(!dwType) return TRUE;
2246     }
2247
2248     if (!((dwType & PRINTER_ENUM_LOCAL) || (dwType & PRINTER_ENUM_NAME))) {
2249         FIXME("dwType = %08lx\n", dwType);
2250         SetLastError(ERROR_INVALID_FLAGS);
2251         return FALSE;
2252     }
2253
2254     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
2255        ERROR_SUCCESS) {
2256         ERR("Can't create Printers key\n");
2257         return FALSE;
2258     }
2259
2260     if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &number, NULL, NULL,
2261                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
2262         RegCloseKey(hkeyPrinters);
2263         ERR("Can't query Printers key\n");
2264         return FALSE;
2265     }
2266     TRACE("Found %ld printers\n", number);
2267
2268     switch(dwLevel) {
2269     case 1:
2270         RegCloseKey(hkeyPrinters);
2271         if (lpdwReturned)
2272             *lpdwReturned = number;
2273         return TRUE;
2274
2275     case 2:
2276         used = number * sizeof(PRINTER_INFO_2W);
2277         break;
2278     case 4:
2279         used = number * sizeof(PRINTER_INFO_4W);
2280         break;
2281     case 5:
2282         used = number * sizeof(PRINTER_INFO_5W);
2283         break;
2284
2285     default:
2286         SetLastError(ERROR_INVALID_LEVEL);
2287         RegCloseKey(hkeyPrinters);
2288         return FALSE;
2289     }
2290     pi = (used <= cbBuf) ? lpbPrinters : NULL;
2291
2292     for(i = 0; i < number; i++) {
2293         if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)) !=
2294            ERROR_SUCCESS) {
2295             ERR("Can't enum key number %ld\n", i);
2296             RegCloseKey(hkeyPrinters);
2297             return FALSE;
2298         }
2299         TRACE("Printer %ld is %s\n", i, debugstr_w(PrinterName));
2300         if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkeyPrinter) !=
2301            ERROR_SUCCESS) {
2302             ERR("Can't open key %s\n", debugstr_w(PrinterName));
2303             RegCloseKey(hkeyPrinters);
2304             return FALSE;
2305         }
2306
2307         if(cbBuf > used) {
2308             buf = lpbPrinters + used;
2309             left = cbBuf - used;
2310         } else {
2311             buf = NULL;
2312             left = 0;
2313         }
2314
2315         switch(dwLevel) {
2316         case 2:
2317             WINSPOOL_GetPrinter_2(hkeyPrinter, (PRINTER_INFO_2W *)pi, buf,
2318                                   left, &needed, unicode);
2319             used += needed;
2320             if(pi) pi += sizeof(PRINTER_INFO_2W);
2321             break;
2322         case 4:
2323             WINSPOOL_GetPrinter_4(hkeyPrinter, (PRINTER_INFO_4W *)pi, buf,
2324                                   left, &needed, unicode);
2325             used += needed;
2326             if(pi) pi += sizeof(PRINTER_INFO_4W);
2327             break;
2328         case 5:
2329             WINSPOOL_GetPrinter_5(hkeyPrinter, (PRINTER_INFO_5W *)pi, buf,
2330                                   left, &needed, unicode);
2331             used += needed;
2332             if(pi) pi += sizeof(PRINTER_INFO_5W);
2333             break;
2334         default:
2335             ERR("Shouldn't be here!\n");
2336             RegCloseKey(hkeyPrinter);
2337             RegCloseKey(hkeyPrinters);
2338             return FALSE;
2339         }
2340         RegCloseKey(hkeyPrinter);
2341     }
2342     RegCloseKey(hkeyPrinters);
2343
2344     if(lpdwNeeded)
2345         *lpdwNeeded = used;
2346
2347     if(used > cbBuf) {
2348         if(lpbPrinters)
2349             memset(lpbPrinters, 0, cbBuf);
2350         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2351         return FALSE;
2352     }
2353     if(lpdwReturned)
2354         *lpdwReturned = number;
2355     SetLastError(ERROR_SUCCESS);
2356     return TRUE;
2357 }
2358
2359
2360 /******************************************************************
2361  *              EnumPrintersW        [WINSPOOL.@]
2362  *
2363  *    Enumerates the available printers, print servers and print
2364  *    providers, depending on the specified flags, name and level.
2365  *
2366  * RETURNS:
2367  *
2368  *    If level is set to 1:
2369  *      Not implemented yet!
2370  *      Returns TRUE with an empty list.
2371  *
2372  *    If level is set to 2:
2373  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
2374  *      Returns an array of PRINTER_INFO_2 data structures in the
2375  *      lpbPrinters buffer. Note that according to MSDN also an
2376  *      OpenPrinter should be performed on every remote printer.
2377  *
2378  *    If level is set to 4 (officially WinNT only):
2379  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
2380  *      Fast: Only the registry is queried to retrieve printer names,
2381  *      no connection to the driver is made.
2382  *      Returns an array of PRINTER_INFO_4 data structures in the
2383  *      lpbPrinters buffer.
2384  *
2385  *    If level is set to 5 (officially WinNT4/Win9x only):
2386  *      Fast: Only the registry is queried to retrieve printer names,
2387  *      no connection to the driver is made.
2388  *      Returns an array of PRINTER_INFO_5 data structures in the
2389  *      lpbPrinters buffer.
2390  *
2391  *    If level set to 3 or 6+:
2392  *          returns zero (failure!)
2393  *
2394  *    Returns nonzero (TRUE) on success, or zero on failure, use GetLastError
2395  *    for information.
2396  *
2397  * BUGS:
2398  *    - Only PRINTER_ENUM_LOCAL and PRINTER_ENUM_NAME are implemented.
2399  *    - Only levels 2, 4 and 5 are implemented at the moment.
2400  *    - 16-bit printer drivers are not enumerated.
2401  *    - Returned amount of bytes used/needed does not match the real Windoze
2402  *      implementation (as in this implementation, all strings are part
2403  *      of the buffer, whereas Win32 keeps them somewhere else)
2404  *    - At level 2, EnumPrinters should also call OpenPrinter for remote printers.
2405  *
2406  * NOTE:
2407  *    - In a regular Wine installation, no registry settings for printers
2408  *      exist, which makes this function return an empty list.
2409  */
2410 BOOL  WINAPI EnumPrintersW(
2411                 DWORD dwType,        /* [in] Types of print objects to enumerate */
2412                 LPWSTR lpszName,     /* [in] name of objects to enumerate */
2413                 DWORD dwLevel,       /* [in] type of printer info structure */
2414                 LPBYTE lpbPrinters,  /* [out] buffer which receives info */
2415                 DWORD cbBuf,         /* [in] max size of buffer in bytes */
2416                 LPDWORD lpdwNeeded,  /* [out] pointer to var: # bytes used/needed */
2417                 LPDWORD lpdwReturned /* [out] number of entries returned */
2418                 )
2419 {
2420     return WINSPOOL_EnumPrinters(dwType, lpszName, dwLevel, lpbPrinters, cbBuf,
2421                                  lpdwNeeded, lpdwReturned, TRUE);
2422 }
2423
2424 /******************************************************************
2425  *              EnumPrintersA        [WINSPOOL.@]
2426  *
2427  */
2428 BOOL WINAPI EnumPrintersA(DWORD dwType, LPSTR lpszName,
2429                           DWORD dwLevel, LPBYTE lpbPrinters,
2430                           DWORD cbBuf, LPDWORD lpdwNeeded,
2431                           LPDWORD lpdwReturned)
2432 {
2433     BOOL ret;
2434     UNICODE_STRING lpszNameW;
2435     PWSTR pwstrNameW;
2436     
2437     pwstrNameW = asciitounicode(&lpszNameW,lpszName);
2438     ret = WINSPOOL_EnumPrinters(dwType, pwstrNameW, dwLevel, lpbPrinters, cbBuf,
2439                                 lpdwNeeded, lpdwReturned, FALSE);
2440     RtlFreeUnicodeString(&lpszNameW);
2441     return ret;
2442 }
2443
2444 /*****************************************************************************
2445  *          WINSPOOL_GetDriverInfoFromReg [internal]
2446  *
2447  *    Enters the information from the registry into the DRIVER_INFO struct
2448  *
2449  * RETURNS
2450  *    zero if the printer driver does not exist in the registry
2451  *    (only if Level > 1) otherwise nonzero
2452  */
2453 static BOOL WINSPOOL_GetDriverInfoFromReg(
2454                             HKEY    hkeyDrivers,
2455                             LPWSTR  DriverName,
2456                             LPWSTR  pEnvironment,
2457                             DWORD   Level,
2458                             LPBYTE  ptr,            /* DRIVER_INFO */
2459                             LPBYTE  pDriverStrings, /* strings buffer */
2460                             DWORD   cbBuf,          /* size of string buffer */
2461                             LPDWORD pcbNeeded,      /* space needed for str. */
2462                             BOOL    unicode)        /* type of strings */
2463 {   DWORD  dw, size, tmp, type;
2464     HKEY   hkeyDriver;
2465     LPBYTE strPtr = pDriverStrings;
2466
2467     TRACE("%s,%s,%ld,%p,%p,%ld,%d\n",
2468           debugstr_w(DriverName), debugstr_w(pEnvironment),
2469           Level, ptr, pDriverStrings, cbBuf, unicode);
2470
2471     if(unicode) {
2472         *pcbNeeded = (lstrlenW(DriverName) + 1) * sizeof(WCHAR);
2473             if (*pcbNeeded <= cbBuf)
2474                strcpyW((LPWSTR)strPtr, DriverName);
2475     } else {
2476         *pcbNeeded = WideCharToMultiByte(CP_ACP, 0, DriverName, -1, NULL, 0,
2477                                           NULL, NULL);
2478         if(*pcbNeeded <= cbBuf)
2479             WideCharToMultiByte(CP_ACP, 0, DriverName, -1, strPtr, *pcbNeeded,
2480                                 NULL, NULL);
2481     }
2482     if(Level == 1) {
2483        if(ptr)
2484           ((PDRIVER_INFO_1W) ptr)->pName = (LPWSTR) strPtr;
2485        return TRUE;
2486     } else {
2487        if(ptr)
2488           ((PDRIVER_INFO_3W) ptr)->pName = (LPWSTR) strPtr;
2489        strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
2490     }
2491
2492     if(!DriverName[0] || RegOpenKeyW(hkeyDrivers, DriverName, &hkeyDriver) != ERROR_SUCCESS) {
2493         ERR("Can't find driver '%s' in registry\n", debugstr_w(DriverName));
2494         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER); /* ? */
2495         return FALSE;
2496     }
2497
2498     size = sizeof(dw);
2499     if(RegQueryValueExA(hkeyDriver, "Version", 0, &type, (PBYTE)&dw, &size) !=
2500         ERROR_SUCCESS)
2501          WARN("Can't get Version\n");
2502     else if(ptr)
2503          ((PDRIVER_INFO_3A) ptr)->cVersion = dw;
2504
2505     if(!pEnvironment)
2506         pEnvironment = (LPWSTR)DefaultEnvironmentW;
2507     if(unicode)
2508         size = (lstrlenW(pEnvironment) + 1) * sizeof(WCHAR);
2509     else
2510         size = WideCharToMultiByte(CP_ACP, 0, pEnvironment, -1, NULL, 0,
2511                                    NULL, NULL);
2512     *pcbNeeded += size;
2513     if(*pcbNeeded <= cbBuf) {
2514         if(unicode)
2515             strcpyW((LPWSTR)strPtr, pEnvironment);
2516         else
2517             WideCharToMultiByte(CP_ACP, 0, pEnvironment, -1, strPtr, size,
2518                                 NULL, NULL);
2519         if(ptr)
2520             ((PDRIVER_INFO_3W) ptr)->pEnvironment = (LPWSTR)strPtr;
2521         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
2522     }
2523
2524     if(WINSPOOL_GetStringFromReg(hkeyDriver, DriverW, strPtr, 0, &size,
2525                                  unicode)) {
2526         *pcbNeeded += size;
2527         if(*pcbNeeded <= cbBuf)
2528             WINSPOOL_GetStringFromReg(hkeyDriver, DriverW, strPtr, size, &tmp,
2529                                       unicode);
2530         if(ptr)
2531             ((PDRIVER_INFO_3W) ptr)->pDriverPath = (LPWSTR)strPtr;
2532         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
2533     }
2534
2535     if(WINSPOOL_GetStringFromReg(hkeyDriver, Data_FileW, strPtr, 0, &size,
2536                                  unicode)) {
2537         *pcbNeeded += size;
2538         if(*pcbNeeded <= cbBuf)
2539             WINSPOOL_GetStringFromReg(hkeyDriver, Data_FileW, strPtr, size,
2540                                       &tmp, unicode);
2541         if(ptr)
2542             ((PDRIVER_INFO_3W) ptr)->pDataFile = (LPWSTR)strPtr;
2543         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
2544     }
2545
2546     if(WINSPOOL_GetStringFromReg(hkeyDriver, Configuration_FileW, strPtr,
2547                                  0, &size, unicode)) {
2548         *pcbNeeded += size;
2549         if(*pcbNeeded <= cbBuf)
2550             WINSPOOL_GetStringFromReg(hkeyDriver, Configuration_FileW, strPtr,
2551                                       size, &tmp, unicode);
2552         if(ptr)
2553             ((PDRIVER_INFO_3W) ptr)->pConfigFile = (LPWSTR)strPtr;
2554         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
2555     }
2556
2557     if(Level == 2 ) {
2558         RegCloseKey(hkeyDriver);
2559         TRACE("buffer space %ld required %ld\n", cbBuf, *pcbNeeded);
2560         return TRUE;
2561     }
2562
2563     if(WINSPOOL_GetStringFromReg(hkeyDriver, Help_FileW, strPtr, 0, &size,
2564                                  unicode)) {
2565         *pcbNeeded += size;
2566         if(*pcbNeeded <= cbBuf)
2567             WINSPOOL_GetStringFromReg(hkeyDriver, Help_FileW, strPtr,
2568                                       size, &tmp, unicode);
2569         if(ptr)
2570             ((PDRIVER_INFO_3W) ptr)->pHelpFile = (LPWSTR)strPtr;
2571         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
2572     }
2573
2574     if(WINSPOOL_GetStringFromReg(hkeyDriver, Dependent_FilesW, strPtr, 0,
2575                              &size, unicode)) {
2576         *pcbNeeded += size;
2577         if(*pcbNeeded <= cbBuf)
2578             WINSPOOL_GetStringFromReg(hkeyDriver, Dependent_FilesW, strPtr,
2579                                       size, &tmp, unicode);
2580         if(ptr)
2581             ((PDRIVER_INFO_3W) ptr)->pDependentFiles = (LPWSTR)strPtr;
2582         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
2583     }
2584
2585     if(WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, 0, &size,
2586                                  unicode)) {
2587         *pcbNeeded += size;
2588         if(*pcbNeeded <= cbBuf)
2589             WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr,
2590                                       size, &tmp, unicode);
2591         if(ptr)
2592             ((PDRIVER_INFO_3W) ptr)->pMonitorName = (LPWSTR)strPtr;
2593         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
2594     }
2595
2596     if(WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, 0, &size,
2597                                  unicode)) {
2598         *pcbNeeded += size;
2599         if(*pcbNeeded <= cbBuf)
2600             WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr,
2601                                       size, &tmp, unicode);
2602         if(ptr)
2603             ((PDRIVER_INFO_3W) ptr)->pDefaultDataType = (LPWSTR)strPtr;
2604         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
2605     }
2606
2607     TRACE("buffer space %ld required %ld\n", cbBuf, *pcbNeeded);
2608     RegCloseKey(hkeyDriver);
2609     return TRUE;
2610 }
2611
2612 /*****************************************************************************
2613  *          WINSPOOL_GetPrinterDriver
2614  */
2615 static BOOL WINSPOOL_GetPrinterDriver(HANDLE hPrinter, LPWSTR pEnvironment,
2616                                       DWORD Level, LPBYTE pDriverInfo,
2617                                       DWORD cbBuf, LPDWORD pcbNeeded,
2618                                       BOOL unicode)
2619 {
2620     LPCWSTR name;
2621     WCHAR DriverName[100];
2622     DWORD ret, type, size, needed = 0;
2623     LPBYTE ptr = NULL;
2624     HKEY hkeyPrinter, hkeyPrinters, hkeyDrivers;
2625
2626     TRACE("(%p,%s,%ld,%p,%ld,%p)\n",hPrinter,debugstr_w(pEnvironment),
2627           Level,pDriverInfo,cbBuf, pcbNeeded);
2628
2629     ZeroMemory(pDriverInfo, cbBuf);
2630
2631     if (!(name = WINSPOOL_GetOpenedPrinter(hPrinter))) return FALSE;
2632
2633     if(Level < 1 || Level > 3) {
2634         SetLastError(ERROR_INVALID_LEVEL);
2635         return FALSE;
2636     }
2637     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
2638        ERROR_SUCCESS) {
2639         ERR("Can't create Printers key\n");
2640         return FALSE;
2641     }
2642     if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter)
2643        != ERROR_SUCCESS) {
2644         ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
2645         RegCloseKey(hkeyPrinters);
2646         SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
2647         return FALSE;
2648     }
2649     size = sizeof(DriverName);
2650     DriverName[0] = 0;
2651     ret = RegQueryValueExW(hkeyPrinter, Printer_DriverW, 0, &type,
2652                            (LPBYTE)DriverName, &size);
2653     RegCloseKey(hkeyPrinter);
2654     RegCloseKey(hkeyPrinters);
2655     if(ret != ERROR_SUCCESS) {
2656         ERR("Can't get DriverName for printer %s\n", debugstr_w(name));
2657         return FALSE;
2658     }
2659
2660     hkeyDrivers = WINSPOOL_OpenDriverReg( pEnvironment, TRUE);
2661     if(!hkeyDrivers) {
2662         ERR("Can't create Drivers key\n");
2663         return FALSE;
2664     }
2665
2666     switch(Level) {
2667     case 1:
2668         size = sizeof(DRIVER_INFO_1W);
2669         break;
2670     case 2:
2671         size = sizeof(DRIVER_INFO_2W);
2672         break;
2673     case 3:
2674         size = sizeof(DRIVER_INFO_3W);
2675         break;
2676     default:
2677         ERR("Invalid level\n");
2678         return FALSE;
2679     }
2680
2681     if(size <= cbBuf)
2682         ptr = pDriverInfo + size;
2683
2684     if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverName,
2685                          pEnvironment, Level, pDriverInfo,
2686                          (cbBuf < size) ? NULL : ptr,
2687                          (cbBuf < size) ? 0 : cbBuf - size,
2688                          &needed, unicode)) {
2689             RegCloseKey(hkeyDrivers);
2690             return FALSE;
2691     }
2692
2693     RegCloseKey(hkeyDrivers);
2694
2695     if(pcbNeeded) *pcbNeeded = size + needed;
2696     TRACE("buffer space %ld required %ld\n", cbBuf, *pcbNeeded);
2697     if(cbBuf >= needed) return TRUE;
2698     SetLastError(ERROR_INSUFFICIENT_BUFFER);
2699     return FALSE;
2700 }
2701
2702 /*****************************************************************************
2703  *          GetPrinterDriverA  [WINSPOOL.@]
2704  */
2705 BOOL WINAPI GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment,
2706                               DWORD Level, LPBYTE pDriverInfo,
2707                               DWORD cbBuf, LPDWORD pcbNeeded)
2708 {
2709     BOOL ret;
2710     UNICODE_STRING pEnvW;
2711     PWSTR pwstrEnvW;
2712     
2713     pwstrEnvW = asciitounicode(&pEnvW, pEnvironment);
2714     ret = WINSPOOL_GetPrinterDriver(hPrinter, pwstrEnvW, Level, pDriverInfo,
2715                                     cbBuf, pcbNeeded, FALSE);
2716     RtlFreeUnicodeString(&pEnvW);
2717     return ret;
2718 }
2719 /*****************************************************************************
2720  *          GetPrinterDriverW  [WINSPOOL.@]
2721  */
2722 BOOL WINAPI GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment,
2723                                   DWORD Level, LPBYTE pDriverInfo,
2724                                   DWORD cbBuf, LPDWORD pcbNeeded)
2725 {
2726     return WINSPOOL_GetPrinterDriver(hPrinter, pEnvironment, Level,
2727                                      pDriverInfo, cbBuf, pcbNeeded, TRUE);
2728 }
2729
2730 /*****************************************************************************
2731  *       GetPrinterDriverDirectoryW  [WINSPOOL.@]
2732  */
2733 BOOL WINAPI GetPrinterDriverDirectoryW(LPWSTR pName, LPWSTR pEnvironment,
2734                                        DWORD Level, LPBYTE pDriverDirectory,
2735                                        DWORD cbBuf, LPDWORD pcbNeeded)
2736 {
2737     DWORD needed;
2738
2739     TRACE("(%s, %s, %ld, %p, %ld, %p)\n", debugstr_w(pName), 
2740           debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
2741     if(pName != NULL) {
2742         FIXME("pName = `%s' - unsupported\n", debugstr_w(pName));
2743         SetLastError(ERROR_INVALID_PARAMETER);
2744         return FALSE;
2745     }
2746     if(pEnvironment != NULL) {
2747         FIXME("pEnvironment = `%s' - unsupported\n", debugstr_w(pEnvironment));
2748         SetLastError(ERROR_INVALID_ENVIRONMENT);
2749         return FALSE;
2750     }
2751     if(Level != 1)  /* win95 ignores this so we just carry on */
2752         WARN("Level = %ld - assuming 1\n", Level);
2753
2754     /* FIXME should read from registry */
2755     needed = GetSystemDirectoryW( (LPWSTR)pDriverDirectory, cbBuf/sizeof(WCHAR));
2756     /* GetSystemDirectoryW returns number of TCHAR without '\0' 
2757      * adjust this now
2758      */
2759     needed++;
2760     needed*=sizeof(WCHAR);
2761
2762     if(pcbNeeded)
2763         *pcbNeeded = needed;
2764     TRACE("required <%08lx>\n", *pcbNeeded);
2765     if(needed > cbBuf) {
2766         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2767         return FALSE;
2768     }
2769     return TRUE;
2770 }
2771
2772
2773 /*****************************************************************************
2774  *       GetPrinterDriverDirectoryA  [WINSPOOL.@]
2775  */
2776 BOOL WINAPI GetPrinterDriverDirectoryA(LPSTR pName, LPSTR pEnvironment,
2777                                        DWORD Level, LPBYTE pDriverDirectory,
2778                                        DWORD cbBuf, LPDWORD pcbNeeded)
2779 {
2780     UNICODE_STRING nameW, environmentW;
2781     BOOL ret;
2782     DWORD pcbNeededW;
2783     INT len = cbBuf * sizeof(WCHAR)/sizeof(CHAR);
2784     WCHAR *driverDirectoryW = NULL;
2785
2786     if (len) driverDirectoryW = HeapAlloc( GetProcessHeap(), 0, len );
2787
2788     if(pName) RtlCreateUnicodeStringFromAsciiz(&nameW, pName);
2789     else nameW.Buffer = NULL;
2790     if(pEnvironment) RtlCreateUnicodeStringFromAsciiz(&environmentW, pEnvironment);
2791     else environmentW.Buffer = NULL;
2792
2793     ret = GetPrinterDriverDirectoryW( nameW.Buffer, environmentW.Buffer, Level,
2794                                       (LPBYTE)driverDirectoryW, len, &pcbNeededW );
2795     if (ret) {
2796         DWORD needed;
2797         needed = 1 + WideCharToMultiByte( CP_ACP, 0, driverDirectoryW, -1, 
2798                                    pDriverDirectory, cbBuf, NULL, NULL);
2799         if(pcbNeeded)
2800             *pcbNeeded = needed;
2801         ret = (needed <= cbBuf) ? TRUE : FALSE;
2802     } else 
2803         if(pcbNeeded) *pcbNeeded = pcbNeededW * sizeof(CHAR)/sizeof(WCHAR);
2804
2805     TRACE("provided<%ld> required <%ld>\n", cbBuf, *pcbNeeded);
2806
2807     HeapFree( GetProcessHeap(), 0, driverDirectoryW );
2808     RtlFreeUnicodeString(&environmentW);
2809     RtlFreeUnicodeString(&nameW);
2810
2811     return ret;
2812 }
2813
2814 /*****************************************************************************
2815  *          AddPrinterDriverA  [WINSPOOL.@]
2816  */
2817 BOOL WINAPI AddPrinterDriverA(LPSTR pName, DWORD level, LPBYTE pDriverInfo)
2818 {
2819     DRIVER_INFO_3A di3;
2820     HKEY hkeyDrivers, hkeyName;
2821
2822     TRACE("(%s,%ld,%p)\n",debugstr_a(pName),level,pDriverInfo);
2823
2824     if(level != 2 && level != 3) {
2825         SetLastError(ERROR_INVALID_LEVEL);
2826         return FALSE;
2827     }
2828     if(pName != NULL) {
2829         FIXME("pName= %s - unsupported\n", debugstr_a(pName));
2830         SetLastError(ERROR_INVALID_PARAMETER);
2831         return FALSE;
2832     }
2833     if(!pDriverInfo) {
2834         WARN("pDriverInfo == NULL\n");
2835         SetLastError(ERROR_INVALID_PARAMETER);
2836         return FALSE;
2837     }
2838
2839     if(level == 3)
2840         di3 = *(DRIVER_INFO_3A *)pDriverInfo;
2841     else {
2842         memset(&di3, 0, sizeof(di3));
2843         memcpy(&di3, pDriverInfo, sizeof(DRIVER_INFO_2A));
2844     }
2845
2846     if(!di3.pName || !di3.pDriverPath || !di3.pConfigFile ||
2847        !di3.pDataFile) {
2848         SetLastError(ERROR_INVALID_PARAMETER);
2849         return FALSE;
2850     }
2851     if(!di3.pDefaultDataType) di3.pDefaultDataType = "";
2852     if(!di3.pDependentFiles) di3.pDependentFiles = "\0";
2853     if(!di3.pHelpFile) di3.pHelpFile = "";
2854     if(!di3.pMonitorName) di3.pMonitorName = "";
2855
2856     hkeyDrivers = WINSPOOL_OpenDriverReg(di3.pEnvironment, FALSE);
2857
2858     if(!hkeyDrivers) {
2859         ERR("Can't create Drivers key\n");
2860         return FALSE;
2861     }
2862
2863     if(level == 2) { /* apparently can't overwrite with level2 */
2864         if(RegOpenKeyA(hkeyDrivers, di3.pName, &hkeyName) == ERROR_SUCCESS) {
2865             RegCloseKey(hkeyName);
2866             RegCloseKey(hkeyDrivers);
2867             WARN("Trying to create existing printer driver %s\n", debugstr_a(di3.pName));
2868             SetLastError(ERROR_PRINTER_DRIVER_ALREADY_INSTALLED);
2869             return FALSE;
2870         }
2871     }
2872     if(RegCreateKeyA(hkeyDrivers, di3.pName, &hkeyName) != ERROR_SUCCESS) {
2873         RegCloseKey(hkeyDrivers);
2874         ERR("Can't create Name key\n");
2875         return FALSE;
2876     }
2877     RegSetValueExA(hkeyName, "Configuration File", 0, REG_SZ, di3.pConfigFile,
2878                    0);
2879     RegSetValueExA(hkeyName, "Data File", 0, REG_SZ, di3.pDataFile, 0);
2880     RegSetValueExA(hkeyName, "Driver", 0, REG_SZ, di3.pDriverPath, 0);
2881     RegSetValueExA(hkeyName, "Version", 0, REG_DWORD, (LPSTR)&di3.cVersion,
2882                    sizeof(DWORD));
2883     RegSetValueExA(hkeyName, "Datatype", 0, REG_SZ, di3.pDefaultDataType, 0);
2884     RegSetValueExA(hkeyName, "Dependent Files", 0, REG_MULTI_SZ,
2885                    di3.pDependentFiles, 0);
2886     RegSetValueExA(hkeyName, "Help File", 0, REG_SZ, di3.pHelpFile, 0);
2887     RegSetValueExA(hkeyName, "Monitor", 0, REG_SZ, di3.pMonitorName, 0);
2888     RegCloseKey(hkeyName);
2889     RegCloseKey(hkeyDrivers);
2890
2891     return TRUE;
2892 }
2893
2894 /*****************************************************************************
2895  *          AddPrinterDriverW  [WINSPOOL.@]
2896  */
2897 BOOL WINAPI AddPrinterDriverW(LPWSTR printerName,DWORD level,
2898                                    LPBYTE pDriverInfo)
2899 {
2900     FIXME("(%s,%ld,%p): stub\n",debugstr_w(printerName),
2901           level,pDriverInfo);
2902     return FALSE;
2903 }
2904
2905 /*****************************************************************************
2906  *          AddPrintProcessorA  [WINSPOOL.@]
2907  */
2908 BOOL WINAPI AddPrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPathName,
2909                                LPSTR pPrintProcessorName)
2910 {
2911     FIXME("(%s,%s,%s,%s): stub\n", debugstr_a(pName), debugstr_a(pEnvironment),
2912           debugstr_a(pPathName), debugstr_a(pPrintProcessorName));
2913     return FALSE;
2914 }
2915
2916 /*****************************************************************************
2917  *          AddPrintProcessorW  [WINSPOOL.@]
2918  */
2919 BOOL WINAPI AddPrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPathName,
2920                                LPWSTR pPrintProcessorName)
2921 {
2922     FIXME("(%s,%s,%s,%s): stub\n", debugstr_w(pName), debugstr_w(pEnvironment),
2923           debugstr_w(pPathName), debugstr_w(pPrintProcessorName));
2924     return FALSE;
2925 }
2926
2927 /*****************************************************************************
2928  *          AddPrintProvidorA  [WINSPOOL.@]
2929  */
2930 BOOL WINAPI AddPrintProvidorA(LPSTR pName, DWORD Level, LPBYTE pProviderInfo)
2931 {
2932     FIXME("(%s,0x%08lx,%p): stub\n", debugstr_a(pName), Level, pProviderInfo);
2933     return FALSE;
2934 }
2935
2936 /*****************************************************************************
2937  *          AddPrintProvidorW  [WINSPOOL.@]
2938  */
2939 BOOL WINAPI AddPrintProvidorW(LPWSTR pName, DWORD Level, LPBYTE pProviderInfo)
2940 {
2941     FIXME("(%s,0x%08lx,%p): stub\n", debugstr_w(pName), Level, pProviderInfo);
2942     return FALSE;
2943 }
2944
2945 /*****************************************************************************
2946  *          AdvancedDocumentPropertiesA  [WINSPOOL.@]
2947  */
2948 LONG WINAPI AdvancedDocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName,
2949                                         PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput)
2950 {
2951     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_a(pDeviceName),
2952           pDevModeOutput, pDevModeInput);
2953     return 0;
2954 }
2955
2956 /*****************************************************************************
2957  *          AdvancedDocumentPropertiesW  [WINSPOOL.@]
2958  */
2959 LONG WINAPI AdvancedDocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName,
2960                                         PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput)
2961 {
2962     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_w(pDeviceName),
2963           pDevModeOutput, pDevModeInput);
2964     return 0;
2965 }
2966
2967 /*****************************************************************************
2968  *          PrinterProperties  [WINSPOOL.@]
2969  *
2970  *     Displays a dialog to set the properties of the printer.
2971  *
2972  * RETURNS
2973  *     nonzero on success or zero on failure
2974  *
2975  * BUGS
2976  *         implemented as stub only
2977  */
2978 BOOL WINAPI PrinterProperties(HWND hWnd,      /* [in] handle to parent window */
2979                               HANDLE hPrinter /* [in] handle to printer object */
2980 ){
2981     FIXME("(%p,%p): stub\n", hWnd, hPrinter);
2982     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2983     return FALSE;
2984 }
2985
2986 /*****************************************************************************
2987  *          EnumJobsA [WINSPOOL.@]
2988  *
2989  */
2990 BOOL WINAPI EnumJobsA(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
2991                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
2992                       LPDWORD pcReturned)
2993 {
2994     FIXME("(%p,first=%ld,no=%ld,level=%ld,job=%p,cb=%ld,%p,%p), stub!\n",
2995         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
2996     );
2997     if(pcbNeeded) *pcbNeeded = 0;
2998     if(pcReturned) *pcReturned = 0;
2999     return FALSE;
3000 }
3001
3002
3003 /*****************************************************************************
3004  *          EnumJobsW [WINSPOOL.@]
3005  *
3006  */
3007 BOOL WINAPI EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
3008                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
3009                       LPDWORD pcReturned)
3010 {
3011     FIXME("(%p,first=%ld,no=%ld,level=%ld,job=%p,cb=%ld,%p,%p), stub!\n",
3012         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
3013     );
3014     if(pcbNeeded) *pcbNeeded = 0;
3015     if(pcReturned) *pcReturned = 0;
3016     return FALSE;
3017 }
3018
3019 /*****************************************************************************
3020  *          WINSPOOL_EnumPrinterDrivers [internal]
3021  *
3022  *    Delivers information about all printer drivers installed on the
3023  *    localhost or a given server
3024  *
3025  * RETURNS
3026  *    nonzero on success or zero on failure. If the buffer for the returned
3027  *    information is too small the function will return an error
3028  *
3029  * BUGS
3030  *    - only implemented for localhost, foreign hosts will return an error
3031  */
3032 static BOOL WINSPOOL_EnumPrinterDrivers(LPWSTR pName, LPWSTR pEnvironment,
3033                                         DWORD Level, LPBYTE pDriverInfo,
3034                                         DWORD cbBuf, LPDWORD pcbNeeded,
3035                                         LPDWORD pcReturned, BOOL unicode)
3036
3037 {   HKEY  hkeyDrivers;
3038     DWORD i, needed, number = 0, size = 0;
3039     WCHAR DriverNameW[255];
3040     PBYTE ptr;
3041
3042     TRACE("%s,%s,%ld,%p,%ld,%d\n",
3043           debugstr_w(pName), debugstr_w(pEnvironment),
3044           Level, pDriverInfo, cbBuf, unicode);
3045
3046     /* check for local drivers */
3047     if(pName) {
3048         ERR("remote drivers unsupported! Current remote host is %s\n",
3049              debugstr_w(pName));
3050         return FALSE;
3051     }
3052
3053     /* check input parameter */
3054     if((Level < 1) || (Level > 3)) {
3055         ERR("unsupported level %ld\n", Level);
3056         SetLastError(ERROR_INVALID_LEVEL);
3057         return FALSE;
3058     }
3059
3060     /* initialize return values */
3061     if(pDriverInfo)
3062         memset( pDriverInfo, 0, cbBuf);
3063     *pcbNeeded  = 0;
3064     *pcReturned = 0;
3065
3066     hkeyDrivers = WINSPOOL_OpenDriverReg(pEnvironment, TRUE);
3067     if(!hkeyDrivers) {
3068         ERR("Can't open Drivers key\n");
3069         return FALSE;
3070     }
3071
3072     if(RegQueryInfoKeyA(hkeyDrivers, NULL, NULL, NULL, &number, NULL, NULL,
3073                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
3074         RegCloseKey(hkeyDrivers);
3075         ERR("Can't query Drivers key\n");
3076         return FALSE;
3077     }
3078     TRACE("Found %ld Drivers\n", number);
3079
3080     /* get size of single struct
3081      * unicode and ascii structure have the same size
3082      */
3083     switch (Level) {
3084         case 1:
3085             size = sizeof(DRIVER_INFO_1A);
3086             break;
3087         case 2:
3088             size = sizeof(DRIVER_INFO_2A);
3089             break;
3090         case 3:
3091             size = sizeof(DRIVER_INFO_3A);
3092             break;
3093     }
3094
3095     /* calculate required buffer size */
3096     *pcbNeeded = size * number;
3097
3098     for( i = 0,  ptr = (pDriverInfo && (cbBuf >= size)) ? pDriverInfo : NULL ;
3099          i < number;
3100          i++, ptr = (ptr && (cbBuf >= size * i)) ? ptr + size : NULL) {
3101         if(RegEnumKeyW(hkeyDrivers, i, DriverNameW, sizeof(DriverNameW))
3102                        != ERROR_SUCCESS) {
3103             ERR("Can't enum key number %ld\n", i);
3104             RegCloseKey(hkeyDrivers);
3105             return FALSE;
3106         }
3107         if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverNameW,
3108                          pEnvironment, Level, ptr,
3109                          (cbBuf < *pcbNeeded) ? NULL : pDriverInfo + *pcbNeeded,
3110                          (cbBuf < *pcbNeeded) ? 0 : cbBuf - *pcbNeeded,
3111                          &needed, unicode)) {
3112             RegCloseKey(hkeyDrivers);
3113             return FALSE;
3114         }
3115         (*pcbNeeded) += needed;
3116     }
3117
3118     RegCloseKey(hkeyDrivers);
3119
3120     if(cbBuf < *pcbNeeded){
3121         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3122         return FALSE;
3123     }
3124
3125     return TRUE;
3126 }
3127
3128 /*****************************************************************************
3129  *          EnumPrinterDriversW  [WINSPOOL.@]
3130  *
3131  *    see function EnumPrinterDrivers for RETURNS, BUGS
3132  */
3133 BOOL WINAPI EnumPrinterDriversW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
3134                                 LPBYTE pDriverInfo, DWORD cbBuf,
3135                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
3136 {
3137     return WINSPOOL_EnumPrinterDrivers(pName, pEnvironment, Level, pDriverInfo,
3138                                        cbBuf, pcbNeeded, pcReturned, TRUE);
3139 }
3140
3141 /*****************************************************************************
3142  *          EnumPrinterDriversA  [WINSPOOL.@]
3143  *
3144  *    see function EnumPrinterDrivers for RETURNS, BUGS
3145  */
3146 BOOL WINAPI EnumPrinterDriversA(LPSTR pName, LPSTR pEnvironment, DWORD Level,
3147                                 LPBYTE pDriverInfo, DWORD cbBuf,
3148                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
3149 {   BOOL ret;
3150     UNICODE_STRING pNameW, pEnvironmentW;
3151     PWSTR pwstrNameW, pwstrEnvironmentW;
3152
3153     pwstrNameW = asciitounicode(&pNameW, pName);
3154     pwstrEnvironmentW = asciitounicode(&pEnvironmentW, pEnvironment);
3155
3156     ret = WINSPOOL_EnumPrinterDrivers(pwstrNameW, pwstrEnvironmentW,
3157                                       Level, pDriverInfo, cbBuf, pcbNeeded,
3158                                       pcReturned, FALSE);
3159     RtlFreeUnicodeString(&pNameW);
3160     RtlFreeUnicodeString(&pEnvironmentW);
3161
3162     return ret;
3163 }
3164
3165 static CHAR PortMonitor[] = "Wine Port Monitor";
3166 static CHAR PortDescription[] = "Wine Port";
3167
3168 static BOOL WINSPOOL_ComPortExists( LPCSTR name )
3169 {
3170     HANDLE handle;
3171
3172     handle = CreateFileA( name, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
3173                          NULL, OPEN_EXISTING, 0, NULL );
3174     if (handle == INVALID_HANDLE_VALUE)
3175         return FALSE;
3176     TRACE("Checking %s exists\n", name );
3177     CloseHandle( handle );
3178     return TRUE;
3179 }
3180
3181 static DWORD WINSPOOL_CountSerialPorts()
3182 {
3183     CHAR name[6];
3184     DWORD n = 0, i;
3185
3186     for (i=0; i<4; i++)
3187     {
3188         strcpy( name, "COMx:" );
3189         name[3] = '1' + i;
3190         if (WINSPOOL_ComPortExists( name ))
3191             n++;
3192     }
3193
3194     return n;
3195 }
3196
3197 /******************************************************************************
3198  *              EnumPortsA   (WINSPOOL.@)
3199  */
3200 BOOL WINAPI EnumPortsA(LPSTR name,DWORD level,LPBYTE buffer,DWORD bufsize,
3201                        LPDWORD bufneeded,LPDWORD bufreturned)
3202 {
3203     CHAR portname[10];
3204     DWORD info_size, ofs, i, printer_count, serial_count, count, n, r;
3205     const LPCSTR szPrinterPortKey = "Software\\Wine\\Wine\\Config\\spooler";
3206     HKEY hkey_printer;
3207     BOOL retval = TRUE;
3208
3209     TRACE("(%s,%ld,%p,%ld,%p,%p)\n",
3210           debugstr_a(name),level,buffer,bufsize,bufneeded,bufreturned);
3211
3212     switch( level )
3213     {
3214     case 1:
3215         info_size = sizeof (PORT_INFO_1A);
3216         break;
3217     case 2:
3218         info_size = sizeof (PORT_INFO_2A);
3219         break;
3220     default:
3221         SetLastError(ERROR_INVALID_LEVEL);
3222         return FALSE;
3223     }
3224     
3225     /* see how many exist */
3226
3227     hkey_printer = 0;
3228     serial_count = WINSPOOL_CountSerialPorts();
3229     printer_count = 0;
3230
3231     r = RegOpenKeyA( HKEY_LOCAL_MACHINE, szPrinterPortKey, &hkey_printer);
3232     if ( r == ERROR_SUCCESS )
3233     {
3234         RegQueryInfoKeyA( hkey_printer, NULL, NULL, NULL, NULL, NULL, NULL,
3235             &printer_count, NULL, NULL, NULL, NULL);
3236     }
3237     count = serial_count + printer_count;
3238
3239     /* then fill in the structure info structure once
3240        we know the offset to the first string */
3241
3242     memset( buffer, 0, bufsize );
3243     n = 0;
3244     ofs = info_size*count; 
3245     for ( i=0; i<count; i++)
3246     {
3247         DWORD vallen = sizeof(portname) - 1;
3248
3249         /* get the serial port values, then the printer values */
3250         if ( i < serial_count )
3251         {
3252             strcpy( portname, "COMx:" );
3253             portname[3] = '1' + i;
3254             if (!WINSPOOL_ComPortExists( portname ))
3255                 continue;
3256
3257             TRACE("Found %s\n", portname );
3258             vallen = strlen( portname );
3259         }
3260         else
3261         {
3262             r = RegEnumValueA( hkey_printer, i-serial_count, 
3263                      portname, &vallen, NULL, NULL, NULL, 0 );
3264             if ( r )
3265                 continue;
3266         }
3267
3268         /* add a colon if necessary, and make it upper case */
3269         CharUpperBuffA(portname,vallen);
3270         if (strcasecmp(portname,"nul")!=0)
3271             if (vallen && (portname[vallen-1] != ':') )
3272                 lstrcatA(portname,":");
3273
3274         /* add the port info structure if we can fit it */
3275         if ( info_size*(n+1) < bufsize )
3276         {
3277             if ( level == 1)
3278             {
3279                 PORT_INFO_1A *info = (PORT_INFO_1A*) &buffer[info_size*n];
3280                 info->pName = (LPSTR) &buffer[ofs];
3281             }
3282             else if ( level == 2)
3283             {
3284                 PORT_INFO_2A *info = (PORT_INFO_2A*) &buffer[info_size*n];
3285                 info->pPortName = (LPSTR) &buffer[ofs];
3286                 /* FIXME: fill in more stuff here */
3287                 info->pMonitorName = PortMonitor;
3288                 info->pDescription = PortDescription;
3289                 info->fPortType = PORT_TYPE_WRITE|PORT_TYPE_READ;
3290             }
3291
3292             /* add the name of the port if we can fit it */
3293             if ( ofs < bufsize )
3294                 lstrcpynA(&buffer[ofs],portname,bufsize - ofs);
3295
3296             n++;
3297         }
3298         else
3299             retval = FALSE;
3300         ofs += lstrlenA(portname)+1;
3301     }
3302
3303     RegCloseKey(hkey_printer);
3304
3305     if(bufneeded)
3306         *bufneeded = ofs;
3307
3308     if(bufreturned)
3309         *bufreturned = n;
3310
3311     return retval;
3312 }
3313
3314 /******************************************************************************
3315  *      EnumPortsW   (WINSPOOL.@)
3316  */
3317 BOOL WINAPI EnumPortsW(LPWSTR name,DWORD level,LPBYTE buffer,DWORD bufsize,
3318                        LPDWORD bufneeded,LPDWORD bufreturned)
3319 {
3320     FIXME("(%s,%ld,%p,%ld,%p,%p) - stub\n",
3321           debugstr_w(name),level,buffer,bufsize,bufneeded,bufreturned);
3322     return FALSE;
3323 }
3324
3325 /******************************************************************************
3326  *              GetDefaultPrinterW   (WINSPOOL.@)
3327  *
3328  * FIXME
3329  *      This function must read the value from data 'device' of key
3330  *      HCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows
3331  */
3332 BOOL WINAPI GetDefaultPrinterW(LPWSTR name, LPDWORD namesize)
3333 {
3334     BOOL  retval = TRUE;
3335     DWORD insize, len;
3336     WCHAR *buffer, *ptr;
3337
3338     if (!namesize)
3339     {
3340         SetLastError(ERROR_INVALID_PARAMETER);
3341         return FALSE;
3342     }
3343
3344     /* make the buffer big enough for the stuff from the profile/registry,
3345      * the content must fit into the local buffer to compute the correct
3346      * size even if the extern buffer is too small or not given.
3347      * (20 for ,driver,port) */
3348     insize = *namesize;
3349     len = max(100, (insize + 20));
3350     buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
3351
3352     if (!GetProfileStringW(windowsW, deviceW, emptyStringW, buffer, len))
3353     {
3354         SetLastError (ERROR_FILE_NOT_FOUND);
3355         retval = FALSE;
3356         goto end;
3357     }
3358     TRACE("%s\n", debugstr_w(buffer));
3359
3360     if ((ptr = strchrW(buffer, ',')) == NULL)
3361     {
3362         SetLastError(ERROR_INVALID_NAME);
3363         retval = FALSE;
3364         goto end;
3365     }
3366
3367     *ptr = 0;
3368     *namesize = strlenW(buffer) + 1;
3369     if(!name || (*namesize > insize))
3370     {
3371         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3372         retval = FALSE;
3373         goto end;
3374     }
3375     strcpyW(name, buffer);
3376
3377 end:
3378     HeapFree( GetProcessHeap(), 0, buffer);
3379     return retval;
3380 }
3381
3382
3383 /******************************************************************************
3384  *              GetDefaultPrinterA   (WINSPOOL.@)
3385  */
3386 BOOL WINAPI GetDefaultPrinterA(LPSTR name, LPDWORD namesize)
3387 {
3388     BOOL  retval = TRUE;
3389     DWORD insize = 0;
3390     WCHAR *bufferW = NULL;
3391
3392     if (!namesize)
3393     {
3394         SetLastError(ERROR_INVALID_PARAMETER);
3395         return FALSE;
3396     }
3397
3398     if(name && *namesize) {
3399         insize = *namesize;
3400         bufferW = HeapAlloc( GetProcessHeap(), 0, insize * sizeof(WCHAR));
3401     }
3402
3403     if(!GetDefaultPrinterW( bufferW, namesize)) {
3404         retval = FALSE;
3405         goto end;
3406     }
3407
3408     *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, name, insize,
3409                                     NULL, NULL);
3410     if (!*namesize)
3411     {
3412         *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
3413         retval = FALSE;
3414     }
3415     TRACE("0x%08lx/0x%08lx:%s\n", *namesize, insize, debugstr_w(bufferW));
3416
3417 end:
3418     HeapFree( GetProcessHeap(), 0, bufferW);
3419     return retval;
3420 }
3421
3422
3423 /******************************************************************************
3424  *              SetPrinterDataExA   (WINSPOOL.@)
3425  */
3426 DWORD WINAPI SetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
3427                                LPCSTR pValueName, DWORD Type,
3428                                LPBYTE pData, DWORD cbData)
3429 {
3430     HKEY hkeyPrinter, hkeySubkey;
3431     DWORD ret;
3432
3433     TRACE("(%p, %s, %s %08lx, %p, %08lx)\n", hPrinter, debugstr_a(pKeyName),
3434           debugstr_a(pValueName), Type, pData, cbData);
3435
3436     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
3437        != ERROR_SUCCESS)
3438         return ret;
3439
3440     if((ret = RegCreateKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
3441        != ERROR_SUCCESS) {
3442         ERR("Can't create subkey %s\n", debugstr_a(pKeyName));
3443         RegCloseKey(hkeyPrinter);
3444         return ret;
3445     }
3446     ret = RegSetValueExA(hkeySubkey, pValueName, 0, Type, pData, cbData);
3447     RegCloseKey(hkeySubkey);
3448     RegCloseKey(hkeyPrinter);
3449     return ret;
3450 }
3451
3452 /******************************************************************************
3453  *              SetPrinterDataExW   (WINSPOOL.@)
3454  */
3455 DWORD WINAPI SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
3456                                LPCWSTR pValueName, DWORD Type,
3457                                LPBYTE pData, DWORD cbData)
3458 {
3459     HKEY hkeyPrinter, hkeySubkey;
3460     DWORD ret;
3461
3462     TRACE("(%p, %s, %s %08lx, %p, %08lx)\n", hPrinter, debugstr_w(pKeyName),
3463           debugstr_w(pValueName), Type, pData, cbData);
3464
3465     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
3466        != ERROR_SUCCESS)
3467         return ret;
3468
3469     if((ret = RegCreateKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
3470        != ERROR_SUCCESS) {
3471         ERR("Can't create subkey %s\n", debugstr_w(pKeyName));
3472         RegCloseKey(hkeyPrinter);
3473         return ret;
3474     }
3475     ret = RegSetValueExW(hkeySubkey, pValueName, 0, Type, pData, cbData);
3476     RegCloseKey(hkeySubkey);
3477     RegCloseKey(hkeyPrinter);
3478     return ret;
3479 }
3480
3481 /******************************************************************************
3482  *              SetPrinterDataA   (WINSPOOL.@)
3483  */
3484 DWORD WINAPI SetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, DWORD Type,
3485                                LPBYTE pData, DWORD cbData)
3486 {
3487     return SetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, Type,
3488                              pData, cbData);
3489 }
3490
3491 /******************************************************************************
3492  *              SetPrinterDataW   (WINSPOOL.@)
3493  */
3494 DWORD WINAPI SetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, DWORD Type,
3495                              LPBYTE pData, DWORD cbData)
3496 {
3497     return SetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, Type,
3498                              pData, cbData);
3499 }
3500
3501 /******************************************************************************
3502  *              GetPrinterDataExA   (WINSPOOL.@)
3503  */
3504 DWORD WINAPI GetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
3505                                LPCSTR pValueName, LPDWORD pType,
3506                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
3507 {
3508     HKEY hkeyPrinter, hkeySubkey;
3509     DWORD ret;
3510
3511     TRACE("(%p, %s, %s %p, %p, %08lx, %p)\n", hPrinter,
3512           debugstr_a(pKeyName), debugstr_a(pValueName), pType, pData, nSize,
3513           pcbNeeded);
3514
3515     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
3516        != ERROR_SUCCESS)
3517         return ret;
3518
3519     if((ret = RegOpenKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
3520        != ERROR_SUCCESS) {
3521         WARN("Can't open subkey %s\n", debugstr_a(pKeyName));
3522         RegCloseKey(hkeyPrinter);
3523         return ret;
3524     }
3525     *pcbNeeded = nSize;
3526     ret = RegQueryValueExA(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
3527     RegCloseKey(hkeySubkey);
3528     RegCloseKey(hkeyPrinter);
3529     return ret;
3530 }
3531
3532 /******************************************************************************
3533  *              GetPrinterDataExW   (WINSPOOL.@)
3534  */
3535 DWORD WINAPI GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
3536                                LPCWSTR pValueName, LPDWORD pType,
3537                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
3538 {
3539     HKEY hkeyPrinter, hkeySubkey;
3540     DWORD ret;
3541
3542     TRACE("(%p, %s, %s %p, %p, %08lx, %p)\n", hPrinter,
3543           debugstr_w(pKeyName), debugstr_w(pValueName), pType, pData, nSize,
3544           pcbNeeded);
3545
3546     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
3547        != ERROR_SUCCESS)
3548         return ret;
3549
3550     if((ret = RegOpenKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
3551        != ERROR_SUCCESS) {
3552         WARN("Can't open subkey %s\n", debugstr_w(pKeyName));
3553         RegCloseKey(hkeyPrinter);
3554         return ret;
3555     }
3556     *pcbNeeded = nSize;
3557     ret = RegQueryValueExW(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
3558     RegCloseKey(hkeySubkey);
3559     RegCloseKey(hkeyPrinter);
3560     return ret;
3561 }
3562
3563 /******************************************************************************
3564  *              GetPrinterDataA   (WINSPOOL.@)
3565  */
3566 DWORD WINAPI GetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, LPDWORD pType,
3567                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
3568 {
3569     return GetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, pType,
3570                              pData, nSize, pcbNeeded);
3571 }
3572
3573 /******************************************************************************
3574  *              GetPrinterDataW   (WINSPOOL.@)
3575  */
3576 DWORD WINAPI GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType,
3577                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
3578 {
3579     return GetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, pType,
3580                              pData, nSize, pcbNeeded);
3581 }
3582
3583 /*******************************************************************************
3584  *              EnumPrinterDataExW      [WINSPOOL.@]
3585  */
3586 DWORD WINAPI EnumPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
3587                                 LPBYTE pEnumValues, DWORD cbEnumValues,
3588                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
3589 {
3590     HKEY                    hkPrinter, hkSubKey;
3591     DWORD                   r, ret, dwIndex, cValues, cbMaxValueNameLen,
3592                             cbValueNameLen, cbMaxValueLen, cbValueLen,
3593                             cbBufSize, dwType;
3594     LPWSTR                  lpValueName;
3595     HANDLE                  hHeap;
3596     PBYTE                   lpValue;
3597     PPRINTER_ENUM_VALUESW   ppev;
3598
3599     TRACE ("%p %s\n", hPrinter, debugstr_w (pKeyName));
3600
3601     if (pKeyName == NULL || *pKeyName == 0)
3602         return ERROR_INVALID_PARAMETER;
3603
3604     ret = WINSPOOL_GetOpenedPrinterRegKey (hPrinter, &hkPrinter);
3605     if (ret != ERROR_SUCCESS)
3606     {
3607         TRACE ("WINSPOOL_GetOpenedPrinterRegKey (%p) returned %li\n",
3608                 hPrinter, ret);
3609         return ret;
3610     }
3611
3612     ret = RegOpenKeyExW (hkPrinter, pKeyName, 0, KEY_READ, &hkSubKey);
3613     if (ret != ERROR_SUCCESS)
3614     {
3615         r = RegCloseKey (hkPrinter);
3616         if (r != ERROR_SUCCESS)
3617             WARN ("RegCloseKey returned %li\n", r);
3618         TRACE ("RegOpenKeyExW (%p, %s) returned %li\n", hPrinter,
3619                 debugstr_w (pKeyName), ret);
3620         return ret;
3621     }
3622
3623     ret = RegCloseKey (hkPrinter);
3624     if (ret != ERROR_SUCCESS)
3625     {
3626         ERR ("RegCloseKey returned %li\n", ret);
3627         r = RegCloseKey (hkSubKey);
3628         if (r != ERROR_SUCCESS)
3629             WARN ("RegCloseKey returned %li\n", r);
3630         return ret;
3631     }
3632
3633     ret = RegQueryInfoKeyW (hkSubKey, NULL, NULL, NULL, NULL, NULL, NULL,
3634             &cValues, &cbMaxValueNameLen, &cbMaxValueLen, NULL, NULL);
3635     if (ret != ERROR_SUCCESS)
3636     {
3637         r = RegCloseKey (hkSubKey);
3638         if (r != ERROR_SUCCESS)
3639             WARN ("RegCloseKey returned %li\n", r);
3640         TRACE ("RegQueryInfoKeyW (%p) returned %li\n", hkSubKey, ret);
3641         return ret;
3642     }
3643
3644     TRACE ("RegQueryInfoKeyW returned cValues = %li, cbMaxValueNameLen = %li, "
3645             "cbMaxValueLen = %li\n", cValues, cbMaxValueNameLen, cbMaxValueLen);
3646
3647     if (cValues == 0)                   /* empty key */
3648     {
3649         r = RegCloseKey (hkSubKey);
3650         if (r != ERROR_SUCCESS)
3651             WARN ("RegCloseKey returned %li\n", r);
3652         *pcbEnumValues = *pnEnumValues = 0;
3653         return ERROR_SUCCESS;
3654     }
3655
3656     ++cbMaxValueNameLen;                        /* allow for trailing '\0' */
3657
3658     hHeap = GetProcessHeap ();
3659     if (hHeap == NULL)
3660     {
3661         ERR ("GetProcessHeap failed\n");
3662         r = RegCloseKey (hkSubKey);
3663         if (r != ERROR_SUCCESS)
3664             WARN ("RegCloseKey returned %li\n", r);
3665         return ERROR_OUTOFMEMORY;
3666     }
3667
3668     lpValueName = HeapAlloc (hHeap, 0, cbMaxValueNameLen * sizeof (WCHAR));
3669     if (lpValueName == NULL)
3670     {
3671         ERR ("Failed to allocate %li bytes from process heap\n",
3672                 cbMaxValueNameLen * sizeof (WCHAR));
3673         r = RegCloseKey (hkSubKey);
3674         if (r != ERROR_SUCCESS)
3675             WARN ("RegCloseKey returned %li\n", r);
3676         return ERROR_OUTOFMEMORY;
3677     }
3678
3679     lpValue = HeapAlloc (hHeap, 0, cbMaxValueLen);
3680     if (lpValue == NULL)
3681     {
3682         ERR ("Failed to allocate %li bytes from process heap\n", cbMaxValueLen);
3683         if (HeapFree (hHeap, 0, lpValueName) == 0)
3684             WARN ("HeapFree failed with code %li\n", GetLastError ());
3685         r = RegCloseKey (hkSubKey);
3686         if (r != ERROR_SUCCESS)
3687             WARN ("RegCloseKey returned %li\n", r);
3688         return ERROR_OUTOFMEMORY;
3689     }
3690
3691     TRACE ("pass 1: calculating buffer required for all names and values\n");
3692
3693     cbBufSize = cValues * sizeof (PRINTER_ENUM_VALUESW);
3694
3695     TRACE ("%li bytes required for %li headers\n", cbBufSize, cValues);
3696
3697     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
3698     {
3699         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
3700         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
3701                 NULL, NULL, lpValue, &cbValueLen);
3702         if (ret != ERROR_SUCCESS)
3703         {
3704             if (HeapFree (hHeap, 0, lpValue) == 0)
3705                 WARN ("HeapFree failed with code %li\n", GetLastError ());
3706             if (HeapFree (hHeap, 0, lpValueName) == 0)
3707                 WARN ("HeapFree failed with code %li\n", GetLastError ());
3708             r = RegCloseKey (hkSubKey);
3709             if (r != ERROR_SUCCESS)
3710                 WARN ("RegCloseKey returned %li\n", r);
3711             TRACE ("RegEnumValueW (%li) returned %li\n", dwIndex, ret);
3712             return ret;
3713         }
3714
3715         TRACE ("%s [%li]: name needs %li bytes, data needs %li bytes\n",
3716                 debugstr_w (lpValueName), dwIndex,
3717                 (cbValueNameLen + 1) * sizeof (WCHAR), cbValueLen);
3718
3719         cbBufSize += (cbValueNameLen + 1) * sizeof (WCHAR);
3720         cbBufSize += cbValueLen;
3721     }
3722
3723     TRACE ("%li bytes required for all %li values\n", cbBufSize, cValues);
3724
3725     *pcbEnumValues = cbBufSize;
3726     *pnEnumValues = cValues;
3727
3728     if (cbEnumValues < cbBufSize)       /* buffer too small */
3729     {
3730         if (HeapFree (hHeap, 0, lpValue) == 0)
3731             WARN ("HeapFree failed with code %li\n", GetLastError ());
3732         if (HeapFree (hHeap, 0, lpValueName) == 0)
3733             WARN ("HeapFree failed with code %li\n", GetLastError ());
3734         r = RegCloseKey (hkSubKey);
3735         if (r != ERROR_SUCCESS)
3736             WARN ("RegCloseKey returned %li\n", r);
3737         TRACE ("%li byte buffer is not large enough\n", cbEnumValues);
3738         return ERROR_MORE_DATA;
3739     }
3740
3741     TRACE ("pass 2: copying all names and values to buffer\n");
3742
3743     ppev = (PPRINTER_ENUM_VALUESW) pEnumValues;         /* array of structs */
3744     pEnumValues += cValues * sizeof (PRINTER_ENUM_VALUESW);
3745
3746     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
3747     {
3748         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
3749         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
3750                 NULL, &dwType, lpValue, &cbValueLen);
3751         if (ret != ERROR_SUCCESS)
3752         {
3753             if (HeapFree (hHeap, 0, lpValue) == 0)
3754                 WARN ("HeapFree failed with code %li\n", GetLastError ());
3755             if (HeapFree (hHeap, 0, lpValueName) == 0)
3756                 WARN ("HeapFree failed with code %li\n", GetLastError ());
3757             r = RegCloseKey (hkSubKey);
3758             if (r != ERROR_SUCCESS)
3759                 WARN ("RegCloseKey returned %li\n", r);
3760             TRACE ("RegEnumValueW (%li) returned %li\n", dwIndex, ret);
3761             return ret;
3762         }
3763
3764         cbValueNameLen = (cbValueNameLen + 1) * sizeof (WCHAR);
3765         memcpy (pEnumValues, lpValueName, cbValueNameLen);
3766         ppev[dwIndex].pValueName = (LPWSTR) pEnumValues;
3767         pEnumValues += cbValueNameLen;
3768
3769         /* return # of *bytes* (including trailing \0), not # of chars */
3770         ppev[dwIndex].cbValueName = cbValueNameLen;
3771
3772         ppev[dwIndex].dwType = dwType;
3773
3774         memcpy (pEnumValues, lpValue, cbValueLen);
3775         ppev[dwIndex].pData = pEnumValues;
3776         pEnumValues += cbValueLen;
3777
3778         ppev[dwIndex].cbData = cbValueLen;
3779
3780         TRACE ("%s [%li]: copied name (%li bytes) and data (%li bytes)\n",
3781                 debugstr_w (lpValueName), dwIndex, cbValueNameLen, cbValueLen);
3782     }
3783
3784     if (HeapFree (hHeap, 0, lpValue) == 0)
3785     {
3786         ret = GetLastError ();
3787         ERR ("HeapFree failed with code %li\n", ret);
3788         if (HeapFree (hHeap, 0, lpValueName) == 0)
3789             WARN ("HeapFree failed with code %li\n", GetLastError ());
3790         r = RegCloseKey (hkSubKey);
3791         if (r != ERROR_SUCCESS)
3792             WARN ("RegCloseKey returned %li\n", r);
3793         return ret;
3794     }
3795
3796     if (HeapFree (hHeap, 0, lpValueName) == 0)
3797     {
3798         ret = GetLastError ();
3799         ERR ("HeapFree failed with code %li\n", ret);
3800         r = RegCloseKey (hkSubKey);
3801         if (r != ERROR_SUCCESS)
3802             WARN ("RegCloseKey returned %li\n", r);
3803         return ret;
3804     }
3805
3806     ret = RegCloseKey (hkSubKey);
3807     if (ret != ERROR_SUCCESS)
3808     {
3809         ERR ("RegCloseKey returned %li\n", ret);
3810         return ret;
3811     }
3812
3813     return ERROR_SUCCESS;
3814 }
3815
3816 /*******************************************************************************
3817  *              EnumPrinterDataExA      [WINSPOOL.@]
3818  *
3819  * This functions returns value names and REG_SZ, REG_EXPAND_SZ, and
3820  * REG_MULTI_SZ values as ASCII strings in Unicode-sized buffers.  This is
3821  * what Windows 2000 SP1 does.
3822  *
3823  */
3824 DWORD WINAPI EnumPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
3825                                 LPBYTE pEnumValues, DWORD cbEnumValues,
3826                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
3827 {
3828     INT     len;
3829     LPWSTR  pKeyNameW;
3830     DWORD   ret, dwIndex, dwBufSize;
3831     HANDLE  hHeap;
3832     LPSTR   pBuffer;
3833
3834     TRACE ("%p %s\n", hPrinter, pKeyName);
3835
3836     if (pKeyName == NULL || *pKeyName == 0)
3837         return ERROR_INVALID_PARAMETER;
3838
3839     len = MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, NULL, 0);
3840     if (len == 0)
3841     {
3842         ret = GetLastError ();
3843         ERR ("MultiByteToWideChar failed with code %li\n", ret);
3844         return ret;
3845     }
3846
3847     hHeap = GetProcessHeap ();
3848     if (hHeap == NULL)
3849     {
3850         ERR ("GetProcessHeap failed\n");
3851         return ERROR_OUTOFMEMORY;
3852     }
3853
3854     pKeyNameW = HeapAlloc (hHeap, 0, len * sizeof (WCHAR));
3855     if (pKeyNameW == NULL)
3856     {
3857         ERR ("Failed to allocate %li bytes from process heap\n",
3858                 (LONG) len * sizeof (WCHAR));
3859         return ERROR_OUTOFMEMORY;
3860     }
3861
3862     if (MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, pKeyNameW, len) == 0)
3863     {
3864         ret = GetLastError ();
3865         ERR ("MultiByteToWideChar failed with code %li\n", ret);
3866         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
3867             WARN ("HeapFree failed with code %li\n", GetLastError ());
3868         return ret;
3869     }
3870
3871     ret = EnumPrinterDataExW (hPrinter, pKeyNameW, pEnumValues, cbEnumValues,
3872             pcbEnumValues, pnEnumValues);
3873     if (ret != ERROR_SUCCESS)
3874     {
3875         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
3876             WARN ("HeapFree failed with code %li\n", GetLastError ());
3877         TRACE ("EnumPrinterDataExW returned %li\n", ret);
3878         return ret;
3879     }
3880
3881     if (HeapFree (hHeap, 0, pKeyNameW) == 0)
3882     {
3883         ret = GetLastError ();
3884         ERR ("HeapFree failed with code %li\n", ret);
3885         return ret;
3886     }
3887
3888     if (*pnEnumValues == 0)     /* empty key */
3889         return ERROR_SUCCESS;
3890
3891     dwBufSize = 0;
3892     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
3893     {
3894         PPRINTER_ENUM_VALUESW ppev =
3895                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
3896
3897         if (dwBufSize < ppev->cbValueName)
3898             dwBufSize = ppev->cbValueName;
3899
3900         if (dwBufSize < ppev->cbData && (ppev->dwType == REG_SZ ||
3901                 ppev->dwType == REG_EXPAND_SZ || ppev->dwType == REG_MULTI_SZ))
3902             dwBufSize = ppev->cbData;
3903     }
3904
3905     TRACE ("Largest Unicode name or value is %li bytes\n", dwBufSize);
3906
3907     pBuffer = HeapAlloc (hHeap, 0, dwBufSize);
3908     if (pBuffer == NULL)
3909     {
3910         ERR ("Failed to allocate %li bytes from process heap\n", dwBufSize);
3911         return ERROR_OUTOFMEMORY;
3912     }
3913
3914     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
3915     {
3916         PPRINTER_ENUM_VALUESW ppev =
3917                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
3918
3919         len = WideCharToMultiByte (CP_ACP, 0, ppev->pValueName,
3920                 ppev->cbValueName / sizeof (WCHAR), pBuffer, dwBufSize, NULL,
3921                 NULL);
3922         if (len == 0)
3923         {
3924             ret = GetLastError ();
3925             ERR ("WideCharToMultiByte failed with code %li\n", ret);
3926             if (HeapFree (hHeap, 0, pBuffer) == 0)
3927                 WARN ("HeapFree failed with code %li\n", GetLastError ());
3928             return ret;
3929         }
3930
3931         memcpy (ppev->pValueName, pBuffer, len);
3932
3933         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
3934
3935         if (ppev->dwType != REG_SZ && ppev->dwType != REG_EXPAND_SZ &&
3936                 ppev->dwType != REG_MULTI_SZ)
3937             continue;
3938
3939         len = WideCharToMultiByte (CP_ACP, 0, (LPWSTR) ppev->pData,
3940                 ppev->cbData / sizeof (WCHAR), pBuffer, dwBufSize, NULL, NULL);
3941         if (len == 0)
3942         {
3943             ret = GetLastError ();
3944             ERR ("WideCharToMultiByte failed with code %li\n", ret);
3945             if (HeapFree (hHeap, 0, pBuffer) == 0)
3946                 WARN ("HeapFree failed with code %li\n", GetLastError ());
3947             return ret;
3948         }
3949
3950         memcpy (ppev->pData, pBuffer, len);
3951
3952         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
3953         TRACE ("  (only first string of REG_MULTI_SZ printed)\n");
3954     }
3955
3956     if (HeapFree (hHeap, 0, pBuffer) == 0)
3957     {
3958         ret = GetLastError ();
3959         ERR ("HeapFree failed with code %li\n", ret);
3960         return ret;
3961     }
3962
3963     return ERROR_SUCCESS;
3964 }
3965
3966 /******************************************************************************
3967  *      AbortPrinter (WINSPOOL.@)
3968  */
3969 BOOL WINAPI AbortPrinter( HANDLE hPrinter )
3970 {
3971     FIXME("(%p), stub!\n", hPrinter);
3972     return TRUE;
3973 }
3974
3975 /******************************************************************************
3976  *              AddPortA (WINSPOOL.@)
3977  */
3978 BOOL WINAPI AddPortA(LPSTR pName, HWND hWnd, LPSTR pMonitorName)
3979 {
3980     FIXME("(%s, %p, %s), stub!\n",debugstr_a(pName),hWnd,debugstr_a(pMonitorName));
3981     return FALSE;
3982 }
3983
3984 /******************************************************************************
3985  *      AddPortW (WINSPOOL.@)
3986  */
3987 BOOL WINAPI AddPortW(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
3988 {
3989     FIXME("(%s, %p, %s), stub!\n",debugstr_w(pName),hWnd,debugstr_w(pMonitorName));
3990     return FALSE;
3991 }
3992
3993 /******************************************************************************
3994  *             AddPortExA (WINSPOOL.@)
3995  *
3996  * Adds a print spooler port without presenting a user interface.
3997  */
3998 BOOL WINAPI AddPortExA(HANDLE hMonitor, LPSTR pName, DWORD Level, LPBYTE lpBuffer, LPSTR lpMonitorName)
3999 {
4000     FIXME("(%p, %s, %ld, %p, %s), stub!\n",hMonitor, debugstr_a(pName), Level,
4001           lpBuffer, debugstr_a(lpMonitorName));
4002     return FALSE;
4003 }
4004
4005 /******************************************************************************
4006  *             AddPortExW (WINSPOOL.@)
4007  *
4008  * See AddPortExW.
4009  */
4010 BOOL WINAPI AddPortExW(HANDLE hMonitor, LPWSTR pName, DWORD Level, LPBYTE lpBuffer, LPWSTR lpMonitorName)
4011 {
4012     FIXME("(%p, %s, %ld, %p, %s), stub!\n", hMonitor, debugstr_w(pName), Level,
4013           lpBuffer, debugstr_w(lpMonitorName));
4014     return FALSE;
4015 }
4016
4017 /******************************************************************************
4018  *      AddPrinterConnectionA (WINSPOOL.@)
4019  */
4020 BOOL WINAPI AddPrinterConnectionA( LPSTR pName )
4021 {
4022     FIXME("%s\n", debugstr_a(pName));
4023     return FALSE;
4024 }
4025
4026 /******************************************************************************
4027  *      AddPrinterConnectionW (WINSPOOL.@)
4028  */
4029 BOOL WINAPI AddPrinterConnectionW( LPWSTR pName )
4030 {
4031     FIXME("%s\n", debugstr_w(pName));
4032     return FALSE;
4033 }
4034
4035 /******************************************************************************
4036  *              AddPrinterDriverExW (WINSPOOL.@)
4037  */
4038 BOOL WINAPI AddPrinterDriverExW( LPWSTR pName, DWORD Level,
4039     LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
4040 {
4041     FIXME("%s %ld %p %ld\n", debugstr_w(pName),
4042            Level, pDriverInfo, dwFileCopyFlags);
4043     SetLastError(ERROR_PRINTER_DRIVER_BLOCKED);
4044     return FALSE;
4045 }
4046
4047 /******************************************************************************
4048  *              AddPrinterDriverExA (WINSPOOL.@)
4049  */
4050 BOOL WINAPI AddPrinterDriverExA( LPSTR pName, DWORD Level,
4051     LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
4052 {
4053     FIXME("%s %ld %p %ld\n", debugstr_a(pName),
4054            Level, pDriverInfo, dwFileCopyFlags);
4055     SetLastError(ERROR_PRINTER_DRIVER_BLOCKED);
4056     return FALSE;
4057 }
4058
4059 /******************************************************************************
4060  *      ConfigurePortA (WINSPOOL.@)
4061  */
4062 BOOL WINAPI ConfigurePortA(LPSTR pName, HWND hWnd, LPSTR pPortName)
4063 {
4064     FIXME("%s %p %s\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
4065     return FALSE;
4066 }
4067
4068 /******************************************************************************
4069  *      ConfigurePortW (WINSPOOL.@)
4070  */
4071 BOOL WINAPI ConfigurePortW(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
4072 {
4073     FIXME("%s %p %s\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
4074     return FALSE;
4075 }
4076
4077 /******************************************************************************
4078  *      ConnectToPrinterDlg (WINSPOOL.@)
4079  */
4080 HANDLE WINAPI ConnectToPrinterDlg( HWND hWnd, DWORD Flags )
4081 {
4082     FIXME("%p %lx\n", hWnd, Flags);
4083     return NULL;
4084 }
4085
4086 /******************************************************************************
4087  *      DeletePrinterConnectionA (WINSPOOL.@)
4088  */
4089 BOOL WINAPI DeletePrinterConnectionA( LPSTR pName )
4090 {
4091     FIXME("%s\n", debugstr_a(pName));
4092     return TRUE;
4093 }
4094
4095 /******************************************************************************
4096  *      DeletePrinterConnectionW (WINSPOOL.@)
4097  */
4098 BOOL WINAPI DeletePrinterConnectionW( LPWSTR pName )
4099 {
4100     FIXME("%s\n", debugstr_w(pName));
4101     return TRUE;
4102 }
4103
4104 /******************************************************************************
4105  *              DeletePrinterDriverExW (WINSPOOL.@)
4106  */
4107 BOOL WINAPI DeletePrinterDriverExW( LPWSTR pName, LPWSTR pEnvironment,
4108     LPWSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
4109 {
4110     FIXME("%s %s %s %lx %lx\n", debugstr_w(pName), debugstr_w(pEnvironment),
4111           debugstr_w(pDriverName), dwDeleteFlag, dwVersionFlag);
4112     return TRUE;
4113 }
4114
4115 /******************************************************************************
4116  *              DeletePrinterDriverExA (WINSPOOL.@)
4117  */
4118 BOOL WINAPI DeletePrinterDriverExA( LPSTR pName, LPSTR pEnvironment,
4119     LPSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
4120 {
4121     FIXME("%s %s %s %lx %lx\n", debugstr_a(pName), debugstr_a(pEnvironment),
4122           debugstr_a(pDriverName), dwDeleteFlag, dwVersionFlag);
4123     return TRUE;
4124 }
4125
4126 /******************************************************************************
4127  *              DeletePrinterDataExW (WINSPOOL.@)
4128  */
4129 DWORD WINAPI DeletePrinterDataExW( HANDLE hPrinter, LPCWSTR pKeyName,
4130                                   LPCWSTR pValueName)
4131 {
4132     FIXME("%p %s %s\n", hPrinter, 
4133           debugstr_w(pKeyName), debugstr_w(pValueName));
4134     return ERROR_INVALID_PARAMETER;
4135 }
4136
4137 /******************************************************************************
4138  *              DeletePrinterDataExA (WINSPOOL.@)
4139  */
4140 DWORD WINAPI DeletePrinterDataExA( HANDLE hPrinter, LPCSTR pKeyName,
4141                                   LPCSTR pValueName)
4142 {
4143     FIXME("%p %s %s\n", hPrinter, 
4144           debugstr_a(pKeyName), debugstr_a(pValueName));
4145     return ERROR_INVALID_PARAMETER;
4146 }
4147
4148 /******************************************************************************
4149  *      DeletePrintProcessorA (WINSPOOL.@)
4150  */
4151 BOOL WINAPI DeletePrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProcessorName)
4152 {
4153     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
4154           debugstr_a(pPrintProcessorName));
4155     return TRUE;
4156 }
4157
4158 /******************************************************************************
4159  *      DeletePrintProcessorW (WINSPOOL.@)
4160  */
4161 BOOL WINAPI DeletePrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProcessorName)
4162 {
4163     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
4164           debugstr_w(pPrintProcessorName));
4165     return TRUE;
4166 }
4167
4168 /******************************************************************************
4169  *      DeletePrintProvidorA (WINSPOOL.@)
4170  */
4171 BOOL WINAPI DeletePrintProvidorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProviderName)
4172 {
4173     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
4174           debugstr_a(pPrintProviderName));
4175     return TRUE;
4176 }
4177
4178 /******************************************************************************
4179  *      DeletePrintProvidorW (WINSPOOL.@)
4180  */
4181 BOOL WINAPI DeletePrintProvidorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProviderName)
4182 {
4183     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
4184           debugstr_w(pPrintProviderName));
4185     return TRUE;
4186 }
4187
4188 /******************************************************************************
4189  *      EnumFormsA (WINSPOOL.@)
4190  */
4191 BOOL WINAPI EnumFormsA( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
4192     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
4193 {
4194     FIXME("%p %lx %p %lx %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
4195     return FALSE;
4196 }
4197
4198 /******************************************************************************
4199  *      EnumFormsW (WINSPOOL.@)
4200  */
4201 BOOL WINAPI EnumFormsW( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
4202     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
4203 {
4204     FIXME("%p %lx %p %lx %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
4205     return FALSE;
4206 }
4207
4208 /*****************************************************************************
4209  *          EnumMonitorsA [WINSPOOL.@]
4210  *
4211  */
4212 BOOL WINAPI EnumMonitorsA(LPSTR pName, DWORD Level, LPBYTE pMonitors,
4213                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
4214 {
4215     FIXME("%s,%ld,%p,%ld,%p,%p\n", debugstr_a(pName), Level, pMonitors,
4216           cbBuf, pcbNeeded, pcReturned);
4217     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4218     return 0;
4219 }
4220
4221 /*****************************************************************************
4222  *          EnumMonitorsW [WINSPOOL.@]
4223  *
4224  */
4225 BOOL WINAPI EnumMonitorsW(LPWSTR pName, DWORD Level, LPBYTE pMonitors,
4226                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
4227 {
4228     FIXME("%s,%ld,%p,%ld,%p,%p\n", debugstr_w(pName), Level, pMonitors,
4229           cbBuf, pcbNeeded, pcReturned);
4230     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4231     return 0;
4232 }
4233
4234 /******************************************************************************
4235  *              XcvDataW (WINSPOOL.@)
4236  *
4237  * Notes:
4238  *  There doesn't seem to be an A version...
4239  */
4240 BOOL WINAPI XcvDataW( HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData,
4241     DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData,
4242     PDWORD pcbOutputNeeded, PDWORD pdwStatus)
4243 {
4244     FIXME("%p %s %p %ld %p %ld %p %p\n", hXcv, debugstr_w(pszDataName), 
4245           pInputData, cbInputData, pOutputData,
4246           cbOutputData, pcbOutputNeeded, pdwStatus);
4247     return FALSE;
4248 }
4249
4250 /*****************************************************************************
4251  *          EnumPrinterDataA [WINSPOOL.@]
4252  *
4253  */
4254 DWORD WINAPI EnumPrinterDataA( HANDLE hPrinter, DWORD dwIndex, LPSTR pValueName,
4255     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
4256     DWORD cbData, LPDWORD pcbData )
4257 {
4258     FIXME("%p %lx %p %lx %p %p %p %lx %p\n", hPrinter, dwIndex, pValueName,
4259           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
4260     return ERROR_NO_MORE_ITEMS;
4261 }
4262
4263 /*****************************************************************************
4264  *          EnumPrinterDataW [WINSPOOL.@]
4265  *
4266  */
4267 DWORD WINAPI EnumPrinterDataW( HANDLE hPrinter, DWORD dwIndex, LPWSTR pValueName,
4268     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
4269     DWORD cbData, LPDWORD pcbData )
4270 {
4271     FIXME("%p %lx %p %lx %p %p %p %lx %p\n", hPrinter, dwIndex, pValueName,
4272           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
4273     return ERROR_NO_MORE_ITEMS;
4274 }
4275
4276 /*****************************************************************************
4277  *          EnumPrintProcessorDatatypesA [WINSPOOL.@]
4278  *
4279  */
4280 BOOL WINAPI EnumPrintProcessorDatatypesA(LPSTR pName, LPSTR pPrintProcessorName,
4281                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
4282                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
4283 {
4284     FIXME("Stub: %s %s %ld %p %ld %p %p\n", debugstr_a(pName),
4285           debugstr_a(pPrintProcessorName), Level, pDatatypes, cbBuf,
4286           pcbNeeded, pcReturned);
4287     return FALSE;
4288 }
4289
4290 /*****************************************************************************
4291  *          EnumPrintProcessorDatatypesW [WINSPOOL.@]
4292  *
4293  */
4294 BOOL WINAPI EnumPrintProcessorDatatypesW(LPWSTR pName, LPWSTR pPrintProcessorName,
4295                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
4296                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
4297 {
4298     FIXME("Stub: %s %s %ld %p %ld %p %p\n", debugstr_w(pName),
4299           debugstr_w(pPrintProcessorName), Level, pDatatypes, cbBuf,
4300           pcbNeeded, pcReturned);
4301     return FALSE;
4302 }
4303
4304 /*****************************************************************************
4305  *          EnumPrintProcessorsA [WINSPOOL.@]
4306  *
4307  */
4308 BOOL WINAPI EnumPrintProcessorsA(LPSTR pName, LPSTR pEnvironment, DWORD Level, 
4309     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
4310 {
4311     FIXME("Stub: %s %s %ld %p %ld %p %p\n", pName, pEnvironment, Level,
4312         pPrintProcessorInfo, cbBuf, pcbNeeded, pcbReturned);
4313     return FALSE;
4314 }
4315
4316 /*****************************************************************************
4317  *          EnumPrintProcessorsW [WINSPOOL.@]
4318  *
4319  */
4320 BOOL WINAPI EnumPrintProcessorsW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
4321     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
4322 {
4323     FIXME("Stub: %s %s %ld %p %ld %p %p\n", debugstr_w(pName),
4324         debugstr_w(pEnvironment), Level, pPrintProcessorInfo,
4325         cbBuf, pcbNeeded, pcbReturned);
4326     return FALSE;
4327 }
4328
4329 /*****************************************************************************
4330  *          ExtDeviceMode [WINSPOOL.@]
4331  *
4332  */
4333 LONG WINAPI ExtDeviceMode( HWND hWnd, HANDLE hInst, LPDEVMODEA pDevModeOutput,
4334     LPSTR pDeviceName, LPSTR pPort, LPDEVMODEA pDevModeInput, LPSTR pProfile,
4335     DWORD fMode)
4336 {
4337     FIXME("Stub: %p %p %p %s %s %p %s %lx\n", hWnd, hInst, pDevModeOutput,
4338           debugstr_a(pDeviceName), debugstr_a(pPort), pDevModeInput,
4339           debugstr_a(pProfile), fMode);
4340     return -1;
4341 }
4342
4343 /*****************************************************************************
4344  *          FindClosePrinterChangeNotification [WINSPOOL.@]
4345  *
4346  */
4347 BOOL WINAPI FindClosePrinterChangeNotification( HANDLE hChange )
4348 {
4349     FIXME("Stub: %p\n", hChange);
4350     return TRUE;
4351 }
4352
4353 /*****************************************************************************
4354  *          FindFirstPrinterChangeNotification [WINSPOOL.@]
4355  *
4356  */
4357 HANDLE WINAPI FindFirstPrinterChangeNotification( HANDLE hPrinter,
4358     DWORD fdwFlags, DWORD fdwOptions, LPVOID pPrinterNotifyOptions )
4359 {
4360     FIXME("Stub: %p %lx %lx %p\n",
4361           hPrinter, fdwFlags, fdwOptions, pPrinterNotifyOptions);
4362     return INVALID_HANDLE_VALUE;
4363 }
4364
4365 /*****************************************************************************
4366  *          FindNextPrinterChangeNotification [WINSPOOL.@]
4367  *
4368  */
4369 BOOL WINAPI FindNextPrinterChangeNotification( HANDLE hChange, PDWORD pdwChange,
4370     LPVOID pPrinterNotifyOptions, LPVOID *ppPrinterNotifyInfo )
4371 {
4372     FIXME("Stub: %p %p %p %p\n",
4373           hChange, pdwChange, pPrinterNotifyOptions, ppPrinterNotifyInfo);
4374     return FALSE;
4375 }
4376
4377 /*****************************************************************************
4378  *          FreePrinterNotifyInfo [WINSPOOL.@]
4379  *
4380  */
4381 BOOL WINAPI FreePrinterNotifyInfo( PPRINTER_NOTIFY_INFO pPrinterNotifyInfo )
4382 {
4383     FIXME("Stub: %p\n", pPrinterNotifyInfo);
4384     return TRUE;
4385 }
4386
4387 /*****************************************************************************
4388  *          GetJobA [WINSPOOL.@]
4389  *
4390  */
4391 BOOL WINAPI GetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
4392                            DWORD cbBuf, LPDWORD pcbNeeded)
4393 {
4394     FIXME("Stub: %p %ld %ld %p %ld %p\n", hPrinter, JobId, Level, pJob,
4395           cbBuf, pcbNeeded);
4396     return FALSE;
4397 }
4398
4399 /*****************************************************************************
4400  *          GetJobW [WINSPOOL.@]
4401  *
4402  */
4403 BOOL WINAPI GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
4404                            DWORD cbBuf, LPDWORD pcbNeeded)
4405 {
4406     FIXME("Stub: %p %ld %ld %p %ld %p\n", hPrinter, JobId, Level, pJob,
4407           cbBuf, pcbNeeded);
4408     return FALSE;
4409 }
4410
4411 /*****************************************************************************
4412  *          ScheduleJob [WINSPOOL.@]
4413  *
4414  */
4415 BOOL WINAPI ScheduleJob( HANDLE hPrinter, DWORD dwJobID )
4416 {
4417     FIXME("Stub: %p %lx\n", hPrinter, dwJobID);
4418     return FALSE;
4419 }