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