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