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