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