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