winspool: Implement EnumPortsW.
[wine] / dlls / winspool.drv / 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  * Copyright 2005, 2006 Detlef Riekenberg
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25
26 #include "config.h"
27 #include "wine/port.h"
28
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <stddef.h>
35 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
38 #include <signal.h>
39 #ifdef HAVE_CUPS_CUPS_H
40 # include <cups/cups.h>
41 # ifndef SONAME_LIBCUPS
42 #  define SONAME_LIBCUPS "libcups.so"
43 # endif
44 #endif
45
46 #define NONAMELESSUNION
47 #define NONAMELESSSTRUCT
48 #include "wine/library.h"
49 #include "windef.h"
50 #include "winbase.h"
51 #include "winuser.h"
52 #include "winerror.h"
53 #include "winreg.h"
54 #include "wingdi.h"
55 #include "winspool.h"
56 #include "winternl.h"
57 #include "wine/windef16.h"
58 #include "wine/unicode.h"
59 #include "wine/debug.h"
60 #include "wine/list.h"
61 #include "heap.h"
62 #include "winnls.h"
63
64 #include "ddk/winsplp.h"
65 #include "wspool.h"
66
67 WINE_DEFAULT_DEBUG_CHANNEL(winspool);
68
69 /* ############################### */
70
71 static CRITICAL_SECTION monitor_handles_cs;
72 static CRITICAL_SECTION_DEBUG monitor_handles_cs_debug = 
73 {
74     0, 0, &monitor_handles_cs,
75     { &monitor_handles_cs_debug.ProcessLocksList, &monitor_handles_cs_debug.ProcessLocksList },
76       0, 0, { (DWORD_PTR)(__FILE__ ": monitor_handles_cs") }
77 };
78 static CRITICAL_SECTION monitor_handles_cs = { &monitor_handles_cs_debug, -1, 0, 0, 0, 0 };
79
80
81 static CRITICAL_SECTION printer_handles_cs;
82 static CRITICAL_SECTION_DEBUG printer_handles_cs_debug = 
83 {
84     0, 0, &printer_handles_cs,
85     { &printer_handles_cs_debug.ProcessLocksList, &printer_handles_cs_debug.ProcessLocksList },
86       0, 0, { (DWORD_PTR)(__FILE__ ": printer_handles_cs") }
87 };
88 static CRITICAL_SECTION printer_handles_cs = { &printer_handles_cs_debug, -1, 0, 0, 0, 0 };
89
90 /* ############################### */
91
92 typedef struct {
93     struct list     entry;
94     LPWSTR          name;
95     LPWSTR          dllname;
96     PMONITORUI      monitorUI;
97     LPMONITOR       monitor;
98     HMODULE         hdll;
99     DWORD           refcount;
100     DWORD           dwMonitorSize;
101     LPPORT_INFO_2W  cache;          /* cached PORT_INFO_2W data */
102     DWORD           pi1_needed;     /* size for PORT_INFO_1W */
103     DWORD           pi2_needed;     /* size for PORT_INFO_2W */
104     DWORD           returned;       /* number of cached PORT_INFO_2W - entries */
105 } monitor_t;
106
107 typedef struct {
108     DWORD job_id;
109     HANDLE hf;
110 } started_doc_t;
111
112 typedef struct {
113     struct list jobs;
114     LONG ref;
115 } jobqueue_t;
116
117 typedef struct {
118     LPWSTR name;
119     jobqueue_t *queue;
120     started_doc_t *doc;
121 } opened_printer_t;
122
123 typedef struct {
124     struct list entry;
125     DWORD job_id;
126     WCHAR *filename;
127     WCHAR *document_title;
128 } job_t;
129
130
131 typedef struct {
132     LPCWSTR  envname;
133     LPCWSTR  subdir;
134     DWORD    driverversion;
135     LPCWSTR  versionregpath;
136     LPCWSTR  versionsubdir;
137 } printenv_t;
138
139 /* ############################### */
140
141 static struct list monitor_handles = LIST_INIT( monitor_handles );
142
143 static opened_printer_t **printer_handles;
144 static int nb_printer_handles;
145 static LONG next_job_id = 1;
146
147 static DWORD (WINAPI *GDI_CallDeviceCapabilities16)( LPCSTR lpszDevice, LPCSTR lpszPort,
148                                                      WORD fwCapability, LPSTR lpszOutput,
149                                                      LPDEVMODEA lpdm );
150 static INT (WINAPI *GDI_CallExtDeviceMode16)( HWND hwnd, LPDEVMODEA lpdmOutput,
151                                               LPSTR lpszDevice, LPSTR lpszPort,
152                                               LPDEVMODEA lpdmInput, LPSTR lpszProfile,
153                                               DWORD fwMode );
154
155 static const WCHAR DriversW[] = { 'S','y','s','t','e','m','\\',
156                                   'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
157                                   'c','o','n','t','r','o','l','\\',
158                                   'P','r','i','n','t','\\',
159                                   'E','n','v','i','r','o','n','m','e','n','t','s','\\',
160                                   '%','s','\\','D','r','i','v','e','r','s','%','s',0 };
161
162 static const WCHAR MonitorsW[] =  { 'S','y','s','t','e','m','\\',
163                                   'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
164                                   'C','o','n','t','r','o','l','\\',
165                                   'P','r','i','n','t','\\',
166                                   'M','o','n','i','t','o','r','s','\\',0};
167
168 static const WCHAR PrintersW[] = {'S','y','s','t','e','m','\\',
169                                   'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
170                                   'C','o','n','t','r','o','l','\\',
171                                   'P','r','i','n','t','\\',
172                                   'P','r','i','n','t','e','r','s',0};
173
174 static const WCHAR LocalPortW[] = {'L','o','c','a','l',' ','P','o','r','t',0};
175
176 static const WCHAR user_default_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
177                                               'M','i','c','r','o','s','o','f','t','\\',
178                                               'W','i','n','d','o','w','s',' ','N','T','\\',
179                                               'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
180                                               'W','i','n','d','o','w','s',0};
181
182 static const WCHAR user_printers_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
183                                                'M','i','c','r','o','s','o','f','t','\\',
184                                                'W','i','n','d','o','w','s',' ','N','T','\\',
185                                                'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
186                                                'D','e','v','i','c','e','s',0};
187
188 static const WCHAR DefaultEnvironmentW[] = {'W','i','n','e',0};
189 static const WCHAR envname_win40W[] = {'W','i','n','d','o','w','s',' ','4','.','0',0};
190 static const WCHAR envname_x86W[] =   {'W','i','n','d','o','w','s',' ','N','T',' ','x','8','6',0};
191 static const WCHAR subdir_win40W[] = {'w','i','n','4','0',0};
192 static const WCHAR subdir_x86W[] =   {'w','3','2','x','8','6',0};
193 static const WCHAR Version3_RegPathW[] = {'\\','V','e','r','s','i','o','n','-','3',0};
194 static const WCHAR Version3_SubdirW[] = {'\\','3',0};
195
196 static const WCHAR spooldriversW[] = {'\\','s','p','o','o','l','\\','d','r','i','v','e','r','s','\\',0};
197 static const WCHAR spoolprtprocsW[] = {'\\','s','p','o','o','l','\\','p','r','t','p','r','o','c','s','\\',0};
198
199 static const WCHAR Configuration_FileW[] = {'C','o','n','f','i','g','u','r','a','t',
200                                       'i','o','n',' ','F','i','l','e',0};
201 static const WCHAR DatatypeW[] = {'D','a','t','a','t','y','p','e',0};
202 static const WCHAR Data_FileW[] = {'D','a','t','a',' ','F','i','l','e',0};
203 static const WCHAR Default_DevModeW[] = {'D','e','f','a','u','l','t',' ','D','e','v',
204                                    'M','o','d','e',0};
205 static const WCHAR Dependent_FilesW[] = {'D','e','p','e','n','d','e','n','t',' ','F',
206                                    'i','l','e','s',0};
207 static const WCHAR DescriptionW[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
208 static const WCHAR DriverW[] = {'D','r','i','v','e','r',0};
209 static const WCHAR Help_FileW[] = {'H','e','l','p',' ','F','i','l','e',0};
210 static const WCHAR LocationW[] = {'L','o','c','a','t','i','o','n',0};
211 static const WCHAR MonitorW[] = {'M','o','n','i','t','o','r',0};
212 static const WCHAR NameW[] = {'N','a','m','e',0};
213 static const WCHAR ParametersW[] = {'P','a','r','a','m','e','t','e','r','s',0};
214 static const WCHAR PortW[] = {'P','o','r','t',0};
215 static const WCHAR Print_ProcessorW[] = {'P','r','i','n','t',' ','P','r','o','c','e',
216                                    's','s','o','r',0};
217 static const WCHAR Printer_DriverW[] = {'P','r','i','n','t','e','r',' ','D','r','i',
218                                   'v','e','r',0};
219 static const WCHAR PrinterDriverDataW[] = {'P','r','i','n','t','e','r','D','r','i',
220                                      'v','e','r','D','a','t','a',0};
221 static const WCHAR Separator_FileW[] = {'S','e','p','a','r','a','t','o','r',' ','F',
222                                   'i','l','e',0};
223 static const WCHAR Share_NameW[] = {'S','h','a','r','e',' ','N','a','m','e',0};
224 static const WCHAR WinPrintW[] = {'W','i','n','P','r','i','n','t',0};
225 static const WCHAR deviceW[]  = {'d','e','v','i','c','e',0};
226 static const WCHAR devicesW[] = {'d','e','v','i','c','e','s',0};
227 static const WCHAR windowsW[] = {'w','i','n','d','o','w','s',0};
228 static const WCHAR emptyStringW[] = {0};
229
230 static const WCHAR May_Delete_Value[] = {'W','i','n','e','M','a','y','D','e','l','e','t','e','M','e',0};
231
232 static const WCHAR CUPS_Port[] = {'C','U','P','S',':',0};
233 static const WCHAR FILE_Port[] = {'F','I','L','E',':',0};
234 static const WCHAR LPR_Port[] = {'L','P','R',':',0};
235
236 static const WCHAR default_doc_title[] = {'L','o','c','a','l',' ','D','o','w','n','l','e','v','e','l',' ',
237                                           'D','o','c','u','m','e','n','t',0};
238
239 static HKEY WINSPOOL_OpenDriverReg( LPVOID pEnvironment, BOOL unicode);
240 static BOOL WINSPOOL_GetPrinterDriver(HANDLE hPrinter, LPWSTR pEnvironment,
241                                       DWORD Level, LPBYTE pDriverInfo,
242                                       DWORD cbBuf, LPDWORD pcbNeeded,
243                                       BOOL unicode);
244 static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey);
245 static DWORD WINSPOOL_SHDeleteKeyW(HKEY hKey, LPCWSTR lpszSubKey);
246
247 /******************************************************************
248  *  validate the user-supplied printing-environment [internal]
249  *
250  * PARAMS
251  *  env  [I] PTR to Environment-String or NULL
252  *
253  * RETURNS
254  *  Failure:  NULL
255  *  Success:  PTR to printenv_t
256  *
257  * NOTES
258  *  An empty string is handled the same way as NULL.
259  *  SetLastEror(ERROR_INVALID_ENVIRONMENT) is called on Failure
260  *  
261  */
262
263 static const  printenv_t * validate_envW(LPCWSTR env)
264 {
265     static const printenv_t env_x86 =   {envname_x86W, subdir_x86W,
266                                          3, Version3_RegPathW, Version3_SubdirW};
267     static const printenv_t env_win40 = {envname_win40W, subdir_win40W,
268                                          0, emptyStringW, emptyStringW};
269     static const printenv_t * const all_printenv[]={&env_x86, &env_win40};
270
271     const printenv_t *result = NULL;
272     unsigned int i;
273
274     TRACE("testing %s\n", debugstr_w(env));
275     if (env && env[0])
276     {
277         for (i = 0; i < sizeof(all_printenv)/sizeof(all_printenv[0]); i++)
278         {
279             if (lstrcmpiW(env, all_printenv[i]->envname) == 0)
280             {
281                 result = all_printenv[i];
282                 break;
283             }
284         }
285
286         if (result == NULL) {
287             FIXME("unsupported Environment: %s\n", debugstr_w(env));
288             SetLastError(ERROR_INVALID_ENVIRONMENT);
289         }
290         /* on win9x, only "Windows 4.0" is allowed, but we ignore this */
291     }
292     else
293     {
294         result = (GetVersion() & 0x80000000) ? &env_win40 : &env_x86;
295     }
296     TRACE("using %p: %s\n", result, debugstr_w(result ? result->envname : NULL));
297
298     return result;
299 }
300
301
302 /* RtlCreateUnicodeStringFromAsciiz will return an empty string in the buffer
303    if passed a NULL string. This returns NULLs to the result. 
304 */
305 static inline PWSTR asciitounicode( UNICODE_STRING * usBufferPtr, LPCSTR src )
306 {
307     if ( (src) )
308     {
309         RtlCreateUnicodeStringFromAsciiz(usBufferPtr, src);
310         return usBufferPtr->Buffer;
311     }
312     usBufferPtr->Buffer = NULL; /* so that RtlFreeUnicodeString won't barf */
313     return NULL;
314 }
315             
316 static LPWSTR strdupW(LPCWSTR p)
317 {
318     LPWSTR ret;
319     DWORD len;
320
321     if(!p) return NULL;
322     len = (strlenW(p) + 1) * sizeof(WCHAR);
323     ret = HeapAlloc(GetProcessHeap(), 0, len);
324     memcpy(ret, p, len);
325     return ret;
326 }
327
328 static void
329 WINSPOOL_SetDefaultPrinter(const char *devname, const char *name,BOOL force) {
330     char qbuf[200];
331
332     /* If forcing, or no profile string entry for device yet, set the entry
333      *
334      * The always change entry if not WINEPS yet is discussable.
335      */
336     if (force                                                           ||
337         !GetProfileStringA("windows","device","*",qbuf,sizeof(qbuf))    ||
338         !strcmp(qbuf,"*")                                               ||
339         !strstr(qbuf,"WINEPS.DRV")
340     ) {
341         char *buf = HeapAlloc(GetProcessHeap(),0,strlen(name)+strlen(devname)+strlen(",WINEPS.DRV,LPR:")+1);
342         HKEY hkey;
343
344         sprintf(buf,"%s,WINEPS.DRV,LPR:%s",devname,name);
345         WriteProfileStringA("windows","device",buf);
346         if(RegCreateKeyW(HKEY_CURRENT_USER, user_default_reg_key, &hkey) == ERROR_SUCCESS) {
347             RegSetValueExA(hkey, "Device", 0, REG_SZ, (LPBYTE)buf, strlen(buf) + 1);
348             RegCloseKey(hkey);
349         }
350         HeapFree(GetProcessHeap(),0,buf);
351     }
352 }
353
354 #ifdef HAVE_CUPS_CUPS_H
355 static typeof(cupsGetDests)  *pcupsGetDests;
356 static typeof(cupsGetPPD)    *pcupsGetPPD;
357 static typeof(cupsPrintFile) *pcupsPrintFile;
358 static void *cupshandle;
359
360 static BOOL CUPS_LoadPrinters(void)
361 {
362     int                   i, nrofdests;
363     BOOL                  hadprinter = FALSE;
364     cups_dest_t          *dests;
365     PRINTER_INFO_2A       pinfo2a;
366     char   *port,*devline;
367     HKEY hkeyPrinter, hkeyPrinters, hkey;
368
369     cupshandle = wine_dlopen(SONAME_LIBCUPS, RTLD_NOW, NULL, 0);
370     if (!cupshandle) 
371         return FALSE;
372     TRACE("loaded %s\n", SONAME_LIBCUPS);
373
374 #define DYNCUPS(x)                                      \
375         p##x = wine_dlsym(cupshandle, #x, NULL,0);      \
376         if (!p##x) return FALSE;
377
378     DYNCUPS(cupsGetPPD);
379     DYNCUPS(cupsGetDests);
380     DYNCUPS(cupsPrintFile);
381 #undef DYNCUPS
382
383     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
384        ERROR_SUCCESS) {
385         ERR("Can't create Printers key\n");
386         return FALSE;
387     }
388
389     nrofdests = pcupsGetDests(&dests);
390     TRACE("Found %d CUPS %s:\n", nrofdests, (nrofdests == 1) ? "printer" : "printers");
391     for (i=0;i<nrofdests;i++) {
392         port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(dests[i].name)+1);
393         sprintf(port,"LPR:%s",dests[i].name);
394         devline=HeapAlloc(GetProcessHeap(),0,sizeof("WINEPS.DRV,")+strlen(port));
395         sprintf(devline,"WINEPS.DRV,%s",port);
396         WriteProfileStringA("devices",dests[i].name,devline);
397         if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
398             RegSetValueExA(hkey, dests[i].name, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
399             RegCloseKey(hkey);
400         }
401         HeapFree(GetProcessHeap(),0,devline);
402
403         TRACE("Printer %d: %s\n", i, dests[i].name);
404         if(RegOpenKeyA(hkeyPrinters, dests[i].name, &hkeyPrinter) == ERROR_SUCCESS) {
405             /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
406                and continue */
407             TRACE("Printer already exists\n");
408             RegDeleteValueW(hkeyPrinter, May_Delete_Value);
409             RegCloseKey(hkeyPrinter);
410         } else {
411             static CHAR data_type[] = "RAW",
412                     print_proc[]    = "WinPrint",
413                     driver_name[]   = "PS Driver",
414                     comment[]       = "WINEPS Printer using CUPS",
415                     location[]      = "<physical location of printer>",
416                     params[]        = "<parameters?>",
417                     share_name[]    = "<share name?>",
418                     sep_file[]      = "<sep file?>";
419
420             memset(&pinfo2a,0,sizeof(pinfo2a));
421             pinfo2a.pPrinterName    = dests[i].name;
422             pinfo2a.pDatatype       = data_type;
423             pinfo2a.pPrintProcessor = print_proc;
424             pinfo2a.pDriverName     = driver_name;
425             pinfo2a.pComment        = comment;
426             pinfo2a.pLocation       = location;
427             pinfo2a.pPortName       = port;
428             pinfo2a.pParameters     = params;
429             pinfo2a.pShareName      = share_name;
430             pinfo2a.pSepFile        = sep_file;
431
432             if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
433                 if (GetLastError() != ERROR_PRINTER_ALREADY_EXISTS)
434                     ERR("printer '%s' not added by AddPrinterA (error %d)\n",dests[i].name,GetLastError());
435             }
436         }
437         HeapFree(GetProcessHeap(),0,port);
438
439         hadprinter = TRUE;
440         if (dests[i].is_default)
441             WINSPOOL_SetDefaultPrinter(dests[i].name, dests[i].name, TRUE);
442     }
443     RegCloseKey(hkeyPrinters);
444     return hadprinter;
445 }
446 #endif
447
448 static BOOL
449 PRINTCAP_ParseEntry(const char *pent, BOOL isfirst) {
450     PRINTER_INFO_2A     pinfo2a;
451     char                *e,*s,*name,*prettyname,*devname;
452     BOOL                ret = FALSE, set_default = FALSE;
453     char                *port,*devline,*env_default;
454     HKEY                hkeyPrinter, hkeyPrinters, hkey;
455
456     while (isspace(*pent)) pent++;
457     s = strchr(pent,':');
458     if(s) *s='\0';
459     name = HeapAlloc(GetProcessHeap(), 0, strlen(pent) + 1);
460     strcpy(name,pent);
461     if(s) {
462         *s=':';
463         pent = s;
464     } else
465         pent = "";
466
467     TRACE("name=%s entry=%s\n",name, pent);
468
469     if(ispunct(*name)) { /* a tc entry, not a real printer */
470         TRACE("skipping tc entry\n");
471         goto end;
472     }
473
474     if(strstr(pent,":server")) { /* server only version so skip */
475         TRACE("skipping server entry\n");
476         goto end;
477     }
478
479     /* Determine whether this is a postscript printer. */
480
481     ret = TRUE;
482     env_default = getenv("PRINTER");
483     prettyname = name;
484     /* Get longest name, usually the one at the right for later display. */
485     while((s=strchr(prettyname,'|'))) {
486         *s = '\0';
487         e = s;
488         while(isspace(*--e)) *e = '\0';
489         TRACE("\t%s\n", debugstr_a(prettyname));
490         if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
491         for(prettyname = s+1; isspace(*prettyname); prettyname++)
492             ;
493     }
494     e = prettyname + strlen(prettyname);
495     while(isspace(*--e)) *e = '\0';
496     TRACE("\t%s\n", debugstr_a(prettyname));
497     if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
498
499     /* prettyname must fit into the dmDeviceName member of DEVMODE struct,
500      * if it is too long, we use it as comment below. */
501     devname = prettyname;
502     if (strlen(devname)>=CCHDEVICENAME-1)
503          devname = name;
504     if (strlen(devname)>=CCHDEVICENAME-1) {
505         ret = FALSE;
506         goto end;
507     }
508
509     port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(name)+1);
510     sprintf(port,"LPR:%s",name);
511
512     devline=HeapAlloc(GetProcessHeap(),0,sizeof("WINEPS.DRV,")+strlen(port));
513     sprintf(devline,"WINEPS.DRV,%s",port);
514     WriteProfileStringA("devices",devname,devline);
515     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
516         RegSetValueExA(hkey, devname, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
517         RegCloseKey(hkey);
518     }
519     HeapFree(GetProcessHeap(),0,devline);
520     
521     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
522        ERROR_SUCCESS) {
523         ERR("Can't create Printers key\n");
524         ret = FALSE;
525         goto end;
526     }
527     if(RegOpenKeyA(hkeyPrinters, devname, &hkeyPrinter) == ERROR_SUCCESS) {
528         /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
529            and continue */
530         TRACE("Printer already exists\n");
531         RegDeleteValueW(hkeyPrinter, May_Delete_Value);
532         RegCloseKey(hkeyPrinter);
533     } else {
534         static CHAR data_type[]   = "RAW",
535                     print_proc[]  = "WinPrint",
536                     driver_name[] = "PS Driver",
537                     comment[]     = "WINEPS Printer using LPR",
538                     params[]      = "<parameters?>",
539                     share_name[]  = "<share name?>",
540                     sep_file[]    = "<sep file?>";
541
542         memset(&pinfo2a,0,sizeof(pinfo2a));
543         pinfo2a.pPrinterName    = devname;
544         pinfo2a.pDatatype       = data_type;
545         pinfo2a.pPrintProcessor = print_proc;
546         pinfo2a.pDriverName     = driver_name;
547         pinfo2a.pComment        = comment;
548         pinfo2a.pLocation       = prettyname;
549         pinfo2a.pPortName       = port;
550         pinfo2a.pParameters     = params;
551         pinfo2a.pShareName      = share_name;
552         pinfo2a.pSepFile        = sep_file;
553
554         if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
555             if (GetLastError()!=ERROR_PRINTER_ALREADY_EXISTS)
556                 ERR("%s not added by AddPrinterA (%d)\n",name,GetLastError());
557         }
558     }
559     RegCloseKey(hkeyPrinters);
560
561     if (isfirst || set_default)
562         WINSPOOL_SetDefaultPrinter(devname,name,TRUE);
563
564     HeapFree(GetProcessHeap(), 0, port);
565  end:
566     HeapFree(GetProcessHeap(), 0, name);
567     return ret;
568 }
569
570 static BOOL
571 PRINTCAP_LoadPrinters(void) {
572     BOOL                hadprinter = FALSE;
573     char                buf[200];
574     FILE                *f;
575     char *pent = NULL;
576     BOOL had_bash = FALSE;
577
578     f = fopen("/etc/printcap","r");
579     if (!f)
580         return FALSE;
581
582     while(fgets(buf,sizeof(buf),f)) {
583         char *start, *end;
584
585         end=strchr(buf,'\n');
586         if (end) *end='\0';
587     
588         start = buf;
589         while(isspace(*start)) start++;
590         if(*start == '#' || *start == '\0')
591             continue;
592
593         if(pent && !had_bash && *start != ':' && *start != '|') { /* start of new entry, parse the previous one */
594             hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
595             HeapFree(GetProcessHeap(),0,pent);
596             pent = NULL;
597         }
598
599         if (end && *--end == '\\') {
600             *end = '\0';
601             had_bash = TRUE;
602         } else
603             had_bash = FALSE;
604
605         if (pent) {
606             pent=HeapReAlloc(GetProcessHeap(),0,pent,strlen(pent)+strlen(start)+1);
607             strcat(pent,start);
608         } else {
609             pent=HeapAlloc(GetProcessHeap(),0,strlen(start)+1);
610             strcpy(pent,start);
611         }
612
613     }
614     if(pent) {
615         hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
616         HeapFree(GetProcessHeap(),0,pent);
617     }
618     fclose(f);
619     return hadprinter;
620 }
621
622 static inline DWORD set_reg_szW(HKEY hkey, const WCHAR *keyname, const WCHAR *value)
623 {
624     if (value)
625         return RegSetValueExW(hkey, keyname, 0, REG_SZ, (const BYTE*)value,
626                    lstrlenW(value) * sizeof(WCHAR));
627     else
628         return ERROR_FILE_NOT_FOUND;
629 }
630
631 void WINSPOOL_LoadSystemPrinters(void)
632 {
633     HKEY                hkey, hkeyPrinters;
634     DRIVER_INFO_3A      di3a;
635     HANDLE              hprn;
636     DWORD               needed, num, i;
637     WCHAR               PrinterName[256];
638     BOOL                done = FALSE;
639     static CHAR         name[]              = "PS Driver",
640                         driver_path[]       = "wineps16",
641                         data_file[]         = "<datafile?>",
642                         config_file[]       = "wineps16",
643                         help_file[]         = "<helpfile?>",
644                         dep_file[]          = "<dependend files?>",
645                         monitor_name[]      = "<monitor name?>",
646                         default_data_type[] = "RAW";
647
648     di3a.cVersion = (GetVersion() & 0x80000000) ? 0 : 3; /* FIXME: add 1, 2 */
649     di3a.pName            = name;
650     di3a.pEnvironment     = NULL;      /* NULL means auto */
651     di3a.pDriverPath      = driver_path;
652     di3a.pDataFile        = data_file;
653     di3a.pConfigFile      = config_file;
654     di3a.pHelpFile        = help_file;
655     di3a.pDependentFiles  = dep_file;
656     di3a.pMonitorName     = monitor_name;
657     di3a.pDefaultDataType = default_data_type;
658
659     if (!AddPrinterDriverA(NULL,3,(LPBYTE)&di3a)) {
660         ERR("Failed adding PS Driver (%d)\n",GetLastError());
661         return;
662     }
663
664     /* This ensures that all printer entries have a valid Name value.  If causes
665        problems later if they don't.  If one is found to be missed we create one
666        and set it equal to the name of the key */
667     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
668         if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &num, NULL, NULL,
669                             NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
670             for(i = 0; i < num; i++) {
671                 if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)) == ERROR_SUCCESS) {
672                     if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkey) == ERROR_SUCCESS) {
673                         if(RegQueryValueExW(hkey, NameW, 0, 0, 0, &needed) == ERROR_FILE_NOT_FOUND) {
674                             set_reg_szW(hkey, NameW, PrinterName);
675                         }
676                         RegCloseKey(hkey);
677                     }
678                 }
679             }
680         }
681         RegCloseKey(hkeyPrinters);
682     }
683
684     /* We want to avoid calling AddPrinter on printers as much as
685        possible, because on cups printers this will (eventually) lead
686        to a call to cupsGetPPD which takes forever, even with non-cups
687        printers AddPrinter takes a while.  So we'll tag all printers that
688        were automatically added last time around, if they still exist
689        we'll leave them be otherwise we'll delete them. */
690     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
691     if(needed) {
692         PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
693         if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
694             for(i = 0; i < num; i++) {
695                 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
696                     if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
697                         if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
698                             DWORD dw = 1;
699                             RegSetValueExW(hkey, May_Delete_Value, 0, REG_DWORD, (LPBYTE)&dw, sizeof(dw));
700                             RegCloseKey(hkey);
701                         }
702                         ClosePrinter(hprn);
703                     }
704                 }
705             }
706         }
707         HeapFree(GetProcessHeap(), 0, pi);
708     }
709
710
711 #ifdef HAVE_CUPS_CUPS_H
712     done = CUPS_LoadPrinters();
713 #endif
714
715     if(!done) { /* If we have any CUPS based printers, skip looking for printcap printers */
716         /* Check for [ppd] section in config file before parsing /etc/printcap */
717         /* @@ Wine registry key: HKCU\Software\Wine\Printing\PPD Files */
718         if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Printing\\PPD Files",
719                         &hkey) == ERROR_SUCCESS) {
720             RegCloseKey(hkey);
721             PRINTCAP_LoadPrinters();
722         }
723     }
724
725     /* Now enumerate the list again and delete any printers that a still tagged */
726     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
727     if(needed) {
728         PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
729         if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
730             for(i = 0; i < num; i++) {
731                 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
732                     if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
733                         if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
734                             DWORD dw, type, size = sizeof(dw);
735                             if(RegQueryValueExW(hkey, May_Delete_Value, NULL, &type, (LPBYTE)&dw, &size) == ERROR_SUCCESS) {
736                                 TRACE("Deleting old printer %s\n", pi[i].pPrinterName);
737                                 DeletePrinter(hprn);
738                             }
739                             RegCloseKey(hkey);
740                         }
741                         ClosePrinter(hprn);
742                     }
743                 }
744             }
745         }
746         HeapFree(GetProcessHeap(), 0, pi);
747     }
748
749     return;
750
751 }
752
753 /*****************************************************************************
754  * enumerate the local monitors (INTERNAL)  
755  *
756  * returns the needed size (in bytes) for pMonitors
757  * and  *lpreturned is set to number of entries returned in pMonitors
758  *
759  */
760 static DWORD get_local_monitors(DWORD level, LPBYTE pMonitors, DWORD cbBuf, LPDWORD lpreturned)
761 {
762     HKEY    hroot = NULL;
763     HKEY    hentry = NULL;
764     LPWSTR  ptr;
765     LPMONITOR_INFO_2W mi;
766     WCHAR   buffer[MAX_PATH];
767     WCHAR   dllname[MAX_PATH];
768     DWORD   dllsize;
769     DWORD   len;
770     DWORD   index = 0;
771     DWORD   needed = 0;
772     DWORD   numentries;
773     DWORD   entrysize;
774
775     entrysize = (level == 1) ? sizeof(MONITOR_INFO_1W) : sizeof(MONITOR_INFO_2W);
776
777     numentries = *lpreturned;       /* this is 0, when we scan the registry */
778     len = entrysize * numentries;
779     ptr = (LPWSTR) &pMonitors[len];
780
781     numentries = 0;
782     len = sizeof(buffer);
783     buffer[0] = '\0';
784
785     /* Windows creates the "Monitors"-Key on reboot / start "spooler" */
786     if (RegCreateKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) == ERROR_SUCCESS) {
787         /* Scan all Monitor-Registry-Keys */
788         while (RegEnumKeyExW(hroot, index, buffer, &len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
789             TRACE("Monitor_%d: %s\n", numentries, debugstr_w(buffer));
790             dllsize = sizeof(dllname);
791             dllname[0] = '\0';
792
793             /* The Monitor must have a Driver-DLL */
794             if (RegOpenKeyExW(hroot, buffer, 0, KEY_READ, &hentry) == ERROR_SUCCESS) {
795                 if (RegQueryValueExW(hentry, DriverW, NULL, NULL, (LPBYTE) dllname, &dllsize) == ERROR_SUCCESS) {
796                     /* We found a valid DLL for this Monitor. */
797                     TRACE("using Driver: %s\n", debugstr_w(dllname));
798                 }
799                 RegCloseKey(hentry);
800             }
801
802             /* Windows returns only Port-Monitors here, but to simplify our code,
803                we do no filtering for Language-Monitors */
804             if (dllname[0]) {
805                 numentries++;
806                 needed += entrysize;
807                 needed += (len+1) * sizeof(WCHAR);  /* len is lstrlenW(monitorname) */
808                 if (level > 1) {
809                     /* we install and return only monitors for "Windows NT x86" */
810                     needed += (lstrlenW(envname_x86W) +1) * sizeof(WCHAR);
811                     needed += dllsize;
812                 }
813
814                 /* required size is calculated. Now fill the user-buffer */
815                 if (pMonitors && (cbBuf >= needed)){
816                     mi = (LPMONITOR_INFO_2W) pMonitors;
817                     pMonitors += entrysize;
818
819                     TRACE("%p: writing MONITOR_INFO_%dW #%d\n", mi, level, numentries);
820                     mi->pName = ptr;
821                     lstrcpyW(ptr, buffer);      /* Name of the Monitor */
822                     ptr += (len+1);               /* len is lstrlenW(monitorname) */
823                     if (level > 1) {
824                         mi->pEnvironment = ptr;
825                         lstrcpyW(ptr, envname_x86W); /* fixed to "Windows NT x86" */
826                         ptr += (lstrlenW(envname_x86W)+1);
827
828                         mi->pDLLName = ptr;
829                         lstrcpyW(ptr, dllname);         /* Name of the Driver-DLL */
830                         ptr += (dllsize / sizeof(WCHAR));
831                     }
832                 }
833             }
834             index++;
835             len = sizeof(buffer);
836             buffer[0] = '\0';
837         }
838         RegCloseKey(hroot);
839     }
840     *lpreturned = numentries;
841     TRACE("need %d byte for %d entries\n", needed, numentries);
842     return needed;
843 }
844
845 /******************************************************************
846  * monitor_unload [internal]
847  *
848  * release a printmonitor and unload it from memory, when needed
849  *
850  */
851 static void monitor_unload(monitor_t * pm)
852 {
853     TRACE("%p (refcount: %d) %s\n", pm, pm->refcount, debugstr_w(pm->name));
854
855     EnterCriticalSection(&monitor_handles_cs);
856
857     if (pm->refcount) pm->refcount--;
858
859     if (pm->refcount == 0) {
860         list_remove(&pm->entry);
861         FreeLibrary(pm->hdll);
862         HeapFree(GetProcessHeap(), 0, pm->name);
863         HeapFree(GetProcessHeap(), 0, pm->dllname);
864         HeapFree(GetProcessHeap(), 0, pm);
865     }
866     LeaveCriticalSection(&monitor_handles_cs);
867 }
868
869 /******************************************************************
870  * monitor_unloadall [internal]
871  *
872  * release all printmonitors and unload them from memory, when needed 
873  */
874
875 static void monitor_unloadall(void)
876 {
877     monitor_t * pm;
878     monitor_t * next;
879
880     EnterCriticalSection(&monitor_handles_cs);
881     /* iterate through the list, with safety against removal */
882     LIST_FOR_EACH_ENTRY_SAFE(pm, next, &monitor_handles, monitor_t, entry)
883     {
884         monitor_unload(pm);
885     }
886     LeaveCriticalSection(&monitor_handles_cs);
887 }
888
889 /******************************************************************
890  * monitor_load [internal]
891  *
892  * load a printmonitor, get the dllname from the registry, when needed 
893  * initialize the monitor and dump found function-pointers
894  *
895  * On failure, SetLastError() is called and NULL is returned
896  */
897
898 static monitor_t * monitor_load(LPWSTR name, LPWSTR dllname)
899 {
900     LPMONITOR2  (WINAPI *pInitializePrintMonitor2) (PMONITORINIT, LPHANDLE);
901     PMONITORUI  (WINAPI *pInitializePrintMonitorUI)(VOID);
902     LPMONITOREX (WINAPI *pInitializePrintMonitor)  (LPWSTR);
903     DWORD (WINAPI *pInitializeMonitorEx)(LPWSTR, LPMONITOR);
904     DWORD (WINAPI *pInitializeMonitor)  (LPWSTR);
905
906     monitor_t * pm = NULL;
907     monitor_t * cursor;
908     LPWSTR  regroot = NULL;
909     LPWSTR  driver = dllname;
910
911     TRACE("(%s, %s)\n", debugstr_w(name), debugstr_w(dllname));
912     /* Is the Monitor already loaded? */
913     EnterCriticalSection(&monitor_handles_cs);
914
915     LIST_FOR_EACH_ENTRY(cursor, &monitor_handles, monitor_t, entry)
916     {
917         if (lstrcmpW(name, cursor->name) == 0) {
918             pm = cursor;
919             break;
920         }
921     }
922
923     if (pm == NULL) {
924         pm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(monitor_t));
925         if (pm == NULL) goto cleanup;
926         list_add_tail(&monitor_handles, &pm->entry);
927     }
928     pm->refcount++;
929
930     if (pm->name == NULL) {
931         /* Load the monitor */
932         LPMONITOREX pmonitorEx;
933         DWORD   len;
934
935         len = lstrlenW(MonitorsW) + lstrlenW(name) + 2; 
936         regroot = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
937
938         if (regroot) {
939             lstrcpyW(regroot, MonitorsW);
940             lstrcatW(regroot, name);
941             /* Get the Driver from the Registry */
942             if (driver == NULL) {
943                 HKEY    hroot;
944                 DWORD   namesize;
945                 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, regroot, &hroot) == ERROR_SUCCESS) {
946                     if (RegQueryValueExW(hroot, DriverW, NULL, NULL, NULL,
947                                         &namesize) == ERROR_SUCCESS) {
948                         driver = HeapAlloc(GetProcessHeap(), 0, namesize);
949                         RegQueryValueExW(hroot, DriverW, NULL, NULL, (LPBYTE) driver, &namesize) ;
950                     }
951                     RegCloseKey(hroot);
952                 }
953             }
954         }
955
956         pm->name = strdupW(name);
957         pm->dllname = strdupW(driver);
958
959         if (!regroot || !pm->name || !pm->dllname) {
960             monitor_unload(pm);
961             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
962             pm = NULL;
963             goto cleanup;
964         }
965
966         pm->hdll = LoadLibraryW(driver);
967         TRACE("%p: LoadLibrary(%s) => %d\n", pm->hdll, debugstr_w(driver), GetLastError());
968
969         if (pm->hdll == NULL) {
970             monitor_unload(pm);
971             SetLastError(ERROR_MOD_NOT_FOUND);
972             pm = NULL;
973             goto cleanup;
974         }
975
976         pInitializePrintMonitor2  = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor2");
977         pInitializePrintMonitorUI = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitorUI");
978         pInitializePrintMonitor   = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor");
979         pInitializeMonitorEx = (void *)GetProcAddress(pm->hdll, "InitializeMonitorEx");
980         pInitializeMonitor   = (void *)GetProcAddress(pm->hdll, "InitializeMonitor");
981
982
983         TRACE("%p: %s,pInitializePrintMonitor2\n", pInitializePrintMonitor2, debugstr_w(driver));
984         TRACE("%p: %s,pInitializePrintMonitorUI\n", pInitializePrintMonitorUI, debugstr_w(driver));
985         TRACE("%p: %s,pInitializePrintMonitor\n", pInitializePrintMonitor, debugstr_w(driver));
986         TRACE("%p: %s,pInitializeMonitorEx\n", pInitializeMonitorEx, debugstr_w(driver));
987         TRACE("%p: %s,pInitializeMonitor\n", pInitializeMonitor, debugstr_w(driver));
988
989         if (pInitializePrintMonitorUI  != NULL) {
990             pm->monitorUI = pInitializePrintMonitorUI();
991             TRACE("%p: MONITORUI from %s,InitializePrintMonitorUI()\n", pm->monitorUI, debugstr_w(driver)); 
992             if (pm->monitorUI) {
993                 TRACE(  "0x%08x: dwMonitorSize (%d)\n",
994                         pm->monitorUI->dwMonitorUISize, pm->monitorUI->dwMonitorUISize );
995
996             }
997         }
998
999         if (pInitializePrintMonitor != NULL) {
1000             pmonitorEx = pInitializePrintMonitor(regroot);
1001             TRACE(  "%p: LPMONITOREX from %s,InitializePrintMonitor(%s)\n", 
1002                     pmonitorEx, debugstr_w(driver), debugstr_w(regroot)); 
1003
1004             if (pmonitorEx) {
1005                 pm->dwMonitorSize = pmonitorEx->dwMonitorSize;
1006                 pm->monitor = &(pmonitorEx->Monitor);
1007             }
1008         }
1009
1010         if (pm->monitor) {
1011             TRACE(  "0x%08x: dwMonitorSize (%d)\n", pm->dwMonitorSize, pm->dwMonitorSize );
1012
1013         }
1014
1015         if (!pm->monitor) {
1016             if (pInitializePrintMonitor2 != NULL) {
1017                 FIXME("%s,InitializePrintMonitor2 not implemented\n", debugstr_w(driver));
1018             }
1019             if (pInitializeMonitorEx != NULL) {
1020                 FIXME("%s,InitializeMonitorEx not implemented\n", debugstr_w(driver));
1021             }
1022             if (pInitializeMonitor != NULL) {
1023                 FIXME("%s,InitializeMonitor not implemented\n", debugstr_w(driver));
1024             }
1025         }
1026         if (!pm->monitor && !pm->monitorUI) {
1027             monitor_unload(pm);
1028             SetLastError(ERROR_PROC_NOT_FOUND);
1029             pm = NULL;
1030         }
1031     }
1032 cleanup:
1033     LeaveCriticalSection(&monitor_handles_cs);
1034     if (driver != dllname) HeapFree(GetProcessHeap(), 0, driver);
1035     HeapFree(GetProcessHeap(), 0, regroot);
1036     TRACE("=> %p\n", pm);
1037     return pm;
1038 }
1039
1040 /******************************************************************
1041  * monitor_loadall [internal]
1042  *
1043  * Load all registered monitors
1044  *
1045  */
1046 static DWORD monitor_loadall(void)
1047 {
1048     monitor_t * pm;
1049     DWORD   registered = 0;
1050     DWORD   loaded = 0;
1051     HKEY    hmonitors;
1052     WCHAR   buffer[MAX_PATH];
1053     DWORD   id = 0;
1054
1055     if (RegOpenKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hmonitors) == ERROR_SUCCESS) {
1056         RegQueryInfoKeyW(hmonitors, NULL, NULL, NULL, &registered, NULL, NULL,
1057                         NULL, NULL, NULL, NULL, NULL);
1058
1059         TRACE("%d monitors registered\n", registered);
1060
1061         EnterCriticalSection(&monitor_handles_cs);
1062         while (id < registered) {
1063             buffer[0] = '\0';
1064             RegEnumKeyW(hmonitors, id, buffer, MAX_PATH);
1065             pm = monitor_load(buffer, NULL);
1066             if (pm) loaded++;
1067             id++;
1068         }
1069         LeaveCriticalSection(&monitor_handles_cs);
1070         RegCloseKey(hmonitors);
1071     }
1072     TRACE("%d monitors loaded\n", loaded);
1073     return loaded;
1074 }
1075
1076 /******************************************************************
1077  * enumerate the local Ports from all loaded monitors (internal)  
1078  *
1079  * returns the needed size (in bytes) for pPorts
1080  * and  *lpreturned is set to number of entries returned in pPorts
1081  *
1082  */
1083 static DWORD get_ports_from_all_monitors(DWORD level, LPBYTE pPorts, DWORD cbBuf, LPDWORD lpreturned)
1084 {
1085     monitor_t * pm;
1086     LPWSTR      ptr;
1087     LPPORT_INFO_2W cache;
1088     LPPORT_INFO_2W out;
1089     DWORD   res;
1090     DWORD   cacheindex;
1091     DWORD   outindex = 0;
1092     DWORD   needed = 0;
1093     DWORD   numentries;
1094     DWORD   entrysize;
1095
1096
1097     TRACE("(%d, %p, %d, %p)\n", level, pPorts, cbBuf, lpreturned);
1098     entrysize = (level == 1) ? sizeof(PORT_INFO_1W) : sizeof(PORT_INFO_2W);
1099
1100     numentries = *lpreturned;       /* this is 0, when we scan the registry */
1101     needed = entrysize * numentries;
1102     ptr = (LPWSTR) &pPorts[needed];
1103
1104     numentries = 0;
1105     needed = 0;
1106
1107     LIST_FOR_EACH_ENTRY(pm, &monitor_handles, monitor_t, entry)
1108     {
1109         if ((pm->monitor) && (pm->monitor->pfnEnumPorts)) {
1110             if (pm->cache == NULL) {
1111                 res = pm->monitor->pfnEnumPorts(NULL, 2, NULL, 0, &(pm->pi2_needed), &(pm->returned));
1112                 if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
1113                     pm->cache = HeapAlloc(GetProcessHeap(), 0, (pm->pi2_needed));
1114                     res = pm->monitor->pfnEnumPorts(NULL, 2, (LPBYTE) pm->cache, pm->pi2_needed, &(pm->pi2_needed), &(pm->returned));
1115                 }
1116                 TRACE("(%s) got %d with %d (cache need %d byte for %d entries)\n", 
1117                         debugstr_w(pm->name), res, GetLastError(), pm->pi2_needed, pm->returned);
1118                 res = FALSE;
1119             }     
1120             if (pm->cache && (level == 1) && (pm->pi1_needed == 0) && (pm->returned > 0)) {
1121                 cacheindex = 0;
1122                 cache = pm->cache;
1123                 while (cacheindex < (pm->returned)) {
1124                     pm->pi1_needed += sizeof(PORT_INFO_1W);
1125                     pm->pi1_needed += (lstrlenW(cache->pPortName) + 1) * sizeof(WCHAR);
1126                     cache++;
1127                     cacheindex++;
1128                 }
1129                 TRACE("%d byte for %d cached PORT_INFO_1W entries (%s)\n",
1130                         pm->pi1_needed, cacheindex, debugstr_w(pm->name));
1131             }
1132             numentries += pm->returned;
1133             needed += (level == 1) ? pm->pi1_needed : pm->pi2_needed;
1134
1135             /* fill the buffer, if we have one */
1136             if (pPorts && (cbBuf >= needed )) {
1137                 cacheindex = 0;
1138                 cache = pm->cache;
1139                 while (cacheindex < pm->returned) {
1140                     out = (LPPORT_INFO_2W) &pPorts[outindex * entrysize];
1141                     out->pPortName = ptr;
1142                     lstrcpyW(ptr, cache->pPortName);
1143                     ptr += (lstrlenW(ptr)+1);
1144                     if (level > 1) {
1145                         out->pMonitorName = ptr;
1146                         lstrcpyW(ptr,  cache->pMonitorName);
1147                         ptr += (lstrlenW(ptr)+1);
1148
1149                         out->pDescription = ptr;
1150                         lstrcpyW(ptr,  cache->pDescription);
1151                         ptr += (lstrlenW(ptr)+1);
1152                         out->fPortType = cache->fPortType;
1153                         out->Reserved = cache->Reserved;
1154                     }
1155                     cache++;
1156                     cacheindex++;
1157                     outindex++;
1158                 }
1159             }
1160         }
1161     }
1162
1163     *lpreturned = numentries;
1164     TRACE("need %d byte for %d entries\n", needed, numentries);
1165     return needed;
1166 }
1167
1168 /******************************************************************
1169  *  get_opened_printer_entry
1170  *  Get the first place empty in the opened printer table
1171  *
1172  * ToDo:
1173  *  - pDefault is ignored
1174  */
1175 static HANDLE get_opened_printer_entry(LPCWSTR name, LPPRINTER_DEFAULTSW pDefault)
1176 {
1177     UINT_PTR handle = nb_printer_handles, i;
1178     jobqueue_t *queue = NULL;
1179     opened_printer_t *printer = NULL;
1180
1181     EnterCriticalSection(&printer_handles_cs);
1182
1183     for (i = 0; i < nb_printer_handles; i++)
1184     {
1185         if (!printer_handles[i])
1186         {
1187             if(handle == nb_printer_handles)
1188                 handle = i;
1189         }
1190         else
1191         {
1192             if(!queue && (name) && !lstrcmpW(name, printer_handles[i]->name))
1193                 queue = printer_handles[i]->queue;
1194         }
1195     }
1196
1197     if (handle >= nb_printer_handles)
1198     {
1199         opened_printer_t **new_array;
1200         if (printer_handles)
1201             new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, printer_handles,
1202                                      (nb_printer_handles + 16) * sizeof(*new_array) );
1203         else
1204             new_array = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
1205                                    (nb_printer_handles + 16) * sizeof(*new_array) );
1206
1207         if (!new_array)
1208         {
1209             handle = 0;
1210             goto end;
1211         }
1212         printer_handles = new_array;
1213         nb_printer_handles += 16;
1214     }
1215
1216     if (!(printer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*printer))))
1217     {
1218         handle = 0;
1219         goto end;
1220     }
1221
1222     if(name) {
1223         printer->name = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
1224         if (!printer->name) {
1225             handle = 0;
1226             goto end;
1227         }
1228         strcpyW(printer->name, name);
1229     }
1230
1231     if(queue)
1232         printer->queue = queue;
1233     else
1234     {
1235         printer->queue = HeapAlloc(GetProcessHeap(), 0, sizeof(*queue));
1236         if (!printer->queue) {
1237             handle = 0;
1238             goto end;
1239         }
1240         list_init(&printer->queue->jobs);
1241         printer->queue->ref = 0;
1242     }
1243     InterlockedIncrement(&printer->queue->ref);
1244
1245     printer_handles[handle] = printer;
1246     handle++;
1247 end:
1248     LeaveCriticalSection(&printer_handles_cs);
1249     if (!handle && printer) {
1250         /* Something Failed: Free the Buffers */
1251         HeapFree(GetProcessHeap(), 0, printer->name);
1252         if (!queue) HeapFree(GetProcessHeap(), 0, printer->queue);
1253         HeapFree(GetProcessHeap(), 0, printer);
1254     }
1255
1256     return (HANDLE)handle;
1257 }
1258
1259 /******************************************************************
1260  *  get_opened_printer
1261  *  Get the pointer to the opened printer referred by the handle
1262  */
1263 static opened_printer_t *get_opened_printer(HANDLE hprn)
1264 {
1265     UINT_PTR idx = (UINT_PTR)hprn;
1266     opened_printer_t *ret = NULL;
1267
1268     EnterCriticalSection(&printer_handles_cs);
1269
1270     if ((idx <= 0) || (idx > nb_printer_handles))
1271         goto end;
1272
1273     ret = printer_handles[idx - 1];
1274 end:
1275     LeaveCriticalSection(&printer_handles_cs);
1276     return ret;
1277 }
1278
1279 /******************************************************************
1280  *  get_opened_printer_name
1281  *  Get the pointer to the opened printer name referred by the handle
1282  */
1283 static LPCWSTR get_opened_printer_name(HANDLE hprn)
1284 {
1285     opened_printer_t *printer = get_opened_printer(hprn);
1286     if(!printer) return NULL;
1287     return printer->name;
1288 }
1289
1290 /******************************************************************
1291  *  WINSPOOL_GetOpenedPrinterRegKey
1292  *
1293  */
1294 static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey)
1295 {
1296     LPCWSTR name = get_opened_printer_name(hPrinter);
1297     DWORD ret;
1298     HKEY hkeyPrinters;
1299
1300     if(!name) return ERROR_INVALID_HANDLE;
1301
1302     if((ret = RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters)) !=
1303        ERROR_SUCCESS)
1304         return ret;
1305
1306     if(RegOpenKeyW(hkeyPrinters, name, phkey) != ERROR_SUCCESS)
1307     {
1308         ERR("Can't find opened printer %s in registry\n",
1309             debugstr_w(name));
1310         RegCloseKey(hkeyPrinters);
1311         return ERROR_INVALID_PRINTER_NAME; /* ? */
1312     }
1313     RegCloseKey(hkeyPrinters);
1314     return ERROR_SUCCESS;
1315 }
1316
1317 /******************************************************************
1318  *                  get_job
1319  *
1320  *  Get the pointer to the specified job.
1321  *  Should hold the printer_handles_cs before calling.
1322  */
1323 static job_t *get_job(HANDLE hprn, DWORD JobId)
1324 {
1325     opened_printer_t *printer = get_opened_printer(hprn);
1326     job_t *job;
1327
1328     if(!printer) return NULL;
1329     LIST_FOR_EACH_ENTRY(job, &printer->queue->jobs, job_t, entry)
1330     {
1331         if(job->job_id == JobId)
1332             return job;
1333     }
1334     return NULL;
1335 }
1336
1337 /***********************************************************
1338  *      DEVMODEcpyAtoW
1339  */
1340 static LPDEVMODEW DEVMODEcpyAtoW(DEVMODEW *dmW, const DEVMODEA *dmA)
1341 {
1342     BOOL Formname;
1343     ptrdiff_t off_formname = (const char *)dmA->dmFormName - (const char *)dmA;
1344     DWORD size;
1345
1346     Formname = (dmA->dmSize > off_formname);
1347     size = dmA->dmSize + CCHDEVICENAME + (Formname ? CCHFORMNAME : 0);
1348     MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmDeviceName, -1,
1349                         dmW->dmDeviceName, CCHDEVICENAME);
1350     if(!Formname) {
1351       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
1352              dmA->dmSize - CCHDEVICENAME);
1353     } else {
1354       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
1355              off_formname - CCHDEVICENAME);
1356       MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmFormName, -1,
1357                           dmW->dmFormName, CCHFORMNAME);
1358       memcpy(&dmW->dmLogPixels, &dmA->dmLogPixels, dmA->dmSize -
1359              (off_formname + CCHFORMNAME));
1360     }
1361     dmW->dmSize = size;
1362     memcpy((char *)dmW + dmW->dmSize, (const char *)dmA + dmA->dmSize,
1363            dmA->dmDriverExtra);
1364     return dmW;
1365 }
1366
1367 /***********************************************************
1368  *      DEVMODEdupWtoA
1369  * Creates an ascii copy of supplied devmode on heap
1370  */
1371 static LPDEVMODEA DEVMODEdupWtoA(HANDLE heap, const DEVMODEW *dmW)
1372 {
1373     LPDEVMODEA dmA;
1374     DWORD size;
1375     BOOL Formname;
1376     ptrdiff_t off_formname = (const char *)dmW->dmFormName - (const char *)dmW;
1377
1378     if(!dmW) return NULL;
1379     Formname = (dmW->dmSize > off_formname);
1380     size = dmW->dmSize - CCHDEVICENAME - (Formname ? CCHFORMNAME : 0);
1381     dmA = HeapAlloc(heap, HEAP_ZERO_MEMORY, size + dmW->dmDriverExtra);
1382     WideCharToMultiByte(CP_ACP, 0, dmW->dmDeviceName, -1,
1383                         (LPSTR)dmA->dmDeviceName, CCHDEVICENAME, NULL, NULL);
1384     if(!Formname) {
1385       memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
1386              dmW->dmSize - CCHDEVICENAME * sizeof(WCHAR));
1387     } else {
1388       memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
1389              off_formname - CCHDEVICENAME * sizeof(WCHAR));
1390       WideCharToMultiByte(CP_ACP, 0, dmW->dmFormName, -1,
1391                           (LPSTR)dmA->dmFormName, CCHFORMNAME, NULL, NULL);
1392       memcpy(&dmA->dmLogPixels, &dmW->dmLogPixels, dmW->dmSize -
1393              (off_formname + CCHFORMNAME * sizeof(WCHAR)));
1394     }
1395     dmA->dmSize = size;
1396     memcpy((char *)dmA + dmA->dmSize, (const char *)dmW + dmW->dmSize,
1397            dmW->dmDriverExtra);
1398     return dmA;
1399 }
1400
1401 /***********************************************************
1402  *             PRINTER_INFO_2AtoW
1403  * Creates a unicode copy of PRINTER_INFO_2A on heap
1404  */
1405 static LPPRINTER_INFO_2W PRINTER_INFO_2AtoW(HANDLE heap, LPPRINTER_INFO_2A piA)
1406 {
1407     LPPRINTER_INFO_2W piW;
1408     UNICODE_STRING usBuffer;
1409
1410     if(!piA) return NULL;
1411     piW = HeapAlloc(heap, 0, sizeof(*piW));
1412     memcpy(piW, piA, sizeof(*piW)); /* copy everything first */
1413     
1414     piW->pServerName = asciitounicode(&usBuffer,piA->pServerName);
1415     piW->pPrinterName = asciitounicode(&usBuffer,piA->pPrinterName);
1416     piW->pShareName = asciitounicode(&usBuffer,piA->pShareName);
1417     piW->pPortName = asciitounicode(&usBuffer,piA->pPortName);
1418     piW->pDriverName = asciitounicode(&usBuffer,piA->pDriverName);
1419     piW->pComment = asciitounicode(&usBuffer,piA->pComment);
1420     piW->pLocation = asciitounicode(&usBuffer,piA->pLocation);
1421     piW->pDevMode = piA->pDevMode ? GdiConvertToDevmodeW(piA->pDevMode) : NULL;
1422     piW->pSepFile = asciitounicode(&usBuffer,piA->pSepFile);
1423     piW->pPrintProcessor = asciitounicode(&usBuffer,piA->pPrintProcessor);
1424     piW->pDatatype = asciitounicode(&usBuffer,piA->pDatatype);
1425     piW->pParameters = asciitounicode(&usBuffer,piA->pParameters);
1426     return piW;
1427 }
1428
1429 /***********************************************************
1430  *       FREE_PRINTER_INFO_2W
1431  * Free PRINTER_INFO_2W and all strings
1432  */
1433 static void FREE_PRINTER_INFO_2W(HANDLE heap, LPPRINTER_INFO_2W piW)
1434 {
1435     if(!piW) return;
1436
1437     HeapFree(heap,0,piW->pServerName);
1438     HeapFree(heap,0,piW->pPrinterName);
1439     HeapFree(heap,0,piW->pShareName);
1440     HeapFree(heap,0,piW->pPortName);
1441     HeapFree(heap,0,piW->pDriverName);
1442     HeapFree(heap,0,piW->pComment);
1443     HeapFree(heap,0,piW->pLocation);
1444     HeapFree(heap,0,piW->pDevMode);
1445     HeapFree(heap,0,piW->pSepFile);
1446     HeapFree(heap,0,piW->pPrintProcessor);
1447     HeapFree(heap,0,piW->pDatatype);
1448     HeapFree(heap,0,piW->pParameters);
1449     HeapFree(heap,0,piW);
1450     return;
1451 }
1452
1453 /******************************************************************
1454  *              DeviceCapabilities     [WINSPOOL.@]
1455  *              DeviceCapabilitiesA    [WINSPOOL.@]
1456  *
1457  */
1458 INT WINAPI DeviceCapabilitiesA(LPCSTR pDevice,LPCSTR pPort, WORD cap,
1459                                LPSTR pOutput, LPDEVMODEA lpdm)
1460 {
1461     INT ret;
1462
1463     if (!GDI_CallDeviceCapabilities16)
1464     {
1465         GDI_CallDeviceCapabilities16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1466                                                               (LPCSTR)104 );
1467         if (!GDI_CallDeviceCapabilities16) return -1;
1468     }
1469     ret = GDI_CallDeviceCapabilities16(pDevice, pPort, cap, pOutput, lpdm);
1470
1471     /* If DC_PAPERSIZE map POINT16s to POINTs */
1472     if(ret != -1 && cap == DC_PAPERSIZE && pOutput) {
1473         POINT16 *tmp = HeapAlloc( GetProcessHeap(), 0, ret * sizeof(POINT16) );
1474         POINT *pt = (POINT *)pOutput;
1475         INT i;
1476         memcpy(tmp, pOutput, ret * sizeof(POINT16));
1477         for(i = 0; i < ret; i++, pt++)
1478         {
1479             pt->x = tmp[i].x;
1480             pt->y = tmp[i].y;
1481         }
1482         HeapFree( GetProcessHeap(), 0, tmp );
1483     }
1484     return ret;
1485 }
1486
1487
1488 /*****************************************************************************
1489  *          DeviceCapabilitiesW        [WINSPOOL.@]
1490  *
1491  * Call DeviceCapabilitiesA since we later call 16bit stuff anyway
1492  *
1493  */
1494 INT WINAPI DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort,
1495                                WORD fwCapability, LPWSTR pOutput,
1496                                const DEVMODEW *pDevMode)
1497 {
1498     LPDEVMODEA dmA = DEVMODEdupWtoA(GetProcessHeap(), pDevMode);
1499     LPSTR pDeviceA = HEAP_strdupWtoA(GetProcessHeap(),0,pDevice);
1500     LPSTR pPortA = HEAP_strdupWtoA(GetProcessHeap(),0,pPort);
1501     INT ret;
1502
1503     if(pOutput && (fwCapability == DC_BINNAMES ||
1504                    fwCapability == DC_FILEDEPENDENCIES ||
1505                    fwCapability == DC_PAPERNAMES)) {
1506       /* These need A -> W translation */
1507         INT size = 0, i;
1508         LPSTR pOutputA;
1509         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, NULL,
1510                                   dmA);
1511         if(ret == -1)
1512             return ret;
1513         switch(fwCapability) {
1514         case DC_BINNAMES:
1515             size = 24;
1516             break;
1517         case DC_PAPERNAMES:
1518         case DC_FILEDEPENDENCIES:
1519             size = 64;
1520             break;
1521         }
1522         pOutputA = HeapAlloc(GetProcessHeap(), 0, size * ret);
1523         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, pOutputA,
1524                                   dmA);
1525         for(i = 0; i < ret; i++)
1526             MultiByteToWideChar(CP_ACP, 0, pOutputA + (i * size), -1,
1527                                 pOutput + (i * size), size);
1528         HeapFree(GetProcessHeap(), 0, pOutputA);
1529     } else {
1530         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability,
1531                                   (LPSTR)pOutput, dmA);
1532     }
1533     HeapFree(GetProcessHeap(),0,pPortA);
1534     HeapFree(GetProcessHeap(),0,pDeviceA);
1535     HeapFree(GetProcessHeap(),0,dmA);
1536     return ret;
1537 }
1538
1539 /******************************************************************
1540  *              DocumentPropertiesA   [WINSPOOL.@]
1541  *
1542  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1543  */
1544 LONG WINAPI DocumentPropertiesA(HWND hWnd,HANDLE hPrinter,
1545                                 LPSTR pDeviceName, LPDEVMODEA pDevModeOutput,
1546                                 LPDEVMODEA pDevModeInput,DWORD fMode )
1547 {
1548     LPSTR lpName = pDeviceName;
1549     static CHAR port[] = "LPT1:";
1550     LONG ret;
1551
1552     TRACE("(%p,%p,%s,%p,%p,%d)\n",
1553         hWnd,hPrinter,pDeviceName,pDevModeOutput,pDevModeInput,fMode
1554     );
1555
1556     if(!pDeviceName) {
1557         LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
1558         if(!lpNameW) {
1559                 ERR("no name from hPrinter?\n");
1560                 SetLastError(ERROR_INVALID_HANDLE);
1561                 return -1;
1562         }
1563         lpName = HEAP_strdupWtoA(GetProcessHeap(),0,lpNameW);
1564     }
1565
1566     if (!GDI_CallExtDeviceMode16)
1567     {
1568         GDI_CallExtDeviceMode16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1569                                                          (LPCSTR)102 );
1570         if (!GDI_CallExtDeviceMode16) {
1571                 ERR("No CallExtDeviceMode16?\n");
1572                 return -1;
1573         }
1574     }
1575     ret = GDI_CallExtDeviceMode16(hWnd, pDevModeOutput, lpName, port,
1576                                   pDevModeInput, NULL, fMode);
1577
1578     if(!pDeviceName)
1579         HeapFree(GetProcessHeap(),0,lpName);
1580     return ret;
1581 }
1582
1583
1584 /*****************************************************************************
1585  *          DocumentPropertiesW (WINSPOOL.@)
1586  *
1587  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1588  */
1589 LONG WINAPI DocumentPropertiesW(HWND hWnd, HANDLE hPrinter,
1590                                 LPWSTR pDeviceName,
1591                                 LPDEVMODEW pDevModeOutput,
1592                                 LPDEVMODEW pDevModeInput, DWORD fMode)
1593 {
1594
1595     LPSTR pDeviceNameA = HEAP_strdupWtoA(GetProcessHeap(),0,pDeviceName);
1596     LPDEVMODEA pDevModeInputA = DEVMODEdupWtoA(GetProcessHeap(),pDevModeInput);
1597     LPDEVMODEA pDevModeOutputA = NULL;
1598     LONG ret;
1599
1600     TRACE("(%p,%p,%s,%p,%p,%d)\n",
1601           hWnd,hPrinter,debugstr_w(pDeviceName),pDevModeOutput,pDevModeInput,
1602           fMode);
1603     if(pDevModeOutput) {
1604         ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, NULL, NULL, 0);
1605         if(ret < 0) return ret;
1606         pDevModeOutputA = HeapAlloc(GetProcessHeap(), 0, ret);
1607     }
1608     ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, pDevModeOutputA,
1609                               pDevModeInputA, fMode);
1610     if(pDevModeOutput) {
1611         DEVMODEcpyAtoW(pDevModeOutput, pDevModeOutputA);
1612         HeapFree(GetProcessHeap(),0,pDevModeOutputA);
1613     }
1614     if(fMode == 0 && ret > 0)
1615         ret += (CCHDEVICENAME + CCHFORMNAME);
1616     HeapFree(GetProcessHeap(),0,pDevModeInputA);
1617     HeapFree(GetProcessHeap(),0,pDeviceNameA);
1618     return ret;
1619 }
1620
1621 /******************************************************************
1622  *              OpenPrinterA        [WINSPOOL.@]
1623  *
1624  * See OpenPrinterW.
1625  *
1626  */
1627 BOOL WINAPI OpenPrinterA(LPSTR lpPrinterName,HANDLE *phPrinter,
1628                          LPPRINTER_DEFAULTSA pDefault)
1629 {
1630     UNICODE_STRING lpPrinterNameW;
1631     UNICODE_STRING usBuffer;
1632     PRINTER_DEFAULTSW DefaultW, *pDefaultW = NULL;
1633     PWSTR pwstrPrinterNameW;
1634     BOOL ret;
1635
1636     pwstrPrinterNameW = asciitounicode(&lpPrinterNameW,lpPrinterName);
1637
1638     if(pDefault) {
1639         DefaultW.pDatatype = asciitounicode(&usBuffer,pDefault->pDatatype);
1640         DefaultW.pDevMode = pDefault->pDevMode ? GdiConvertToDevmodeW(pDefault->pDevMode) : NULL;
1641         DefaultW.DesiredAccess = pDefault->DesiredAccess;
1642         pDefaultW = &DefaultW;
1643     }
1644     ret = OpenPrinterW(pwstrPrinterNameW, phPrinter, pDefaultW);
1645     if(pDefault) {
1646         RtlFreeUnicodeString(&usBuffer);
1647         HeapFree(GetProcessHeap(), 0, DefaultW.pDevMode);
1648     }
1649     RtlFreeUnicodeString(&lpPrinterNameW);
1650     return ret;
1651 }
1652
1653 /******************************************************************
1654  *              OpenPrinterW        [WINSPOOL.@]
1655  *
1656  * Open a Printer / Printserver or a Printer-Object
1657  *
1658  * PARAMS
1659  *  lpPrinterName [I] Name of Printserver, Printer, or Printer-Object
1660  *  phPrinter     [O] The resulting Handle is stored here
1661  *  pDefault      [I] PTR to Default Printer Settings or NULL
1662  *
1663  * RETURNS
1664  *  Success: TRUE
1665  *  Failure: FALSE
1666  *
1667  * NOTES
1668  *  lpPrinterName is one of:
1669  *|  Printserver (NT only): "Servername" or NULL for the local Printserver
1670  *|  Printer: "PrinterName"
1671  *|  Printer-Object: "PrinterName,Job xxx"
1672  *|  XcvMonitor: "Servername,XcvMonitor MonitorName"
1673  *|  XcvPort: "Servername,XcvPort PortName"
1674  *
1675  * BUGS
1676  *|  Printer-Object not supported
1677  *|  XcvMonitor not supported
1678  *|  XcvPort not supported
1679  *|  pDefaults is ignored
1680  *
1681  */
1682 BOOL WINAPI OpenPrinterW(LPWSTR lpPrinterName,HANDLE *phPrinter, LPPRINTER_DEFAULTSW pDefault)
1683 {
1684     HKEY hkeyPrinters = NULL;
1685     HKEY hkeyPrinter = NULL;
1686
1687     TRACE("(%s, %p, %p)\n", debugstr_w(lpPrinterName), phPrinter, pDefault);
1688     if (pDefault) {
1689         FIXME("PRINTER_DEFAULTS ignored => %s,%p,0x%08x\n",
1690         debugstr_w(pDefault->pDatatype), pDefault->pDevMode, pDefault->DesiredAccess);
1691     }
1692
1693     if(lpPrinterName != NULL)
1694     {
1695         /* Check any Printer exists */
1696         if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) != ERROR_SUCCESS) {
1697             ERR("Can't create Printers key\n");
1698             SetLastError(ERROR_FILE_NOT_FOUND);
1699             return FALSE;
1700         }
1701         if((lpPrinterName[0] == '\0') ||        /* explicitly exclude "" */
1702            (RegOpenKeyW(hkeyPrinters, lpPrinterName, &hkeyPrinter) != ERROR_SUCCESS)) {
1703
1704             WARN("Printer not found in Registry: '%s'\n", debugstr_w(lpPrinterName));
1705             RegCloseKey(hkeyPrinters);
1706             SetLastError(ERROR_INVALID_PRINTER_NAME);
1707             return FALSE;
1708         }
1709         RegCloseKey(hkeyPrinter);
1710         RegCloseKey(hkeyPrinters);
1711     }
1712     if(!phPrinter) {
1713         /* NT: FALSE with ERROR_INVALID_PARAMETER, 9x: TRUE */
1714         SetLastError(ERROR_INVALID_PARAMETER);
1715         return FALSE;
1716     }
1717
1718     /* Get the unique handle of the printer or Printserver */
1719     *phPrinter = get_opened_printer_entry(lpPrinterName, pDefault);
1720     return (*phPrinter != 0);
1721 }
1722
1723 /******************************************************************
1724  *              AddMonitorA        [WINSPOOL.@]
1725  *
1726  * See AddMonitorW.
1727  *
1728  */
1729 BOOL WINAPI AddMonitorA(LPSTR pName, DWORD Level, LPBYTE pMonitors)
1730 {
1731     LPWSTR  nameW = NULL;
1732     INT     len;
1733     BOOL    res;
1734     LPMONITOR_INFO_2A mi2a;
1735     MONITOR_INFO_2W mi2w;
1736
1737     mi2a = (LPMONITOR_INFO_2A) pMonitors;
1738     TRACE("(%s, %d, %p) :  %s %s %s\n", debugstr_a(pName), Level, pMonitors, 
1739             mi2a ? debugstr_a(mi2a->pName) : NULL,
1740             mi2a ? debugstr_a(mi2a->pEnvironment) : NULL,
1741             mi2a ? debugstr_a(mi2a->pDLLName) : NULL);
1742
1743     if  (Level != 2) {
1744         SetLastError(ERROR_INVALID_LEVEL);
1745         return FALSE;
1746     }
1747
1748     /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
1749     if (mi2a == NULL) {
1750         return FALSE;
1751     }
1752
1753     if (pName) {
1754         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
1755         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1756         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
1757     }
1758
1759     memset(&mi2w, 0, sizeof(MONITOR_INFO_2W));
1760     if (mi2a->pName) {
1761         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, NULL, 0);
1762         mi2w.pName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1763         MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, mi2w.pName, len);
1764     }
1765     if (mi2a->pEnvironment) {
1766         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, NULL, 0);
1767         mi2w.pEnvironment = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1768         MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, mi2w.pEnvironment, len);
1769     }
1770     if (mi2a->pDLLName) {
1771         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, NULL, 0);
1772         mi2w.pDLLName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1773         MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, mi2w.pDLLName, len);
1774     }
1775
1776     res = AddMonitorW(nameW, Level, (LPBYTE) &mi2w);
1777
1778     HeapFree(GetProcessHeap(), 0, mi2w.pName); 
1779     HeapFree(GetProcessHeap(), 0, mi2w.pEnvironment); 
1780     HeapFree(GetProcessHeap(), 0, mi2w.pDLLName); 
1781
1782     HeapFree(GetProcessHeap(), 0, nameW); 
1783     return (res);
1784 }
1785
1786 /******************************************************************************
1787  *              AddMonitorW        [WINSPOOL.@]
1788  *
1789  * Install a Printmonitor
1790  *
1791  * PARAMS
1792  *  pName       [I] Servername or NULL (local Computer)
1793  *  Level       [I] Structure-Level (Must be 2)
1794  *  pMonitors   [I] PTR to MONITOR_INFO_2
1795  *
1796  * RETURNS
1797  *  Success: TRUE
1798  *  Failure: FALSE
1799  *
1800  * NOTES
1801  *  All Files for the Monitor must already be copied to %winsysdir% ("%SystemRoot%\system32")
1802  *
1803  */
1804 BOOL WINAPI AddMonitorW(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
1805 {
1806     monitor_t * pm = NULL;
1807     LPMONITOR_INFO_2W mi2w;
1808     HKEY    hroot = NULL;
1809     HKEY    hentry = NULL;
1810     DWORD   disposition;
1811     BOOL    res = FALSE;
1812
1813     mi2w = (LPMONITOR_INFO_2W) pMonitors;
1814     TRACE("(%s, %d, %p) :  %s %s %s\n", debugstr_w(pName), Level, pMonitors, 
1815             mi2w ? debugstr_w(mi2w->pName) : NULL,
1816             mi2w ? debugstr_w(mi2w->pEnvironment) : NULL,
1817             mi2w ? debugstr_w(mi2w->pDLLName) : NULL);
1818
1819     if (Level != 2) {
1820         SetLastError(ERROR_INVALID_LEVEL);
1821         return FALSE;
1822     }
1823
1824     /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
1825     if (mi2w == NULL) {
1826         return FALSE;
1827     }
1828
1829     if (pName && (pName[0])) {
1830         FIXME("for server %s not implemented\n", debugstr_w(pName));
1831         SetLastError(ERROR_ACCESS_DENIED);
1832         return FALSE;
1833     }
1834
1835
1836     if (!mi2w->pName || (! mi2w->pName[0])) {
1837         WARN("pName not valid : %s\n", debugstr_w(mi2w->pName));
1838         SetLastError(ERROR_INVALID_PARAMETER);
1839         return FALSE;
1840     }
1841     if (!mi2w->pEnvironment || lstrcmpW(mi2w->pEnvironment, envname_x86W)) {
1842         WARN("Environment %s requested (we support only %s)\n", 
1843                 debugstr_w(mi2w->pEnvironment), debugstr_w(envname_x86W));
1844         SetLastError(ERROR_INVALID_ENVIRONMENT);
1845         return FALSE;
1846     }
1847
1848     if (!mi2w->pDLLName || (! mi2w->pDLLName[0])) {
1849         WARN("pDLLName not valid : %s\n", debugstr_w(mi2w->pDLLName));
1850         SetLastError(ERROR_INVALID_PARAMETER);
1851         return FALSE;
1852     }
1853
1854     /* Load and initialize the monitor. SetLastError() is called on failure */
1855     if ((pm = monitor_load(mi2w->pName, mi2w->pDLLName)) == NULL) {
1856         return FALSE;
1857     }
1858     monitor_unload(pm);
1859
1860     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) != ERROR_SUCCESS) {
1861         ERR("unable to create key %s\n", debugstr_w(MonitorsW));
1862         return FALSE;
1863     }
1864
1865     if(RegCreateKeyExW( hroot, mi2w->pName, 0, NULL, REG_OPTION_NON_VOLATILE,
1866                         KEY_WRITE | KEY_QUERY_VALUE, NULL, &hentry,
1867                         &disposition) == ERROR_SUCCESS) {
1868
1869         /* Some installers set options for the port before calling AddMonitor.
1870            We query the "Driver" entry to verify that the monitor is installed,
1871            before we return an error.
1872            When a user installs two print monitors at the same time with the
1873            same name but with a different driver DLL and a task switch comes
1874            between RegQueryValueExW and RegSetValueExW, a race condition
1875            is possible but silently ignored. */
1876
1877         DWORD   namesize = 0;
1878
1879         if ((disposition == REG_OPENED_EXISTING_KEY) &&
1880             (RegQueryValueExW(hentry, DriverW, NULL, NULL, NULL,
1881                               &namesize) == ERROR_SUCCESS)) {
1882             TRACE("monitor %s already exists\n", debugstr_w(mi2w->pName));
1883             /* NT: ERROR_PRINT_MONITOR_ALREADY_INSTALLED (3006)
1884                9x: ERROR_ALREADY_EXISTS (183) */
1885             SetLastError(ERROR_PRINT_MONITOR_ALREADY_INSTALLED);
1886         }
1887         else
1888         {
1889                INT len;
1890                len = (lstrlenW(mi2w->pDLLName) +1) * sizeof(WCHAR);
1891                res = (RegSetValueExW(hentry, DriverW, 0,
1892                       REG_SZ, (LPBYTE) mi2w->pDLLName, len) == ERROR_SUCCESS);
1893         }
1894         RegCloseKey(hentry);
1895     }
1896
1897     RegCloseKey(hroot);
1898     return (res);
1899 }
1900
1901 /******************************************************************
1902  *              DeletePrinterDriverA        [WINSPOOL.@]
1903  *
1904  */
1905 BOOL WINAPI
1906 DeletePrinterDriverA (LPSTR pName, LPSTR pEnvironment, LPSTR pDriverName)
1907 {
1908     FIXME("(%s,%s,%s):stub\n",debugstr_a(pName),debugstr_a(pEnvironment),
1909           debugstr_a(pDriverName));
1910     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1911     return FALSE;
1912 }
1913
1914 /******************************************************************
1915  *              DeletePrinterDriverW        [WINSPOOL.@]
1916  *
1917  */
1918 BOOL WINAPI
1919 DeletePrinterDriverW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pDriverName)
1920 {
1921     FIXME("(%s,%s,%s):stub\n",debugstr_w(pName),debugstr_w(pEnvironment),
1922           debugstr_w(pDriverName));
1923     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1924     return FALSE;
1925 }
1926
1927 /******************************************************************
1928  *              DeleteMonitorA        [WINSPOOL.@]
1929  *
1930  * See DeleteMonitorW.
1931  *
1932  */
1933 BOOL WINAPI DeleteMonitorA (LPSTR pName, LPSTR pEnvironment, LPSTR pMonitorName)
1934 {
1935     LPWSTR  nameW = NULL;
1936     LPWSTR  EnvironmentW = NULL;
1937     LPWSTR  MonitorNameW = NULL;
1938     BOOL    res;
1939     INT     len;
1940
1941     if (pName) {
1942         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
1943         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1944         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
1945     }
1946
1947     if (pEnvironment) {
1948         len = MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, NULL, 0);
1949         EnvironmentW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1950         MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, EnvironmentW, len);
1951     }
1952     if (pMonitorName) {
1953         len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
1954         MonitorNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1955         MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, MonitorNameW, len);
1956     }
1957
1958     res = DeleteMonitorW(nameW, EnvironmentW, MonitorNameW);
1959
1960     HeapFree(GetProcessHeap(), 0, MonitorNameW); 
1961     HeapFree(GetProcessHeap(), 0, EnvironmentW);
1962     HeapFree(GetProcessHeap(), 0, nameW); 
1963     return (res);
1964 }
1965
1966 /******************************************************************
1967  *              DeleteMonitorW        [WINSPOOL.@]
1968  *
1969  * Delete a specific Printmonitor from a Printing-Environment
1970  *
1971  * PARAMS
1972  *  pName        [I] Servername or NULL (local Computer)
1973  *  pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
1974  *  pMonitorName [I] Name of the Monitor, that should be deleted
1975  *
1976  * RETURNS
1977  *  Success: TRUE
1978  *  Failure: FALSE
1979  *
1980  * NOTES
1981  *  pEnvironment is ignored in Windows for the local Computer.
1982  *
1983  */
1984
1985 BOOL WINAPI DeleteMonitorW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
1986 {
1987     HKEY    hroot = NULL;
1988
1989     TRACE("(%s, %s, %s)\n",debugstr_w(pName),debugstr_w(pEnvironment),
1990            debugstr_w(pMonitorName));
1991
1992     if (pName && (pName[0])) {
1993         FIXME("for server %s not implemented\n", debugstr_w(pName));
1994         SetLastError(ERROR_ACCESS_DENIED);
1995         return FALSE;
1996     }
1997
1998     /*  pEnvironment is ignored in Windows for the local Computer */
1999
2000     if (!pMonitorName || !pMonitorName[0]) {
2001         WARN("pMonitorName %s is invalid\n", debugstr_w(pMonitorName));
2002         SetLastError(ERROR_INVALID_PARAMETER);
2003         return FALSE;
2004     }
2005
2006     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) != ERROR_SUCCESS) {
2007         ERR("unable to create key %s\n", debugstr_w(MonitorsW));
2008         return FALSE;
2009     }
2010
2011     /* change this, when advapi32.dll/RegDeleteTree is implemented */
2012     if(WINSPOOL_SHDeleteKeyW(hroot, pMonitorName) == ERROR_SUCCESS) {
2013         TRACE("monitor %s deleted\n", debugstr_w(pMonitorName));
2014         RegCloseKey(hroot);
2015         return TRUE;
2016     }
2017
2018     WARN("monitor %s does not exists\n", debugstr_w(pMonitorName));
2019     RegCloseKey(hroot);
2020
2021     /* NT: ERROR_UNKNOWN_PRINT_MONITOR (3000), 9x: ERROR_INVALID_PARAMETER (87) */
2022     SetLastError(ERROR_UNKNOWN_PRINT_MONITOR);
2023     return (FALSE);
2024 }
2025
2026 /******************************************************************
2027  *              DeletePortA        [WINSPOOL.@]
2028  *
2029  * See DeletePortW.
2030  *
2031  */
2032 BOOL WINAPI
2033 DeletePortA (LPSTR pName, HWND hWnd, LPSTR pPortName)
2034 {
2035     FIXME("(%s,%p,%s):stub\n",debugstr_a(pName),hWnd,
2036           debugstr_a(pPortName));
2037     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2038     return FALSE;
2039 }
2040
2041 /******************************************************************
2042  *              DeletePortW        [WINSPOOL.@]
2043  *
2044  * Delete a specific Port
2045  *
2046  * PARAMS
2047  *  pName     [I] Servername or NULL (local Computer)
2048  *  hWnd      [I] Handle to parent Window for the Dialog-Box
2049  *  pPortName [I] Name of the Port, that should be deleted
2050  *
2051  * RETURNS
2052  *  Success: TRUE
2053  *  Failure: FALSE
2054  *
2055  * BUGS
2056  *  only a Stub
2057  *
2058  */
2059 BOOL WINAPI
2060 DeletePortW (LPWSTR pName, HWND hWnd, LPWSTR pPortName)
2061 {
2062     FIXME("(%s,%p,%s):stub\n",debugstr_w(pName),hWnd,
2063           debugstr_w(pPortName));
2064     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2065     return FALSE;
2066 }
2067
2068 /******************************************************************************
2069  *    SetPrinterW  [WINSPOOL.@]
2070  */
2071 BOOL WINAPI SetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD Command)
2072 {
2073     FIXME("(%p, %d, %p, %d): stub\n", hPrinter, Level, pPrinter, Command);
2074     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2075     return FALSE;
2076 }
2077
2078 /******************************************************************************
2079  *    WritePrinter  [WINSPOOL.@]
2080  */
2081 BOOL WINAPI WritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
2082 {
2083     opened_printer_t *printer;
2084     BOOL ret = FALSE;
2085
2086     TRACE("(%p, %p, %d, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
2087
2088     EnterCriticalSection(&printer_handles_cs);
2089     printer = get_opened_printer(hPrinter);
2090     if(!printer)
2091     {
2092         SetLastError(ERROR_INVALID_HANDLE);
2093         goto end;
2094     }
2095
2096     if(!printer->doc)
2097     {
2098         SetLastError(ERROR_SPL_NO_STARTDOC);
2099         goto end;
2100     }
2101
2102     ret = WriteFile(printer->doc->hf, pBuf, cbBuf, pcWritten, NULL);
2103 end:
2104     LeaveCriticalSection(&printer_handles_cs);
2105     return ret;
2106 }
2107
2108 /*****************************************************************************
2109  *          AddFormA  [WINSPOOL.@]
2110  */
2111 BOOL WINAPI AddFormA(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
2112 {
2113     FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
2114     return 1;
2115 }
2116
2117 /*****************************************************************************
2118  *          AddFormW  [WINSPOOL.@]
2119  */
2120 BOOL WINAPI AddFormW(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
2121 {
2122     FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
2123     return 1;
2124 }
2125
2126 /*****************************************************************************
2127  *          AddJobA  [WINSPOOL.@]
2128  */
2129 BOOL WINAPI AddJobA(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
2130 {
2131     BOOL ret;
2132     BYTE buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
2133     DWORD needed;
2134
2135     if(Level != 1) {
2136         SetLastError(ERROR_INVALID_LEVEL);
2137         return FALSE;
2138     }
2139
2140     ret = AddJobW(hPrinter, Level, buf, sizeof(buf), &needed);
2141
2142     if(ret) {
2143         ADDJOB_INFO_1W *addjobW = (ADDJOB_INFO_1W*)buf;
2144         DWORD len = WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, NULL, 0, NULL, NULL);
2145         *pcbNeeded = len + sizeof(ADDJOB_INFO_1A);
2146         if(*pcbNeeded > cbBuf) {
2147             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2148             ret = FALSE;
2149         } else {
2150             ADDJOB_INFO_1A *addjobA = (ADDJOB_INFO_1A*)pData;
2151             addjobA->JobId = addjobW->JobId;
2152             addjobA->Path = (char *)(addjobA + 1);
2153             WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, addjobA->Path, len, NULL, NULL);
2154         }
2155     }
2156     return ret;
2157 }
2158
2159 /*****************************************************************************
2160  *          AddJobW  [WINSPOOL.@]
2161  */
2162 BOOL WINAPI AddJobW(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
2163 {
2164     opened_printer_t *printer;
2165     job_t *job;
2166     BOOL ret = FALSE;
2167     static const WCHAR spool_path[] = {'s','p','o','o','l','\\','P','R','I','N','T','E','R','S','\\',0};
2168     static const WCHAR fmtW[] = {'%','s','%','0','5','d','.','S','P','L',0};
2169     WCHAR path[MAX_PATH], filename[MAX_PATH];
2170     DWORD len;
2171     ADDJOB_INFO_1W *addjob;
2172
2173     TRACE("(%p,%d,%p,%d,%p)\n", hPrinter, Level, pData, cbBuf, pcbNeeded);
2174     
2175     EnterCriticalSection(&printer_handles_cs);
2176
2177     printer = get_opened_printer(hPrinter);
2178
2179     if(!printer) {
2180         SetLastError(ERROR_INVALID_HANDLE);
2181         goto end;
2182     }
2183
2184     if(Level != 1) {
2185         SetLastError(ERROR_INVALID_LEVEL);
2186         goto end;
2187     }
2188
2189     job = HeapAlloc(GetProcessHeap(), 0, sizeof(*job));
2190     if(!job)
2191         goto end;
2192
2193     job->job_id = InterlockedIncrement(&next_job_id);
2194
2195     len = GetSystemDirectoryW(path, sizeof(path) / sizeof(WCHAR));
2196     if(path[len - 1] != '\\')
2197         path[len++] = '\\';
2198     memcpy(path + len, spool_path, sizeof(spool_path));    
2199     sprintfW(filename, fmtW, path, job->job_id);
2200
2201     len = strlenW(filename);
2202     job->filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
2203     memcpy(job->filename, filename, (len + 1) * sizeof(WCHAR));
2204     job->document_title = strdupW(default_doc_title);
2205     list_add_tail(&printer->queue->jobs, &job->entry);
2206
2207     *pcbNeeded = (len + 1) * sizeof(WCHAR) + sizeof(*addjob);
2208     if(*pcbNeeded <= cbBuf) {
2209         addjob = (ADDJOB_INFO_1W*)pData;
2210         addjob->JobId = job->job_id;
2211         addjob->Path = (WCHAR *)(addjob + 1);
2212         memcpy(addjob->Path, filename, (len + 1) * sizeof(WCHAR));
2213         ret = TRUE;
2214     } else 
2215         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2216
2217 end:
2218     LeaveCriticalSection(&printer_handles_cs);
2219     return ret;
2220 }
2221
2222 /*****************************************************************************
2223  *          GetPrintProcessorDirectoryA  [WINSPOOL.@]
2224  *
2225  * Return the PATH for the Print-Processors
2226  *
2227  * See GetPrintProcessorDirectoryW.
2228  *
2229  *
2230  */
2231 BOOL WINAPI GetPrintProcessorDirectoryA(LPSTR server, LPSTR env,
2232                                         DWORD level,  LPBYTE Info,
2233                                         DWORD cbBuf,  LPDWORD pcbNeeded)
2234 {
2235     LPWSTR  serverW = NULL;
2236     LPWSTR  envW = NULL;
2237     BOOL    ret;
2238     INT     len;
2239
2240     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_a(server), 
2241           debugstr_a(env), level, Info, cbBuf, pcbNeeded);
2242  
2243
2244     if (server) {
2245         len = MultiByteToWideChar(CP_ACP, 0, server, -1, NULL, 0);
2246         serverW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2247         MultiByteToWideChar(CP_ACP, 0, server, -1, serverW, len);
2248     }
2249
2250     if (env) {
2251         len = MultiByteToWideChar(CP_ACP, 0, env, -1, NULL, 0);
2252         envW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2253         MultiByteToWideChar(CP_ACP, 0, env, -1, envW, len);
2254     }
2255
2256     /* NT requires the buffersize from GetPrintProcessorDirectoryW also
2257        for GetPrintProcessorDirectoryA and WC2MB is done in-place.
2258      */
2259     ret = GetPrintProcessorDirectoryW(serverW, envW, level, Info, 
2260                                       cbBuf, pcbNeeded);
2261
2262     if (ret) ret = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)Info, -1, (LPSTR)Info,
2263                                        cbBuf, NULL, NULL) > 0;
2264
2265
2266     TRACE(" required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
2267     HeapFree(GetProcessHeap(), 0, envW);
2268     HeapFree(GetProcessHeap(), 0, serverW);
2269     return ret;
2270 }
2271
2272 /*****************************************************************************
2273  *          GetPrintProcessorDirectoryW  [WINSPOOL.@]
2274  *
2275  * Return the PATH for the Print-Processors
2276  *
2277  * PARAMS
2278  *   server     [I] Servername (NT only) or NULL (local Computer)
2279  *   env        [I] Printing-Environment (see below) or NULL (Default)
2280  *   level      [I] Structure-Level (must be 1)
2281  *   Info       [O] PTR to Buffer that receives the Result
2282  *   cbBuf      [I] Size of Buffer at "Info"
2283  *   pcbNeeded  [O] PTR to DWORD that receives the size in Bytes used / 
2284  *                  required for the Buffer at "Info"
2285  *
2286  * RETURNS
2287  *   Success: TRUE  and in pcbNeeded the Bytes used in Info
2288  *   Failure: FALSE and in pcbNeeded the Bytes required for Info,
2289  *   if cbBuf is too small
2290  * 
2291  *   Native Values returned in Info on Success:
2292  *|  NT(Windows NT x86):  "%winsysdir%\\spool\\PRTPROCS\\w32x86" 
2293  *|  NT(Windows 4.0):     "%winsysdir%\\spool\\PRTPROCS\\win40" 
2294  *|  win9x(Windows 4.0):  "%winsysdir%" 
2295  *
2296  *   "%winsysdir%" is the Value from GetSystemDirectoryW()
2297  *
2298  * BUGS
2299  *  Only NULL or "" is supported for server
2300  *
2301  */
2302 BOOL WINAPI GetPrintProcessorDirectoryW(LPWSTR server, LPWSTR env,
2303                                         DWORD level,  LPBYTE Info,
2304                                         DWORD cbBuf,  LPDWORD pcbNeeded)
2305 {
2306     DWORD needed;
2307     const printenv_t * env_t;
2308
2309     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(server),
2310             debugstr_w(env), level, Info, cbBuf, pcbNeeded);
2311
2312     if(server != NULL && server[0]) {
2313         FIXME("server not supported: %s\n", debugstr_w(server));
2314         SetLastError(ERROR_INVALID_PARAMETER);
2315         return FALSE;
2316     }
2317
2318     env_t = validate_envW(env);
2319     if(!env_t) return FALSE;  /* environment invalid or unsupported */
2320
2321     if(level != 1) {
2322         WARN("(Level: %d) is ignored in win9x\n", level);
2323         SetLastError(ERROR_INVALID_LEVEL);
2324         return FALSE;
2325     }
2326
2327     /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
2328     needed = GetSystemDirectoryW(NULL, 0);
2329     /* add the Size for the Subdirectories */
2330     needed += lstrlenW(spoolprtprocsW);
2331     needed += lstrlenW(env_t->subdir);
2332     needed *= sizeof(WCHAR);  /* return-value is size in Bytes */
2333
2334     if(pcbNeeded) *pcbNeeded = needed;
2335     TRACE ("required: 0x%x/%d\n", needed, needed);
2336     if (needed > cbBuf) {
2337         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2338         return FALSE;
2339     }
2340     if(pcbNeeded == NULL) {
2341         /* NT: RPC_X_NULL_REF_POINTER, 9x: ignored */
2342         WARN("(pcbNeeded == NULL) is ignored in win9x\n");
2343         SetLastError(RPC_X_NULL_REF_POINTER);
2344         return FALSE;
2345     }
2346     if(Info == NULL) {
2347         /* NT: RPC_X_NULL_REF_POINTER, 9x: ERROR_INVALID_PARAMETER */
2348         SetLastError(RPC_X_NULL_REF_POINTER);
2349         return FALSE;
2350     }
2351     
2352     GetSystemDirectoryW((LPWSTR) Info, cbBuf/sizeof(WCHAR));
2353     /* add the Subdirectories */
2354     lstrcatW((LPWSTR) Info, spoolprtprocsW);
2355     lstrcatW((LPWSTR) Info, env_t->subdir);
2356     TRACE(" => %s\n", debugstr_w((LPWSTR) Info));
2357     return TRUE;
2358 }
2359
2360 /*****************************************************************************
2361  *          WINSPOOL_OpenDriverReg [internal]
2362  *
2363  * opens the registry for the printer drivers depending on the given input
2364  * variable pEnvironment
2365  *
2366  * RETURNS:
2367  *    the opened hkey on success
2368  *    NULL on error
2369  */
2370 static HKEY WINSPOOL_OpenDriverReg( LPVOID pEnvironment, BOOL unicode)
2371 {   
2372     HKEY  retval = NULL;
2373     LPWSTR buffer;
2374     const printenv_t * env;
2375
2376     TRACE("(%s, %d)\n",
2377           (unicode) ? debugstr_w(pEnvironment) : debugstr_a(pEnvironment), unicode);
2378
2379     if (!pEnvironment || unicode) {
2380         /* pEnvironment was NULL or an Unicode-String: use it direct */
2381         env = validate_envW(pEnvironment);
2382     }
2383     else
2384     {
2385         /* pEnvironment was an ANSI-String: convert to unicode first */
2386         LPWSTR  buffer;
2387         INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, NULL, 0);
2388         buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2389         if (buffer) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, buffer, len);
2390         env = validate_envW(buffer);
2391         HeapFree(GetProcessHeap(), 0, buffer);
2392     }
2393     if (!env) return NULL;
2394
2395     buffer = HeapAlloc( GetProcessHeap(), 0,
2396                 (strlenW(DriversW) + strlenW(env->envname) + 
2397                  strlenW(env->versionregpath) + 1) * sizeof(WCHAR));
2398     if(buffer) {
2399         wsprintfW(buffer, DriversW, env->envname, env->versionregpath);
2400         RegCreateKeyW(HKEY_LOCAL_MACHINE, buffer, &retval);
2401         HeapFree(GetProcessHeap(), 0, buffer);
2402     }
2403     return retval;
2404 }
2405
2406 /*****************************************************************************
2407  *          AddPrinterW  [WINSPOOL.@]
2408  */
2409 HANDLE WINAPI AddPrinterW(LPWSTR pName, DWORD Level, LPBYTE pPrinter)
2410 {
2411     PRINTER_INFO_2W *pi = (PRINTER_INFO_2W *) pPrinter;
2412     LPDEVMODEA dmA;
2413     LPDEVMODEW dmW;
2414     HANDLE retval;
2415     HKEY hkeyPrinter, hkeyPrinters, hkeyDriver, hkeyDrivers;
2416     LONG size;
2417
2418     TRACE("(%s,%d,%p)\n", debugstr_w(pName), Level, pPrinter);
2419
2420     if(pName != NULL) {
2421         ERR("pName = %s - unsupported\n", debugstr_w(pName));
2422         SetLastError(ERROR_INVALID_PARAMETER);
2423         return 0;
2424     }
2425     if(Level != 2) {
2426         ERR("Level = %d, unsupported!\n", Level);
2427         SetLastError(ERROR_INVALID_LEVEL);
2428         return 0;
2429     }
2430     if(!pPrinter) {
2431         SetLastError(ERROR_INVALID_PARAMETER);
2432         return 0;
2433     }
2434     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
2435        ERROR_SUCCESS) {
2436         ERR("Can't create Printers key\n");
2437         return 0;
2438     }
2439     if(!RegOpenKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter)) {
2440         if (!RegQueryValueA(hkeyPrinter,"Attributes",NULL,NULL)) {
2441             SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
2442             RegCloseKey(hkeyPrinter);
2443             RegCloseKey(hkeyPrinters);
2444             return 0;
2445         }
2446         RegCloseKey(hkeyPrinter);
2447     }
2448     hkeyDrivers = WINSPOOL_OpenDriverReg( NULL, TRUE);
2449     if(!hkeyDrivers) {
2450         ERR("Can't create Drivers key\n");
2451         RegCloseKey(hkeyPrinters);
2452         return 0;
2453     }
2454     if(RegOpenKeyW(hkeyDrivers, pi->pDriverName, &hkeyDriver) !=
2455        ERROR_SUCCESS) {
2456         WARN("Can't find driver %s\n", debugstr_w(pi->pDriverName));
2457         RegCloseKey(hkeyPrinters);
2458         RegCloseKey(hkeyDrivers);
2459         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER);
2460         return 0;
2461     }
2462     RegCloseKey(hkeyDriver);
2463     RegCloseKey(hkeyDrivers);
2464
2465     if(lstrcmpiW(pi->pPrintProcessor, WinPrintW)) {  /* FIXME */
2466         FIXME("Can't find processor %s\n", debugstr_w(pi->pPrintProcessor));
2467         SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR);
2468         RegCloseKey(hkeyPrinters);
2469         return 0;
2470     }
2471
2472     if(RegCreateKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter) !=
2473        ERROR_SUCCESS) {
2474         FIXME("Can't create printer %s\n", debugstr_w(pi->pPrinterName));
2475         SetLastError(ERROR_INVALID_PRINTER_NAME);
2476         RegCloseKey(hkeyPrinters);
2477         return 0;
2478     }
2479     RegSetValueExA(hkeyPrinter, "Attributes", 0, REG_DWORD,
2480                    (LPBYTE)&pi->Attributes, sizeof(DWORD));
2481     set_reg_szW(hkeyPrinter, DatatypeW, pi->pDatatype);
2482
2483     /* See if we can load the driver.  We may need the devmode structure anyway
2484      *
2485      * FIXME:
2486      * Note that DocumentPropertiesW will briefly try to open the printer we
2487      * just create to find a DEVMODEA struct (it will use the WINEPS default
2488      * one in case it is not there, so we are ok).
2489      */
2490     size = DocumentPropertiesW(0, 0, pi->pPrinterName, NULL, NULL, 0);
2491
2492     if(size < 0) {
2493         FIXME("DocumentPropertiesW on printer '%s' fails\n", debugstr_w(pi->pPrinterName));
2494         size = sizeof(DEVMODEW);
2495     }
2496     if(pi->pDevMode)
2497         dmW = pi->pDevMode;
2498     else 
2499     {
2500         dmW = HeapAlloc(GetProcessHeap(), 0, size);
2501         ZeroMemory(dmW,size);
2502         dmW->dmSize = size;
2503         if (0>DocumentPropertiesW(0,0,pi->pPrinterName,dmW,NULL,DM_OUT_BUFFER))
2504         {
2505             WARN("DocumentPropertiesW on printer '%s' failed!\n", debugstr_w(pi->pPrinterName));
2506             HeapFree(GetProcessHeap(),0,dmW);
2507             dmW=NULL;
2508         }
2509         else
2510         {
2511             /* set devmode to printer name */
2512             lstrcpynW(dmW->dmDeviceName, pi->pPrinterName, CCHDEVICENAME);
2513         }
2514     }
2515
2516     /* Write DEVMODEA not DEVMODEW into reg.  This is what win9x does
2517        and we support these drivers.  NT writes DEVMODEW so somehow
2518        we'll need to distinguish between these when we support NT
2519        drivers */
2520     if (dmW)
2521     {
2522         dmA = DEVMODEdupWtoA(GetProcessHeap(), dmW);
2523         RegSetValueExA(hkeyPrinter, "Default DevMode", 0, REG_BINARY, 
2524                        (LPBYTE)dmA, dmA->dmSize + dmA->dmDriverExtra);
2525         HeapFree(GetProcessHeap(), 0, dmA);
2526         if(!pi->pDevMode)
2527             HeapFree(GetProcessHeap(), 0, dmW);
2528     }
2529     set_reg_szW(hkeyPrinter, DescriptionW, pi->pComment);
2530     set_reg_szW(hkeyPrinter, LocationW, pi->pLocation);
2531     set_reg_szW(hkeyPrinter, NameW, pi->pPrinterName);
2532     set_reg_szW(hkeyPrinter, ParametersW, pi->pParameters);
2533
2534     set_reg_szW(hkeyPrinter, PortW, pi->pPortName);
2535     set_reg_szW(hkeyPrinter, Print_ProcessorW, pi->pPrintProcessor);
2536     set_reg_szW(hkeyPrinter, Printer_DriverW, pi->pDriverName);
2537     RegSetValueExA(hkeyPrinter, "Priority", 0, REG_DWORD,
2538                    (LPBYTE)&pi->Priority, sizeof(DWORD));
2539     set_reg_szW(hkeyPrinter, Separator_FileW, pi->pSepFile);
2540     set_reg_szW(hkeyPrinter, Share_NameW, pi->pShareName);
2541     RegSetValueExA(hkeyPrinter, "StartTime", 0, REG_DWORD,
2542                    (LPBYTE)&pi->StartTime, sizeof(DWORD));
2543     RegSetValueExA(hkeyPrinter, "Status", 0, REG_DWORD,
2544                    (LPBYTE)&pi->Status, sizeof(DWORD));
2545     RegSetValueExA(hkeyPrinter, "UntilTime", 0, REG_DWORD,
2546                    (LPBYTE)&pi->UntilTime, sizeof(DWORD));
2547
2548     RegCloseKey(hkeyPrinter);
2549     RegCloseKey(hkeyPrinters);
2550     if(!OpenPrinterW(pi->pPrinterName, &retval, NULL)) {
2551         ERR("OpenPrinter failing\n");
2552         return 0;
2553     }
2554     return retval;
2555 }
2556
2557 /*****************************************************************************
2558  *          AddPrinterA  [WINSPOOL.@]
2559  */
2560 HANDLE WINAPI AddPrinterA(LPSTR pName, DWORD Level, LPBYTE pPrinter)
2561 {
2562     UNICODE_STRING pNameW;
2563     PWSTR pwstrNameW;
2564     PRINTER_INFO_2W *piW;
2565     PRINTER_INFO_2A *piA = (PRINTER_INFO_2A*)pPrinter;
2566     HANDLE ret;
2567
2568     TRACE("(%s,%d,%p): stub\n", debugstr_a(pName), Level, pPrinter);
2569     if(Level != 2) {
2570         ERR("Level = %d, unsupported!\n", Level);
2571         SetLastError(ERROR_INVALID_LEVEL);
2572         return 0;
2573     }
2574     pwstrNameW = asciitounicode(&pNameW,pName);
2575     piW = PRINTER_INFO_2AtoW(GetProcessHeap(), piA);
2576
2577     ret = AddPrinterW(pwstrNameW, Level, (LPBYTE)piW);
2578
2579     FREE_PRINTER_INFO_2W(GetProcessHeap(), piW);
2580     RtlFreeUnicodeString(&pNameW);
2581     return ret;
2582 }
2583
2584
2585 /*****************************************************************************
2586  *          ClosePrinter  [WINSPOOL.@]
2587  */
2588 BOOL WINAPI ClosePrinter(HANDLE hPrinter)
2589 {
2590     UINT_PTR i = (UINT_PTR)hPrinter;
2591     opened_printer_t *printer = NULL;
2592     BOOL ret = FALSE;
2593
2594     TRACE("Handle %p\n", hPrinter);
2595
2596     EnterCriticalSection(&printer_handles_cs);
2597
2598     if ((i > 0) && (i <= nb_printer_handles))
2599         printer = printer_handles[i - 1];
2600
2601     if(printer)
2602     {
2603         struct list *cursor, *cursor2;
2604
2605         if(printer->doc)
2606             EndDocPrinter(hPrinter);
2607
2608         if(InterlockedDecrement(&printer->queue->ref) == 0)
2609         {
2610             LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
2611             {
2612                 job_t *job = LIST_ENTRY(cursor, job_t, entry);
2613                 ScheduleJob(hPrinter, job->job_id);
2614             }
2615             HeapFree(GetProcessHeap(), 0, printer->queue);
2616         }
2617         HeapFree(GetProcessHeap(), 0, printer->name);
2618         HeapFree(GetProcessHeap(), 0, printer);
2619         printer_handles[i - 1] = NULL;
2620         ret = TRUE;
2621     }
2622     LeaveCriticalSection(&printer_handles_cs);
2623     return ret;
2624 }
2625
2626 /*****************************************************************************
2627  *          DeleteFormA  [WINSPOOL.@]
2628  */
2629 BOOL WINAPI DeleteFormA(HANDLE hPrinter, LPSTR pFormName)
2630 {
2631     FIXME("(%p,%s): stub\n", hPrinter, pFormName);
2632     return 1;
2633 }
2634
2635 /*****************************************************************************
2636  *          DeleteFormW  [WINSPOOL.@]
2637  */
2638 BOOL WINAPI DeleteFormW(HANDLE hPrinter, LPWSTR pFormName)
2639 {
2640     FIXME("(%p,%s): stub\n", hPrinter, debugstr_w(pFormName));
2641     return 1;
2642 }
2643
2644 /*****************************************************************************
2645  *   WINSPOOL_SHRegDeleteKey
2646  *
2647  *   Recursively delete subkeys.
2648  *   Cut & paste from shlwapi.
2649  * 
2650  */
2651 static DWORD WINSPOOL_SHDeleteKeyW(HKEY hKey, LPCWSTR lpszSubKey)
2652 {
2653   DWORD dwRet, dwKeyCount = 0, dwMaxSubkeyLen = 0, dwSize, i;
2654   WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
2655   HKEY hSubKey = 0;
2656
2657   dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
2658   if(!dwRet)
2659   {
2660     /* Find how many subkeys there are */
2661     dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwKeyCount,
2662                              &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
2663     if(!dwRet)
2664     {
2665       dwMaxSubkeyLen++;
2666       if (dwMaxSubkeyLen > sizeof(szNameBuf)/sizeof(WCHAR))
2667         /* Name too big: alloc a buffer for it */
2668         lpszName = HeapAlloc(GetProcessHeap(), 0, dwMaxSubkeyLen*sizeof(WCHAR));
2669
2670       if(!lpszName)
2671         dwRet = ERROR_NOT_ENOUGH_MEMORY;
2672       else
2673       {
2674         /* Recursively delete all the subkeys */
2675         for(i = 0; i < dwKeyCount && !dwRet; i++)
2676         {
2677           dwSize = dwMaxSubkeyLen;
2678           dwRet = RegEnumKeyExW(hSubKey, i, lpszName, &dwSize, NULL, NULL, NULL, NULL);
2679           if(!dwRet)
2680             dwRet = WINSPOOL_SHDeleteKeyW(hSubKey, lpszName);
2681         }
2682
2683         if (lpszName != szNameBuf)
2684           HeapFree(GetProcessHeap(), 0, lpszName); /* Free buffer if allocated */
2685       }
2686     }
2687
2688     RegCloseKey(hSubKey);
2689     if(!dwRet)
2690       dwRet = RegDeleteKeyW(hKey, lpszSubKey);
2691   }
2692   return dwRet;
2693 }
2694
2695 /*****************************************************************************
2696  *          DeletePrinter  [WINSPOOL.@]
2697  */
2698 BOOL WINAPI DeletePrinter(HANDLE hPrinter)
2699 {
2700     LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
2701     HKEY hkeyPrinters, hkey;
2702
2703     if(!lpNameW) {
2704         SetLastError(ERROR_INVALID_HANDLE);
2705         return FALSE;
2706     }
2707     if(RegOpenKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
2708         WINSPOOL_SHDeleteKeyW(hkeyPrinters, lpNameW);
2709         RegCloseKey(hkeyPrinters);
2710     }
2711     WriteProfileStringW(devicesW, lpNameW, NULL);
2712     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
2713         RegDeleteValueW(hkey, lpNameW);
2714         RegCloseKey(hkey);
2715     }
2716     return TRUE;
2717 }
2718
2719 /*****************************************************************************
2720  *          SetPrinterA  [WINSPOOL.@]
2721  */
2722 BOOL WINAPI SetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
2723                            DWORD Command)
2724 {
2725     FIXME("(%p,%d,%p,%d): stub\n",hPrinter,Level,pPrinter,Command);
2726     return FALSE;
2727 }
2728
2729 /*****************************************************************************
2730  *          SetJobA  [WINSPOOL.@]
2731  */
2732 BOOL WINAPI SetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level,
2733                     LPBYTE pJob, DWORD Command)
2734 {
2735     BOOL ret;
2736     LPBYTE JobW;
2737     UNICODE_STRING usBuffer;
2738
2739     TRACE("(%p, %d, %d, %p, %d)\n",hPrinter, JobId, Level, pJob, Command);
2740
2741     /* JobId, pPrinterName, pMachineName, pDriverName, Size, Submitted, Time and TotalPages
2742        are all ignored by SetJob, so we don't bother copying them */
2743     switch(Level)
2744     {
2745     case 0:
2746         JobW = NULL;
2747         break;
2748     case 1:
2749       {
2750         JOB_INFO_1W *info1W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info1W));
2751         JOB_INFO_1A *info1A = (JOB_INFO_1A*)pJob;
2752
2753         JobW = (LPBYTE)info1W;
2754         info1W->pUserName = asciitounicode(&usBuffer, info1A->pUserName);
2755         info1W->pDocument = asciitounicode(&usBuffer, info1A->pDocument);
2756         info1W->pDatatype = asciitounicode(&usBuffer, info1A->pDatatype);
2757         info1W->pStatus = asciitounicode(&usBuffer, info1A->pStatus);
2758         info1W->Status = info1A->Status;
2759         info1W->Priority = info1A->Priority;
2760         info1W->Position = info1A->Position;
2761         info1W->PagesPrinted = info1A->PagesPrinted;
2762         break;
2763       }
2764     case 2:
2765       {
2766         JOB_INFO_2W *info2W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info2W));
2767         JOB_INFO_2A *info2A = (JOB_INFO_2A*)pJob;
2768
2769         JobW = (LPBYTE)info2W;
2770         info2W->pUserName = asciitounicode(&usBuffer, info2A->pUserName);
2771         info2W->pDocument = asciitounicode(&usBuffer, info2A->pDocument);
2772         info2W->pNotifyName = asciitounicode(&usBuffer, info2A->pNotifyName);
2773         info2W->pDatatype = asciitounicode(&usBuffer, info2A->pDatatype);
2774         info2W->pPrintProcessor = asciitounicode(&usBuffer, info2A->pPrintProcessor);
2775         info2W->pParameters = asciitounicode(&usBuffer, info2A->pParameters);
2776         info2W->pDevMode = info2A->pDevMode ? GdiConvertToDevmodeW(info2A->pDevMode) : NULL;
2777         info2W->pStatus = asciitounicode(&usBuffer, info2A->pStatus);
2778         info2W->pSecurityDescriptor = info2A->pSecurityDescriptor;
2779         info2W->Status = info2A->Status;
2780         info2W->Priority = info2A->Priority;
2781         info2W->Position = info2A->Position;
2782         info2W->StartTime = info2A->StartTime;
2783         info2W->UntilTime = info2A->UntilTime;
2784         info2W->PagesPrinted = info2A->PagesPrinted;
2785         break;
2786       }
2787     case 3:
2788         JobW = HeapAlloc(GetProcessHeap(), 0, sizeof(JOB_INFO_3));
2789         memcpy(JobW, pJob, sizeof(JOB_INFO_3));
2790         break;
2791     default:
2792         SetLastError(ERROR_INVALID_LEVEL);
2793         return FALSE;
2794     }
2795
2796     ret = SetJobW(hPrinter, JobId, Level, JobW, Command);
2797
2798     switch(Level)
2799     {
2800     case 1:
2801       {
2802         JOB_INFO_1W *info1W = (JOB_INFO_1W*)JobW;
2803         HeapFree(GetProcessHeap(), 0, info1W->pUserName);
2804         HeapFree(GetProcessHeap(), 0, info1W->pDocument); 
2805         HeapFree(GetProcessHeap(), 0, info1W->pDatatype);
2806         HeapFree(GetProcessHeap(), 0, info1W->pStatus);
2807         break;
2808       }
2809     case 2:
2810       {
2811         JOB_INFO_2W *info2W = (JOB_INFO_2W*)JobW;
2812         HeapFree(GetProcessHeap(), 0, info2W->pUserName);
2813         HeapFree(GetProcessHeap(), 0, info2W->pDocument); 
2814         HeapFree(GetProcessHeap(), 0, info2W->pNotifyName);
2815         HeapFree(GetProcessHeap(), 0, info2W->pDatatype);
2816         HeapFree(GetProcessHeap(), 0, info2W->pPrintProcessor);
2817         HeapFree(GetProcessHeap(), 0, info2W->pParameters);
2818         HeapFree(GetProcessHeap(), 0, info2W->pDevMode);
2819         HeapFree(GetProcessHeap(), 0, info2W->pStatus);
2820         break;
2821       }
2822     }
2823     HeapFree(GetProcessHeap(), 0, JobW);
2824
2825     return ret;
2826 }
2827
2828 /*****************************************************************************
2829  *          SetJobW  [WINSPOOL.@]
2830  */
2831 BOOL WINAPI SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level,
2832                     LPBYTE pJob, DWORD Command)
2833 {
2834     BOOL ret = FALSE;
2835     job_t *job;
2836
2837     TRACE("(%p, %d, %d, %p, %d)\n", hPrinter, JobId, Level, pJob, Command);
2838     FIXME("Ignoring everything other than document title\n");
2839
2840     EnterCriticalSection(&printer_handles_cs);
2841     job = get_job(hPrinter, JobId);
2842     if(!job)
2843         goto end;
2844
2845     switch(Level)
2846     {
2847     case 0:
2848         break;
2849     case 1:
2850       {
2851         JOB_INFO_1W *info1 = (JOB_INFO_1W*)pJob;
2852         HeapFree(GetProcessHeap(), 0, job->document_title);
2853         job->document_title = strdupW(info1->pDocument);
2854         break;
2855       }
2856     case 2:
2857       {
2858         JOB_INFO_2W *info2 = (JOB_INFO_2W*)pJob;
2859         HeapFree(GetProcessHeap(), 0, job->document_title);
2860         job->document_title = strdupW(info2->pDocument);
2861         break;
2862       }
2863     case 3:
2864         break;
2865     default:
2866         SetLastError(ERROR_INVALID_LEVEL);
2867         goto end;
2868     }
2869     ret = TRUE;
2870 end:
2871     LeaveCriticalSection(&printer_handles_cs);
2872     return ret;
2873 }
2874
2875 /*****************************************************************************
2876  *          EndDocPrinter  [WINSPOOL.@]
2877  */
2878 BOOL WINAPI EndDocPrinter(HANDLE hPrinter)
2879 {
2880     opened_printer_t *printer;
2881     BOOL ret = FALSE;
2882     TRACE("(%p)\n", hPrinter);
2883
2884     EnterCriticalSection(&printer_handles_cs);
2885
2886     printer = get_opened_printer(hPrinter);
2887     if(!printer)
2888     {
2889         SetLastError(ERROR_INVALID_HANDLE);
2890         goto end;
2891     }
2892
2893     if(!printer->doc)    
2894     {
2895         SetLastError(ERROR_SPL_NO_STARTDOC);
2896         goto end;
2897     }
2898
2899     CloseHandle(printer->doc->hf);
2900     ScheduleJob(hPrinter, printer->doc->job_id);
2901     HeapFree(GetProcessHeap(), 0, printer->doc);
2902     printer->doc = NULL;
2903     ret = TRUE;
2904 end:
2905     LeaveCriticalSection(&printer_handles_cs);
2906     return ret;
2907 }
2908
2909 /*****************************************************************************
2910  *          EndPagePrinter  [WINSPOOL.@]
2911  */
2912 BOOL WINAPI EndPagePrinter(HANDLE hPrinter)
2913 {
2914     FIXME("(%p): stub\n", hPrinter);
2915     return TRUE;
2916 }
2917
2918 /*****************************************************************************
2919  *          StartDocPrinterA  [WINSPOOL.@]
2920  */
2921 DWORD WINAPI StartDocPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
2922 {
2923     UNICODE_STRING usBuffer;
2924     DOC_INFO_2W doc2W;
2925     DOC_INFO_2A *doc2 = (DOC_INFO_2A*)pDocInfo;
2926     DWORD ret;
2927
2928     /* DOC_INFO_1, 2 and 3 all have the strings in the same place with either two (DOC_INFO_2)
2929        or one (DOC_INFO_3) extra DWORDs */
2930
2931     switch(Level) {
2932     case 2:
2933         doc2W.JobId = doc2->JobId;
2934         /* fall through */
2935     case 3:
2936         doc2W.dwMode = doc2->dwMode;
2937         /* fall through */
2938     case 1:
2939         doc2W.pDocName = asciitounicode(&usBuffer, doc2->pDocName);
2940         doc2W.pOutputFile = asciitounicode(&usBuffer, doc2->pOutputFile);
2941         doc2W.pDatatype = asciitounicode(&usBuffer, doc2->pDatatype);
2942         break;
2943
2944     default:
2945         SetLastError(ERROR_INVALID_LEVEL);
2946         return FALSE;
2947     }
2948
2949     ret = StartDocPrinterW(hPrinter, Level, (LPBYTE)&doc2W);
2950
2951     HeapFree(GetProcessHeap(), 0, doc2W.pDatatype);
2952     HeapFree(GetProcessHeap(), 0, doc2W.pOutputFile);
2953     HeapFree(GetProcessHeap(), 0, doc2W.pDocName);
2954
2955     return ret;
2956 }
2957
2958 /*****************************************************************************
2959  *          StartDocPrinterW  [WINSPOOL.@]
2960  */
2961 DWORD WINAPI StartDocPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
2962 {
2963     DOC_INFO_2W *doc = (DOC_INFO_2W *)pDocInfo;
2964     opened_printer_t *printer;
2965     BYTE addjob_buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
2966     ADDJOB_INFO_1W *addjob = (ADDJOB_INFO_1W*) addjob_buf;
2967     JOB_INFO_1W job_info;
2968     DWORD needed, ret = 0;
2969     HANDLE hf;
2970     WCHAR *filename;
2971
2972     TRACE("(hPrinter = %p, Level = %d, pDocInfo = %p {pDocName = %s, pOutputFile = %s, pDatatype = %s}):\n",
2973           hPrinter, Level, doc, debugstr_w(doc->pDocName), debugstr_w(doc->pOutputFile),
2974           debugstr_w(doc->pDatatype));
2975
2976     if(Level < 1 || Level > 3)
2977     {
2978         SetLastError(ERROR_INVALID_LEVEL);
2979         return 0;
2980     }
2981
2982     EnterCriticalSection(&printer_handles_cs);
2983     printer = get_opened_printer(hPrinter);
2984     if(!printer)
2985     {
2986         SetLastError(ERROR_INVALID_HANDLE);
2987         goto end;
2988     }
2989
2990     if(printer->doc)
2991     {
2992         SetLastError(ERROR_INVALID_PRINTER_STATE);
2993         goto end;
2994     }
2995
2996     /* Even if we're printing to a file we still add a print job, we'll
2997        just ignore the spool file name */
2998
2999     if(!AddJobW(hPrinter, 1, addjob_buf, sizeof(addjob_buf), &needed))
3000     {
3001         ERR("AddJob failed gle %08x\n", GetLastError());
3002         goto end;
3003     }
3004
3005     if(doc->pOutputFile)
3006         filename = doc->pOutputFile;
3007     else
3008         filename = addjob->Path;
3009
3010     hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3011     if(hf == INVALID_HANDLE_VALUE)
3012         goto end;
3013
3014     memset(&job_info, 0, sizeof(job_info));
3015     job_info.pDocument = doc->pDocName;
3016     SetJobW(hPrinter, addjob->JobId, 1, (LPBYTE)&job_info, 0);
3017
3018     printer->doc = HeapAlloc(GetProcessHeap(), 0, sizeof(*printer->doc));
3019     printer->doc->hf = hf;
3020     ret = printer->doc->job_id = addjob->JobId;
3021 end:
3022     LeaveCriticalSection(&printer_handles_cs);
3023
3024     return ret;
3025 }
3026
3027 /*****************************************************************************
3028  *          StartPagePrinter  [WINSPOOL.@]
3029  */
3030 BOOL WINAPI StartPagePrinter(HANDLE hPrinter)
3031 {
3032     FIXME("(%p): stub\n", hPrinter);
3033     return TRUE;
3034 }
3035
3036 /*****************************************************************************
3037  *          GetFormA  [WINSPOOL.@]
3038  */
3039 BOOL WINAPI GetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
3040                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
3041 {
3042     FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,pFormName,
3043          Level,pForm,cbBuf,pcbNeeded);
3044     return FALSE;
3045 }
3046
3047 /*****************************************************************************
3048  *          GetFormW  [WINSPOOL.@]
3049  */
3050 BOOL WINAPI GetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
3051                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
3052 {
3053     FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,
3054           debugstr_w(pFormName),Level,pForm,cbBuf,pcbNeeded);
3055     return FALSE;
3056 }
3057
3058 /*****************************************************************************
3059  *          SetFormA  [WINSPOOL.@]
3060  */
3061 BOOL WINAPI SetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
3062                         LPBYTE pForm)
3063 {
3064     FIXME("(%p,%s,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
3065     return FALSE;
3066 }
3067
3068 /*****************************************************************************
3069  *          SetFormW  [WINSPOOL.@]
3070  */
3071 BOOL WINAPI SetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
3072                         LPBYTE pForm)
3073 {
3074     FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
3075     return FALSE;
3076 }
3077
3078 /*****************************************************************************
3079  *          ReadPrinter  [WINSPOOL.@]
3080  */
3081 BOOL WINAPI ReadPrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf,
3082                            LPDWORD pNoBytesRead)
3083 {
3084     FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pBuf,cbBuf,pNoBytesRead);
3085     return FALSE;
3086 }
3087
3088 /*****************************************************************************
3089  *          ResetPrinterA  [WINSPOOL.@]
3090  */
3091 BOOL WINAPI ResetPrinterA(HANDLE hPrinter, LPPRINTER_DEFAULTSA pDefault)
3092 {
3093     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
3094     return FALSE;
3095 }
3096
3097 /*****************************************************************************
3098  *          ResetPrinterW  [WINSPOOL.@]
3099  */
3100 BOOL WINAPI ResetPrinterW(HANDLE hPrinter, LPPRINTER_DEFAULTSW pDefault)
3101 {
3102     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
3103     return FALSE;
3104 }
3105
3106 /*****************************************************************************
3107  *    WINSPOOL_GetDWORDFromReg
3108  *
3109  * Return DWORD associated with ValueName from hkey.
3110  */
3111 static DWORD WINSPOOL_GetDWORDFromReg(HKEY hkey, LPCSTR ValueName)
3112 {
3113     DWORD sz = sizeof(DWORD), type, value = 0;
3114     LONG ret;
3115
3116     ret = RegQueryValueExA(hkey, ValueName, 0, &type, (LPBYTE)&value, &sz);
3117
3118     if(ret != ERROR_SUCCESS) {
3119         WARN("Got ret = %d on name %s\n", ret, ValueName);
3120         return 0;
3121     }
3122     if(type != REG_DWORD) {
3123         ERR("Got type %d\n", type);
3124         return 0;
3125     }
3126     return value;
3127 }
3128
3129 /*****************************************************************************
3130  *    WINSPOOL_GetStringFromReg
3131  *
3132  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
3133  * String is stored either as unicode or ascii.
3134  * Bit of a hack here to get the ValueName if we want ascii.
3135  */
3136 static BOOL WINSPOOL_GetStringFromReg(HKEY hkey, LPCWSTR ValueName, LPBYTE ptr,
3137                                       DWORD buflen, DWORD *needed,
3138                                       BOOL unicode)
3139 {
3140     DWORD sz = buflen, type;
3141     LONG ret;
3142
3143     if(unicode)
3144         ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3145     else {
3146         LPSTR ValueNameA = HEAP_strdupWtoA(GetProcessHeap(),0,ValueName);
3147         ret = RegQueryValueExA(hkey, ValueNameA, 0, &type, ptr, &sz);
3148         HeapFree(GetProcessHeap(),0,ValueNameA);
3149     }
3150     if(ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) {
3151         WARN("Got ret = %d\n", ret);
3152         *needed = 0;
3153         return FALSE;
3154     }
3155     /* add space for terminating '\0' */
3156     sz += unicode ? sizeof(WCHAR) : 1;
3157     *needed = sz;
3158
3159     if (ptr)
3160         TRACE("%s: %s\n", debugstr_w(ValueName), unicode ? debugstr_w((LPCWSTR)ptr) : debugstr_a((LPCSTR)ptr));
3161
3162     return TRUE;
3163 }
3164
3165 /*****************************************************************************
3166  *    WINSPOOL_GetDefaultDevMode
3167  *
3168  * Get a default DevMode values for wineps.
3169  * FIXME - use ppd.
3170  */
3171
3172 static void WINSPOOL_GetDefaultDevMode(
3173         LPBYTE ptr,
3174         DWORD buflen, DWORD *needed,
3175         BOOL unicode)
3176 {
3177     DEVMODEA    dm;
3178     static const char szwps[] = "wineps.drv";
3179
3180         /* fill default DEVMODE - should be read from ppd... */
3181         ZeroMemory( &dm, sizeof(dm) );
3182         memcpy(dm.dmDeviceName,szwps,sizeof szwps);
3183         dm.dmSpecVersion = DM_SPECVERSION;
3184         dm.dmDriverVersion = 1;
3185         dm.dmSize = sizeof(DEVMODEA);
3186         dm.dmDriverExtra = 0;
3187         dm.dmFields =
3188                 DM_ORIENTATION | DM_PAPERSIZE |
3189                 DM_PAPERLENGTH | DM_PAPERWIDTH |
3190                 DM_SCALE |
3191                 DM_COPIES |
3192                 DM_DEFAULTSOURCE | DM_PRINTQUALITY |
3193                 DM_YRESOLUTION | DM_TTOPTION;
3194
3195         dm.u1.s1.dmOrientation = DMORIENT_PORTRAIT;
3196         dm.u1.s1.dmPaperSize = DMPAPER_A4;
3197         dm.u1.s1.dmPaperLength = 2970;
3198         dm.u1.s1.dmPaperWidth = 2100;
3199
3200         dm.dmScale = 100;
3201         dm.dmCopies = 1;
3202         dm.dmDefaultSource = DMBIN_AUTO;
3203         dm.dmPrintQuality = DMRES_MEDIUM;
3204         /* dm.dmColor */
3205         /* dm.dmDuplex */
3206         dm.dmYResolution = 300; /* 300dpi */
3207         dm.dmTTOption = DMTT_BITMAP;
3208         /* dm.dmCollate */
3209         /* dm.dmFormName */
3210         /* dm.dmLogPixels */
3211         /* dm.dmBitsPerPel */
3212         /* dm.dmPelsWidth */
3213         /* dm.dmPelsHeight */
3214         /* dm.dmDisplayFlags */
3215         /* dm.dmDisplayFrequency */
3216         /* dm.dmICMMethod */
3217         /* dm.dmICMIntent */
3218         /* dm.dmMediaType */
3219         /* dm.dmDitherType */
3220         /* dm.dmReserved1 */
3221         /* dm.dmReserved2 */
3222         /* dm.dmPanningWidth */
3223         /* dm.dmPanningHeight */
3224
3225     if(unicode) {
3226         if(buflen >= sizeof(DEVMODEW)) {
3227             DEVMODEW *pdmW = GdiConvertToDevmodeW(&dm);
3228             memcpy(ptr, pdmW, sizeof(DEVMODEW));
3229             HeapFree(GetProcessHeap(),0,pdmW);
3230         }
3231         *needed = sizeof(DEVMODEW);
3232     }
3233     else
3234     {
3235         if(buflen >= sizeof(DEVMODEA)) {
3236             memcpy(ptr, &dm, sizeof(DEVMODEA));
3237         }
3238         *needed = sizeof(DEVMODEA);
3239     }
3240 }
3241
3242 /*****************************************************************************
3243  *    WINSPOOL_GetDevModeFromReg
3244  *
3245  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
3246  * DevMode is stored either as unicode or ascii.
3247  */
3248 static BOOL WINSPOOL_GetDevModeFromReg(HKEY hkey, LPCWSTR ValueName,
3249                                        LPBYTE ptr,
3250                                        DWORD buflen, DWORD *needed,
3251                                        BOOL unicode)
3252 {
3253     DWORD sz = buflen, type;
3254     LONG ret;
3255
3256     if (ptr && buflen>=sizeof(DEVMODEA)) memset(ptr, 0, sizeof(DEVMODEA));
3257     ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3258     if ((ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA)) sz = 0;
3259     if (sz < sizeof(DEVMODEA))
3260     {
3261         TRACE("corrupted registry for %s ( size %d)\n",debugstr_w(ValueName),sz);
3262         return FALSE;
3263     }
3264     /* ensures that dmSize is not erratically bogus if registry is invalid */
3265     if (ptr && ((DEVMODEA*)ptr)->dmSize < sizeof(DEVMODEA))
3266         ((DEVMODEA*)ptr)->dmSize = sizeof(DEVMODEA);
3267     if(unicode) {
3268         sz += (CCHDEVICENAME + CCHFORMNAME);
3269         if(buflen >= sz) {
3270             DEVMODEW *dmW = GdiConvertToDevmodeW((DEVMODEA*)ptr);
3271             memcpy(ptr, dmW, sz);
3272             HeapFree(GetProcessHeap(),0,dmW);
3273         }
3274     }
3275     *needed = sz;
3276     return TRUE;
3277 }
3278
3279 /*********************************************************************
3280  *    WINSPOOL_GetPrinter_2
3281  *
3282  * Fills out a PRINTER_INFO_2A|W struct storing the strings in buf.
3283  * The strings are either stored as unicode or ascii.
3284  */
3285 static BOOL WINSPOOL_GetPrinter_2(HKEY hkeyPrinter, PRINTER_INFO_2W *pi2,
3286                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3287                                   BOOL unicode)
3288 {
3289     DWORD size, left = cbBuf;
3290     BOOL space = (cbBuf > 0);
3291     LPBYTE ptr = buf;
3292
3293     *pcbNeeded = 0;
3294
3295     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3296                                  unicode)) {
3297         if(space && size <= left) {
3298             pi2->pPrinterName = (LPWSTR)ptr;
3299             ptr += size;
3300             left -= size;
3301         } else
3302             space = FALSE;
3303         *pcbNeeded += size;
3304     }
3305     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Share_NameW, ptr, left, &size,
3306                                  unicode)) {
3307         if(space && size <= left) {
3308             pi2->pShareName = (LPWSTR)ptr;
3309             ptr += size;
3310             left -= size;
3311         } else
3312             space = FALSE;
3313         *pcbNeeded += size;
3314     }
3315     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
3316                                  unicode)) {
3317         if(space && size <= left) {
3318             pi2->pPortName = (LPWSTR)ptr;
3319             ptr += size;
3320             left -= size;
3321         } else
3322             space = FALSE;
3323         *pcbNeeded += size;
3324     }
3325     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Printer_DriverW, ptr, left,
3326                                  &size, unicode)) {
3327         if(space && size <= left) {
3328             pi2->pDriverName = (LPWSTR)ptr;
3329             ptr += size;
3330             left -= size;
3331         } else
3332             space = FALSE;
3333         *pcbNeeded += size;
3334     }
3335     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size,
3336                                  unicode)) {
3337         if(space && size <= left) {
3338             pi2->pComment = (LPWSTR)ptr;
3339             ptr += size;
3340             left -= size;
3341         } else
3342             space = FALSE;
3343         *pcbNeeded += size;
3344     }
3345     if(WINSPOOL_GetStringFromReg(hkeyPrinter, LocationW, ptr, left, &size,
3346                                  unicode)) {
3347         if(space && size <= left) {
3348             pi2->pLocation = (LPWSTR)ptr;
3349             ptr += size;
3350             left -= size;
3351         } else
3352             space = FALSE;
3353         *pcbNeeded += size;
3354     }
3355     if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, ptr, left,
3356                                   &size, unicode)) {
3357         if(space && size <= left) {
3358             pi2->pDevMode = (LPDEVMODEW)ptr;
3359             ptr += size;
3360             left -= size;
3361         } else
3362             space = FALSE;
3363         *pcbNeeded += size;
3364     }
3365     else
3366     {
3367         WINSPOOL_GetDefaultDevMode(ptr, left, &size, unicode);
3368         if(space && size <= left) {
3369             pi2->pDevMode = (LPDEVMODEW)ptr;
3370             ptr += size;
3371             left -= size;
3372         } else
3373             space = FALSE;
3374         *pcbNeeded += size;
3375     }
3376     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Separator_FileW, ptr, left,
3377                                  &size, unicode)) {
3378         if(space && size <= left) {
3379             pi2->pSepFile = (LPWSTR)ptr;
3380             ptr += size;
3381             left -= size;
3382         } else
3383             space = FALSE;
3384         *pcbNeeded += size;
3385     }
3386     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Print_ProcessorW, ptr, left,
3387                                  &size, unicode)) {
3388         if(space && size <= left) {
3389             pi2->pPrintProcessor = (LPWSTR)ptr;
3390             ptr += size;
3391             left -= size;
3392         } else
3393             space = FALSE;
3394         *pcbNeeded += size;
3395     }
3396     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DatatypeW, ptr, left,
3397                                  &size, unicode)) {
3398         if(space && size <= left) {
3399             pi2->pDatatype = (LPWSTR)ptr;
3400             ptr += size;
3401             left -= size;
3402         } else
3403             space = FALSE;
3404         *pcbNeeded += size;
3405     }
3406     if(WINSPOOL_GetStringFromReg(hkeyPrinter, ParametersW, ptr, left,
3407                                  &size, unicode)) {
3408         if(space && size <= left) {
3409             pi2->pParameters = (LPWSTR)ptr;
3410             ptr += size;
3411             left -= size;
3412         } else
3413             space = FALSE;
3414         *pcbNeeded += size;
3415     }
3416     if(pi2) {
3417         pi2->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3418         pi2->Priority = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Priority");
3419         pi2->DefaultPriority = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3420                                                         "Default Priority");
3421         pi2->StartTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "StartTime");
3422         pi2->UntilTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "UntilTime");
3423     }
3424
3425     if(!space && pi2) /* zero out pi2 if we can't completely fill buf */
3426         memset(pi2, 0, sizeof(*pi2));
3427
3428     return space;
3429 }
3430
3431 /*********************************************************************
3432  *    WINSPOOL_GetPrinter_4
3433  *
3434  * Fills out a PRINTER_INFO_4 struct storing the strings in buf.
3435  */
3436 static BOOL WINSPOOL_GetPrinter_4(HKEY hkeyPrinter, PRINTER_INFO_4W *pi4,
3437                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3438                                   BOOL unicode)
3439 {
3440     DWORD size, left = cbBuf;
3441     BOOL space = (cbBuf > 0);
3442     LPBYTE ptr = buf;
3443
3444     *pcbNeeded = 0;
3445
3446     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3447                                  unicode)) {
3448         if(space && size <= left) {
3449             pi4->pPrinterName = (LPWSTR)ptr;
3450             ptr += size;
3451             left -= size;
3452         } else
3453             space = FALSE;
3454         *pcbNeeded += size;
3455     }
3456     if(pi4) {
3457         pi4->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3458     }
3459
3460     if(!space && pi4) /* zero out pi4 if we can't completely fill buf */
3461         memset(pi4, 0, sizeof(*pi4));
3462
3463     return space;
3464 }
3465
3466 /*********************************************************************
3467  *    WINSPOOL_GetPrinter_5
3468  *
3469  * Fills out a PRINTER_INFO_5 struct storing the strings in buf.
3470  */
3471 static BOOL WINSPOOL_GetPrinter_5(HKEY hkeyPrinter, PRINTER_INFO_5W *pi5,
3472                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3473                                   BOOL unicode)
3474 {
3475     DWORD size, left = cbBuf;
3476     BOOL space = (cbBuf > 0);
3477     LPBYTE ptr = buf;
3478
3479     *pcbNeeded = 0;
3480
3481     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3482                                  unicode)) {
3483         if(space && size <= left) {
3484             pi5->pPrinterName = (LPWSTR)ptr;
3485             ptr += size;
3486             left -= size;
3487         } else
3488             space = FALSE;
3489         *pcbNeeded += size;
3490     }
3491     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
3492                                  unicode)) {
3493         if(space && size <= left) {
3494             pi5->pPortName = (LPWSTR)ptr;
3495             ptr += size;
3496             left -= size;
3497         } else
3498             space = FALSE;
3499         *pcbNeeded += size;
3500     }
3501     if(pi5) {
3502         pi5->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3503         pi5->DeviceNotSelectedTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3504                                                                 "dnsTimeout");
3505         pi5->TransmissionRetryTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3506                                                                  "txTimeout");
3507     }
3508
3509     if(!space && pi5) /* zero out pi5 if we can't completely fill buf */
3510         memset(pi5, 0, sizeof(*pi5));
3511
3512     return space;
3513 }
3514
3515 /*****************************************************************************
3516  *          WINSPOOL_GetPrinter
3517  *
3518  *    Implementation of GetPrinterA|W.  Relies on PRINTER_INFO_*W being
3519  *    essentially the same as PRINTER_INFO_*A. i.e. the structure itself is
3520  *    just a collection of pointers to strings.
3521  */
3522 static BOOL WINSPOOL_GetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
3523                                 DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
3524 {
3525     LPCWSTR name;
3526     DWORD size, needed = 0;
3527     LPBYTE ptr = NULL;
3528     HKEY hkeyPrinter, hkeyPrinters;
3529     BOOL ret;
3530
3531     TRACE("(%p,%d,%p,%d,%p)\n",hPrinter,Level,pPrinter,cbBuf, pcbNeeded);
3532
3533     if (!(name = get_opened_printer_name(hPrinter))) {
3534         SetLastError(ERROR_INVALID_HANDLE);
3535         return FALSE;
3536     }
3537
3538     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
3539        ERROR_SUCCESS) {
3540         ERR("Can't create Printers key\n");
3541         return FALSE;
3542     }
3543     if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter) != ERROR_SUCCESS)
3544     {
3545         ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
3546         RegCloseKey(hkeyPrinters);
3547         SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
3548         return FALSE;
3549     }
3550
3551     switch(Level) {
3552     case 2:
3553       {
3554         PRINTER_INFO_2W *pi2 = (PRINTER_INFO_2W *)pPrinter;
3555
3556         size = sizeof(PRINTER_INFO_2W);
3557         if(size <= cbBuf) {
3558             ptr = pPrinter + size;
3559             cbBuf -= size;
3560             memset(pPrinter, 0, size);
3561         } else {
3562             pi2 = NULL;
3563             cbBuf = 0;
3564         }
3565         ret = WINSPOOL_GetPrinter_2(hkeyPrinter, pi2, ptr, cbBuf, &needed,
3566                                     unicode);
3567         needed += size;
3568         break;
3569       }
3570
3571     case 4:
3572       {
3573         PRINTER_INFO_4W *pi4 = (PRINTER_INFO_4W *)pPrinter;
3574
3575         size = sizeof(PRINTER_INFO_4W);
3576         if(size <= cbBuf) {
3577             ptr = pPrinter + size;
3578             cbBuf -= size;
3579             memset(pPrinter, 0, size);
3580         } else {
3581             pi4 = NULL;
3582             cbBuf = 0;
3583         }
3584         ret = WINSPOOL_GetPrinter_4(hkeyPrinter, pi4, ptr, cbBuf, &needed,
3585                                     unicode);
3586         needed += size;
3587         break;
3588       }
3589
3590
3591     case 5:
3592       {
3593         PRINTER_INFO_5W *pi5 = (PRINTER_INFO_5W *)pPrinter;
3594
3595         size = sizeof(PRINTER_INFO_5W);
3596         if(size <= cbBuf) {
3597             ptr = pPrinter + size;
3598             cbBuf -= size;
3599             memset(pPrinter, 0, size);
3600         } else {
3601             pi5 = NULL;
3602             cbBuf = 0;
3603         }
3604
3605         ret = WINSPOOL_GetPrinter_5(hkeyPrinter, pi5, ptr, cbBuf, &needed,
3606                                     unicode);
3607         needed += size;
3608         break;
3609       }
3610
3611     default:
3612         FIXME("Unimplemented level %d\n", Level);
3613         SetLastError(ERROR_INVALID_LEVEL);
3614         RegCloseKey(hkeyPrinters);
3615         RegCloseKey(hkeyPrinter);
3616         return FALSE;
3617     }
3618
3619     RegCloseKey(hkeyPrinter);
3620     RegCloseKey(hkeyPrinters);
3621
3622     TRACE("returning %d needed = %d\n", ret, needed);
3623     if(pcbNeeded) *pcbNeeded = needed;
3624     if(!ret)
3625         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3626     return ret;
3627 }
3628
3629 /*****************************************************************************
3630  *          GetPrinterW  [WINSPOOL.@]
3631  */
3632 BOOL WINAPI GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
3633                         DWORD cbBuf, LPDWORD pcbNeeded)
3634 {
3635     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
3636                                TRUE);
3637 }
3638
3639 /*****************************************************************************
3640  *          GetPrinterA  [WINSPOOL.@]
3641  */
3642 BOOL WINAPI GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
3643                     DWORD cbBuf, LPDWORD pcbNeeded)
3644 {
3645     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
3646                                FALSE);
3647 }
3648
3649 /*****************************************************************************
3650  *          WINSPOOL_EnumPrinters
3651  *
3652  *    Implementation of EnumPrintersA|W
3653  */
3654 static BOOL WINSPOOL_EnumPrinters(DWORD dwType, LPWSTR lpszName,
3655                                   DWORD dwLevel, LPBYTE lpbPrinters,
3656                                   DWORD cbBuf, LPDWORD lpdwNeeded,
3657                                   LPDWORD lpdwReturned, BOOL unicode)
3658
3659 {
3660     HKEY hkeyPrinters, hkeyPrinter;
3661     WCHAR PrinterName[255];
3662     DWORD needed = 0, number = 0;
3663     DWORD used, i, left;
3664     PBYTE pi, buf;
3665
3666     if(lpbPrinters)
3667         memset(lpbPrinters, 0, cbBuf);
3668     if(lpdwReturned)
3669         *lpdwReturned = 0;
3670     if(lpdwNeeded)
3671         *lpdwNeeded = 0;
3672
3673     /* PRINTER_ENUM_DEFAULT is only supported under win9x, we behave like NT */
3674     if(dwType == PRINTER_ENUM_DEFAULT)
3675         return TRUE;
3676
3677     if (dwType & PRINTER_ENUM_CONNECTIONS) {
3678         FIXME("We don't handle PRINTER_ENUM_CONNECTIONS\n");
3679         dwType &= ~PRINTER_ENUM_CONNECTIONS; /* we don't handle that */
3680         if(!dwType) return TRUE;
3681     }
3682
3683     if (!((dwType & PRINTER_ENUM_LOCAL) || (dwType & PRINTER_ENUM_NAME))) {
3684         FIXME("dwType = %08x\n", dwType);
3685         SetLastError(ERROR_INVALID_FLAGS);
3686         return FALSE;
3687     }
3688
3689     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
3690        ERROR_SUCCESS) {
3691         ERR("Can't create Printers key\n");
3692         return FALSE;
3693     }
3694
3695     if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &number, NULL, NULL,
3696                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
3697         RegCloseKey(hkeyPrinters);
3698         ERR("Can't query Printers key\n");
3699         return FALSE;
3700     }
3701     TRACE("Found %d printers\n", number);
3702
3703     switch(dwLevel) {
3704     case 1:
3705         RegCloseKey(hkeyPrinters);
3706         if (lpdwReturned)
3707             *lpdwReturned = number;
3708         return TRUE;
3709
3710     case 2:
3711         used = number * sizeof(PRINTER_INFO_2W);
3712         break;
3713     case 4:
3714         used = number * sizeof(PRINTER_INFO_4W);
3715         break;
3716     case 5:
3717         used = number * sizeof(PRINTER_INFO_5W);
3718         break;
3719
3720     default:
3721         SetLastError(ERROR_INVALID_LEVEL);
3722         RegCloseKey(hkeyPrinters);
3723         return FALSE;
3724     }
3725     pi = (used <= cbBuf) ? lpbPrinters : NULL;
3726
3727     for(i = 0; i < number; i++) {
3728         if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)) !=
3729            ERROR_SUCCESS) {
3730             ERR("Can't enum key number %d\n", i);
3731             RegCloseKey(hkeyPrinters);
3732             return FALSE;
3733         }
3734         TRACE("Printer %d is %s\n", i, debugstr_w(PrinterName));
3735         if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkeyPrinter) !=
3736            ERROR_SUCCESS) {
3737             ERR("Can't open key %s\n", debugstr_w(PrinterName));
3738             RegCloseKey(hkeyPrinters);
3739             return FALSE;
3740         }
3741
3742         if(cbBuf > used) {
3743             buf = lpbPrinters + used;
3744             left = cbBuf - used;
3745         } else {
3746             buf = NULL;
3747             left = 0;
3748         }
3749
3750         switch(dwLevel) {
3751         case 2:
3752             WINSPOOL_GetPrinter_2(hkeyPrinter, (PRINTER_INFO_2W *)pi, buf,
3753                                   left, &needed, unicode);
3754             used += needed;
3755             if(pi) pi += sizeof(PRINTER_INFO_2W);
3756             break;
3757         case 4:
3758             WINSPOOL_GetPrinter_4(hkeyPrinter, (PRINTER_INFO_4W *)pi, buf,
3759                                   left, &needed, unicode);
3760             used += needed;
3761             if(pi) pi += sizeof(PRINTER_INFO_4W);
3762             break;
3763         case 5:
3764             WINSPOOL_GetPrinter_5(hkeyPrinter, (PRINTER_INFO_5W *)pi, buf,
3765                                   left, &needed, unicode);
3766             used += needed;
3767             if(pi) pi += sizeof(PRINTER_INFO_5W);
3768             break;
3769         default:
3770             ERR("Shouldn't be here!\n");
3771             RegCloseKey(hkeyPrinter);
3772             RegCloseKey(hkeyPrinters);
3773             return FALSE;
3774         }
3775         RegCloseKey(hkeyPrinter);
3776     }
3777     RegCloseKey(hkeyPrinters);
3778
3779     if(lpdwNeeded)
3780         *lpdwNeeded = used;
3781
3782     if(used > cbBuf) {
3783         if(lpbPrinters)
3784             memset(lpbPrinters, 0, cbBuf);
3785         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3786         return FALSE;
3787     }
3788     if(lpdwReturned)
3789         *lpdwReturned = number;
3790     SetLastError(ERROR_SUCCESS);
3791     return TRUE;
3792 }
3793
3794
3795 /******************************************************************
3796  *              EnumPrintersW        [WINSPOOL.@]
3797  *
3798  *    Enumerates the available printers, print servers and print
3799  *    providers, depending on the specified flags, name and level.
3800  *
3801  * RETURNS:
3802  *
3803  *    If level is set to 1:
3804  *      Not implemented yet!
3805  *      Returns TRUE with an empty list.
3806  *
3807  *    If level is set to 2:
3808  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
3809  *      Returns an array of PRINTER_INFO_2 data structures in the
3810  *      lpbPrinters buffer. Note that according to MSDN also an
3811  *      OpenPrinter should be performed on every remote printer.
3812  *
3813  *    If level is set to 4 (officially WinNT only):
3814  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
3815  *      Fast: Only the registry is queried to retrieve printer names,
3816  *      no connection to the driver is made.
3817  *      Returns an array of PRINTER_INFO_4 data structures in the
3818  *      lpbPrinters buffer.
3819  *
3820  *    If level is set to 5 (officially WinNT4/Win9x only):
3821  *      Fast: Only the registry is queried to retrieve printer names,
3822  *      no connection to the driver is made.
3823  *      Returns an array of PRINTER_INFO_5 data structures in the
3824  *      lpbPrinters buffer.
3825  *
3826  *    If level set to 3 or 6+:
3827  *          returns zero (failure!)
3828  *
3829  *    Returns nonzero (TRUE) on success, or zero on failure, use GetLastError
3830  *    for information.
3831  *
3832  * BUGS:
3833  *    - Only PRINTER_ENUM_LOCAL and PRINTER_ENUM_NAME are implemented.
3834  *    - Only levels 2, 4 and 5 are implemented at the moment.
3835  *    - 16-bit printer drivers are not enumerated.
3836  *    - Returned amount of bytes used/needed does not match the real Windoze
3837  *      implementation (as in this implementation, all strings are part
3838  *      of the buffer, whereas Win32 keeps them somewhere else)
3839  *    - At level 2, EnumPrinters should also call OpenPrinter for remote printers.
3840  *
3841  * NOTE:
3842  *    - In a regular Wine installation, no registry settings for printers
3843  *      exist, which makes this function return an empty list.
3844  */
3845 BOOL  WINAPI EnumPrintersW(
3846                 DWORD dwType,        /* [in] Types of print objects to enumerate */
3847                 LPWSTR lpszName,     /* [in] name of objects to enumerate */
3848                 DWORD dwLevel,       /* [in] type of printer info structure */
3849                 LPBYTE lpbPrinters,  /* [out] buffer which receives info */
3850                 DWORD cbBuf,         /* [in] max size of buffer in bytes */
3851                 LPDWORD lpdwNeeded,  /* [out] pointer to var: # bytes used/needed */
3852                 LPDWORD lpdwReturned /* [out] number of entries returned */
3853                 )
3854 {
3855     return WINSPOOL_EnumPrinters(dwType, lpszName, dwLevel, lpbPrinters, cbBuf,
3856                                  lpdwNeeded, lpdwReturned, TRUE);
3857 }
3858
3859 /******************************************************************
3860  *              EnumPrintersA        [WINSPOOL.@]
3861  *
3862  */
3863 BOOL WINAPI EnumPrintersA(DWORD dwType, LPSTR lpszName,
3864                           DWORD dwLevel, LPBYTE lpbPrinters,
3865                           DWORD cbBuf, LPDWORD lpdwNeeded,
3866                           LPDWORD lpdwReturned)
3867 {
3868     BOOL ret;
3869     UNICODE_STRING lpszNameW;
3870     PWSTR pwstrNameW;
3871     
3872     pwstrNameW = asciitounicode(&lpszNameW,lpszName);
3873     ret = WINSPOOL_EnumPrinters(dwType, pwstrNameW, dwLevel, lpbPrinters, cbBuf,
3874                                 lpdwNeeded, lpdwReturned, FALSE);
3875     RtlFreeUnicodeString(&lpszNameW);
3876     return ret;
3877 }
3878
3879 /*****************************************************************************
3880  *          WINSPOOL_GetDriverInfoFromReg [internal]
3881  *
3882  *    Enters the information from the registry into the DRIVER_INFO struct
3883  *
3884  * RETURNS
3885  *    zero if the printer driver does not exist in the registry
3886  *    (only if Level > 1) otherwise nonzero
3887  */
3888 static BOOL WINSPOOL_GetDriverInfoFromReg(
3889                             HKEY    hkeyDrivers,
3890                             LPWSTR  DriverName,
3891                             LPCWSTR pEnvironment,
3892                             DWORD   Level,
3893                             LPBYTE  ptr,            /* DRIVER_INFO */
3894                             LPBYTE  pDriverStrings, /* strings buffer */
3895                             DWORD   cbBuf,          /* size of string buffer */
3896                             LPDWORD pcbNeeded,      /* space needed for str. */
3897                             BOOL    unicode)        /* type of strings */
3898 {
3899     DWORD  size, tmp;
3900     HKEY   hkeyDriver;
3901     LPBYTE strPtr = pDriverStrings;
3902
3903     TRACE("%s,%s,%d,%p,%p,%d,%d\n",
3904           debugstr_w(DriverName), debugstr_w(pEnvironment),
3905           Level, ptr, pDriverStrings, cbBuf, unicode);
3906
3907     if(unicode) {
3908         *pcbNeeded = (lstrlenW(DriverName) + 1) * sizeof(WCHAR);
3909             if (*pcbNeeded <= cbBuf)
3910                strcpyW((LPWSTR)strPtr, DriverName);
3911     } else {
3912         *pcbNeeded = WideCharToMultiByte(CP_ACP, 0, DriverName, -1, NULL, 0,
3913                                           NULL, NULL);
3914         if(*pcbNeeded <= cbBuf)
3915             WideCharToMultiByte(CP_ACP, 0, DriverName, -1,
3916                                 (LPSTR)strPtr, *pcbNeeded, NULL, NULL);
3917     }
3918     if(Level == 1) {
3919        if(ptr)
3920           ((PDRIVER_INFO_1W) ptr)->pName = (LPWSTR) strPtr;
3921        return TRUE;
3922     } else {
3923        if(ptr)
3924           ((PDRIVER_INFO_2W) ptr)->pName = (LPWSTR) strPtr;
3925        strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
3926     }
3927
3928     if(!DriverName[0] || RegOpenKeyW(hkeyDrivers, DriverName, &hkeyDriver) != ERROR_SUCCESS) {
3929         ERR("Can't find driver '%s' in registry\n", debugstr_w(DriverName));
3930         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER); /* ? */
3931         return FALSE;
3932     }
3933
3934     if(ptr)
3935         ((PDRIVER_INFO_2A) ptr)->cVersion = (GetVersion() & 0x80000000) ? 0 : 3; /* FIXME: add 1, 2 */
3936
3937     if(!pEnvironment)
3938         pEnvironment = DefaultEnvironmentW;
3939     if(unicode)
3940         size = (lstrlenW(pEnvironment) + 1) * sizeof(WCHAR);
3941     else
3942         size = WideCharToMultiByte(CP_ACP, 0, pEnvironment, -1, NULL, 0,
3943                                    NULL, NULL);
3944     *pcbNeeded += size;
3945     if(*pcbNeeded <= cbBuf) {
3946         if(unicode)
3947             strcpyW((LPWSTR)strPtr, pEnvironment);
3948         else
3949             WideCharToMultiByte(CP_ACP, 0, pEnvironment, -1,
3950                                 (LPSTR)strPtr, size, NULL, NULL);
3951         if(ptr)
3952             ((PDRIVER_INFO_2W) ptr)->pEnvironment = (LPWSTR)strPtr;
3953         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
3954     }
3955
3956     if(WINSPOOL_GetStringFromReg(hkeyDriver, DriverW, strPtr, 0, &size,
3957                                  unicode)) {
3958         *pcbNeeded += size;
3959         if(*pcbNeeded <= cbBuf)
3960             WINSPOOL_GetStringFromReg(hkeyDriver, DriverW, strPtr, size, &tmp,
3961                                       unicode);
3962         if(ptr)
3963             ((PDRIVER_INFO_2W) ptr)->pDriverPath = (LPWSTR)strPtr;
3964         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
3965     }
3966
3967     if(WINSPOOL_GetStringFromReg(hkeyDriver, Data_FileW, strPtr, 0, &size,
3968                                  unicode)) {
3969         *pcbNeeded += size;
3970         if(*pcbNeeded <= cbBuf)
3971             WINSPOOL_GetStringFromReg(hkeyDriver, Data_FileW, strPtr, size,
3972                                       &tmp, unicode);
3973         if(ptr)
3974             ((PDRIVER_INFO_2W) ptr)->pDataFile = (LPWSTR)strPtr;
3975         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3976     }
3977
3978     if(WINSPOOL_GetStringFromReg(hkeyDriver, Configuration_FileW, strPtr,
3979                                  0, &size, unicode)) {
3980         *pcbNeeded += size;
3981         if(*pcbNeeded <= cbBuf)
3982             WINSPOOL_GetStringFromReg(hkeyDriver, Configuration_FileW, strPtr,
3983                                       size, &tmp, unicode);
3984         if(ptr)
3985             ((PDRIVER_INFO_2W) ptr)->pConfigFile = (LPWSTR)strPtr;
3986         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3987     }
3988
3989     if(Level == 2 ) {
3990         RegCloseKey(hkeyDriver);
3991         TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
3992         return TRUE;
3993     }
3994
3995     if (Level != 5 && WINSPOOL_GetStringFromReg(hkeyDriver, Help_FileW, strPtr, 0, &size,
3996                                  unicode)) {
3997         *pcbNeeded += size;
3998         if(*pcbNeeded <= cbBuf)
3999             WINSPOOL_GetStringFromReg(hkeyDriver, Help_FileW, strPtr,
4000                                       size, &tmp, unicode);
4001         if(ptr)
4002             ((PDRIVER_INFO_3W) ptr)->pHelpFile = (LPWSTR)strPtr;
4003         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4004     }
4005
4006     if (Level != 5 && WINSPOOL_GetStringFromReg(hkeyDriver, Dependent_FilesW, strPtr, 0,
4007                              &size, unicode)) {
4008         *pcbNeeded += size;
4009         if(*pcbNeeded <= cbBuf)
4010             WINSPOOL_GetStringFromReg(hkeyDriver, Dependent_FilesW, strPtr,
4011                                       size, &tmp, unicode);
4012         if(ptr)
4013             ((PDRIVER_INFO_3W) ptr)->pDependentFiles = (LPWSTR)strPtr;
4014         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4015     }
4016
4017     if (Level != 5 && WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, 0, &size,
4018                                  unicode)) {
4019         *pcbNeeded += size;
4020         if(*pcbNeeded <= cbBuf)
4021             WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr,
4022                                       size, &tmp, unicode);
4023         if(ptr)
4024             ((PDRIVER_INFO_3W) ptr)->pMonitorName = (LPWSTR)strPtr;
4025         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4026     }
4027
4028     if (Level != 5 && WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, 0, &size,
4029                                  unicode)) {
4030         *pcbNeeded += size;
4031         if(*pcbNeeded <= cbBuf)
4032             WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr,
4033                                       size, &tmp, unicode);
4034         if(ptr)
4035             ((PDRIVER_INFO_3W) ptr)->pDefaultDataType = (LPWSTR)strPtr;
4036         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4037     }
4038
4039     TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4040     RegCloseKey(hkeyDriver);
4041     return TRUE;
4042 }
4043
4044 /*****************************************************************************
4045  *          WINSPOOL_GetPrinterDriver
4046  */
4047 static BOOL WINSPOOL_GetPrinterDriver(HANDLE hPrinter, LPWSTR pEnvironment,
4048                                       DWORD Level, LPBYTE pDriverInfo,
4049                                       DWORD cbBuf, LPDWORD pcbNeeded,
4050                                       BOOL unicode)
4051 {
4052     LPCWSTR name;
4053     WCHAR DriverName[100];
4054     DWORD ret, type, size, needed = 0;
4055     LPBYTE ptr = NULL;
4056     HKEY hkeyPrinter, hkeyPrinters, hkeyDrivers;
4057
4058     TRACE("(%p,%s,%d,%p,%d,%p)\n",hPrinter,debugstr_w(pEnvironment),
4059           Level,pDriverInfo,cbBuf, pcbNeeded);
4060
4061     ZeroMemory(pDriverInfo, cbBuf);
4062
4063     if (!(name = get_opened_printer_name(hPrinter))) {
4064         SetLastError(ERROR_INVALID_HANDLE);
4065         return FALSE;
4066     }
4067     if(Level < 1 || Level > 6) {
4068         SetLastError(ERROR_INVALID_LEVEL);
4069         return FALSE;
4070     }
4071     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
4072        ERROR_SUCCESS) {
4073         ERR("Can't create Printers key\n");
4074         return FALSE;
4075     }
4076     if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter)
4077        != ERROR_SUCCESS) {
4078         ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
4079         RegCloseKey(hkeyPrinters);
4080         SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
4081         return FALSE;
4082     }
4083     size = sizeof(DriverName);
4084     DriverName[0] = 0;
4085     ret = RegQueryValueExW(hkeyPrinter, Printer_DriverW, 0, &type,
4086                            (LPBYTE)DriverName, &size);
4087     RegCloseKey(hkeyPrinter);
4088     RegCloseKey(hkeyPrinters);
4089     if(ret != ERROR_SUCCESS) {
4090         ERR("Can't get DriverName for printer %s\n", debugstr_w(name));
4091         return FALSE;
4092     }
4093
4094     hkeyDrivers = WINSPOOL_OpenDriverReg( pEnvironment, TRUE);
4095     if(!hkeyDrivers) {
4096         ERR("Can't create Drivers key\n");
4097         return FALSE;
4098     }
4099
4100     switch(Level) {
4101     case 1:
4102         size = sizeof(DRIVER_INFO_1W);
4103         break;
4104     case 2:
4105         size = sizeof(DRIVER_INFO_2W);
4106         break;
4107     case 3:
4108         size = sizeof(DRIVER_INFO_3W);
4109         break;
4110     case 4:
4111         size = sizeof(DRIVER_INFO_4W);
4112         break;
4113     case 5:
4114         size = sizeof(DRIVER_INFO_5W);
4115         break;
4116     case 6:
4117         size = sizeof(DRIVER_INFO_6W);
4118         break;
4119     default:
4120         ERR("Invalid level\n");
4121         return FALSE;
4122     }
4123
4124     if(size <= cbBuf)
4125         ptr = pDriverInfo + size;
4126
4127     if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverName,
4128                          pEnvironment, Level, pDriverInfo,
4129                          (cbBuf < size) ? NULL : ptr,
4130                          (cbBuf < size) ? 0 : cbBuf - size,
4131                          &needed, unicode)) {
4132             RegCloseKey(hkeyDrivers);
4133             return FALSE;
4134     }
4135
4136     RegCloseKey(hkeyDrivers);
4137
4138     if(pcbNeeded) *pcbNeeded = size + needed;
4139     TRACE("buffer space %d required %d\n", cbBuf, size + needed);
4140     if(cbBuf >= needed) return TRUE;
4141     SetLastError(ERROR_INSUFFICIENT_BUFFER);
4142     return FALSE;
4143 }
4144
4145 /*****************************************************************************
4146  *          GetPrinterDriverA  [WINSPOOL.@]
4147  */
4148 BOOL WINAPI GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment,
4149                               DWORD Level, LPBYTE pDriverInfo,
4150                               DWORD cbBuf, LPDWORD pcbNeeded)
4151 {
4152     BOOL ret;
4153     UNICODE_STRING pEnvW;
4154     PWSTR pwstrEnvW;
4155     
4156     pwstrEnvW = asciitounicode(&pEnvW, pEnvironment);
4157     ret = WINSPOOL_GetPrinterDriver(hPrinter, pwstrEnvW, Level, pDriverInfo,
4158                                     cbBuf, pcbNeeded, FALSE);
4159     RtlFreeUnicodeString(&pEnvW);
4160     return ret;
4161 }
4162 /*****************************************************************************
4163  *          GetPrinterDriverW  [WINSPOOL.@]
4164  */
4165 BOOL WINAPI GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment,
4166                                   DWORD Level, LPBYTE pDriverInfo,
4167                                   DWORD cbBuf, LPDWORD pcbNeeded)
4168 {
4169     return WINSPOOL_GetPrinterDriver(hPrinter, pEnvironment, Level,
4170                                      pDriverInfo, cbBuf, pcbNeeded, TRUE);
4171 }
4172
4173 /*****************************************************************************
4174  *       GetPrinterDriverDirectoryW  [WINSPOOL.@]
4175  *
4176  * Return the PATH for the Printer-Drivers (UNICODE)
4177  *
4178  * PARAMS
4179  *   pName            [I] Servername (NT only) or NULL (local Computer)
4180  *   pEnvironment     [I] Printing-Environment (see below) or NULL (Default)
4181  *   Level            [I] Structure-Level (must be 1)
4182  *   pDriverDirectory [O] PTR to Buffer that receives the Result
4183  *   cbBuf            [I] Size of Buffer at pDriverDirectory
4184  *   pcbNeeded        [O] PTR to DWORD that receives the size in Bytes used / 
4185  *                        required for pDriverDirectory
4186  *
4187  * RETURNS
4188  *   Success: TRUE  and in pcbNeeded the Bytes used in pDriverDirectory
4189  *   Failure: FALSE and in pcbNeeded the Bytes required for pDriverDirectory,
4190  *   if cbBuf is too small
4191  * 
4192  *   Native Values returned in pDriverDirectory on Success:
4193  *|  NT(Windows NT x86):  "%winsysdir%\\spool\\DRIVERS\\w32x86" 
4194  *|  NT(Windows 4.0):     "%winsysdir%\\spool\\DRIVERS\\win40" 
4195  *|  win9x(Windows 4.0):  "%winsysdir%" 
4196  *
4197  *   "%winsysdir%" is the Value from GetSystemDirectoryW()
4198  *
4199  * FIXME
4200  *-  Only NULL or "" is supported for pName
4201  *
4202  */
4203 BOOL WINAPI GetPrinterDriverDirectoryW(LPWSTR pName, LPWSTR pEnvironment,
4204                                        DWORD Level, LPBYTE pDriverDirectory,
4205                                        DWORD cbBuf, LPDWORD pcbNeeded)
4206 {
4207     DWORD needed;
4208     const printenv_t * env;
4209
4210     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName), 
4211           debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
4212     if(pName != NULL && pName[0]) {
4213         FIXME("pName unsupported: %s\n", debugstr_w(pName));
4214         SetLastError(ERROR_INVALID_PARAMETER);
4215         return FALSE;
4216     }
4217
4218     env = validate_envW(pEnvironment);
4219     if(!env) return FALSE;  /* pEnvironment invalid or unsupported */
4220
4221     if(Level != 1) {
4222         WARN("(Level: %d) is ignored in win9x\n", Level);
4223         SetLastError(ERROR_INVALID_LEVEL);
4224         return FALSE;
4225     }
4226
4227     /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
4228     needed = GetSystemDirectoryW(NULL, 0);
4229     /* add the Size for the Subdirectories */
4230     needed += lstrlenW(spooldriversW);
4231     needed += lstrlenW(env->subdir);
4232     needed *= sizeof(WCHAR);  /* return-value is size in Bytes */
4233
4234     if(pcbNeeded)
4235         *pcbNeeded = needed;
4236     TRACE("required: 0x%x/%d\n", needed, needed);
4237     if(needed > cbBuf) {
4238         SetLastError(ERROR_INSUFFICIENT_BUFFER);
4239         return FALSE;
4240     }
4241     if(pcbNeeded == NULL) {
4242         WARN("(pcbNeeded == NULL) is ignored in win9x\n");
4243         SetLastError(RPC_X_NULL_REF_POINTER);
4244         return FALSE;
4245     }
4246     if(pDriverDirectory == NULL) {
4247         /* ERROR_INVALID_USER_BUFFER is NT, ERROR_INVALID_PARAMETER is win9x */
4248         SetLastError(ERROR_INVALID_USER_BUFFER);
4249         return FALSE;
4250     }
4251     
4252     GetSystemDirectoryW((LPWSTR) pDriverDirectory, cbBuf/sizeof(WCHAR));
4253     /* add the Subdirectories */
4254     lstrcatW((LPWSTR) pDriverDirectory, spooldriversW);
4255     lstrcatW((LPWSTR) pDriverDirectory, env->subdir);
4256     TRACE(" => %s\n", debugstr_w((LPWSTR) pDriverDirectory));
4257     return TRUE;
4258 }
4259
4260
4261 /*****************************************************************************
4262  *       GetPrinterDriverDirectoryA  [WINSPOOL.@]
4263  *
4264  * Return the PATH for the Printer-Drivers (ANSI)
4265  *
4266  * See GetPrinterDriverDirectoryW.
4267  *
4268  * NOTES
4269  * On NT, pDriverDirectory need the same Size as the Unicode-Version
4270  *
4271  */
4272 BOOL WINAPI GetPrinterDriverDirectoryA(LPSTR pName, LPSTR pEnvironment,
4273                                        DWORD Level, LPBYTE pDriverDirectory,
4274                                        DWORD cbBuf, LPDWORD pcbNeeded)
4275 {
4276     UNICODE_STRING nameW, environmentW;
4277     BOOL ret;
4278     DWORD pcbNeededW;
4279     INT len = cbBuf * sizeof(WCHAR)/sizeof(CHAR);
4280     WCHAR *driverDirectoryW = NULL;
4281
4282     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_a(pName), 
4283           debugstr_a(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
4284  
4285     if (len) driverDirectoryW = HeapAlloc( GetProcessHeap(), 0, len );
4286
4287     if(pName) RtlCreateUnicodeStringFromAsciiz(&nameW, pName);
4288     else nameW.Buffer = NULL;
4289     if(pEnvironment) RtlCreateUnicodeStringFromAsciiz(&environmentW, pEnvironment);
4290     else environmentW.Buffer = NULL;
4291
4292     ret = GetPrinterDriverDirectoryW( nameW.Buffer, environmentW.Buffer, Level,
4293                                       (LPBYTE)driverDirectoryW, len, &pcbNeededW );
4294     if (ret) {
4295         DWORD needed;
4296         needed =  WideCharToMultiByte( CP_ACP, 0, driverDirectoryW, -1, 
4297                                    (LPSTR)pDriverDirectory, cbBuf, NULL, NULL);
4298         if(pcbNeeded)
4299             *pcbNeeded = needed;
4300         ret = (needed <= cbBuf) ? TRUE : FALSE;
4301     } else 
4302         if(pcbNeeded) *pcbNeeded = pcbNeededW * sizeof(CHAR)/sizeof(WCHAR);
4303
4304     TRACE("required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
4305
4306     HeapFree( GetProcessHeap(), 0, driverDirectoryW );
4307     RtlFreeUnicodeString(&environmentW);
4308     RtlFreeUnicodeString(&nameW);
4309
4310     return ret;
4311 }
4312
4313 /*****************************************************************************
4314  *          AddPrinterDriverA  [WINSPOOL.@]
4315  */
4316 BOOL WINAPI AddPrinterDriverA(LPSTR pName, DWORD level, LPBYTE pDriverInfo)
4317 {
4318     DRIVER_INFO_3A di3;
4319     HKEY hkeyDrivers, hkeyName;
4320     static CHAR empty[]    = "",
4321                 nullnull[] = "\0";
4322
4323     TRACE("(%s,%d,%p)\n",debugstr_a(pName),level,pDriverInfo);
4324
4325     if(level != 2 && level != 3) {
4326         SetLastError(ERROR_INVALID_LEVEL);
4327         return FALSE;
4328     }
4329     if ((pName) && (pName[0])) {
4330         FIXME("pName= %s - unsupported\n", debugstr_a(pName));
4331         SetLastError(ERROR_INVALID_PARAMETER);
4332         return FALSE;
4333     }
4334     if(!pDriverInfo) {
4335         WARN("pDriverInfo == NULL\n");
4336         SetLastError(ERROR_INVALID_PARAMETER);
4337         return FALSE;
4338     }
4339
4340     if(level == 3)
4341         di3 = *(DRIVER_INFO_3A *)pDriverInfo;
4342     else {
4343         memset(&di3, 0, sizeof(di3));
4344         memcpy(&di3, pDriverInfo, sizeof(DRIVER_INFO_2A));
4345     }
4346
4347     if(!di3.pName || !di3.pDriverPath || !di3.pConfigFile ||
4348        !di3.pDataFile) {
4349         SetLastError(ERROR_INVALID_PARAMETER);
4350         return FALSE;
4351     }
4352
4353     if(!di3.pDefaultDataType) di3.pDefaultDataType = empty;
4354     if(!di3.pDependentFiles) di3.pDependentFiles = nullnull;
4355     if(!di3.pHelpFile) di3.pHelpFile = empty;
4356     if(!di3.pMonitorName) di3.pMonitorName = empty;
4357
4358     hkeyDrivers = WINSPOOL_OpenDriverReg(di3.pEnvironment, FALSE);
4359
4360     if(!hkeyDrivers) {
4361         ERR("Can't create Drivers key\n");
4362         return FALSE;
4363     }
4364
4365     if(level == 2) { /* apparently can't overwrite with level2 */
4366         if(RegOpenKeyA(hkeyDrivers, di3.pName, &hkeyName) == ERROR_SUCCESS) {
4367             RegCloseKey(hkeyName);
4368             RegCloseKey(hkeyDrivers);
4369             WARN("Trying to create existing printer driver %s\n", debugstr_a(di3.pName));
4370             SetLastError(ERROR_PRINTER_DRIVER_ALREADY_INSTALLED);
4371             return FALSE;
4372         }
4373     }
4374     if(RegCreateKeyA(hkeyDrivers, di3.pName, &hkeyName) != ERROR_SUCCESS) {
4375         RegCloseKey(hkeyDrivers);
4376         ERR("Can't create Name key\n");
4377         return FALSE;
4378     }
4379     RegSetValueExA(hkeyName, "Configuration File", 0, REG_SZ, (LPBYTE) di3.pConfigFile,
4380                    0);
4381     RegSetValueExA(hkeyName, "Data File", 0, REG_SZ, (LPBYTE) di3.pDataFile, 0);
4382     RegSetValueExA(hkeyName, "Driver", 0, REG_SZ, (LPBYTE) di3.pDriverPath, 0);
4383     RegSetValueExA(hkeyName, "Version", 0, REG_DWORD, (LPBYTE) &di3.cVersion,
4384                    sizeof(DWORD));
4385     RegSetValueExA(hkeyName, "Datatype", 0, REG_SZ, (LPBYTE) di3.pDefaultDataType, 0);
4386     RegSetValueExA(hkeyName, "Dependent Files", 0, REG_MULTI_SZ,
4387                    (LPBYTE) di3.pDependentFiles, 0);
4388     RegSetValueExA(hkeyName, "Help File", 0, REG_SZ, (LPBYTE) di3.pHelpFile, 0);
4389     RegSetValueExA(hkeyName, "Monitor", 0, REG_SZ, (LPBYTE) di3.pMonitorName, 0);
4390     RegCloseKey(hkeyName);
4391     RegCloseKey(hkeyDrivers);
4392
4393     return TRUE;
4394 }
4395
4396 /*****************************************************************************
4397  *          AddPrinterDriverW  [WINSPOOL.@]
4398  */
4399 BOOL WINAPI AddPrinterDriverW(LPWSTR printerName,DWORD level,
4400                                    LPBYTE pDriverInfo)
4401 {
4402     FIXME("(%s,%d,%p): stub\n",debugstr_w(printerName),
4403           level,pDriverInfo);
4404     return FALSE;
4405 }
4406
4407 /*****************************************************************************
4408  *          AddPrintProcessorA  [WINSPOOL.@]
4409  */
4410 BOOL WINAPI AddPrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPathName,
4411                                LPSTR pPrintProcessorName)
4412 {
4413     FIXME("(%s,%s,%s,%s): stub\n", debugstr_a(pName), debugstr_a(pEnvironment),
4414           debugstr_a(pPathName), debugstr_a(pPrintProcessorName));
4415     return FALSE;
4416 }
4417
4418 /*****************************************************************************
4419  *          AddPrintProcessorW  [WINSPOOL.@]
4420  */
4421 BOOL WINAPI AddPrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPathName,
4422                                LPWSTR pPrintProcessorName)
4423 {
4424     FIXME("(%s,%s,%s,%s): stub\n", debugstr_w(pName), debugstr_w(pEnvironment),
4425           debugstr_w(pPathName), debugstr_w(pPrintProcessorName));
4426     return FALSE;
4427 }
4428
4429 /*****************************************************************************
4430  *          AddPrintProvidorA  [WINSPOOL.@]
4431  */
4432 BOOL WINAPI AddPrintProvidorA(LPSTR pName, DWORD Level, LPBYTE pProviderInfo)
4433 {
4434     FIXME("(%s,0x%08x,%p): stub\n", debugstr_a(pName), Level, pProviderInfo);
4435     return FALSE;
4436 }
4437
4438 /*****************************************************************************
4439  *          AddPrintProvidorW  [WINSPOOL.@]
4440  */
4441 BOOL WINAPI AddPrintProvidorW(LPWSTR pName, DWORD Level, LPBYTE pProviderInfo)
4442 {
4443     FIXME("(%s,0x%08x,%p): stub\n", debugstr_w(pName), Level, pProviderInfo);
4444     return FALSE;
4445 }
4446
4447 /*****************************************************************************
4448  *          AdvancedDocumentPropertiesA  [WINSPOOL.@]
4449  */
4450 LONG WINAPI AdvancedDocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName,
4451                                         PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput)
4452 {
4453     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_a(pDeviceName),
4454           pDevModeOutput, pDevModeInput);
4455     return 0;
4456 }
4457
4458 /*****************************************************************************
4459  *          AdvancedDocumentPropertiesW  [WINSPOOL.@]
4460  */
4461 LONG WINAPI AdvancedDocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName,
4462                                         PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput)
4463 {
4464     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_w(pDeviceName),
4465           pDevModeOutput, pDevModeInput);
4466     return 0;
4467 }
4468
4469 /*****************************************************************************
4470  *          PrinterProperties  [WINSPOOL.@]
4471  *
4472  *     Displays a dialog to set the properties of the printer.
4473  *
4474  * RETURNS
4475  *     nonzero on success or zero on failure
4476  *
4477  * BUGS
4478  *         implemented as stub only
4479  */
4480 BOOL WINAPI PrinterProperties(HWND hWnd,      /* [in] handle to parent window */
4481                               HANDLE hPrinter /* [in] handle to printer object */
4482 ){
4483     FIXME("(%p,%p): stub\n", hWnd, hPrinter);
4484     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4485     return FALSE;
4486 }
4487
4488 /*****************************************************************************
4489  *          EnumJobsA [WINSPOOL.@]
4490  *
4491  */
4492 BOOL WINAPI EnumJobsA(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
4493                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
4494                       LPDWORD pcReturned)
4495 {
4496     FIXME("(%p,first=%d,no=%d,level=%d,job=%p,cb=%d,%p,%p), stub!\n",
4497         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
4498     );
4499     if(pcbNeeded) *pcbNeeded = 0;
4500     if(pcReturned) *pcReturned = 0;
4501     return FALSE;
4502 }
4503
4504
4505 /*****************************************************************************
4506  *          EnumJobsW [WINSPOOL.@]
4507  *
4508  */
4509 BOOL WINAPI EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
4510                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
4511                       LPDWORD pcReturned)
4512 {
4513     FIXME("(%p,first=%d,no=%d,level=%d,job=%p,cb=%d,%p,%p), stub!\n",
4514         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
4515     );
4516     if(pcbNeeded) *pcbNeeded = 0;
4517     if(pcReturned) *pcReturned = 0;
4518     return FALSE;
4519 }
4520
4521 /*****************************************************************************
4522  *          WINSPOOL_EnumPrinterDrivers [internal]
4523  *
4524  *    Delivers information about all printer drivers installed on the
4525  *    localhost or a given server
4526  *
4527  * RETURNS
4528  *    nonzero on success or zero on failure. If the buffer for the returned
4529  *    information is too small the function will return an error
4530  *
4531  * BUGS
4532  *    - only implemented for localhost, foreign hosts will return an error
4533  */
4534 static BOOL WINSPOOL_EnumPrinterDrivers(LPWSTR pName, LPWSTR pEnvironment,
4535                                         DWORD Level, LPBYTE pDriverInfo,
4536                                         DWORD cbBuf, LPDWORD pcbNeeded,
4537                                         LPDWORD pcReturned, BOOL unicode)
4538
4539 {   HKEY  hkeyDrivers;
4540     DWORD i, needed, number = 0, size = 0;
4541     WCHAR DriverNameW[255];
4542     PBYTE ptr;
4543
4544     TRACE("%s,%s,%d,%p,%d,%d\n",
4545           debugstr_w(pName), debugstr_w(pEnvironment),
4546           Level, pDriverInfo, cbBuf, unicode);
4547
4548     /* check for local drivers */
4549     if((pName) && (pName[0])) {
4550         FIXME("remote drivers (%s) not supported!\n", debugstr_w(pName));
4551         SetLastError(ERROR_ACCESS_DENIED);
4552         return FALSE;
4553     }
4554
4555     /* check input parameter */
4556     if((Level < 1) || (Level > 3)) {
4557         ERR("unsupported level %d\n", Level);
4558         SetLastError(ERROR_INVALID_LEVEL);
4559         return FALSE;
4560     }
4561
4562     /* initialize return values */
4563     if(pDriverInfo)
4564         memset( pDriverInfo, 0, cbBuf);
4565     *pcbNeeded  = 0;
4566     *pcReturned = 0;
4567
4568     hkeyDrivers = WINSPOOL_OpenDriverReg(pEnvironment, TRUE);
4569     if(!hkeyDrivers) {
4570         ERR("Can't open Drivers key\n");
4571         return FALSE;
4572     }
4573
4574     if(RegQueryInfoKeyA(hkeyDrivers, NULL, NULL, NULL, &number, NULL, NULL,
4575                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
4576         RegCloseKey(hkeyDrivers);
4577         ERR("Can't query Drivers key\n");
4578         return FALSE;
4579     }
4580     TRACE("Found %d Drivers\n", number);
4581
4582     /* get size of single struct
4583      * unicode and ascii structure have the same size
4584      */
4585     switch (Level) {
4586         case 1:
4587             size = sizeof(DRIVER_INFO_1A);
4588             break;
4589         case 2:
4590             size = sizeof(DRIVER_INFO_2A);
4591             break;
4592         case 3:
4593             size = sizeof(DRIVER_INFO_3A);
4594             break;
4595     }
4596
4597     /* calculate required buffer size */
4598     *pcbNeeded = size * number;
4599
4600     for( i = 0,  ptr = (pDriverInfo && (cbBuf >= size)) ? pDriverInfo : NULL ;
4601          i < number;
4602          i++, ptr = (ptr && (cbBuf >= size * i)) ? ptr + size : NULL) {
4603         if(RegEnumKeyW(hkeyDrivers, i, DriverNameW, sizeof(DriverNameW))
4604                        != ERROR_SUCCESS) {
4605             ERR("Can't enum key number %d\n", i);
4606             RegCloseKey(hkeyDrivers);
4607             return FALSE;
4608         }
4609         if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverNameW,
4610                          pEnvironment, Level, ptr,
4611                          (cbBuf < *pcbNeeded) ? NULL : pDriverInfo + *pcbNeeded,
4612                          (cbBuf < *pcbNeeded) ? 0 : cbBuf - *pcbNeeded,
4613                          &needed, unicode)) {
4614             RegCloseKey(hkeyDrivers);
4615             return FALSE;
4616         }
4617         (*pcbNeeded) += needed;
4618     }
4619
4620     RegCloseKey(hkeyDrivers);
4621
4622     if(cbBuf < *pcbNeeded){
4623         SetLastError(ERROR_INSUFFICIENT_BUFFER);
4624         return FALSE;
4625     }
4626
4627     *pcReturned = number;
4628     return TRUE;
4629 }
4630
4631 /*****************************************************************************
4632  *          EnumPrinterDriversW  [WINSPOOL.@]
4633  *
4634  *    see function EnumPrinterDrivers for RETURNS, BUGS
4635  */
4636 BOOL WINAPI EnumPrinterDriversW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
4637                                 LPBYTE pDriverInfo, DWORD cbBuf,
4638                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
4639 {
4640     return WINSPOOL_EnumPrinterDrivers(pName, pEnvironment, Level, pDriverInfo,
4641                                        cbBuf, pcbNeeded, pcReturned, TRUE);
4642 }
4643
4644 /*****************************************************************************
4645  *          EnumPrinterDriversA  [WINSPOOL.@]
4646  *
4647  *    see function EnumPrinterDrivers for RETURNS, BUGS
4648  */
4649 BOOL WINAPI EnumPrinterDriversA(LPSTR pName, LPSTR pEnvironment, DWORD Level,
4650                                 LPBYTE pDriverInfo, DWORD cbBuf,
4651                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
4652 {   BOOL ret;
4653     UNICODE_STRING pNameW, pEnvironmentW;
4654     PWSTR pwstrNameW, pwstrEnvironmentW;
4655
4656     pwstrNameW = asciitounicode(&pNameW, pName);
4657     pwstrEnvironmentW = asciitounicode(&pEnvironmentW, pEnvironment);
4658
4659     ret = WINSPOOL_EnumPrinterDrivers(pwstrNameW, pwstrEnvironmentW,
4660                                       Level, pDriverInfo, cbBuf, pcbNeeded,
4661                                       pcReturned, FALSE);
4662     RtlFreeUnicodeString(&pNameW);
4663     RtlFreeUnicodeString(&pEnvironmentW);
4664
4665     return ret;
4666 }
4667
4668 static CHAR PortMonitor[] = "Wine Port Monitor";
4669 static CHAR PortDescription[] = "Wine Port";
4670
4671 static BOOL WINSPOOL_ComPortExists( LPCSTR name )
4672 {
4673     HANDLE handle;
4674
4675     handle = CreateFileA( name, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
4676                          NULL, OPEN_EXISTING, 0, NULL );
4677     if (handle == INVALID_HANDLE_VALUE)
4678         return FALSE;
4679     TRACE("Checking %s exists\n", name );
4680     CloseHandle( handle );
4681     return TRUE;
4682 }
4683
4684 static DWORD WINSPOOL_CountSerialPorts(void)
4685 {
4686     CHAR name[6];
4687     DWORD n = 0, i;
4688
4689     for (i=0; i<4; i++)
4690     {
4691         strcpy( name, "COMx:" );
4692         name[3] = '1' + i;
4693         if (WINSPOOL_ComPortExists( name ))
4694             n++;
4695     }
4696
4697     return n;
4698 }
4699
4700 /******************************************************************************
4701  *              EnumPortsA   (WINSPOOL.@)
4702  *
4703  * See EnumPortsW.
4704  *
4705  * BUGS
4706  *  ANSI-Version did not call the UNICODE-Version
4707  *
4708  */
4709 BOOL WINAPI EnumPortsA(LPSTR name,DWORD level,LPBYTE buffer,DWORD bufsize,
4710                        LPDWORD bufneeded,LPDWORD bufreturned)
4711 {
4712     CHAR portname[10];
4713     DWORD info_size, ofs, i, printer_count, serial_count, count, n, r;
4714     const LPCSTR szPrinterPortKey = "Software\\Wine\\Wine\\Config\\spooler";
4715     HKEY hkey_printer;
4716     BOOL retval = TRUE;
4717
4718     TRACE("(%s,%d,%p,%d,%p,%p)\n",
4719           debugstr_a(name),level,buffer,bufsize,bufneeded,bufreturned);
4720
4721     switch( level )
4722     {
4723     case 1:
4724         info_size = sizeof (PORT_INFO_1A);
4725         break;
4726     case 2:
4727         info_size = sizeof (PORT_INFO_2A);
4728         break;
4729     default:
4730         SetLastError(ERROR_INVALID_LEVEL);
4731         return FALSE;
4732     }
4733     
4734     /* see how many exist */
4735
4736     hkey_printer = 0;
4737     serial_count = WINSPOOL_CountSerialPorts();
4738     printer_count = 0;
4739
4740     r = RegOpenKeyA( HKEY_LOCAL_MACHINE, szPrinterPortKey, &hkey_printer);
4741     if ( r == ERROR_SUCCESS )
4742     {
4743         RegQueryInfoKeyA( hkey_printer, NULL, NULL, NULL, NULL, NULL, NULL,
4744             &printer_count, NULL, NULL, NULL, NULL);
4745     }
4746     count = serial_count + printer_count;
4747
4748     /* then fill in the structure info structure once
4749        we know the offset to the first string */
4750
4751     memset( buffer, 0, bufsize );
4752     n = 0;
4753     ofs = info_size*count; 
4754     for ( i=0; i<count; i++)
4755     {
4756         DWORD vallen = sizeof(portname) - 1;
4757
4758         /* get the serial port values, then the printer values */
4759         if ( i < serial_count )
4760         {
4761             strcpy( portname, "COMx:" );
4762             portname[3] = '1' + i;
4763             if (!WINSPOOL_ComPortExists( portname ))
4764                 continue;
4765
4766             TRACE("Found %s\n", portname );
4767             vallen = strlen( portname );
4768         }
4769         else
4770         {
4771             r = RegEnumValueA( hkey_printer, i-serial_count, 
4772                      portname, &vallen, NULL, NULL, NULL, 0 );
4773             if ( r )
4774                 continue;
4775         }
4776
4777         /* add a colon if necessary, and make it upper case */
4778         CharUpperBuffA(portname,vallen);
4779         if (strcasecmp(portname,"nul")!=0)
4780             if (vallen && (portname[vallen-1] != ':') )
4781                 lstrcatA(portname,":");
4782
4783         /* add the port info structure if we can fit it */
4784         if ( info_size*(n+1) < bufsize )
4785         {
4786             if ( level == 1)
4787             {
4788                 PORT_INFO_1A *info = (PORT_INFO_1A*) &buffer[info_size*n];
4789                 info->pName = (LPSTR) &buffer[ofs];
4790             }
4791             else if ( level == 2)
4792             {
4793                 PORT_INFO_2A *info = (PORT_INFO_2A*) &buffer[info_size*n];
4794                 info->pPortName = (LPSTR) &buffer[ofs];
4795                 /* FIXME: fill in more stuff here */
4796                 info->pMonitorName = PortMonitor;
4797                 info->pDescription = PortDescription;
4798                 info->fPortType = PORT_TYPE_WRITE|PORT_TYPE_READ;
4799             }
4800
4801             /* add the name of the port if we can fit it */
4802             if ( ofs < bufsize )
4803                 lstrcpynA((LPSTR)&buffer[ofs],portname,bufsize - ofs);
4804
4805             n++;
4806         }
4807         else
4808             retval = FALSE;
4809         ofs += lstrlenA(portname)+1;
4810     }
4811
4812     RegCloseKey(hkey_printer);
4813
4814     if(bufneeded)
4815         *bufneeded = ofs;
4816
4817     if(bufreturned)
4818         *bufreturned = n;
4819
4820     return retval;
4821 }
4822
4823 /******************************************************************************
4824  *      EnumPortsW   (WINSPOOL.@)
4825  *
4826  * Enumerate available Ports
4827  *
4828  * PARAMS
4829  *  name        [I] Servername or NULL (local Computer)
4830  *  level       [I] Structure-Level (1 or 2)
4831  *  buffer      [O] PTR to Buffer that receives the Result
4832  *  bufsize     [I] Size of Buffer at buffer
4833  *  bufneeded   [O] PTR to DWORD that receives the size in Bytes used / required for buffer
4834  *  bufreturned [O] PTR to DWORD that receives the number of Ports in buffer
4835  *
4836  * RETURNS
4837  *  Success: TRUE
4838  *  Failure: FALSE and in bufneeded the Bytes required for buffer, if bufsize is too small
4839  *
4840  */
4841
4842 BOOL WINAPI EnumPortsW(LPWSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
4843 {
4844     DWORD   needed = 0;
4845     DWORD   numentries = 0;
4846     BOOL    res = FALSE;
4847
4848     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pPorts,
4849           cbBuf, pcbNeeded, pcReturned);
4850
4851     if (pName && (pName[0])) {
4852         FIXME("not implemented for Server %s\n", debugstr_w(pName));
4853         SetLastError(ERROR_ACCESS_DENIED);
4854         goto emP_cleanup;
4855     }
4856
4857     /* Level is not checked in win9x */
4858     if (!Level || (Level > 2)) {
4859         WARN("level (%d) is ignored in win9x\n", Level);
4860         SetLastError(ERROR_INVALID_LEVEL);
4861         goto emP_cleanup;
4862     }
4863     if (!pcbNeeded) {
4864         SetLastError(RPC_X_NULL_REF_POINTER);
4865         goto emP_cleanup;
4866     }
4867
4868     EnterCriticalSection(&monitor_handles_cs);
4869     monitor_loadall();
4870
4871     /* Scan all local Ports */
4872     numentries = 0;
4873     needed = get_ports_from_all_monitors(Level, NULL, 0, &numentries);
4874
4875     /* we calculated the needed buffersize. now do the error-checks */
4876     if (cbBuf < needed) {
4877         monitor_unloadall();
4878         SetLastError(ERROR_INSUFFICIENT_BUFFER);
4879         goto emP_cleanup_cs;
4880     }
4881     else if (!pPorts || !pcReturned) {
4882         monitor_unloadall();
4883         SetLastError(RPC_X_NULL_REF_POINTER);
4884         goto emP_cleanup_cs;
4885     }
4886
4887     /* Fill the Buffer */
4888     needed = get_ports_from_all_monitors(Level, pPorts, cbBuf, &numentries);
4889     res = TRUE;
4890     monitor_unloadall();
4891
4892 emP_cleanup_cs:
4893     LeaveCriticalSection(&monitor_handles_cs);
4894
4895 emP_cleanup:
4896     if (pcbNeeded)  *pcbNeeded = needed;
4897     if (pcReturned) *pcReturned = (res) ? numentries : 0;
4898
4899     TRACE("returning %d with %d (%d byte for %d of %d entries)\n", 
4900             (res), GetLastError(), needed, (res)? numentries : 0, numentries);
4901
4902     return (res);
4903 }
4904
4905 /******************************************************************************
4906  *              GetDefaultPrinterW   (WINSPOOL.@)
4907  *
4908  * FIXME
4909  *      This function must read the value from data 'device' of key
4910  *      HCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows
4911  */
4912 BOOL WINAPI GetDefaultPrinterW(LPWSTR name, LPDWORD namesize)
4913 {
4914     BOOL  retval = TRUE;
4915     DWORD insize, len;
4916     WCHAR *buffer, *ptr;
4917
4918     if (!namesize)
4919     {
4920         SetLastError(ERROR_INVALID_PARAMETER);
4921         return FALSE;
4922     }
4923
4924     /* make the buffer big enough for the stuff from the profile/registry,
4925      * the content must fit into the local buffer to compute the correct
4926      * size even if the extern buffer is too small or not given.
4927      * (20 for ,driver,port) */
4928     insize = *namesize;
4929     len = max(100, (insize + 20));
4930     buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
4931
4932     if (!GetProfileStringW(windowsW, deviceW, emptyStringW, buffer, len))
4933     {
4934         SetLastError (ERROR_FILE_NOT_FOUND);
4935         retval = FALSE;
4936         goto end;
4937     }
4938     TRACE("%s\n", debugstr_w(buffer));
4939
4940     if ((ptr = strchrW(buffer, ',')) == NULL)
4941     {
4942         SetLastError(ERROR_INVALID_NAME);
4943         retval = FALSE;
4944         goto end;
4945     }
4946
4947     *ptr = 0;
4948     *namesize = strlenW(buffer) + 1;
4949     if(!name || (*namesize > insize))
4950     {
4951         SetLastError(ERROR_INSUFFICIENT_BUFFER);
4952         retval = FALSE;
4953         goto end;
4954     }
4955     strcpyW(name, buffer);
4956
4957 end:
4958     HeapFree( GetProcessHeap(), 0, buffer);
4959     return retval;
4960 }
4961
4962
4963 /******************************************************************************
4964  *              GetDefaultPrinterA   (WINSPOOL.@)
4965  */
4966 BOOL WINAPI GetDefaultPrinterA(LPSTR name, LPDWORD namesize)
4967 {
4968     BOOL  retval = TRUE;
4969     DWORD insize = 0;
4970     WCHAR *bufferW = NULL;
4971
4972     if (!namesize)
4973     {
4974         SetLastError(ERROR_INVALID_PARAMETER);
4975         return FALSE;
4976     }
4977
4978     if(name && *namesize) {
4979         insize = *namesize;
4980         bufferW = HeapAlloc( GetProcessHeap(), 0, insize * sizeof(WCHAR));
4981     }
4982
4983     if(!GetDefaultPrinterW( bufferW, namesize)) {
4984         retval = FALSE;
4985         goto end;
4986     }
4987
4988     *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, name, insize,
4989                                     NULL, NULL);
4990     if (!*namesize)
4991     {
4992         *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
4993         retval = FALSE;
4994     }
4995     TRACE("0x%08x/0x%08x:%s\n", *namesize, insize, debugstr_w(bufferW));
4996
4997 end:
4998     HeapFree( GetProcessHeap(), 0, bufferW);
4999     return retval;
5000 }
5001
5002
5003 /******************************************************************************
5004  *              SetDefaultPrinterW   (WINSPOOL.204)
5005  *
5006  * Set the Name of the Default Printer
5007  *
5008  * PARAMS
5009  *  pszPrinter [I] Name of the Printer or NULL
5010  *
5011  * RETURNS
5012  *  Success:    True
5013  *  Failure:    FALSE
5014  *
5015  * NOTES
5016  *  When the Parameter is NULL or points to an Empty String and
5017  *  a Default Printer was already present, then this Function changes nothing.
5018  *  Without a Default Printer and NULL (or an Empty String) as Parameter,
5019  *  the First enumerated local Printer is used.
5020  *
5021  */
5022 BOOL WINAPI SetDefaultPrinterW(LPCWSTR pszPrinter)
5023 {
5024
5025     TRACE("(%s)\n", debugstr_w(pszPrinter));
5026
5027     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5028     return FALSE;
5029 }
5030
5031 /******************************************************************************
5032  *              SetDefaultPrinterA   (WINSPOOL.202)
5033  *
5034  * See SetDefaultPrinterW.
5035  *
5036  */
5037 BOOL WINAPI SetDefaultPrinterA(LPCSTR pszPrinter)
5038 {
5039
5040     TRACE("(%s)\n", debugstr_a(pszPrinter));
5041
5042     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5043     return FALSE;
5044 }
5045
5046
5047 /******************************************************************************
5048  *              SetPrinterDataExA   (WINSPOOL.@)
5049  */
5050 DWORD WINAPI SetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5051                                LPCSTR pValueName, DWORD Type,
5052                                LPBYTE pData, DWORD cbData)
5053 {
5054     HKEY hkeyPrinter, hkeySubkey;
5055     DWORD ret;
5056
5057     TRACE("(%p, %s, %s %08x, %p, %08x)\n", hPrinter, debugstr_a(pKeyName),
5058           debugstr_a(pValueName), Type, pData, cbData);
5059
5060     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5061        != ERROR_SUCCESS)
5062         return ret;
5063
5064     if((ret = RegCreateKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
5065        != ERROR_SUCCESS) {
5066         ERR("Can't create subkey %s\n", debugstr_a(pKeyName));
5067         RegCloseKey(hkeyPrinter);
5068         return ret;
5069     }
5070     ret = RegSetValueExA(hkeySubkey, pValueName, 0, Type, pData, cbData);
5071     RegCloseKey(hkeySubkey);
5072     RegCloseKey(hkeyPrinter);
5073     return ret;
5074 }
5075
5076 /******************************************************************************
5077  *              SetPrinterDataExW   (WINSPOOL.@)
5078  */
5079 DWORD WINAPI SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5080                                LPCWSTR pValueName, DWORD Type,
5081                                LPBYTE pData, DWORD cbData)
5082 {
5083     HKEY hkeyPrinter, hkeySubkey;
5084     DWORD ret;
5085
5086     TRACE("(%p, %s, %s %08x, %p, %08x)\n", hPrinter, debugstr_w(pKeyName),
5087           debugstr_w(pValueName), Type, pData, cbData);
5088
5089     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5090        != ERROR_SUCCESS)
5091         return ret;
5092
5093     if((ret = RegCreateKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
5094        != ERROR_SUCCESS) {
5095         ERR("Can't create subkey %s\n", debugstr_w(pKeyName));
5096         RegCloseKey(hkeyPrinter);
5097         return ret;
5098     }
5099     ret = RegSetValueExW(hkeySubkey, pValueName, 0, Type, pData, cbData);
5100     RegCloseKey(hkeySubkey);
5101     RegCloseKey(hkeyPrinter);
5102     return ret;
5103 }
5104
5105 /******************************************************************************
5106  *              SetPrinterDataA   (WINSPOOL.@)
5107  */
5108 DWORD WINAPI SetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, DWORD Type,
5109                                LPBYTE pData, DWORD cbData)
5110 {
5111     return SetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, Type,
5112                              pData, cbData);
5113 }
5114
5115 /******************************************************************************
5116  *              SetPrinterDataW   (WINSPOOL.@)
5117  */
5118 DWORD WINAPI SetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, DWORD Type,
5119                              LPBYTE pData, DWORD cbData)
5120 {
5121     return SetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, Type,
5122                              pData, cbData);
5123 }
5124
5125 /******************************************************************************
5126  *              GetPrinterDataExA   (WINSPOOL.@)
5127  */
5128 DWORD WINAPI GetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5129                                LPCSTR pValueName, LPDWORD pType,
5130                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5131 {
5132     HKEY hkeyPrinter, hkeySubkey;
5133     DWORD ret;
5134
5135     TRACE("(%p, %s, %s %p, %p, %08x, %p)\n", hPrinter,
5136           debugstr_a(pKeyName), debugstr_a(pValueName), pType, pData, nSize,
5137           pcbNeeded);
5138
5139     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5140        != ERROR_SUCCESS)
5141         return ret;
5142
5143     if((ret = RegOpenKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
5144        != ERROR_SUCCESS) {
5145         WARN("Can't open subkey %s\n", debugstr_a(pKeyName));
5146         RegCloseKey(hkeyPrinter);
5147         return ret;
5148     }
5149     *pcbNeeded = nSize;
5150     ret = RegQueryValueExA(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
5151     RegCloseKey(hkeySubkey);
5152     RegCloseKey(hkeyPrinter);
5153     return ret;
5154 }
5155
5156 /******************************************************************************
5157  *              GetPrinterDataExW   (WINSPOOL.@)
5158  */
5159 DWORD WINAPI GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5160                                LPCWSTR pValueName, LPDWORD pType,
5161                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5162 {
5163     HKEY hkeyPrinter, hkeySubkey;
5164     DWORD ret;
5165
5166     TRACE("(%p, %s, %s %p, %p, %08x, %p)\n", hPrinter,
5167           debugstr_w(pKeyName), debugstr_w(pValueName), pType, pData, nSize,
5168           pcbNeeded);
5169
5170     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5171        != ERROR_SUCCESS)
5172         return ret;
5173
5174     if((ret = RegOpenKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
5175        != ERROR_SUCCESS) {
5176         WARN("Can't open subkey %s\n", debugstr_w(pKeyName));
5177         RegCloseKey(hkeyPrinter);
5178         return ret;
5179     }
5180     *pcbNeeded = nSize;
5181     ret = RegQueryValueExW(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
5182     RegCloseKey(hkeySubkey);
5183     RegCloseKey(hkeyPrinter);
5184     return ret;
5185 }
5186
5187 /******************************************************************************
5188  *              GetPrinterDataA   (WINSPOOL.@)
5189  */
5190 DWORD WINAPI GetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, LPDWORD pType,
5191                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5192 {
5193     return GetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, pType,
5194                              pData, nSize, pcbNeeded);
5195 }
5196
5197 /******************************************************************************
5198  *              GetPrinterDataW   (WINSPOOL.@)
5199  */
5200 DWORD WINAPI GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType,
5201                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5202 {
5203     return GetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, pType,
5204                              pData, nSize, pcbNeeded);
5205 }
5206
5207 /*******************************************************************************
5208  *              EnumPrinterDataExW      [WINSPOOL.@]
5209  */
5210 DWORD WINAPI EnumPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5211                                 LPBYTE pEnumValues, DWORD cbEnumValues,
5212                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
5213 {
5214     HKEY                    hkPrinter, hkSubKey;
5215     DWORD                   r, ret, dwIndex, cValues, cbMaxValueNameLen,
5216                             cbValueNameLen, cbMaxValueLen, cbValueLen,
5217                             cbBufSize, dwType;
5218     LPWSTR                  lpValueName;
5219     HANDLE                  hHeap;
5220     PBYTE                   lpValue;
5221     PPRINTER_ENUM_VALUESW   ppev;
5222
5223     TRACE ("%p %s\n", hPrinter, debugstr_w (pKeyName));
5224
5225     if (pKeyName == NULL || *pKeyName == 0)
5226         return ERROR_INVALID_PARAMETER;
5227
5228     ret = WINSPOOL_GetOpenedPrinterRegKey (hPrinter, &hkPrinter);
5229     if (ret != ERROR_SUCCESS)
5230     {
5231         TRACE ("WINSPOOL_GetOpenedPrinterRegKey (%p) returned %i\n",
5232                 hPrinter, ret);
5233         return ret;
5234     }
5235
5236     ret = RegOpenKeyExW (hkPrinter, pKeyName, 0, KEY_READ, &hkSubKey);
5237     if (ret != ERROR_SUCCESS)
5238     {
5239         r = RegCloseKey (hkPrinter);
5240         if (r != ERROR_SUCCESS)
5241             WARN ("RegCloseKey returned %i\n", r);
5242         TRACE ("RegOpenKeyExW (%p, %s) returned %i\n", hPrinter,
5243                 debugstr_w (pKeyName), ret);
5244         return ret;
5245     }
5246
5247     ret = RegCloseKey (hkPrinter);
5248     if (ret != ERROR_SUCCESS)
5249     {
5250         ERR ("RegCloseKey returned %i\n", ret);
5251         r = RegCloseKey (hkSubKey);
5252         if (r != ERROR_SUCCESS)
5253             WARN ("RegCloseKey returned %i\n", r);
5254         return ret;
5255     }
5256
5257     ret = RegQueryInfoKeyW (hkSubKey, NULL, NULL, NULL, NULL, NULL, NULL,
5258             &cValues, &cbMaxValueNameLen, &cbMaxValueLen, NULL, NULL);
5259     if (ret != ERROR_SUCCESS)
5260     {
5261         r = RegCloseKey (hkSubKey);
5262         if (r != ERROR_SUCCESS)
5263             WARN ("RegCloseKey returned %i\n", r);
5264         TRACE ("RegQueryInfoKeyW (%p) returned %i\n", hkSubKey, ret);
5265         return ret;
5266     }
5267
5268     TRACE ("RegQueryInfoKeyW returned cValues = %i, cbMaxValueNameLen = %i, "
5269             "cbMaxValueLen = %i\n", cValues, cbMaxValueNameLen, cbMaxValueLen);
5270
5271     if (cValues == 0)                   /* empty key */
5272     {
5273         r = RegCloseKey (hkSubKey);
5274         if (r != ERROR_SUCCESS)
5275             WARN ("RegCloseKey returned %i\n", r);
5276         *pcbEnumValues = *pnEnumValues = 0;
5277         return ERROR_SUCCESS;
5278     }
5279
5280     ++cbMaxValueNameLen;                        /* allow for trailing '\0' */
5281
5282     hHeap = GetProcessHeap ();
5283     if (hHeap == NULL)
5284     {
5285         ERR ("GetProcessHeap failed\n");
5286         r = RegCloseKey (hkSubKey);
5287         if (r != ERROR_SUCCESS)
5288             WARN ("RegCloseKey returned %i\n", r);
5289         return ERROR_OUTOFMEMORY;
5290     }
5291
5292     lpValueName = HeapAlloc (hHeap, 0, cbMaxValueNameLen * sizeof (WCHAR));
5293     if (lpValueName == NULL)
5294     {
5295         ERR ("Failed to allocate %i WCHARs from process heap\n", cbMaxValueNameLen);
5296         r = RegCloseKey (hkSubKey);
5297         if (r != ERROR_SUCCESS)
5298             WARN ("RegCloseKey returned %i\n", r);
5299         return ERROR_OUTOFMEMORY;
5300     }
5301
5302     lpValue = HeapAlloc (hHeap, 0, cbMaxValueLen);
5303     if (lpValue == NULL)
5304     {
5305         ERR ("Failed to allocate %i bytes from process heap\n", cbMaxValueLen);
5306         if (HeapFree (hHeap, 0, lpValueName) == 0)
5307             WARN ("HeapFree failed with code %i\n", GetLastError ());
5308         r = RegCloseKey (hkSubKey);
5309         if (r != ERROR_SUCCESS)
5310             WARN ("RegCloseKey returned %i\n", r);
5311         return ERROR_OUTOFMEMORY;
5312     }
5313
5314     TRACE ("pass 1: calculating buffer required for all names and values\n");
5315
5316     cbBufSize = cValues * sizeof (PRINTER_ENUM_VALUESW);
5317
5318     TRACE ("%i bytes required for %i headers\n", cbBufSize, cValues);
5319
5320     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
5321     {
5322         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
5323         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
5324                 NULL, NULL, lpValue, &cbValueLen);
5325         if (ret != ERROR_SUCCESS)
5326         {
5327             if (HeapFree (hHeap, 0, lpValue) == 0)
5328                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5329             if (HeapFree (hHeap, 0, lpValueName) == 0)
5330                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5331             r = RegCloseKey (hkSubKey);
5332             if (r != ERROR_SUCCESS)
5333                 WARN ("RegCloseKey returned %i\n", r);
5334             TRACE ("RegEnumValueW (%i) returned %i\n", dwIndex, ret);
5335             return ret;
5336         }
5337
5338         TRACE ("%s [%i]: name needs %i WCHARs, data needs %i bytes\n",
5339                 debugstr_w (lpValueName), dwIndex,
5340                 cbValueNameLen + 1, cbValueLen);
5341
5342         cbBufSize += (cbValueNameLen + 1) * sizeof (WCHAR);
5343         cbBufSize += cbValueLen;
5344     }
5345
5346     TRACE ("%i bytes required for all %i values\n", cbBufSize, cValues);
5347
5348     *pcbEnumValues = cbBufSize;
5349     *pnEnumValues = cValues;
5350
5351     if (cbEnumValues < cbBufSize)       /* buffer too small */
5352     {
5353         if (HeapFree (hHeap, 0, lpValue) == 0)
5354             WARN ("HeapFree failed with code %i\n", GetLastError ());
5355         if (HeapFree (hHeap, 0, lpValueName) == 0)
5356             WARN ("HeapFree failed with code %i\n", GetLastError ());
5357         r = RegCloseKey (hkSubKey);
5358         if (r != ERROR_SUCCESS)
5359             WARN ("RegCloseKey returned %i\n", r);
5360         TRACE ("%i byte buffer is not large enough\n", cbEnumValues);
5361         return ERROR_MORE_DATA;
5362     }
5363
5364     TRACE ("pass 2: copying all names and values to buffer\n");
5365
5366     ppev = (PPRINTER_ENUM_VALUESW) pEnumValues;         /* array of structs */
5367     pEnumValues += cValues * sizeof (PRINTER_ENUM_VALUESW);
5368
5369     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
5370     {
5371         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
5372         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
5373                 NULL, &dwType, lpValue, &cbValueLen);
5374         if (ret != ERROR_SUCCESS)
5375         {
5376             if (HeapFree (hHeap, 0, lpValue) == 0)
5377                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5378             if (HeapFree (hHeap, 0, lpValueName) == 0)
5379                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5380             r = RegCloseKey (hkSubKey);
5381             if (r != ERROR_SUCCESS)
5382                 WARN ("RegCloseKey returned %i\n", r);
5383             TRACE ("RegEnumValueW (%i) returned %i\n", dwIndex, ret);
5384             return ret;
5385         }
5386
5387         cbValueNameLen = (cbValueNameLen + 1) * sizeof (WCHAR);
5388         memcpy (pEnumValues, lpValueName, cbValueNameLen);
5389         ppev[dwIndex].pValueName = (LPWSTR) pEnumValues;
5390         pEnumValues += cbValueNameLen;
5391
5392         /* return # of *bytes* (including trailing \0), not # of chars */
5393         ppev[dwIndex].cbValueName = cbValueNameLen;
5394
5395         ppev[dwIndex].dwType = dwType;
5396
5397         memcpy (pEnumValues, lpValue, cbValueLen);
5398         ppev[dwIndex].pData = pEnumValues;
5399         pEnumValues += cbValueLen;
5400
5401         ppev[dwIndex].cbData = cbValueLen;
5402
5403         TRACE ("%s [%i]: copied name (%i bytes) and data (%i bytes)\n",
5404                 debugstr_w (lpValueName), dwIndex, cbValueNameLen, cbValueLen);
5405     }
5406
5407     if (HeapFree (hHeap, 0, lpValue) == 0)
5408     {
5409         ret = GetLastError ();
5410         ERR ("HeapFree failed with code %i\n", ret);
5411         if (HeapFree (hHeap, 0, lpValueName) == 0)
5412             WARN ("HeapFree failed with code %i\n", GetLastError ());
5413         r = RegCloseKey (hkSubKey);
5414         if (r != ERROR_SUCCESS)
5415             WARN ("RegCloseKey returned %i\n", r);
5416         return ret;
5417     }
5418
5419     if (HeapFree (hHeap, 0, lpValueName) == 0)
5420     {
5421         ret = GetLastError ();
5422         ERR ("HeapFree failed with code %i\n", ret);
5423         r = RegCloseKey (hkSubKey);
5424         if (r != ERROR_SUCCESS)
5425             WARN ("RegCloseKey returned %i\n", r);
5426         return ret;
5427     }
5428
5429     ret = RegCloseKey (hkSubKey);
5430     if (ret != ERROR_SUCCESS)
5431     {
5432         ERR ("RegCloseKey returned %i\n", ret);
5433         return ret;
5434     }
5435
5436     return ERROR_SUCCESS;
5437 }
5438
5439 /*******************************************************************************
5440  *              EnumPrinterDataExA      [WINSPOOL.@]
5441  *
5442  * This functions returns value names and REG_SZ, REG_EXPAND_SZ, and
5443  * REG_MULTI_SZ values as ASCII strings in Unicode-sized buffers.  This is
5444  * what Windows 2000 SP1 does.
5445  *
5446  */
5447 DWORD WINAPI EnumPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5448                                 LPBYTE pEnumValues, DWORD cbEnumValues,
5449                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
5450 {
5451     INT     len;
5452     LPWSTR  pKeyNameW;
5453     DWORD   ret, dwIndex, dwBufSize;
5454     HANDLE  hHeap;
5455     LPSTR   pBuffer;
5456
5457     TRACE ("%p %s\n", hPrinter, pKeyName);
5458
5459     if (pKeyName == NULL || *pKeyName == 0)
5460         return ERROR_INVALID_PARAMETER;
5461
5462     len = MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, NULL, 0);
5463     if (len == 0)
5464     {
5465         ret = GetLastError ();
5466         ERR ("MultiByteToWideChar failed with code %i\n", ret);
5467         return ret;
5468     }
5469
5470     hHeap = GetProcessHeap ();
5471     if (hHeap == NULL)
5472     {
5473         ERR ("GetProcessHeap failed\n");
5474         return ERROR_OUTOFMEMORY;
5475     }
5476
5477     pKeyNameW = HeapAlloc (hHeap, 0, len * sizeof (WCHAR));
5478     if (pKeyNameW == NULL)
5479     {
5480         ERR ("Failed to allocate %i bytes from process heap\n",
5481              (LONG)(len * sizeof (WCHAR)));
5482         return ERROR_OUTOFMEMORY;
5483     }
5484
5485     if (MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, pKeyNameW, len) == 0)
5486     {
5487         ret = GetLastError ();
5488         ERR ("MultiByteToWideChar failed with code %i\n", ret);
5489         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
5490             WARN ("HeapFree failed with code %i\n", GetLastError ());
5491         return ret;
5492     }
5493
5494     ret = EnumPrinterDataExW (hPrinter, pKeyNameW, pEnumValues, cbEnumValues,
5495             pcbEnumValues, pnEnumValues);
5496     if (ret != ERROR_SUCCESS)
5497     {
5498         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
5499             WARN ("HeapFree failed with code %i\n", GetLastError ());
5500         TRACE ("EnumPrinterDataExW returned %i\n", ret);
5501         return ret;
5502     }
5503
5504     if (HeapFree (hHeap, 0, pKeyNameW) == 0)
5505     {
5506         ret = GetLastError ();
5507         ERR ("HeapFree failed with code %i\n", ret);
5508         return ret;
5509     }
5510
5511     if (*pnEnumValues == 0)     /* empty key */
5512         return ERROR_SUCCESS;
5513
5514     dwBufSize = 0;
5515     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
5516     {
5517         PPRINTER_ENUM_VALUESW ppev =
5518                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
5519
5520         if (dwBufSize < ppev->cbValueName)
5521             dwBufSize = ppev->cbValueName;
5522
5523         if (dwBufSize < ppev->cbData && (ppev->dwType == REG_SZ ||
5524                 ppev->dwType == REG_EXPAND_SZ || ppev->dwType == REG_MULTI_SZ))
5525             dwBufSize = ppev->cbData;
5526     }
5527
5528     TRACE ("Largest Unicode name or value is %i bytes\n", dwBufSize);
5529
5530     pBuffer = HeapAlloc (hHeap, 0, dwBufSize);
5531     if (pBuffer == NULL)
5532     {
5533         ERR ("Failed to allocate %i bytes from process heap\n", dwBufSize);
5534         return ERROR_OUTOFMEMORY;
5535     }
5536
5537     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
5538     {
5539         PPRINTER_ENUM_VALUESW ppev =
5540                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
5541
5542         len = WideCharToMultiByte (CP_ACP, 0, ppev->pValueName,
5543                 ppev->cbValueName / sizeof (WCHAR), pBuffer, dwBufSize, NULL,
5544                 NULL);
5545         if (len == 0)
5546         {
5547             ret = GetLastError ();
5548             ERR ("WideCharToMultiByte failed with code %i\n", ret);
5549             if (HeapFree (hHeap, 0, pBuffer) == 0)
5550                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5551             return ret;
5552         }
5553
5554         memcpy (ppev->pValueName, pBuffer, len);
5555
5556         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
5557
5558         if (ppev->dwType != REG_SZ && ppev->dwType != REG_EXPAND_SZ &&
5559                 ppev->dwType != REG_MULTI_SZ)
5560             continue;
5561
5562         len = WideCharToMultiByte (CP_ACP, 0, (LPWSTR) ppev->pData,
5563                 ppev->cbData / sizeof (WCHAR), pBuffer, dwBufSize, NULL, NULL);
5564         if (len == 0)
5565         {
5566             ret = GetLastError ();
5567             ERR ("WideCharToMultiByte failed with code %i\n", ret);
5568             if (HeapFree (hHeap, 0, pBuffer) == 0)
5569                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5570             return ret;
5571         }
5572
5573         memcpy (ppev->pData, pBuffer, len);
5574
5575         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
5576         TRACE ("  (only first string of REG_MULTI_SZ printed)\n");
5577     }
5578
5579     if (HeapFree (hHeap, 0, pBuffer) == 0)
5580     {
5581         ret = GetLastError ();
5582         ERR ("HeapFree failed with code %i\n", ret);
5583         return ret;
5584     }
5585
5586     return ERROR_SUCCESS;
5587 }
5588
5589 /******************************************************************************
5590  *      AbortPrinter (WINSPOOL.@)
5591  */
5592 BOOL WINAPI AbortPrinter( HANDLE hPrinter )
5593 {
5594     FIXME("(%p), stub!\n", hPrinter);
5595     return TRUE;
5596 }
5597
5598 /******************************************************************************
5599  *              AddPortA (WINSPOOL.@)
5600  *
5601  * See AddPortW.
5602  *
5603  */
5604 BOOL WINAPI AddPortA(LPSTR pName, HWND hWnd, LPSTR pMonitorName)
5605 {
5606     FIXME("(%s, %p, %s), stub!\n",debugstr_a(pName),hWnd,debugstr_a(pMonitorName));
5607     return FALSE;
5608 }
5609
5610 /******************************************************************************
5611  *      AddPortW (WINSPOOL.@)
5612  *
5613  * Add a Port for a specific Monitor
5614  *
5615  * PARAMS
5616  *  pName        [I] Servername or NULL (local Computer)
5617  *  hWnd         [I] Handle to parent Window for the Dialog-Box
5618  *  pMonitorName [I] Name of the Monitor that manage the Port
5619  *
5620  * RETURNS
5621  *  Success: TRUE
5622  *  Failure: FALSE
5623  *
5624  * BUGS
5625  *  only a Stub
5626  *
5627  */
5628 BOOL WINAPI AddPortW(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
5629 {
5630     FIXME("(%s, %p, %s), stub!\n",debugstr_w(pName),hWnd,debugstr_w(pMonitorName));
5631     return FALSE;
5632 }
5633
5634 /******************************************************************************
5635  *             AddPortExA (WINSPOOL.@)
5636  *
5637  * See AddPortExW.
5638  *
5639  */
5640 BOOL WINAPI AddPortExA(HANDLE hMonitor, LPSTR pName, DWORD Level, LPBYTE lpBuffer, LPSTR lpMonitorName)
5641 {
5642     FIXME("(%p, %s, %d, %p, %s), stub!\n",hMonitor, debugstr_a(pName), Level,
5643           lpBuffer, debugstr_a(lpMonitorName));
5644     return FALSE;
5645 }
5646
5647 /******************************************************************************
5648  *             AddPortExW (WINSPOOL.@)
5649  *
5650  * Add a Port for a specific Monitor, without presenting a user interface
5651  *
5652  * PARAMS
5653  *  hMonitor      [I] Handle from InitializePrintMonitor2()
5654  *  pName         [I] Servername or NULL (local Computer)
5655  *  Level         [I] Structure-Level (1 or 2) for lpBuffer
5656  *  lpBuffer      [I] PTR to: PORT_INFO_1 or PORT_INFO_2
5657  *  lpMonitorName [I] Name of the Monitor that manage the Port or NULL
5658  *
5659  * RETURNS
5660  *  Success: TRUE
5661  *  Failure: FALSE
5662  *
5663  * BUGS
5664  *  only a Stub
5665  *
5666  */
5667 BOOL WINAPI AddPortExW(HANDLE hMonitor, LPWSTR pName, DWORD Level, LPBYTE lpBuffer, LPWSTR lpMonitorName)
5668 {
5669     FIXME("(%p, %s, %d, %p, %s), stub!\n", hMonitor, debugstr_w(pName), Level,
5670           lpBuffer, debugstr_w(lpMonitorName));
5671     return FALSE;
5672 }
5673
5674 /******************************************************************************
5675  *      AddPrinterConnectionA (WINSPOOL.@)
5676  */
5677 BOOL WINAPI AddPrinterConnectionA( LPSTR pName )
5678 {
5679     FIXME("%s\n", debugstr_a(pName));
5680     return FALSE;
5681 }
5682
5683 /******************************************************************************
5684  *      AddPrinterConnectionW (WINSPOOL.@)
5685  */
5686 BOOL WINAPI AddPrinterConnectionW( LPWSTR pName )
5687 {
5688     FIXME("%s\n", debugstr_w(pName));
5689     return FALSE;
5690 }
5691
5692 /******************************************************************************
5693  *              AddPrinterDriverExW (WINSPOOL.@)
5694  */
5695 BOOL WINAPI AddPrinterDriverExW( LPWSTR pName, DWORD Level,
5696     LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
5697 {
5698     FIXME("%s %d %p %d\n", debugstr_w(pName),
5699            Level, pDriverInfo, dwFileCopyFlags);
5700     SetLastError(ERROR_PRINTER_DRIVER_BLOCKED);
5701     return FALSE;
5702 }
5703
5704 /******************************************************************************
5705  *              AddPrinterDriverExA (WINSPOOL.@)
5706  */
5707 BOOL WINAPI AddPrinterDriverExA( LPSTR pName, DWORD Level,
5708     LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
5709 {
5710     FIXME("%s %d %p %d\n", debugstr_a(pName),
5711            Level, pDriverInfo, dwFileCopyFlags);
5712     SetLastError(ERROR_PRINTER_DRIVER_BLOCKED);
5713     return FALSE;
5714 }
5715
5716 /******************************************************************************
5717  *      ConfigurePortA (WINSPOOL.@)
5718  *
5719  * See ConfigurePortW.
5720  *
5721  */
5722 BOOL WINAPI ConfigurePortA(LPSTR pName, HWND hWnd, LPSTR pPortName)
5723 {
5724     FIXME("%s %p %s\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
5725     return FALSE;
5726 }
5727
5728 /******************************************************************************
5729  *      ConfigurePortW (WINSPOOL.@)
5730  *
5731  * Display the Configuration-Dialog for a specific Port
5732  *
5733  * PARAMS
5734  *  pName     [I] Servername or NULL (local Computer)
5735  *  hWnd      [I] Handle to parent Window for the Dialog-Box
5736  *  pPortName [I] Name of the Port, that should be configured
5737  *
5738  * RETURNS
5739  *  Success: TRUE
5740  *  Failure: FALSE
5741  *
5742  * BUGS
5743  *  only a Stub
5744  *
5745  */
5746 BOOL WINAPI ConfigurePortW(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
5747 {
5748     FIXME("%s %p %s\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
5749     return FALSE;
5750 }
5751
5752 /******************************************************************************
5753  *      ConnectToPrinterDlg (WINSPOOL.@)
5754  */
5755 HANDLE WINAPI ConnectToPrinterDlg( HWND hWnd, DWORD Flags )
5756 {
5757     FIXME("%p %x\n", hWnd, Flags);
5758     return NULL;
5759 }
5760
5761 /******************************************************************************
5762  *      DeletePrinterConnectionA (WINSPOOL.@)
5763  */
5764 BOOL WINAPI DeletePrinterConnectionA( LPSTR pName )
5765 {
5766     FIXME("%s\n", debugstr_a(pName));
5767     return TRUE;
5768 }
5769
5770 /******************************************************************************
5771  *      DeletePrinterConnectionW (WINSPOOL.@)
5772  */
5773 BOOL WINAPI DeletePrinterConnectionW( LPWSTR pName )
5774 {
5775     FIXME("%s\n", debugstr_w(pName));
5776     return TRUE;
5777 }
5778
5779 /******************************************************************************
5780  *              DeletePrinterDriverExW (WINSPOOL.@)
5781  */
5782 BOOL WINAPI DeletePrinterDriverExW( LPWSTR pName, LPWSTR pEnvironment,
5783     LPWSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
5784 {
5785     FIXME("%s %s %s %x %x\n", debugstr_w(pName), debugstr_w(pEnvironment),
5786           debugstr_w(pDriverName), dwDeleteFlag, dwVersionFlag);
5787     return TRUE;
5788 }
5789
5790 /******************************************************************************
5791  *              DeletePrinterDriverExA (WINSPOOL.@)
5792  */
5793 BOOL WINAPI DeletePrinterDriverExA( LPSTR pName, LPSTR pEnvironment,
5794     LPSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
5795 {
5796     FIXME("%s %s %s %x %x\n", debugstr_a(pName), debugstr_a(pEnvironment),
5797           debugstr_a(pDriverName), dwDeleteFlag, dwVersionFlag);
5798     return TRUE;
5799 }
5800
5801 /******************************************************************************
5802  *              DeletePrinterDataExW (WINSPOOL.@)
5803  */
5804 DWORD WINAPI DeletePrinterDataExW( HANDLE hPrinter, LPCWSTR pKeyName,
5805                                   LPCWSTR pValueName)
5806 {
5807     FIXME("%p %s %s\n", hPrinter, 
5808           debugstr_w(pKeyName), debugstr_w(pValueName));
5809     return ERROR_INVALID_PARAMETER;
5810 }
5811
5812 /******************************************************************************
5813  *              DeletePrinterDataExA (WINSPOOL.@)
5814  */
5815 DWORD WINAPI DeletePrinterDataExA( HANDLE hPrinter, LPCSTR pKeyName,
5816                                   LPCSTR pValueName)
5817 {
5818     FIXME("%p %s %s\n", hPrinter, 
5819           debugstr_a(pKeyName), debugstr_a(pValueName));
5820     return ERROR_INVALID_PARAMETER;
5821 }
5822
5823 /******************************************************************************
5824  *      DeletePrintProcessorA (WINSPOOL.@)
5825  */
5826 BOOL WINAPI DeletePrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProcessorName)
5827 {
5828     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
5829           debugstr_a(pPrintProcessorName));
5830     return TRUE;
5831 }
5832
5833 /******************************************************************************
5834  *      DeletePrintProcessorW (WINSPOOL.@)
5835  */
5836 BOOL WINAPI DeletePrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProcessorName)
5837 {
5838     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
5839           debugstr_w(pPrintProcessorName));
5840     return TRUE;
5841 }
5842
5843 /******************************************************************************
5844  *      DeletePrintProvidorA (WINSPOOL.@)
5845  */
5846 BOOL WINAPI DeletePrintProvidorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProviderName)
5847 {
5848     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
5849           debugstr_a(pPrintProviderName));
5850     return TRUE;
5851 }
5852
5853 /******************************************************************************
5854  *      DeletePrintProvidorW (WINSPOOL.@)
5855  */
5856 BOOL WINAPI DeletePrintProvidorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProviderName)
5857 {
5858     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
5859           debugstr_w(pPrintProviderName));
5860     return TRUE;
5861 }
5862
5863 /******************************************************************************
5864  *      EnumFormsA (WINSPOOL.@)
5865  */
5866 BOOL WINAPI EnumFormsA( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
5867     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
5868 {
5869     FIXME("%p %x %p %x %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
5870     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5871     return FALSE;
5872 }
5873
5874 /******************************************************************************
5875  *      EnumFormsW (WINSPOOL.@)
5876  */
5877 BOOL WINAPI EnumFormsW( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
5878     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
5879 {
5880     FIXME("%p %x %p %x %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
5881     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5882     return FALSE;
5883 }
5884
5885 /*****************************************************************************
5886  *          EnumMonitorsA [WINSPOOL.@]
5887  *
5888  * See EnumMonitorsW.
5889  *
5890  */
5891 BOOL WINAPI EnumMonitorsA(LPSTR pName, DWORD Level, LPBYTE pMonitors,
5892                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
5893 {
5894     BOOL    res;
5895     LPBYTE  bufferW = NULL;
5896     LPWSTR  nameW = NULL;
5897     DWORD   needed = 0;
5898     DWORD   numentries = 0;
5899     INT     len;
5900
5901     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), Level, pMonitors,
5902           cbBuf, pcbNeeded, pcReturned);
5903
5904     /* convert servername to unicode */
5905     if (pName) {
5906         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
5907         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5908         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
5909     }
5910     /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the monitors */
5911     needed = cbBuf * sizeof(WCHAR);    
5912     if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
5913     res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5914
5915     if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
5916         if (pcbNeeded) needed = *pcbNeeded;
5917         /* HeapReAlloc return NULL, when bufferW was NULL */
5918         bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
5919                               HeapAlloc(GetProcessHeap(), 0, needed);
5920
5921         /* Try again with the large Buffer */
5922         res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5923     }
5924     numentries = pcReturned ? *pcReturned : 0;
5925     needed = 0;
5926     /*
5927        W2k require the buffersize from EnumMonitorsW also for EnumMonitorsA.
5928        We use the smaller Ansi-Size to avoid conflicts with fixed Buffers of old Apps.
5929      */
5930     if (res) {
5931         /* EnumMonitorsW collected all Data. Parse them to caclulate ANSI-Size */
5932         DWORD   entrysize = 0;
5933         DWORD   index;
5934         LPSTR   ptr;
5935         LPMONITOR_INFO_2W mi2w;
5936         LPMONITOR_INFO_2A mi2a;
5937
5938         /* MONITOR_INFO_*W and MONITOR_INFO_*A have the same size */
5939         entrysize = (Level == 1) ? sizeof(MONITOR_INFO_1A) : sizeof(MONITOR_INFO_2A);
5940
5941         /* First pass: calculate the size for all Entries */
5942         mi2w = (LPMONITOR_INFO_2W) bufferW;
5943         mi2a = (LPMONITOR_INFO_2A) pMonitors;
5944         index = 0;
5945         while (index < numentries) {
5946             index++;
5947             needed += entrysize;    /* MONITOR_INFO_?A */
5948             TRACE("%p: parsing #%d (%s)\n", mi2w, index, debugstr_w(mi2w->pName));
5949
5950             needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
5951                                             NULL, 0, NULL, NULL);
5952             if (Level > 1) {
5953                 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
5954                                                 NULL, 0, NULL, NULL);
5955                 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
5956                                                 NULL, 0, NULL, NULL);
5957             }
5958             /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
5959             mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
5960             mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
5961         }
5962
5963         /* check for errors and quit on failure */
5964         if (cbBuf < needed) {
5965             SetLastError(ERROR_INSUFFICIENT_BUFFER);
5966             res = FALSE;
5967             goto emA_cleanup;
5968         }
5969         len = entrysize * numentries;       /* room for all MONITOR_INFO_?A */
5970         ptr = (LPSTR) &pMonitors[len];      /* room for strings */
5971         cbBuf -= len ;                      /* free Bytes in the user-Buffer */
5972         mi2w = (LPMONITOR_INFO_2W) bufferW;
5973         mi2a = (LPMONITOR_INFO_2A) pMonitors;
5974         index = 0;
5975         /* Second Pass: Fill the User Buffer (if we have one) */
5976         while ((index < numentries) && pMonitors) {
5977             index++;
5978             TRACE("%p: writing MONITOR_INFO_%dA #%d\n", mi2a, Level, index);
5979             mi2a->pName = ptr;
5980             len = WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
5981                                             ptr, cbBuf , NULL, NULL);
5982             ptr += len;
5983             cbBuf -= len;
5984             if (Level > 1) {
5985                 mi2a->pEnvironment = ptr;
5986                 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
5987                                             ptr, cbBuf, NULL, NULL);
5988                 ptr += len;
5989                 cbBuf -= len;
5990
5991                 mi2a->pDLLName = ptr;
5992                 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
5993                                             ptr, cbBuf, NULL, NULL);
5994                 ptr += len;
5995                 cbBuf -= len;
5996             }
5997             /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
5998             mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
5999             mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
6000         }
6001     }
6002 emA_cleanup:
6003     if (pcbNeeded)  *pcbNeeded = needed;
6004     if (pcReturned) *pcReturned = (res) ? numentries : 0;
6005
6006     HeapFree(GetProcessHeap(), 0, nameW);
6007     HeapFree(GetProcessHeap(), 0, bufferW);
6008
6009     TRACE("returning %d with %d (%d byte for %d entries)\n", 
6010             (res), GetLastError(), needed, numentries);
6011
6012     return (res);
6013
6014 }
6015
6016 /*****************************************************************************
6017  *          EnumMonitorsW [WINSPOOL.@]
6018  *
6019  * Enumerate available Port-Monitors
6020  *
6021  * PARAMS
6022  *  pName       [I] Servername or NULL (local Computer)
6023  *  Level       [I] Structure-Level (1:Win9x+NT or 2:NT only)
6024  *  pMonitors   [O] PTR to Buffer that receives the Result
6025  *  cbBuf       [I] Size of Buffer at pMonitors
6026  *  pcbNeeded   [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
6027  *  pcReturned  [O] PTR to DWORD that receives the number of Monitors in pMonitors
6028  *
6029  * RETURNS
6030  *  Success: TRUE
6031  *  Failure: FALSE and in pcbNeeded the Bytes required for buffer, if cbBuf is too small
6032  *
6033  * NOTES
6034  *  Windows reads the Registry once and cache the Results.
6035  *
6036  *|  Language-Monitors are also installed in the same Registry-Location but 
6037  *|  they are filtered in Windows (not returned by EnumMonitors).
6038  *|  We do no filtering to simplify our Code.
6039  *
6040  */
6041 BOOL WINAPI EnumMonitorsW(LPWSTR pName, DWORD Level, LPBYTE pMonitors,
6042                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
6043 {
6044     DWORD   needed = 0;
6045     DWORD   numentries = 0;
6046     BOOL    res = FALSE;
6047
6048     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pMonitors,
6049           cbBuf, pcbNeeded, pcReturned);
6050
6051     if (pName && (lstrlenW(pName))) {
6052         FIXME("for Server %s not implemented\n", debugstr_w(pName));
6053         SetLastError(ERROR_ACCESS_DENIED);
6054         goto emW_cleanup;
6055     }
6056
6057     /* Level is not checked in win9x */
6058     if (!Level || (Level > 2)) {
6059         WARN("level (%d) is ignored in win9x\n", Level);
6060         SetLastError(ERROR_INVALID_LEVEL);
6061         goto emW_cleanup;
6062     }
6063     if (!pcbNeeded) {
6064         SetLastError(RPC_X_NULL_REF_POINTER);
6065         goto emW_cleanup;
6066     }
6067
6068     /* Scan all Monitor-Keys */
6069     numentries = 0;
6070     needed = get_local_monitors(Level, NULL, 0, &numentries);
6071
6072     /* we calculated the needed buffersize. now do the error-checks */
6073     if (cbBuf < needed) {
6074         SetLastError(ERROR_INSUFFICIENT_BUFFER);
6075         goto emW_cleanup;
6076     }
6077     else if (!pMonitors || !pcReturned) {
6078         SetLastError(RPC_X_NULL_REF_POINTER);
6079         goto emW_cleanup;
6080     }
6081
6082     /* fill the Buffer with the Monitor-Keys */
6083     needed = get_local_monitors(Level, pMonitors, cbBuf, &numentries);
6084     res = TRUE;
6085
6086 emW_cleanup:
6087     if (pcbNeeded)  *pcbNeeded = needed;
6088     if (pcReturned) *pcReturned = numentries;
6089
6090     TRACE("returning %d with %d (%d byte for %d entries)\n", 
6091             res, GetLastError(), needed, numentries);
6092
6093     return (res);
6094 }
6095
6096 /******************************************************************************
6097  *              XcvDataW (WINSPOOL.@)
6098  *
6099  * Notes:
6100  *  There doesn't seem to be an A version...
6101  */
6102 BOOL WINAPI XcvDataW( HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData,
6103     DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData,
6104     PDWORD pcbOutputNeeded, PDWORD pdwStatus)
6105 {
6106     FIXME("%p %s %p %d %p %d %p %p\n", hXcv, debugstr_w(pszDataName), 
6107           pInputData, cbInputData, pOutputData,
6108           cbOutputData, pcbOutputNeeded, pdwStatus);
6109     return FALSE;
6110 }
6111
6112 /*****************************************************************************
6113  *          EnumPrinterDataA [WINSPOOL.@]
6114  *
6115  */
6116 DWORD WINAPI EnumPrinterDataA( HANDLE hPrinter, DWORD dwIndex, LPSTR pValueName,
6117     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
6118     DWORD cbData, LPDWORD pcbData )
6119 {
6120     FIXME("%p %x %p %x %p %p %p %x %p\n", hPrinter, dwIndex, pValueName,
6121           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
6122     return ERROR_NO_MORE_ITEMS;
6123 }
6124
6125 /*****************************************************************************
6126  *          EnumPrinterDataW [WINSPOOL.@]
6127  *
6128  */
6129 DWORD WINAPI EnumPrinterDataW( HANDLE hPrinter, DWORD dwIndex, LPWSTR pValueName,
6130     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
6131     DWORD cbData, LPDWORD pcbData )
6132 {
6133     FIXME("%p %x %p %x %p %p %p %x %p\n", hPrinter, dwIndex, pValueName,
6134           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
6135     return ERROR_NO_MORE_ITEMS;
6136 }
6137
6138 /*****************************************************************************
6139  *          EnumPrintProcessorDatatypesA [WINSPOOL.@]
6140  *
6141  */
6142 BOOL WINAPI EnumPrintProcessorDatatypesA(LPSTR pName, LPSTR pPrintProcessorName,
6143                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
6144                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
6145 {
6146     FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_a(pName),
6147           debugstr_a(pPrintProcessorName), Level, pDatatypes, cbBuf,
6148           pcbNeeded, pcReturned);
6149     return FALSE;
6150 }
6151
6152 /*****************************************************************************
6153  *          EnumPrintProcessorDatatypesW [WINSPOOL.@]
6154  *
6155  */
6156 BOOL WINAPI EnumPrintProcessorDatatypesW(LPWSTR pName, LPWSTR pPrintProcessorName,
6157                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
6158                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
6159 {
6160     FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_w(pName),
6161           debugstr_w(pPrintProcessorName), Level, pDatatypes, cbBuf,
6162           pcbNeeded, pcReturned);
6163     return FALSE;
6164 }
6165
6166 /*****************************************************************************
6167  *          EnumPrintProcessorsA [WINSPOOL.@]
6168  *
6169  */
6170 BOOL WINAPI EnumPrintProcessorsA(LPSTR pName, LPSTR pEnvironment, DWORD Level, 
6171     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
6172 {
6173     FIXME("Stub: %s %s %d %p %d %p %p\n", pName, pEnvironment, Level,
6174         pPrintProcessorInfo, cbBuf, pcbNeeded, pcbReturned);
6175     return FALSE;
6176 }
6177
6178 /*****************************************************************************
6179  *          EnumPrintProcessorsW [WINSPOOL.@]
6180  *
6181  */
6182 BOOL WINAPI EnumPrintProcessorsW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
6183     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
6184 {
6185     FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_w(pName),
6186         debugstr_w(pEnvironment), Level, pPrintProcessorInfo,
6187         cbBuf, pcbNeeded, pcbReturned);
6188     return FALSE;
6189 }
6190
6191 /*****************************************************************************
6192  *          ExtDeviceMode [WINSPOOL.@]
6193  *
6194  */
6195 LONG WINAPI ExtDeviceMode( HWND hWnd, HANDLE hInst, LPDEVMODEA pDevModeOutput,
6196     LPSTR pDeviceName, LPSTR pPort, LPDEVMODEA pDevModeInput, LPSTR pProfile,
6197     DWORD fMode)
6198 {
6199     FIXME("Stub: %p %p %p %s %s %p %s %x\n", hWnd, hInst, pDevModeOutput,
6200           debugstr_a(pDeviceName), debugstr_a(pPort), pDevModeInput,
6201           debugstr_a(pProfile), fMode);
6202     return -1;
6203 }
6204
6205 /*****************************************************************************
6206  *          FindClosePrinterChangeNotification [WINSPOOL.@]
6207  *
6208  */
6209 BOOL WINAPI FindClosePrinterChangeNotification( HANDLE hChange )
6210 {
6211     FIXME("Stub: %p\n", hChange);
6212     return TRUE;
6213 }
6214
6215 /*****************************************************************************
6216  *          FindFirstPrinterChangeNotification [WINSPOOL.@]
6217  *
6218  */
6219 HANDLE WINAPI FindFirstPrinterChangeNotification( HANDLE hPrinter,
6220     DWORD fdwFlags, DWORD fdwOptions, LPVOID pPrinterNotifyOptions )
6221 {
6222     FIXME("Stub: %p %x %x %p\n",
6223           hPrinter, fdwFlags, fdwOptions, pPrinterNotifyOptions);
6224     return INVALID_HANDLE_VALUE;
6225 }
6226
6227 /*****************************************************************************
6228  *          FindNextPrinterChangeNotification [WINSPOOL.@]
6229  *
6230  */
6231 BOOL WINAPI FindNextPrinterChangeNotification( HANDLE hChange, PDWORD pdwChange,
6232     LPVOID pPrinterNotifyOptions, LPVOID *ppPrinterNotifyInfo )
6233 {
6234     FIXME("Stub: %p %p %p %p\n",
6235           hChange, pdwChange, pPrinterNotifyOptions, ppPrinterNotifyInfo);
6236     return FALSE;
6237 }
6238
6239 /*****************************************************************************
6240  *          FreePrinterNotifyInfo [WINSPOOL.@]
6241  *
6242  */
6243 BOOL WINAPI FreePrinterNotifyInfo( PPRINTER_NOTIFY_INFO pPrinterNotifyInfo )
6244 {
6245     FIXME("Stub: %p\n", pPrinterNotifyInfo);
6246     return TRUE;
6247 }
6248
6249 /*****************************************************************************
6250  *          string_to_buf
6251  *
6252  * Copies a unicode string into a buffer.  The buffer will either contain unicode or
6253  * ansi depending on the unicode parameter.
6254  */
6255 static BOOL string_to_buf(LPCWSTR str, LPBYTE ptr, DWORD cb, DWORD *size, BOOL unicode)
6256 {
6257     if(!str)
6258     {
6259         *size = 0;
6260         return TRUE;
6261     }
6262
6263     if(unicode)
6264     {
6265         *size = (strlenW(str) + 1) * sizeof(WCHAR);
6266         if(*size <= cb)
6267         {
6268             memcpy(ptr, str, *size);
6269             return TRUE;
6270         }
6271         return FALSE;
6272     }
6273     else
6274     {
6275         *size = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
6276         if(*size <= cb)
6277         {
6278             WideCharToMultiByte(CP_ACP, 0, str, -1, (LPSTR)ptr, *size, NULL, NULL);
6279             return TRUE;
6280         }
6281         return FALSE;
6282     }
6283 }
6284
6285 /*****************************************************************************
6286  *          get_job_info_1
6287  */
6288 static BOOL get_job_info_1(job_t *job, JOB_INFO_1W *ji1, LPBYTE buf, DWORD cbBuf,
6289                            LPDWORD pcbNeeded, BOOL unicode)
6290 {
6291     DWORD size, left = cbBuf;
6292     BOOL space = (cbBuf > 0);
6293     LPBYTE ptr = buf;
6294
6295     *pcbNeeded = 0;
6296
6297     if(space)
6298     {
6299         ji1->JobId = job->job_id;
6300     }
6301
6302     string_to_buf(job->document_title, ptr, left, &size, unicode);
6303     if(space && size <= left)
6304     {
6305         ji1->pDocument = (LPWSTR)ptr;
6306         ptr += size;
6307         left -= size;
6308     }
6309     else
6310         space = FALSE;
6311     *pcbNeeded += size;
6312
6313     return space;
6314 }
6315
6316 /*****************************************************************************
6317  *          get_job_info_2
6318  */
6319 static BOOL get_job_info_2(job_t *job, JOB_INFO_2W *ji2, LPBYTE buf, DWORD cbBuf,
6320                            LPDWORD pcbNeeded, BOOL unicode)
6321 {
6322     DWORD size, left = cbBuf;
6323     BOOL space = (cbBuf > 0);
6324     LPBYTE ptr = buf;
6325
6326     *pcbNeeded = 0;
6327
6328     if(space)
6329     {
6330         ji2->JobId = job->job_id;
6331     }
6332
6333     string_to_buf(job->document_title, ptr, left, &size, unicode);
6334     if(space && size <= left)
6335     {
6336         ji2->pDocument = (LPWSTR)ptr;
6337         ptr += size;
6338         left -= size;
6339     }
6340     else
6341         space = FALSE;
6342     *pcbNeeded += size;
6343
6344     return space;
6345 }
6346
6347 /*****************************************************************************
6348  *          get_job_info
6349  */
6350 static BOOL get_job_info(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
6351                          DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
6352 {
6353     BOOL ret = FALSE;
6354     DWORD needed = 0, size;
6355     job_t *job;
6356     LPBYTE ptr = pJob;
6357
6358     TRACE("%p %d %d %p %d %p\n", hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
6359
6360     EnterCriticalSection(&printer_handles_cs);
6361     job = get_job(hPrinter, JobId);
6362     if(!job)
6363         goto end;
6364
6365     switch(Level)
6366     {
6367     case 1:
6368         size = sizeof(JOB_INFO_1W);
6369         if(cbBuf >= size)
6370         {
6371             cbBuf -= size;
6372             ptr += size;
6373             memset(pJob, 0, size);
6374         }
6375         else
6376             cbBuf = 0;
6377         ret = get_job_info_1(job, (JOB_INFO_1W *)pJob, ptr, cbBuf, &needed, unicode);
6378         needed += size;
6379         break;
6380
6381     case 2:
6382         size = sizeof(JOB_INFO_2W);
6383         if(cbBuf >= size)
6384         {
6385             cbBuf -= size;
6386             ptr += size;
6387             memset(pJob, 0, size);
6388         }
6389         else
6390             cbBuf = 0;
6391         ret = get_job_info_2(job, (JOB_INFO_2W *)pJob, ptr, cbBuf, &needed, unicode);
6392         needed += size;
6393         break;
6394
6395     case 3:
6396         size = sizeof(JOB_INFO_3);
6397         if(cbBuf >= size)
6398         {
6399             cbBuf -= size;
6400             memset(pJob, 0, size);
6401             ret = TRUE;
6402         }
6403         else
6404             cbBuf = 0;
6405         needed = size;
6406         break;
6407
6408     default:
6409         SetLastError(ERROR_INVALID_LEVEL);
6410         goto end;
6411     }
6412     if(pcbNeeded)
6413         *pcbNeeded = needed;
6414 end:
6415     LeaveCriticalSection(&printer_handles_cs);
6416     return ret;
6417 }
6418
6419 /*****************************************************************************
6420  *          GetJobA [WINSPOOL.@]
6421  *
6422  */
6423 BOOL WINAPI GetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
6424                     DWORD cbBuf, LPDWORD pcbNeeded)
6425 {
6426     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, FALSE);
6427 }
6428
6429 /*****************************************************************************
6430  *          GetJobW [WINSPOOL.@]
6431  *
6432  */
6433 BOOL WINAPI GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
6434                     DWORD cbBuf, LPDWORD pcbNeeded)
6435 {
6436     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, TRUE);
6437 }
6438
6439 /*****************************************************************************
6440  *          schedule_lpr
6441  */
6442 static BOOL schedule_lpr(LPCWSTR printer_name, LPCWSTR filename)
6443 {
6444     char *unixname, *queue, *cmd;
6445     char fmt[] = "lpr -P%s %s";
6446     DWORD len;
6447
6448     if(!(unixname = wine_get_unix_file_name(filename)))
6449         return FALSE;
6450
6451     len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
6452     queue = HeapAlloc(GetProcessHeap(), 0, len);
6453     WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
6454
6455     cmd = HeapAlloc(GetProcessHeap(), 0, strlen(unixname) + len + sizeof(fmt) - 5);
6456     sprintf(cmd, fmt, queue, unixname);
6457
6458     TRACE("printing with: %s\n", cmd);
6459     system(cmd);
6460
6461     HeapFree(GetProcessHeap(), 0, cmd);
6462     HeapFree(GetProcessHeap(), 0, queue);
6463     HeapFree(GetProcessHeap(), 0, unixname);
6464     return TRUE;
6465 }
6466
6467 /*****************************************************************************
6468  *          schedule_cups
6469  */
6470 static BOOL schedule_cups(LPCWSTR printer_name, LPCWSTR filename, LPCWSTR document_title)
6471 {
6472 #if HAVE_CUPS_CUPS_H
6473     if(pcupsPrintFile)
6474     {
6475         char *unixname, *queue, *doc_titleA;
6476         DWORD len;
6477         BOOL ret;
6478
6479         if(!(unixname = wine_get_unix_file_name(filename)))
6480             return FALSE;
6481
6482         len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
6483         queue = HeapAlloc(GetProcessHeap(), 0, len);
6484         WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
6485
6486         len = WideCharToMultiByte(CP_ACP, 0, document_title, -1, NULL, 0, NULL, NULL);
6487         doc_titleA = HeapAlloc(GetProcessHeap(), 0, len);
6488         WideCharToMultiByte(CP_ACP, 0, document_title, -1, doc_titleA, len, NULL, NULL);
6489
6490         TRACE("printing via cups\n");
6491         ret = pcupsPrintFile(queue, unixname, doc_titleA, 0, NULL);
6492         HeapFree(GetProcessHeap(), 0, doc_titleA);
6493         HeapFree(GetProcessHeap(), 0, queue);
6494         HeapFree(GetProcessHeap(), 0, unixname);
6495         return ret;
6496     }
6497     else
6498 #endif
6499     {
6500         return schedule_lpr(printer_name, filename);
6501     }
6502 }
6503
6504 INT_PTR CALLBACK file_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
6505 {
6506     LPWSTR filename;
6507
6508     switch(msg)
6509     {
6510     case WM_INITDIALOG:
6511         SetWindowLongPtrW(hwnd, DWLP_USER, lparam);
6512         return TRUE;
6513
6514     case WM_COMMAND:
6515         if(HIWORD(wparam) == BN_CLICKED)
6516         {
6517             if(LOWORD(wparam) == IDOK)
6518             {
6519                 HANDLE hf;
6520                 DWORD len = SendDlgItemMessageW(hwnd, EDITBOX, WM_GETTEXTLENGTH, 0, 0);
6521                 LPWSTR *output;
6522
6523                 filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
6524                 GetDlgItemTextW(hwnd, EDITBOX, filename, len + 1);
6525
6526                 if(GetFileAttributesW(filename) != INVALID_FILE_ATTRIBUTES)
6527                 {
6528                     WCHAR caption[200], message[200];
6529                     int mb_ret;
6530
6531                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
6532                     LoadStringW(WINSPOOL_hInstance, IDS_FILE_EXISTS, message, sizeof(message) / sizeof(WCHAR));
6533                     mb_ret = MessageBoxW(hwnd, message, caption, MB_OKCANCEL | MB_ICONEXCLAMATION);
6534                     if(mb_ret == IDCANCEL)
6535                     {
6536                         HeapFree(GetProcessHeap(), 0, filename);
6537                         return TRUE;
6538                     }
6539                 }
6540                 hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
6541                 if(hf == INVALID_HANDLE_VALUE)
6542                 {
6543                     WCHAR caption[200], message[200];
6544
6545                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
6546                     LoadStringW(WINSPOOL_hInstance, IDS_CANNOT_OPEN, message, sizeof(message) / sizeof(WCHAR));
6547                     MessageBoxW(hwnd, message, caption, MB_OK | MB_ICONEXCLAMATION);
6548                     HeapFree(GetProcessHeap(), 0, filename);
6549                     return TRUE;
6550                 }
6551                 CloseHandle(hf);
6552                 DeleteFileW(filename);
6553                 output = (LPWSTR *)GetWindowLongPtrW(hwnd, DWLP_USER);
6554                 *output = filename;
6555                 EndDialog(hwnd, IDOK);
6556                 return TRUE;
6557             }
6558             if(LOWORD(wparam) == IDCANCEL)
6559             {
6560                 EndDialog(hwnd, IDCANCEL);
6561                 return TRUE;
6562             }
6563         }
6564         return FALSE;
6565     }
6566     return FALSE;
6567 }
6568
6569 /*****************************************************************************
6570  *          get_filename
6571  */
6572 static BOOL get_filename(LPWSTR *filename)
6573 {
6574     return DialogBoxParamW(WINSPOOL_hInstance, MAKEINTRESOURCEW(FILENAME_DIALOG), GetForegroundWindow(),
6575                            file_dlg_proc, (LPARAM)filename) == IDOK;
6576 }
6577
6578 /*****************************************************************************
6579  *          schedule_file
6580  */
6581 static BOOL schedule_file(LPCWSTR filename)
6582 {
6583     LPWSTR output = NULL;
6584
6585     if(get_filename(&output))
6586     {
6587         TRACE("copy to %s\n", debugstr_w(output));
6588         CopyFileW(filename, output, FALSE);
6589         HeapFree(GetProcessHeap(), 0, output);
6590         return TRUE;
6591     }
6592     return FALSE;
6593 }
6594
6595 /*****************************************************************************
6596  *          schedule_pipe
6597  */
6598 static BOOL schedule_pipe(LPCWSTR cmd, LPCWSTR filename)
6599 {
6600 #ifdef HAVE_FORK
6601     char *unixname, *cmdA;
6602     DWORD len;
6603     int fds[2] = {-1, -1}, file_fd = -1, no_read;
6604     BOOL ret = FALSE;
6605     char buf[1024];
6606
6607     if(!(unixname = wine_get_unix_file_name(filename)))
6608         return FALSE;
6609
6610     len = WideCharToMultiByte(CP_ACP, 0, cmd, -1, NULL, 0, NULL, NULL);
6611     cmdA = HeapAlloc(GetProcessHeap(), 0, len);
6612     WideCharToMultiByte(CP_ACP, 0, cmd, -1, cmdA, len, NULL, NULL);
6613
6614     TRACE("printing with: %s\n", cmdA);
6615
6616     if((file_fd = open(unixname, O_RDONLY)) == -1)
6617         goto end;
6618
6619     if (pipe(fds))
6620     {
6621         ERR("pipe() failed!\n"); 
6622         goto end;
6623     }
6624
6625     if (fork() == 0)
6626     {
6627         close(0);
6628         dup2(fds[0], 0);
6629         close(fds[1]);
6630
6631         /* reset signals that we previously set to SIG_IGN */
6632         signal(SIGPIPE, SIG_DFL);
6633         signal(SIGCHLD, SIG_DFL);
6634
6635         system(cmdA);
6636         exit(0);
6637     }
6638
6639     while((no_read = read(file_fd, buf, sizeof(buf))) > 0)
6640         write(fds[1], buf, no_read);
6641
6642     ret = TRUE;
6643
6644 end:
6645     if(file_fd != -1) close(file_fd);
6646     if(fds[0] != -1) close(fds[0]);
6647     if(fds[1] != -1) close(fds[1]);
6648
6649     HeapFree(GetProcessHeap(), 0, cmdA);
6650     HeapFree(GetProcessHeap(), 0, unixname);
6651     return ret;
6652 #else
6653     return FALSE;
6654 #endif
6655 }
6656
6657 /*****************************************************************************
6658  *          schedule_unixfile
6659  */
6660 static BOOL schedule_unixfile(LPCWSTR output, LPCWSTR filename)
6661 {
6662     int in_fd, out_fd, no_read;
6663     char buf[1024];
6664     BOOL ret = FALSE;
6665     char *unixname, *outputA;
6666     DWORD len;
6667
6668     if(!(unixname = wine_get_unix_file_name(filename)))
6669         return FALSE;
6670
6671     len = WideCharToMultiByte(CP_ACP, 0, output, -1, NULL, 0, NULL, NULL);
6672     outputA = HeapAlloc(GetProcessHeap(), 0, len);
6673     WideCharToMultiByte(CP_ACP, 0, output, -1, outputA, len, NULL, NULL);
6674     
6675     out_fd = open(outputA, O_CREAT | O_TRUNC | O_WRONLY, 0666);
6676     in_fd = open(unixname, O_RDONLY);
6677     if(out_fd == -1 || in_fd == -1)
6678         goto end;
6679
6680     while((no_read = read(in_fd, buf, sizeof(buf))) > 0)
6681         write(out_fd, buf, no_read);
6682
6683     ret = TRUE;
6684 end:
6685     if(in_fd != -1) close(in_fd);
6686     if(out_fd != -1) close(out_fd);
6687     HeapFree(GetProcessHeap(), 0, outputA);
6688     HeapFree(GetProcessHeap(), 0, unixname);
6689     return ret;
6690 }
6691
6692 /*****************************************************************************
6693  *          ScheduleJob [WINSPOOL.@]
6694  *
6695  */
6696 BOOL WINAPI ScheduleJob( HANDLE hPrinter, DWORD dwJobID )
6697 {
6698     opened_printer_t *printer;
6699     BOOL ret = FALSE;
6700     struct list *cursor, *cursor2;
6701
6702     TRACE("(%p, %x)\n", hPrinter, dwJobID);
6703     EnterCriticalSection(&printer_handles_cs);
6704     printer = get_opened_printer(hPrinter);
6705     if(!printer)
6706         goto end;
6707
6708     LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
6709     {
6710         job_t *job = LIST_ENTRY(cursor, job_t, entry);
6711         HANDLE hf;
6712
6713         if(job->job_id != dwJobID) continue;
6714
6715         hf = CreateFileW(job->filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
6716         if(hf != INVALID_HANDLE_VALUE)
6717         {
6718             PRINTER_INFO_5W *pi5;
6719             DWORD needed;
6720             HKEY hkey;
6721             WCHAR output[1024];
6722             static const WCHAR spooler_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
6723                                                 'P','r','i','n','t','i','n','g','\\','S','p','o','o','l','e','r',0};
6724
6725             GetPrinterW(hPrinter, 5, NULL, 0, &needed);
6726             pi5 = HeapAlloc(GetProcessHeap(), 0, needed);
6727             GetPrinterW(hPrinter, 5, (LPBYTE)pi5, needed, &needed);
6728             TRACE("need to schedule job %d filename %s to port %s\n", job->job_id, debugstr_w(job->filename),
6729                   debugstr_w(pi5->pPortName));
6730             
6731             output[0] = 0;
6732
6733             /* @@ Wine registry key: HKCU\Software\Wine\Printing\Spooler */
6734             if(RegOpenKeyW(HKEY_CURRENT_USER, spooler_key, &hkey) == ERROR_SUCCESS)
6735             {
6736                 DWORD type, count = sizeof(output);
6737                 RegQueryValueExW(hkey, pi5->pPortName, NULL, &type, (LPBYTE)output, &count);
6738                 RegCloseKey(hkey);
6739             }
6740             if(output[0] == '|')
6741             {
6742                 schedule_pipe(output + 1, job->filename);
6743             }
6744             else if(output[0])
6745             {
6746                 schedule_unixfile(output, job->filename);
6747             }
6748             else if(!strncmpW(pi5->pPortName, LPR_Port, strlenW(LPR_Port)))
6749             {
6750                 schedule_lpr(pi5->pPortName + strlenW(LPR_Port), job->filename);
6751             }
6752             else if(!strncmpW(pi5->pPortName, CUPS_Port, strlenW(CUPS_Port)))
6753             {
6754                 schedule_cups(pi5->pPortName + strlenW(CUPS_Port), job->filename, job->document_title);
6755             }
6756             else if(!strncmpW(pi5->pPortName, FILE_Port, strlenW(FILE_Port)))
6757             {
6758                 schedule_file(job->filename);
6759             }
6760             else
6761             {
6762                 FIXME("can't schedule to port %s\n", debugstr_w(pi5->pPortName));
6763             }
6764             HeapFree(GetProcessHeap(), 0, pi5);
6765             CloseHandle(hf);
6766             DeleteFileW(job->filename);
6767         }
6768         list_remove(cursor);
6769         HeapFree(GetProcessHeap(), 0, job->document_title);
6770         HeapFree(GetProcessHeap(), 0, job->filename);
6771         HeapFree(GetProcessHeap(), 0, job);
6772         ret = TRUE;
6773         break;
6774     }
6775 end:
6776     LeaveCriticalSection(&printer_handles_cs);
6777     return ret;
6778 }
6779
6780 /*****************************************************************************
6781  *          StartDocDlgA [WINSPOOL.@]
6782  */
6783 LPSTR WINAPI StartDocDlgA( HANDLE hPrinter, DOCINFOA *doc )
6784 {
6785     UNICODE_STRING usBuffer;
6786     DOCINFOW docW;
6787     LPWSTR retW;
6788     LPWSTR docnameW = NULL, outputW = NULL, datatypeW = NULL;
6789     LPSTR ret = NULL;
6790
6791     docW.cbSize = sizeof(docW);
6792     if (doc->lpszDocName)
6793     {
6794         docnameW = asciitounicode(&usBuffer, doc->lpszDocName);
6795         if (!(docW.lpszDocName = docnameW)) return NULL;
6796     }
6797     if (doc->lpszOutput)
6798     {
6799         outputW = asciitounicode(&usBuffer, doc->lpszOutput);
6800         if (!(docW.lpszOutput = outputW)) return NULL;
6801     }
6802     if (doc->lpszDatatype)
6803     {
6804         datatypeW = asciitounicode(&usBuffer, doc->lpszDatatype);
6805         if (!(docW.lpszDatatype = datatypeW)) return NULL;
6806     }
6807     docW.fwType = doc->fwType;
6808
6809     retW = StartDocDlgW(hPrinter, &docW);
6810
6811     if(retW)
6812     {
6813         DWORD len = WideCharToMultiByte(CP_ACP, 0, retW, -1, NULL, 0, NULL, NULL);
6814         ret = HeapAlloc(GetProcessHeap(), 0, len);
6815         WideCharToMultiByte(CP_ACP, 0, retW, -1, ret, len, NULL, NULL);
6816         HeapFree(GetProcessHeap(), 0, retW);
6817     }
6818
6819     HeapFree(GetProcessHeap(), 0, datatypeW);
6820     HeapFree(GetProcessHeap(), 0, outputW);
6821     HeapFree(GetProcessHeap(), 0, docnameW);
6822
6823     return ret;
6824 }
6825
6826 /*****************************************************************************
6827  *          StartDocDlgW [WINSPOOL.@]
6828  *
6829  * Undocumented: Apparently used by gdi32:StartDocW() to popup the file dialog
6830  * when lpszOutput is "FILE:" or if lpszOutput is NULL and the default printer
6831  * port is "FILE:". Also returns the full path if passed a relative path.
6832  *
6833  * The caller should free the returned string from the process heap.
6834  */
6835 LPWSTR WINAPI StartDocDlgW( HANDLE hPrinter, DOCINFOW *doc )
6836 {
6837     LPWSTR ret = NULL;
6838     DWORD len, attr;
6839
6840     if(doc->lpszOutput == NULL) /* Check whether default port is FILE: */
6841     {
6842         PRINTER_INFO_5W *pi5;
6843         GetPrinterW(hPrinter, 5, NULL, 0, &len);
6844         if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
6845             return NULL;
6846         pi5 = HeapAlloc(GetProcessHeap(), 0, len);
6847         GetPrinterW(hPrinter, 5, (LPBYTE)pi5, len, &len);
6848         if(!pi5->pPortName || strcmpW(pi5->pPortName, FILE_Port))
6849         {
6850             HeapFree(GetProcessHeap(), 0, pi5);
6851             return NULL;
6852         }
6853         HeapFree(GetProcessHeap(), 0, pi5);
6854     }
6855
6856     if(doc->lpszOutput == NULL || !strcmpW(doc->lpszOutput, FILE_Port))
6857     {
6858         LPWSTR name;
6859         get_filename(&name);
6860         if(name)
6861         {
6862             if(!(len = GetFullPathNameW(name, 0, NULL, NULL)))
6863             {
6864                 HeapFree(GetProcessHeap(), 0, name);
6865                 return NULL;
6866             }
6867             ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6868             GetFullPathNameW(name, len, ret, NULL);
6869             HeapFree(GetProcessHeap(), 0, name);
6870         }
6871         return ret;
6872     }
6873
6874     if(!(len = GetFullPathNameW(doc->lpszOutput, 0, NULL, NULL)))
6875         return NULL;
6876
6877     ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6878     GetFullPathNameW(doc->lpszOutput, len, ret, NULL);
6879         
6880     attr = GetFileAttributesW(ret);
6881     if(attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY))
6882     {
6883         HeapFree(GetProcessHeap(), 0, ret);
6884         ret = NULL;
6885     }
6886     return ret;
6887 }