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