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