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