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