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