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