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