oledb32: Check start for NULL before strstrW (Coverity).
[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  * FIXME - use ppd.
3990  */
3991
3992 static void WINSPOOL_GetDefaultDevMode(
3993         LPBYTE ptr,
3994         DWORD buflen, DWORD *needed)
3995 {
3996     DEVMODEW    dm;
3997     static const WCHAR szWwps[] = {'w', 'i', 'n', 'e', 'p', 's', '.', 'd', 'r', 'v', 0 };
3998
3999         /* fill default DEVMODE - should be read from ppd... */
4000         ZeroMemory( &dm, sizeof(dm) );
4001         memcpy(dm.dmDeviceName,szWwps,sizeof szWwps);
4002         dm.dmSpecVersion = DM_SPECVERSION;
4003         dm.dmDriverVersion = 1;
4004         dm.dmSize = sizeof(DEVMODEW);
4005         dm.dmDriverExtra = 0;
4006         dm.dmFields =
4007                 DM_ORIENTATION | DM_PAPERSIZE |
4008                 DM_PAPERLENGTH | DM_PAPERWIDTH |
4009                 DM_SCALE |
4010                 DM_COPIES |
4011                 DM_DEFAULTSOURCE | DM_PRINTQUALITY |
4012                 DM_YRESOLUTION | DM_TTOPTION;
4013
4014         dm.u1.s1.dmOrientation = DMORIENT_PORTRAIT;
4015         dm.u1.s1.dmPaperSize = DMPAPER_A4;
4016         dm.u1.s1.dmPaperLength = 2970;
4017         dm.u1.s1.dmPaperWidth = 2100;
4018
4019         dm.u1.s1.dmScale = 100;
4020         dm.u1.s1.dmCopies = 1;
4021         dm.u1.s1.dmDefaultSource = DMBIN_AUTO;
4022         dm.u1.s1.dmPrintQuality = DMRES_MEDIUM;
4023         /* dm.dmColor */
4024         /* dm.dmDuplex */
4025         dm.dmYResolution = 300; /* 300dpi */
4026         dm.dmTTOption = DMTT_BITMAP;
4027         /* dm.dmCollate */
4028         /* dm.dmFormName */
4029         /* dm.dmLogPixels */
4030         /* dm.dmBitsPerPel */
4031         /* dm.dmPelsWidth */
4032         /* dm.dmPelsHeight */
4033         /* dm.u2.dmDisplayFlags */
4034         /* dm.dmDisplayFrequency */
4035         /* dm.dmICMMethod */
4036         /* dm.dmICMIntent */
4037         /* dm.dmMediaType */
4038         /* dm.dmDitherType */
4039         /* dm.dmReserved1 */
4040         /* dm.dmReserved2 */
4041         /* dm.dmPanningWidth */
4042         /* dm.dmPanningHeight */
4043
4044     if(buflen >= sizeof(DEVMODEW))
4045         memcpy(ptr, &dm, sizeof(DEVMODEW));
4046     *needed = sizeof(DEVMODEW);
4047 }
4048
4049 /*****************************************************************************
4050  *    WINSPOOL_GetDevModeFromReg
4051  *
4052  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
4053  * DevMode is stored either as unicode or ascii.
4054  */
4055 static BOOL WINSPOOL_GetDevModeFromReg(HKEY hkey, LPCWSTR ValueName,
4056                                        LPBYTE ptr,
4057                                        DWORD buflen, DWORD *needed)
4058 {
4059     DWORD sz = buflen, type;
4060     LONG ret;
4061
4062     if (ptr && buflen>=sizeof(DEVMODEA)) memset(ptr, 0, sizeof(DEVMODEA));
4063     ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
4064     if ((ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA)) sz = 0;
4065     if (sz < sizeof(DEVMODEA))
4066     {
4067         TRACE("corrupted registry for %s ( size %d)\n",debugstr_w(ValueName),sz);
4068         return FALSE;
4069     }
4070     /* ensures that dmSize is not erratically bogus if registry is invalid */
4071     if (ptr && ((DEVMODEA*)ptr)->dmSize < sizeof(DEVMODEA))
4072         ((DEVMODEA*)ptr)->dmSize = sizeof(DEVMODEA);
4073     sz += (CCHDEVICENAME + CCHFORMNAME);
4074     if (ptr && (buflen >= sz)) {
4075         DEVMODEW *dmW = GdiConvertToDevmodeW((DEVMODEA*)ptr);
4076         memcpy(ptr, dmW, sz);
4077         HeapFree(GetProcessHeap(),0,dmW);
4078     }
4079     *needed = sz;
4080     return TRUE;
4081 }
4082
4083 /*********************************************************************
4084  *    WINSPOOL_GetPrinter_1
4085  *
4086  * Fills out a PRINTER_INFO_1W struct storing the strings in buf.
4087  */
4088 static BOOL WINSPOOL_GetPrinter_1(HKEY hkeyPrinter, PRINTER_INFO_1W *pi1,
4089                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded)
4090 {
4091     DWORD size, left = cbBuf;
4092     BOOL space = (cbBuf > 0);
4093     LPBYTE ptr = buf;
4094
4095     *pcbNeeded = 0;
4096
4097     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size)) {
4098         if(space && size <= left) {
4099             pi1->pName = (LPWSTR)ptr;
4100             ptr += size;
4101             left -= size;
4102         } else
4103             space = FALSE;
4104         *pcbNeeded += size;
4105     }
4106
4107     /* FIXME: pDescription should be something like "Name,Driver_Name,". */
4108     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size)) {
4109         if(space && size <= left) {
4110             pi1->pDescription = (LPWSTR)ptr;
4111             ptr += size;
4112             left -= size;
4113         } else
4114             space = FALSE;
4115         *pcbNeeded += size;
4116     }
4117
4118     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size)) {
4119         if(space && size <= left) {
4120             pi1->pComment = (LPWSTR)ptr;
4121             ptr += size;
4122             left -= size;
4123         } else
4124             space = FALSE;
4125         *pcbNeeded += size;
4126     }
4127
4128     if(pi1) pi1->Flags = PRINTER_ENUM_ICON8; /* We're a printer */
4129
4130     if(!space && pi1) /* zero out pi1 if we can't completely fill buf */
4131         memset(pi1, 0, sizeof(*pi1));
4132
4133     return space;
4134 }
4135 /*********************************************************************
4136  *    WINSPOOL_GetPrinter_2
4137  *
4138  * Fills out a PRINTER_INFO_2W struct storing the strings in buf.
4139  */
4140 static BOOL WINSPOOL_GetPrinter_2(HKEY hkeyPrinter, PRINTER_INFO_2W *pi2,
4141                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded)
4142 {
4143     DWORD size, left = cbBuf;
4144     BOOL space = (cbBuf > 0);
4145     LPBYTE ptr = buf;
4146
4147     *pcbNeeded = 0;
4148
4149     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size)) {
4150         if(space && size <= left) {
4151             pi2->pPrinterName = (LPWSTR)ptr;
4152             ptr += size;
4153             left -= size;
4154         } else
4155             space = FALSE;
4156         *pcbNeeded += size;
4157     }
4158     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Share_NameW, ptr, left, &size)) {
4159         if(space && size <= left) {
4160             pi2->pShareName = (LPWSTR)ptr;
4161             ptr += size;
4162             left -= size;
4163         } else
4164             space = FALSE;
4165         *pcbNeeded += size;
4166     }
4167     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size)) {
4168         if(space && size <= left) {
4169             pi2->pPortName = (LPWSTR)ptr;
4170             ptr += size;
4171             left -= size;
4172         } else
4173             space = FALSE;
4174         *pcbNeeded += size;
4175     }
4176     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Printer_DriverW, ptr, left, &size)) {
4177         if(space && size <= left) {
4178             pi2->pDriverName = (LPWSTR)ptr;
4179             ptr += size;
4180             left -= size;
4181         } else
4182             space = FALSE;
4183         *pcbNeeded += size;
4184     }
4185     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size)) {
4186         if(space && size <= left) {
4187             pi2->pComment = (LPWSTR)ptr;
4188             ptr += size;
4189             left -= size;
4190         } else
4191             space = FALSE;
4192         *pcbNeeded += size;
4193     }
4194     if(WINSPOOL_GetStringFromReg(hkeyPrinter, LocationW, ptr, left, &size)) {
4195         if(space && size <= left) {
4196             pi2->pLocation = (LPWSTR)ptr;
4197             ptr += size;
4198             left -= size;
4199         } else
4200             space = FALSE;
4201         *pcbNeeded += size;
4202     }
4203     if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, ptr, left, &size)) {
4204         if(space && size <= left) {
4205             pi2->pDevMode = (LPDEVMODEW)ptr;
4206             ptr += size;
4207             left -= size;
4208         } else
4209             space = FALSE;
4210         *pcbNeeded += size;
4211     }
4212     else
4213     {
4214         WINSPOOL_GetDefaultDevMode(ptr, left, &size);
4215         if(space && size <= left) {
4216             pi2->pDevMode = (LPDEVMODEW)ptr;
4217             ptr += size;
4218             left -= size;
4219         } else
4220             space = FALSE;
4221         *pcbNeeded += size;
4222     }
4223     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Separator_FileW, ptr, left, &size)) {
4224         if(space && size <= left) {
4225             pi2->pSepFile = (LPWSTR)ptr;
4226             ptr += size;
4227             left -= size;
4228         } else
4229             space = FALSE;
4230         *pcbNeeded += size;
4231     }
4232     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Print_ProcessorW, ptr, left, &size)) {
4233         if(space && size <= left) {
4234             pi2->pPrintProcessor = (LPWSTR)ptr;
4235             ptr += size;
4236             left -= size;
4237         } else
4238             space = FALSE;
4239         *pcbNeeded += size;
4240     }
4241     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DatatypeW, ptr, left, &size)) {
4242         if(space && size <= left) {
4243             pi2->pDatatype = (LPWSTR)ptr;
4244             ptr += size;
4245             left -= size;
4246         } else
4247             space = FALSE;
4248         *pcbNeeded += size;
4249     }
4250     if(WINSPOOL_GetStringFromReg(hkeyPrinter, ParametersW, ptr, left, &size)) {
4251         if(space && size <= left) {
4252             pi2->pParameters = (LPWSTR)ptr;
4253             ptr += size;
4254             left -= size;
4255         } else
4256             space = FALSE;
4257         *pcbNeeded += size;
4258     }
4259     if(pi2) {
4260         pi2->Attributes = get_dword_from_reg( hkeyPrinter, AttributesW );
4261         pi2->Priority = get_dword_from_reg( hkeyPrinter, PriorityW );
4262         pi2->DefaultPriority = get_dword_from_reg( hkeyPrinter, Default_PriorityW );
4263         pi2->StartTime = get_dword_from_reg( hkeyPrinter, StartTimeW );
4264         pi2->UntilTime = get_dword_from_reg( hkeyPrinter, UntilTimeW );
4265     }
4266
4267     if(!space && pi2) /* zero out pi2 if we can't completely fill buf */
4268         memset(pi2, 0, sizeof(*pi2));
4269
4270     return space;
4271 }
4272
4273 /*********************************************************************
4274  *    WINSPOOL_GetPrinter_4
4275  *
4276  * Fills out a PRINTER_INFO_4 struct storing the strings in buf.
4277  */
4278 static BOOL WINSPOOL_GetPrinter_4(HKEY hkeyPrinter, PRINTER_INFO_4W *pi4,
4279                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded)
4280 {
4281     DWORD size, left = cbBuf;
4282     BOOL space = (cbBuf > 0);
4283     LPBYTE ptr = buf;
4284
4285     *pcbNeeded = 0;
4286
4287     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size)) {
4288         if(space && size <= left) {
4289             pi4->pPrinterName = (LPWSTR)ptr;
4290             ptr += size;
4291             left -= size;
4292         } else
4293             space = FALSE;
4294         *pcbNeeded += size;
4295     }
4296     if(pi4) {
4297         pi4->Attributes = get_dword_from_reg( hkeyPrinter, AttributesW );
4298     }
4299
4300     if(!space && pi4) /* zero out pi4 if we can't completely fill buf */
4301         memset(pi4, 0, sizeof(*pi4));
4302
4303     return space;
4304 }
4305
4306 /*********************************************************************
4307  *    WINSPOOL_GetPrinter_5
4308  *
4309  * Fills out a PRINTER_INFO_5 struct storing the strings in buf.
4310  */
4311 static BOOL WINSPOOL_GetPrinter_5(HKEY hkeyPrinter, PRINTER_INFO_5W *pi5,
4312                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded)
4313 {
4314     DWORD size, left = cbBuf;
4315     BOOL space = (cbBuf > 0);
4316     LPBYTE ptr = buf;
4317
4318     *pcbNeeded = 0;
4319
4320     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size)) {
4321         if(space && size <= left) {
4322             pi5->pPrinterName = (LPWSTR)ptr;
4323             ptr += size;
4324             left -= size;
4325         } else
4326             space = FALSE;
4327         *pcbNeeded += size;
4328     }
4329     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size)) {
4330         if(space && size <= left) {
4331             pi5->pPortName = (LPWSTR)ptr;
4332             ptr += size;
4333             left -= size;
4334         } else
4335             space = FALSE;
4336         *pcbNeeded += size;
4337     }
4338     if(pi5) {
4339         pi5->Attributes = get_dword_from_reg( hkeyPrinter, AttributesW );
4340         pi5->DeviceNotSelectedTimeout = get_dword_from_reg( hkeyPrinter, dnsTimeoutW );
4341         pi5->TransmissionRetryTimeout = get_dword_from_reg( hkeyPrinter, txTimeoutW );
4342     }
4343
4344     if(!space && pi5) /* zero out pi5 if we can't completely fill buf */
4345         memset(pi5, 0, sizeof(*pi5));
4346
4347     return space;
4348 }
4349
4350 /*********************************************************************
4351  *    WINSPOOL_GetPrinter_7
4352  *
4353  * Fills out a PRINTER_INFO_7 struct storing the strings in buf.
4354  */
4355 static BOOL WINSPOOL_GetPrinter_7(HKEY hkeyPrinter, PRINTER_INFO_7W *pi7, LPBYTE buf,
4356                                   DWORD cbBuf, LPDWORD pcbNeeded)
4357 {
4358     DWORD size, left = cbBuf;
4359     BOOL space = (cbBuf > 0);
4360     LPBYTE ptr = buf;
4361
4362     *pcbNeeded = 0;
4363
4364     if (! WINSPOOL_GetStringFromReg(hkeyPrinter, ObjectGUIDW, ptr, left, &size))
4365     {
4366         ptr = NULL;
4367         size = sizeof(pi7->pszObjectGUID);
4368     }
4369     if (space && size <= left) {
4370         pi7->pszObjectGUID = (LPWSTR)ptr;
4371         ptr += size;
4372         left -= size;
4373     } else
4374         space = FALSE;
4375     *pcbNeeded += size;
4376     if (pi7) {
4377         /* We do not have a Directory Service */
4378         pi7->dwAction = DSPRINT_UNPUBLISH;
4379     }
4380
4381     if (!space && pi7) /* zero out pi7 if we can't completely fill buf */
4382         memset(pi7, 0, sizeof(*pi7));
4383
4384     return space;
4385 }
4386
4387 /*********************************************************************
4388  *    WINSPOOL_GetPrinter_9
4389  *
4390  * Fills out a PRINTER_INFO_9AW struct storing the strings in buf.
4391  */
4392 static BOOL WINSPOOL_GetPrinter_9(HKEY hkeyPrinter, PRINTER_INFO_9W *pi9, LPBYTE buf,
4393                                   DWORD cbBuf, LPDWORD pcbNeeded)
4394 {
4395     DWORD size;
4396     BOOL space = (cbBuf > 0);
4397
4398     *pcbNeeded = 0;
4399
4400     if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, buf, cbBuf, &size)) {
4401         if(space && size <= cbBuf) {
4402             pi9->pDevMode = (LPDEVMODEW)buf;
4403         } else
4404             space = FALSE;
4405         *pcbNeeded += size;
4406     }
4407     else
4408     {
4409         WINSPOOL_GetDefaultDevMode(buf, cbBuf, &size);
4410         if(space && size <= cbBuf) {
4411             pi9->pDevMode = (LPDEVMODEW)buf;
4412         } else
4413             space = FALSE;
4414         *pcbNeeded += size;
4415     }
4416
4417     if(!space && pi9) /* zero out pi9 if we can't completely fill buf */
4418         memset(pi9, 0, sizeof(*pi9));
4419
4420     return space;
4421 }
4422
4423 /*****************************************************************************
4424  *          GetPrinterW  [WINSPOOL.@]
4425  */
4426 BOOL WINAPI GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
4427                         DWORD cbBuf, LPDWORD pcbNeeded)
4428 {
4429     DWORD size, needed = 0, err;
4430     LPBYTE ptr = NULL;
4431     HKEY hkeyPrinter;
4432     BOOL ret;
4433
4434     TRACE("(%p,%d,%p,%d,%p)\n",hPrinter,Level,pPrinter,cbBuf, pcbNeeded);
4435
4436     err = WINSPOOL_GetOpenedPrinterRegKey( hPrinter, &hkeyPrinter );
4437     if (err)
4438     {
4439         SetLastError( err );
4440         return FALSE;
4441     }
4442
4443     switch(Level) {
4444     case 2:
4445       {
4446         PRINTER_INFO_2W *pi2 = (PRINTER_INFO_2W *)pPrinter;
4447
4448         size = sizeof(PRINTER_INFO_2W);
4449         if(size <= cbBuf) {
4450             ptr = pPrinter + size;
4451             cbBuf -= size;
4452             memset(pPrinter, 0, size);
4453         } else {
4454             pi2 = NULL;
4455             cbBuf = 0;
4456         }
4457         ret = WINSPOOL_GetPrinter_2(hkeyPrinter, pi2, ptr, cbBuf, &needed);
4458         needed += size;
4459         break;
4460       }
4461
4462     case 4:
4463       {
4464         PRINTER_INFO_4W *pi4 = (PRINTER_INFO_4W *)pPrinter;
4465
4466         size = sizeof(PRINTER_INFO_4W);
4467         if(size <= cbBuf) {
4468             ptr = pPrinter + size;
4469             cbBuf -= size;
4470             memset(pPrinter, 0, size);
4471         } else {
4472             pi4 = NULL;
4473             cbBuf = 0;
4474         }
4475         ret = WINSPOOL_GetPrinter_4(hkeyPrinter, pi4, ptr, cbBuf, &needed);
4476         needed += size;
4477         break;
4478       }
4479
4480
4481     case 5:
4482       {
4483         PRINTER_INFO_5W *pi5 = (PRINTER_INFO_5W *)pPrinter;
4484
4485         size = sizeof(PRINTER_INFO_5W);
4486         if(size <= cbBuf) {
4487             ptr = pPrinter + size;
4488             cbBuf -= size;
4489             memset(pPrinter, 0, size);
4490         } else {
4491             pi5 = NULL;
4492             cbBuf = 0;
4493         }
4494
4495         ret = WINSPOOL_GetPrinter_5(hkeyPrinter, pi5, ptr, cbBuf, &needed);
4496         needed += size;
4497         break;
4498       }
4499
4500
4501     case 6:
4502       {
4503         PRINTER_INFO_6 *pi6 = (PRINTER_INFO_6 *) pPrinter;
4504
4505         size = sizeof(PRINTER_INFO_6);
4506         if (size <= cbBuf) {
4507             /* FIXME: We do not update the status yet */
4508             pi6->dwStatus = get_dword_from_reg( hkeyPrinter, StatusW );
4509             ret = TRUE;
4510         } else {
4511             ret = FALSE;
4512         }
4513
4514         needed += size;
4515         break;
4516       }
4517
4518     case 7:
4519       {
4520         PRINTER_INFO_7W *pi7 = (PRINTER_INFO_7W *) pPrinter;
4521
4522         size = sizeof(PRINTER_INFO_7W);
4523         if (size <= cbBuf) {
4524             ptr = pPrinter + size;
4525             cbBuf -= size;
4526             memset(pPrinter, 0, size);
4527         } else {
4528             pi7 = NULL;
4529             cbBuf = 0;
4530         }
4531
4532         ret = WINSPOOL_GetPrinter_7(hkeyPrinter, pi7, ptr, cbBuf, &needed);
4533         needed += size;
4534         break;
4535       }
4536
4537
4538     case 8:
4539         /* 8 is the global default printer info and 9 already gets it instead of the per-user one */
4540         /* still, PRINTER_INFO_8W is the same as PRINTER_INFO_9W */
4541         /* fall through */
4542     case 9:
4543       {
4544         PRINTER_INFO_9W *pi9 = (PRINTER_INFO_9W *)pPrinter;
4545
4546         size = sizeof(PRINTER_INFO_9W);
4547         if(size <= cbBuf) {
4548             ptr = pPrinter + size;
4549             cbBuf -= size;
4550             memset(pPrinter, 0, size);
4551         } else {
4552             pi9 = NULL;
4553             cbBuf = 0;
4554         }
4555
4556         ret = WINSPOOL_GetPrinter_9(hkeyPrinter, pi9, ptr, cbBuf, &needed);
4557         needed += size;
4558         break;
4559       }
4560
4561
4562     default:
4563         FIXME("Unimplemented level %d\n", Level);
4564         SetLastError(ERROR_INVALID_LEVEL);
4565         RegCloseKey(hkeyPrinter);
4566         return FALSE;
4567     }
4568
4569     RegCloseKey(hkeyPrinter);
4570
4571     TRACE("returning %d needed = %d\n", ret, needed);
4572     if(pcbNeeded) *pcbNeeded = needed;
4573     if(!ret)
4574         SetLastError(ERROR_INSUFFICIENT_BUFFER);
4575     return ret;
4576 }
4577
4578 /*****************************************************************************
4579  *          GetPrinterA  [WINSPOOL.@]
4580  */
4581 BOOL WINAPI GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
4582                     DWORD cbBuf, LPDWORD pcbNeeded)
4583 {
4584     BOOL ret;
4585     LPBYTE buf = NULL;
4586
4587     if (cbBuf)
4588         buf = HeapAlloc(GetProcessHeap(), 0, cbBuf);
4589
4590     ret = GetPrinterW(hPrinter, Level, buf, cbBuf, pcbNeeded);
4591     if (ret)
4592         convert_printerinfo_W_to_A(pPrinter, buf, Level, cbBuf, 1);
4593     HeapFree(GetProcessHeap(), 0, buf);
4594
4595     return ret;
4596 }
4597
4598 /*****************************************************************************
4599  *          WINSPOOL_EnumPrintersW
4600  *
4601  *    Implementation of EnumPrintersW
4602  */
4603 static BOOL WINSPOOL_EnumPrintersW(DWORD dwType, LPWSTR lpszName,
4604                                   DWORD dwLevel, LPBYTE lpbPrinters,
4605                                   DWORD cbBuf, LPDWORD lpdwNeeded,
4606                                   LPDWORD lpdwReturned)
4607
4608 {
4609     HKEY hkeyPrinters, hkeyPrinter;
4610     WCHAR PrinterName[255];
4611     DWORD needed = 0, number = 0;
4612     DWORD used, i, left;
4613     PBYTE pi, buf;
4614
4615     if(lpbPrinters)
4616         memset(lpbPrinters, 0, cbBuf);
4617     if(lpdwReturned)
4618         *lpdwReturned = 0;
4619     if(lpdwNeeded)
4620         *lpdwNeeded = 0;
4621
4622     /* PRINTER_ENUM_DEFAULT is only supported under win9x, we behave like NT */
4623     if(dwType == PRINTER_ENUM_DEFAULT)
4624         return TRUE;
4625
4626     if (dwType & PRINTER_ENUM_CONNECTIONS) {
4627         TRACE("ignoring PRINTER_ENUM_CONNECTIONS\n");
4628         dwType &= ~PRINTER_ENUM_CONNECTIONS; /* we don't handle that */
4629         if (!dwType) {
4630             FIXME("We don't handle PRINTER_ENUM_CONNECTIONS\n");
4631             return TRUE;
4632         }
4633
4634     }
4635
4636     if (!((dwType & PRINTER_ENUM_LOCAL) || (dwType & PRINTER_ENUM_NAME))) {
4637         FIXME("dwType = %08x\n", dwType);
4638         SetLastError(ERROR_INVALID_FLAGS);
4639         return FALSE;
4640     }
4641
4642     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
4643        ERROR_SUCCESS) {
4644         ERR("Can't create Printers key\n");
4645         return FALSE;
4646     }
4647
4648     if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &number, NULL, NULL,
4649                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
4650         RegCloseKey(hkeyPrinters);
4651         ERR("Can't query Printers key\n");
4652         return FALSE;
4653     }
4654     TRACE("Found %d printers\n", number);
4655
4656     switch(dwLevel) {
4657     case 1:
4658         used = number * sizeof(PRINTER_INFO_1W);
4659         break;
4660     case 2:
4661         used = number * sizeof(PRINTER_INFO_2W);
4662         break;
4663     case 4:
4664         used = number * sizeof(PRINTER_INFO_4W);
4665         break;
4666     case 5:
4667         used = number * sizeof(PRINTER_INFO_5W);
4668         break;
4669
4670     default:
4671         SetLastError(ERROR_INVALID_LEVEL);
4672         RegCloseKey(hkeyPrinters);
4673         return FALSE;
4674     }
4675     pi = (used <= cbBuf) ? lpbPrinters : NULL;
4676
4677     for(i = 0; i < number; i++) {
4678         if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)/sizeof(PrinterName[0])) !=
4679            ERROR_SUCCESS) {
4680             ERR("Can't enum key number %d\n", i);
4681             RegCloseKey(hkeyPrinters);
4682             return FALSE;
4683         }
4684         TRACE("Printer %d is %s\n", i, debugstr_w(PrinterName));
4685         if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkeyPrinter) !=
4686            ERROR_SUCCESS) {
4687             ERR("Can't open key %s\n", debugstr_w(PrinterName));
4688             RegCloseKey(hkeyPrinters);
4689             return FALSE;
4690         }
4691
4692         if(cbBuf > used) {
4693             buf = lpbPrinters + used;
4694             left = cbBuf - used;
4695         } else {
4696             buf = NULL;
4697             left = 0;
4698         }
4699
4700         switch(dwLevel) {
4701         case 1:
4702             WINSPOOL_GetPrinter_1(hkeyPrinter, (PRINTER_INFO_1W *)pi, buf,
4703                                   left, &needed);
4704             used += needed;
4705             if(pi) pi += sizeof(PRINTER_INFO_1W);
4706             break;
4707         case 2:
4708             WINSPOOL_GetPrinter_2(hkeyPrinter, (PRINTER_INFO_2W *)pi, buf,
4709                                   left, &needed);
4710             used += needed;
4711             if(pi) pi += sizeof(PRINTER_INFO_2W);
4712             break;
4713         case 4:
4714             WINSPOOL_GetPrinter_4(hkeyPrinter, (PRINTER_INFO_4W *)pi, buf,
4715                                   left, &needed);
4716             used += needed;
4717             if(pi) pi += sizeof(PRINTER_INFO_4W);
4718             break;
4719         case 5:
4720             WINSPOOL_GetPrinter_5(hkeyPrinter, (PRINTER_INFO_5W *)pi, buf,
4721                                   left, &needed);
4722             used += needed;
4723             if(pi) pi += sizeof(PRINTER_INFO_5W);
4724             break;
4725         default:
4726             ERR("Shouldn't be here!\n");
4727             RegCloseKey(hkeyPrinter);
4728             RegCloseKey(hkeyPrinters);
4729             return FALSE;
4730         }
4731         RegCloseKey(hkeyPrinter);
4732     }
4733     RegCloseKey(hkeyPrinters);
4734
4735     if(lpdwNeeded)
4736         *lpdwNeeded = used;
4737
4738     if(used > cbBuf) {
4739         if(lpbPrinters)
4740             memset(lpbPrinters, 0, cbBuf);
4741         SetLastError(ERROR_INSUFFICIENT_BUFFER);
4742         return FALSE;
4743     }
4744     if(lpdwReturned)
4745         *lpdwReturned = number;
4746     SetLastError(ERROR_SUCCESS);
4747     return TRUE;
4748 }
4749
4750
4751 /******************************************************************
4752  *              EnumPrintersW        [WINSPOOL.@]
4753  *
4754  *    Enumerates the available printers, print servers and print
4755  *    providers, depending on the specified flags, name and level.
4756  *
4757  * RETURNS:
4758  *
4759  *    If level is set to 1:
4760  *      Returns an array of PRINTER_INFO_1 data structures in the
4761  *      lpbPrinters buffer.
4762  *
4763  *    If level is set to 2:
4764  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
4765  *      Returns an array of PRINTER_INFO_2 data structures in the
4766  *      lpbPrinters buffer. Note that according to MSDN also an
4767  *      OpenPrinter should be performed on every remote printer.
4768  *
4769  *    If level is set to 4 (officially WinNT only):
4770  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
4771  *      Fast: Only the registry is queried to retrieve printer names,
4772  *      no connection to the driver is made.
4773  *      Returns an array of PRINTER_INFO_4 data structures in the
4774  *      lpbPrinters buffer.
4775  *
4776  *    If level is set to 5 (officially WinNT4/Win9x only):
4777  *      Fast: Only the registry is queried to retrieve printer names,
4778  *      no connection to the driver is made.
4779  *      Returns an array of PRINTER_INFO_5 data structures in the
4780  *      lpbPrinters buffer.
4781  *
4782  *    If level set to 3 or 6+:
4783  *          returns zero (failure!)
4784  *
4785  *    Returns nonzero (TRUE) on success, or zero on failure, use GetLastError
4786  *    for information.
4787  *
4788  * BUGS:
4789  *    - Only PRINTER_ENUM_LOCAL and PRINTER_ENUM_NAME are implemented.
4790  *    - Only levels 2, 4 and 5 are implemented at the moment.
4791  *    - 16-bit printer drivers are not enumerated.
4792  *    - Returned amount of bytes used/needed does not match the real Windoze
4793  *      implementation (as in this implementation, all strings are part
4794  *      of the buffer, whereas Win32 keeps them somewhere else)
4795  *    - At level 2, EnumPrinters should also call OpenPrinter for remote printers.
4796  *
4797  * NOTE:
4798  *    - In a regular Wine installation, no registry settings for printers
4799  *      exist, which makes this function return an empty list.
4800  */
4801 BOOL  WINAPI EnumPrintersW(
4802                 DWORD dwType,        /* [in] Types of print objects to enumerate */
4803                 LPWSTR lpszName,     /* [in] name of objects to enumerate */
4804                 DWORD dwLevel,       /* [in] type of printer info structure */
4805                 LPBYTE lpbPrinters,  /* [out] buffer which receives info */
4806                 DWORD cbBuf,         /* [in] max size of buffer in bytes */
4807                 LPDWORD lpdwNeeded,  /* [out] pointer to var: # bytes used/needed */
4808                 LPDWORD lpdwReturned /* [out] number of entries returned */
4809                 )
4810 {
4811     return WINSPOOL_EnumPrintersW(dwType, lpszName, dwLevel, lpbPrinters, cbBuf,
4812                                  lpdwNeeded, lpdwReturned);
4813 }
4814
4815 /******************************************************************
4816  * EnumPrintersA    [WINSPOOL.@]
4817  *
4818  * See EnumPrintersW
4819  *
4820  */
4821 BOOL WINAPI EnumPrintersA(DWORD flags, LPSTR pName, DWORD level, LPBYTE pPrinters,
4822                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
4823 {
4824     BOOL ret;
4825     UNICODE_STRING pNameU;
4826     LPWSTR pNameW;
4827     LPBYTE pPrintersW;
4828
4829     TRACE("(0x%x, %s, %u, %p, %d, %p, %p)\n", flags, debugstr_a(pName), level,
4830                                               pPrinters, cbBuf, pcbNeeded, pcReturned);
4831
4832     pNameW = asciitounicode(&pNameU, pName);
4833
4834     /* Request a buffer with a size, that is big enough for EnumPrintersW.
4835        MS Office need this */
4836     pPrintersW = (pPrinters && cbBuf) ? HeapAlloc(GetProcessHeap(), 0, cbBuf) : NULL;
4837
4838     ret = EnumPrintersW(flags, pNameW, level, pPrintersW, cbBuf, pcbNeeded, pcReturned);
4839
4840     RtlFreeUnicodeString(&pNameU);
4841     if (ret) {
4842         convert_printerinfo_W_to_A(pPrinters, pPrintersW, level, *pcbNeeded, *pcReturned);
4843     }
4844     HeapFree(GetProcessHeap(), 0, pPrintersW);
4845     return ret;
4846 }
4847
4848 /*****************************************************************************
4849  *          WINSPOOL_GetDriverInfoFromReg [internal]
4850  *
4851  *    Enters the information from the registry into the DRIVER_INFO struct
4852  *
4853  * RETURNS
4854  *    zero if the printer driver does not exist in the registry
4855  *    (only if Level > 1) otherwise nonzero
4856  */
4857 static BOOL WINSPOOL_GetDriverInfoFromReg(
4858                             HKEY    hkeyDrivers,
4859                             LPWSTR  DriverName,
4860                             const printenv_t * env,
4861                             DWORD   Level,
4862                             LPBYTE  ptr,            /* DRIVER_INFO */
4863                             LPBYTE  pDriverStrings, /* strings buffer */
4864                             DWORD   cbBuf,          /* size of string buffer */
4865                             LPDWORD pcbNeeded)      /* space needed for str. */
4866 {
4867     DWORD  size, tmp;
4868     HKEY   hkeyDriver;
4869     WCHAR  driverdir[MAX_PATH];
4870     DWORD  dirlen;
4871     LPBYTE strPtr = pDriverStrings;
4872     LPDRIVER_INFO_8W di = (LPDRIVER_INFO_8W) ptr;
4873
4874     TRACE("(%p, %s, %p, %d, %p, %p, %d)\n", hkeyDrivers,
4875           debugstr_w(DriverName), env,
4876           Level, di, pDriverStrings, cbBuf);
4877
4878     if (di) ZeroMemory(di, di_sizeof[Level]);
4879
4880     *pcbNeeded = (lstrlenW(DriverName) + 1) * sizeof(WCHAR);
4881     if (*pcbNeeded <= cbBuf)
4882        strcpyW((LPWSTR)strPtr, DriverName);
4883
4884     /* pName for level 1 has a different offset! */
4885     if (Level == 1) {
4886        if (di) ((LPDRIVER_INFO_1W) di)->pName = (LPWSTR) strPtr;
4887        return TRUE;
4888     }
4889
4890     /* .cVersion and .pName for level > 1 */
4891     if (di) {
4892         di->cVersion = env->driverversion;
4893         di->pName = (LPWSTR) strPtr;
4894         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4895     }
4896
4897     /* Reserve Space for the largest subdir and a Backslash*/
4898     size = sizeof(driverdir) - sizeof(Version3_SubdirW) - sizeof(WCHAR);
4899     if (!GetPrinterDriverDirectoryW(NULL, (LPWSTR) env->envname, 1, (LPBYTE) driverdir, size, &size)) {
4900         /* Should never Fail */
4901         return FALSE;
4902     }
4903     lstrcatW(driverdir, env->versionsubdir);
4904     lstrcatW(driverdir, backslashW);
4905
4906     /* dirlen must not include the terminating zero */
4907     dirlen = lstrlenW(driverdir) * sizeof(WCHAR);
4908
4909     if (!DriverName[0] || RegOpenKeyW(hkeyDrivers, DriverName, &hkeyDriver) != ERROR_SUCCESS) {
4910         ERR("Can't find driver %s in registry\n", debugstr_w(DriverName));
4911         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER); /* ? */
4912         return FALSE;
4913     }
4914
4915     /* pEnvironment */
4916     size = (lstrlenW(env->envname) + 1) * sizeof(WCHAR);
4917
4918     *pcbNeeded += size;
4919     if (*pcbNeeded <= cbBuf) {
4920         lstrcpyW((LPWSTR)strPtr, env->envname);
4921         if (di) di->pEnvironment = (LPWSTR)strPtr;
4922         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4923     }
4924
4925     /* .pDriverPath is the Graphics rendering engine.
4926         The full Path is required to avoid a crash in some apps */
4927     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, DriverW, strPtr, 0, &size)) {
4928         *pcbNeeded += size;
4929         if (*pcbNeeded <= cbBuf)
4930             get_filename_from_reg(hkeyDriver, driverdir, dirlen, DriverW, strPtr, size, &tmp);
4931
4932         if (di) di->pDriverPath = (LPWSTR)strPtr;
4933         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4934     }
4935
4936     /* .pDataFile: For postscript-drivers, this is the ppd-file */
4937     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Data_FileW, strPtr, 0, &size)) {
4938         *pcbNeeded += size;
4939         if (*pcbNeeded <= cbBuf)
4940             get_filename_from_reg(hkeyDriver, driverdir, dirlen, Data_FileW, strPtr, size, &size);
4941
4942         if (di) di->pDataFile = (LPWSTR)strPtr;
4943         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4944     }
4945
4946     /* .pConfigFile is the Driver user Interface */
4947     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Configuration_FileW, strPtr, 0, &size)) {
4948         *pcbNeeded += size;
4949         if (*pcbNeeded <= cbBuf)
4950             get_filename_from_reg(hkeyDriver, driverdir, dirlen, Configuration_FileW, strPtr, size, &size);
4951
4952         if (di) di->pConfigFile = (LPWSTR)strPtr;
4953         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4954     }
4955
4956     if (Level == 2 ) {
4957         RegCloseKey(hkeyDriver);
4958         TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4959         return TRUE;
4960     }
4961
4962     if (Level == 5 ) {
4963         RegCloseKey(hkeyDriver);
4964         FIXME("level 5: incomplete\n");
4965         return TRUE;
4966     }
4967
4968     /* .pHelpFile */
4969     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Help_FileW, strPtr, 0, &size)) {
4970         *pcbNeeded += size;
4971         if (*pcbNeeded <= cbBuf)
4972             get_filename_from_reg(hkeyDriver, driverdir, dirlen, Help_FileW, strPtr, size, &size);
4973
4974         if (di) di->pHelpFile = (LPWSTR)strPtr;
4975         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4976     }
4977
4978     /* .pDependentFiles */
4979     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Dependent_FilesW, strPtr, 0, &size)) {
4980         *pcbNeeded += size;
4981         if (*pcbNeeded <= cbBuf)
4982             get_filename_from_reg(hkeyDriver, driverdir, dirlen, Dependent_FilesW, strPtr, size, &size);
4983
4984         if (di) di->pDependentFiles = (LPWSTR)strPtr;
4985         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4986     }
4987     else if (GetVersion() & 0x80000000) {
4988         /* PowerPoint XP expects that pDependentFiles is always valid on win9x */
4989         size = 2 * sizeof(WCHAR);
4990         *pcbNeeded += size;
4991         if ((*pcbNeeded <= cbBuf) && strPtr) ZeroMemory(strPtr, size);
4992
4993         if (di) di->pDependentFiles = (LPWSTR)strPtr;
4994         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4995     }
4996
4997     /* .pMonitorName is the optional Language Monitor */
4998     if (WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, 0, &size)) {
4999         *pcbNeeded += size;
5000         if (*pcbNeeded <= cbBuf)
5001             WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, size, &size);
5002
5003         if (di) di->pMonitorName = (LPWSTR)strPtr;
5004         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
5005     }
5006
5007     /* .pDefaultDataType */
5008     if (WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, 0, &size)) {
5009         *pcbNeeded += size;
5010         if(*pcbNeeded <= cbBuf)
5011             WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, size, &size);
5012
5013         if (di) di->pDefaultDataType = (LPWSTR)strPtr;
5014         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
5015     }
5016
5017     if (Level == 3 ) {
5018         RegCloseKey(hkeyDriver);
5019         TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
5020         return TRUE;
5021     }
5022
5023     /* .pszzPreviousNames */
5024     if (WINSPOOL_GetStringFromReg(hkeyDriver, Previous_NamesW, strPtr, 0, &size)) {
5025         *pcbNeeded += size;
5026         if(*pcbNeeded <= cbBuf)
5027             WINSPOOL_GetStringFromReg(hkeyDriver, Previous_NamesW, strPtr, size, &size);
5028
5029         if (di) di->pszzPreviousNames = (LPWSTR)strPtr;
5030         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
5031     }
5032
5033     if (Level == 4 ) {
5034         RegCloseKey(hkeyDriver);
5035         TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
5036         return TRUE;
5037     }
5038
5039     /* support is missing, but not important enough for a FIXME */
5040     TRACE("%s: DriverDate + DriverVersion not supported\n", debugstr_w(DriverName));
5041
5042     /* .pszMfgName */
5043     if (WINSPOOL_GetStringFromReg(hkeyDriver, ManufacturerW, strPtr, 0, &size)) {
5044         *pcbNeeded += size;
5045         if(*pcbNeeded <= cbBuf)
5046             WINSPOOL_GetStringFromReg(hkeyDriver, ManufacturerW, strPtr, size, &size);
5047
5048         if (di) di->pszMfgName = (LPWSTR)strPtr;
5049         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
5050     }
5051
5052     /* .pszOEMUrl */
5053     if (WINSPOOL_GetStringFromReg(hkeyDriver, OEM_UrlW, strPtr, 0, &size)) {
5054         *pcbNeeded += size;
5055         if(*pcbNeeded <= cbBuf)
5056             WINSPOOL_GetStringFromReg(hkeyDriver, OEM_UrlW, strPtr, size, &size);
5057
5058         if (di) di->pszOEMUrl = (LPWSTR)strPtr;
5059         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
5060     }
5061
5062     /* .pszHardwareID */
5063     if (WINSPOOL_GetStringFromReg(hkeyDriver, HardwareIDW, strPtr, 0, &size)) {
5064         *pcbNeeded += size;
5065         if(*pcbNeeded <= cbBuf)
5066             WINSPOOL_GetStringFromReg(hkeyDriver, HardwareIDW, strPtr, size, &size);
5067
5068         if (di) di->pszHardwareID = (LPWSTR)strPtr;
5069         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
5070     }
5071
5072     /* .pszProvider */
5073     if (WINSPOOL_GetStringFromReg(hkeyDriver, ProviderW, strPtr, 0, &size)) {
5074         *pcbNeeded += size;
5075         if(*pcbNeeded <= cbBuf)
5076             WINSPOOL_GetStringFromReg(hkeyDriver, ProviderW, strPtr, size, &size);
5077
5078         if (di) di->pszProvider = (LPWSTR)strPtr;
5079         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
5080     }
5081
5082     if (Level == 6 ) {
5083         RegCloseKey(hkeyDriver);
5084         return TRUE;
5085     }
5086
5087     /* support is missing, but not important enough for a FIXME */
5088     TRACE("level 8: incomplete\n");
5089
5090     TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
5091     RegCloseKey(hkeyDriver);
5092     return TRUE;
5093 }
5094
5095 /*****************************************************************************
5096  *          GetPrinterDriverW  [WINSPOOL.@]
5097  */
5098 BOOL WINAPI GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment,
5099                                   DWORD Level, LPBYTE pDriverInfo,
5100                                   DWORD cbBuf, LPDWORD pcbNeeded)
5101 {
5102     LPCWSTR name;
5103     WCHAR DriverName[100];
5104     DWORD ret, type, size, needed = 0;
5105     LPBYTE ptr = NULL;
5106     HKEY hkeyPrinter, hkeyDrivers;
5107     const printenv_t * env;
5108
5109     TRACE("(%p,%s,%d,%p,%d,%p)\n",hPrinter,debugstr_w(pEnvironment),
5110           Level,pDriverInfo,cbBuf, pcbNeeded);
5111
5112     if (cbBuf > 0)
5113         ZeroMemory(pDriverInfo, cbBuf);
5114
5115     if (!(name = get_opened_printer_name(hPrinter))) {
5116         SetLastError(ERROR_INVALID_HANDLE);
5117         return FALSE;
5118     }
5119
5120     if (Level < 1 || Level == 7 || Level > 8) {
5121         SetLastError(ERROR_INVALID_LEVEL);
5122         return FALSE;
5123     }
5124
5125     env = validate_envW(pEnvironment);
5126     if (!env) return FALSE;     /* SetLastError() is in validate_envW */
5127
5128     ret = open_printer_reg_key( name, &hkeyPrinter );
5129     if (ret)
5130     {
5131         ERR( "Can't find opened printer %s in registry\n", debugstr_w(name) );
5132         SetLastError( ret );
5133         return FALSE;
5134     }
5135
5136     size = sizeof(DriverName);
5137     DriverName[0] = 0;
5138     ret = RegQueryValueExW(hkeyPrinter, Printer_DriverW, 0, &type,
5139                            (LPBYTE)DriverName, &size);
5140     RegCloseKey(hkeyPrinter);
5141     if(ret != ERROR_SUCCESS) {
5142         ERR("Can't get DriverName for printer %s\n", debugstr_w(name));
5143         return FALSE;
5144     }
5145
5146     hkeyDrivers = WINSPOOL_OpenDriverReg(pEnvironment);
5147     if(!hkeyDrivers) {
5148         ERR("Can't create Drivers key\n");
5149         return FALSE;
5150     }
5151
5152     size = di_sizeof[Level];
5153     if ((size <= cbBuf) && pDriverInfo)
5154         ptr = pDriverInfo + size;
5155
5156     if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverName,
5157                          env, Level, pDriverInfo, ptr,
5158                          (cbBuf < size) ? 0 : cbBuf - size,
5159                          &needed)) {
5160             RegCloseKey(hkeyDrivers);
5161             return FALSE;
5162     }
5163
5164     RegCloseKey(hkeyDrivers);
5165
5166     if(pcbNeeded) *pcbNeeded = size + needed;
5167     TRACE("buffer space %d required %d\n", cbBuf, size + needed);
5168     if(cbBuf >= size + needed) return TRUE;
5169     SetLastError(ERROR_INSUFFICIENT_BUFFER);
5170     return FALSE;
5171 }
5172
5173 /*****************************************************************************
5174  *          GetPrinterDriverA  [WINSPOOL.@]
5175  */
5176 BOOL WINAPI GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment,
5177                               DWORD Level, LPBYTE pDriverInfo,
5178                               DWORD cbBuf, LPDWORD pcbNeeded)
5179 {
5180     BOOL ret;
5181     UNICODE_STRING pEnvW;
5182     PWSTR pwstrEnvW;
5183     LPBYTE buf = NULL;
5184
5185     if (cbBuf)
5186     {
5187         ZeroMemory(pDriverInfo, cbBuf);
5188         buf = HeapAlloc(GetProcessHeap(), 0, cbBuf);
5189     }
5190
5191     pwstrEnvW = asciitounicode(&pEnvW, pEnvironment);
5192     ret = GetPrinterDriverW(hPrinter, pwstrEnvW, Level, buf,
5193                                     cbBuf, pcbNeeded);
5194     if (ret)
5195         convert_driverinfo_W_to_A(pDriverInfo, buf, Level, cbBuf, 1);
5196
5197     HeapFree(GetProcessHeap(), 0, buf);
5198
5199     RtlFreeUnicodeString(&pEnvW);
5200     return ret;
5201 }
5202
5203 /*****************************************************************************
5204  *       GetPrinterDriverDirectoryW  [WINSPOOL.@]
5205  *
5206  * Return the PATH for the Printer-Drivers (UNICODE)
5207  *
5208  * PARAMS
5209  *   pName            [I] Servername (NT only) or NULL (local Computer)
5210  *   pEnvironment     [I] Printing-Environment (see below) or NULL (Default)
5211  *   Level            [I] Structure-Level (must be 1)
5212  *   pDriverDirectory [O] PTR to Buffer that receives the Result
5213  *   cbBuf            [I] Size of Buffer at pDriverDirectory
5214  *   pcbNeeded        [O] PTR to DWORD that receives the size in Bytes used / 
5215  *                        required for pDriverDirectory
5216  *
5217  * RETURNS
5218  *   Success: TRUE  and in pcbNeeded the Bytes used in pDriverDirectory
5219  *   Failure: FALSE and in pcbNeeded the Bytes required for pDriverDirectory,
5220  *   if cbBuf is too small
5221  * 
5222  *   Native Values returned in pDriverDirectory on Success:
5223  *|  NT(Windows NT x86):  "%winsysdir%\\spool\\DRIVERS\\w32x86" 
5224  *|  NT(Windows 4.0):     "%winsysdir%\\spool\\DRIVERS\\win40" 
5225  *|  win9x(Windows 4.0):  "%winsysdir%" 
5226  *
5227  *   "%winsysdir%" is the Value from GetSystemDirectoryW()
5228  *
5229  * FIXME
5230  *-  Only NULL or "" is supported for pName
5231  *
5232  */
5233 BOOL WINAPI GetPrinterDriverDirectoryW(LPWSTR pName, LPWSTR pEnvironment,
5234                                        DWORD Level, LPBYTE pDriverDirectory,
5235                                        DWORD cbBuf, LPDWORD pcbNeeded)
5236 {
5237     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName), 
5238           debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
5239
5240     if ((backend == NULL)  && !load_backend()) return FALSE;
5241
5242     if (Level != 1) {
5243         /* (Level != 1) is ignored in win9x */
5244         SetLastError(ERROR_INVALID_LEVEL);
5245         return FALSE;
5246     }
5247     if (pcbNeeded == NULL) {
5248         /* (pcbNeeded == NULL) is ignored in win9x */
5249         SetLastError(RPC_X_NULL_REF_POINTER);
5250         return FALSE;
5251     }
5252
5253     return backend->fpGetPrinterDriverDirectory(pName, pEnvironment, Level,
5254                                                 pDriverDirectory, cbBuf, pcbNeeded);
5255
5256 }
5257
5258
5259 /*****************************************************************************
5260  *       GetPrinterDriverDirectoryA  [WINSPOOL.@]
5261  *
5262  * Return the PATH for the Printer-Drivers (ANSI)
5263  *
5264  * See GetPrinterDriverDirectoryW.
5265  *
5266  * NOTES
5267  * On NT, pDriverDirectory need the same Size as the Unicode-Version
5268  *
5269  */
5270 BOOL WINAPI GetPrinterDriverDirectoryA(LPSTR pName, LPSTR pEnvironment,
5271                                        DWORD Level, LPBYTE pDriverDirectory,
5272                                        DWORD cbBuf, LPDWORD pcbNeeded)
5273 {
5274     UNICODE_STRING nameW, environmentW;
5275     BOOL ret;
5276     DWORD pcbNeededW;
5277     INT len = cbBuf * sizeof(WCHAR)/sizeof(CHAR);
5278     WCHAR *driverDirectoryW = NULL;
5279
5280     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_a(pName), 
5281           debugstr_a(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
5282  
5283     if (len) driverDirectoryW = HeapAlloc( GetProcessHeap(), 0, len );
5284
5285     if(pName) RtlCreateUnicodeStringFromAsciiz(&nameW, pName);
5286     else nameW.Buffer = NULL;
5287     if(pEnvironment) RtlCreateUnicodeStringFromAsciiz(&environmentW, pEnvironment);
5288     else environmentW.Buffer = NULL;
5289
5290     ret = GetPrinterDriverDirectoryW( nameW.Buffer, environmentW.Buffer, Level,
5291                                       (LPBYTE)driverDirectoryW, len, &pcbNeededW );
5292     if (ret) {
5293         DWORD needed;
5294         needed =  WideCharToMultiByte( CP_ACP, 0, driverDirectoryW, -1, 
5295                                    (LPSTR)pDriverDirectory, cbBuf, NULL, NULL);
5296         if(pcbNeeded)
5297             *pcbNeeded = needed;
5298         ret = needed <= cbBuf;
5299     } else 
5300         if(pcbNeeded) *pcbNeeded = pcbNeededW * sizeof(CHAR)/sizeof(WCHAR);
5301
5302     TRACE("required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
5303
5304     HeapFree( GetProcessHeap(), 0, driverDirectoryW );
5305     RtlFreeUnicodeString(&environmentW);
5306     RtlFreeUnicodeString(&nameW);
5307
5308     return ret;
5309 }
5310
5311 /*****************************************************************************
5312  *          AddPrinterDriverA  [WINSPOOL.@]
5313  *
5314  * See AddPrinterDriverW.
5315  *
5316  */
5317 BOOL WINAPI AddPrinterDriverA(LPSTR pName, DWORD level, LPBYTE pDriverInfo)
5318 {
5319     TRACE("(%s, %d, %p)\n", debugstr_a(pName), level, pDriverInfo);
5320     return AddPrinterDriverExA(pName, level, pDriverInfo, APD_COPY_NEW_FILES);
5321 }
5322
5323 /******************************************************************************
5324  *  AddPrinterDriverW (WINSPOOL.@)
5325  *
5326  * Install a Printer Driver
5327  *
5328  * PARAMS
5329  *  pName           [I] Servername or NULL (local Computer)
5330  *  level           [I] Level for the supplied DRIVER_INFO_*W struct
5331  *  pDriverInfo     [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter
5332  *
5333  * RESULTS
5334  *  Success: TRUE
5335  *  Failure: FALSE
5336  *
5337  */
5338 BOOL WINAPI AddPrinterDriverW(LPWSTR pName, DWORD level, LPBYTE pDriverInfo)
5339 {
5340     TRACE("(%s, %d, %p)\n", debugstr_w(pName), level, pDriverInfo);
5341     return AddPrinterDriverExW(pName, level, pDriverInfo, APD_COPY_NEW_FILES);
5342 }
5343
5344 /*****************************************************************************
5345  *          AddPrintProcessorA  [WINSPOOL.@]
5346  */
5347 BOOL WINAPI AddPrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPathName,
5348                                LPSTR pPrintProcessorName)
5349 {
5350     FIXME("(%s,%s,%s,%s): stub\n", debugstr_a(pName), debugstr_a(pEnvironment),
5351           debugstr_a(pPathName), debugstr_a(pPrintProcessorName));
5352     return FALSE;
5353 }
5354
5355 /*****************************************************************************
5356  *          AddPrintProcessorW  [WINSPOOL.@]
5357  */
5358 BOOL WINAPI AddPrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPathName,
5359                                LPWSTR pPrintProcessorName)
5360 {
5361     FIXME("(%s,%s,%s,%s): stub\n", debugstr_w(pName), debugstr_w(pEnvironment),
5362           debugstr_w(pPathName), debugstr_w(pPrintProcessorName));
5363     return TRUE;
5364 }
5365
5366 /*****************************************************************************
5367  *          AddPrintProvidorA  [WINSPOOL.@]
5368  */
5369 BOOL WINAPI AddPrintProvidorA(LPSTR pName, DWORD Level, LPBYTE pProviderInfo)
5370 {
5371     FIXME("(%s,0x%08x,%p): stub\n", debugstr_a(pName), Level, pProviderInfo);
5372     return FALSE;
5373 }
5374
5375 /*****************************************************************************
5376  *          AddPrintProvidorW  [WINSPOOL.@]
5377  */
5378 BOOL WINAPI AddPrintProvidorW(LPWSTR pName, DWORD Level, LPBYTE pProviderInfo)
5379 {
5380     FIXME("(%s,0x%08x,%p): stub\n", debugstr_w(pName), Level, pProviderInfo);
5381     return FALSE;
5382 }
5383
5384 /*****************************************************************************
5385  *          AdvancedDocumentPropertiesA  [WINSPOOL.@]
5386  */
5387 LONG WINAPI AdvancedDocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName,
5388                                         PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput)
5389 {
5390     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_a(pDeviceName),
5391           pDevModeOutput, pDevModeInput);
5392     return 0;
5393 }
5394
5395 /*****************************************************************************
5396  *          AdvancedDocumentPropertiesW  [WINSPOOL.@]
5397  */
5398 LONG WINAPI AdvancedDocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName,
5399                                         PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput)
5400 {
5401     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_w(pDeviceName),
5402           pDevModeOutput, pDevModeInput);
5403     return 0;
5404 }
5405
5406 /*****************************************************************************
5407  *          PrinterProperties  [WINSPOOL.@]
5408  *
5409  *     Displays a dialog to set the properties of the printer.
5410  *
5411  * RETURNS
5412  *     nonzero on success or zero on failure
5413  *
5414  * BUGS
5415  *         implemented as stub only
5416  */
5417 BOOL WINAPI PrinterProperties(HWND hWnd,      /* [in] handle to parent window */
5418                               HANDLE hPrinter /* [in] handle to printer object */
5419 ){
5420     FIXME("(%p,%p): stub\n", hWnd, hPrinter);
5421     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5422     return FALSE;
5423 }
5424
5425 /*****************************************************************************
5426  *          EnumJobsA [WINSPOOL.@]
5427  *
5428  */
5429 BOOL WINAPI EnumJobsA(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
5430                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
5431                       LPDWORD pcReturned)
5432 {
5433     FIXME("(%p,first=%d,no=%d,level=%d,job=%p,cb=%d,%p,%p), stub!\n",
5434         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
5435     );
5436     if(pcbNeeded) *pcbNeeded = 0;
5437     if(pcReturned) *pcReturned = 0;
5438     return FALSE;
5439 }
5440
5441
5442 /*****************************************************************************
5443  *          EnumJobsW [WINSPOOL.@]
5444  *
5445  */
5446 BOOL WINAPI EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
5447                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
5448                       LPDWORD pcReturned)
5449 {
5450     FIXME("(%p,first=%d,no=%d,level=%d,job=%p,cb=%d,%p,%p), stub!\n",
5451         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
5452     );
5453     if(pcbNeeded) *pcbNeeded = 0;
5454     if(pcReturned) *pcReturned = 0;
5455     return FALSE;
5456 }
5457
5458 /*****************************************************************************
5459  *          WINSPOOL_EnumPrinterDrivers [internal]
5460  *
5461  *    Delivers information about all printer drivers installed on the
5462  *    localhost or a given server
5463  *
5464  * RETURNS
5465  *    nonzero on success or zero on failure. If the buffer for the returned
5466  *    information is too small the function will return an error
5467  *
5468  * BUGS
5469  *    - only implemented for localhost, foreign hosts will return an error
5470  */
5471 static BOOL WINSPOOL_EnumPrinterDrivers(LPWSTR pName, LPCWSTR pEnvironment,
5472                                         DWORD Level, LPBYTE pDriverInfo,
5473                                         DWORD driver_index,
5474                                         DWORD cbBuf, LPDWORD pcbNeeded,
5475                                         LPDWORD pcFound, DWORD data_offset)
5476
5477 {   HKEY  hkeyDrivers;
5478     DWORD i, size = 0;
5479     const printenv_t * env;
5480
5481     TRACE("%s,%s,%d,%p,%d,%d,%d\n",
5482           debugstr_w(pName), debugstr_w(pEnvironment),
5483           Level, pDriverInfo, driver_index, cbBuf, data_offset);
5484
5485     env = validate_envW(pEnvironment);
5486     if (!env) return FALSE;     /* SetLastError() is in validate_envW */
5487
5488     *pcFound = 0;
5489
5490     hkeyDrivers = WINSPOOL_OpenDriverReg(pEnvironment);
5491     if(!hkeyDrivers) {
5492         ERR("Can't open Drivers key\n");
5493         return FALSE;
5494     }
5495
5496     if(RegQueryInfoKeyA(hkeyDrivers, NULL, NULL, NULL, pcFound, NULL, NULL,
5497                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
5498         RegCloseKey(hkeyDrivers);
5499         ERR("Can't query Drivers key\n");
5500         return FALSE;
5501     }
5502     TRACE("Found %d Drivers\n", *pcFound);
5503
5504     /* get size of single struct
5505      * unicode and ascii structure have the same size
5506      */
5507     size = di_sizeof[Level];
5508
5509     if (data_offset == 0)
5510         data_offset = size * (*pcFound);
5511     *pcbNeeded = data_offset;
5512
5513     for( i = 0; i < *pcFound; i++) {
5514         WCHAR DriverNameW[255];
5515         PBYTE table_ptr = NULL;
5516         PBYTE data_ptr = NULL;
5517         DWORD needed = 0;
5518
5519         if(RegEnumKeyW(hkeyDrivers, i, DriverNameW, sizeof(DriverNameW)/sizeof(DriverNameW[0]))
5520                        != ERROR_SUCCESS) {
5521             ERR("Can't enum key number %d\n", i);
5522             RegCloseKey(hkeyDrivers);
5523             return FALSE;
5524         }
5525
5526         if (pDriverInfo && ((driver_index + i + 1) * size) <= cbBuf)
5527             table_ptr = pDriverInfo + (driver_index + i) * size;
5528         if (pDriverInfo && *pcbNeeded <= cbBuf)
5529             data_ptr = pDriverInfo + *pcbNeeded;
5530
5531         if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverNameW,
5532                          env, Level, table_ptr, data_ptr,
5533                          (cbBuf < *pcbNeeded) ? 0 : cbBuf - *pcbNeeded,
5534                          &needed)) {
5535             RegCloseKey(hkeyDrivers);
5536             return FALSE;
5537         }
5538
5539         *pcbNeeded += needed;
5540     }
5541
5542     RegCloseKey(hkeyDrivers);
5543
5544     if(cbBuf < *pcbNeeded){
5545         SetLastError(ERROR_INSUFFICIENT_BUFFER);
5546         return FALSE;
5547     }
5548
5549     return TRUE;
5550 }
5551
5552 /*****************************************************************************
5553  *          EnumPrinterDriversW  [WINSPOOL.@]
5554  *
5555  *    see function EnumPrinterDrivers for RETURNS, BUGS
5556  */
5557 BOOL WINAPI EnumPrinterDriversW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
5558                                 LPBYTE pDriverInfo, DWORD cbBuf,
5559                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
5560 {
5561     static const WCHAR allW[] = {'a','l','l',0};
5562     BOOL ret;
5563     DWORD found;
5564
5565     if ((pcbNeeded == NULL) || (pcReturned == NULL))
5566     {
5567         SetLastError(RPC_X_NULL_REF_POINTER);
5568         return FALSE;
5569     }
5570
5571     /* check for local drivers */
5572     if((pName) && (pName[0])) {
5573         FIXME("remote drivers (%s) not supported!\n", debugstr_w(pName));
5574         SetLastError(ERROR_ACCESS_DENIED);
5575         return FALSE;
5576     }
5577
5578     /* check input parameter */
5579     if ((Level < 1) || (Level == 7) || (Level > 8)) {
5580         SetLastError(ERROR_INVALID_LEVEL);
5581         return FALSE;
5582     }
5583
5584     if(pDriverInfo && cbBuf > 0)
5585         memset( pDriverInfo, 0, cbBuf);
5586
5587     /* Exception:  pull all printers */
5588     if (pEnvironment && !strcmpW(pEnvironment, allW))
5589     {
5590         DWORD i, needed, bufsize = cbBuf;
5591         DWORD total_found = 0;
5592         DWORD data_offset;
5593
5594         /* Precompute the overall total; we need this to know
5595            where pointers end and data begins (i.e. data_offset) */
5596         for (i = 0; i < sizeof(all_printenv)/sizeof(all_printenv[0]); i++)
5597         {
5598             needed = found = 0;
5599             ret = WINSPOOL_EnumPrinterDrivers(pName, all_printenv[i]->envname, Level,
5600                                               NULL, 0, 0, &needed, &found, 0);
5601             if (!ret && GetLastError() != ERROR_INSUFFICIENT_BUFFER) return FALSE;
5602             total_found += found;
5603         }
5604
5605         data_offset = di_sizeof[Level] * total_found;
5606
5607         *pcReturned = 0;
5608         *pcbNeeded = 0;
5609         total_found = 0;
5610         for (i = 0; i < sizeof(all_printenv)/sizeof(all_printenv[0]); i++)
5611         {
5612             needed = found = 0;
5613             ret = WINSPOOL_EnumPrinterDrivers(pName, all_printenv[i]->envname, Level,
5614                                               pDriverInfo, total_found, bufsize, &needed, &found, data_offset);
5615             if (!ret && GetLastError() != ERROR_INSUFFICIENT_BUFFER) return FALSE;
5616             else if (ret)
5617                 *pcReturned += found;
5618             *pcbNeeded = needed;
5619             data_offset = needed;
5620             total_found += found;
5621         }
5622         return ret;
5623     }
5624
5625     /* Normal behavior */
5626     ret = WINSPOOL_EnumPrinterDrivers(pName, pEnvironment, Level, pDriverInfo,
5627                                        0, cbBuf, pcbNeeded, &found, 0);
5628     if (ret)
5629         *pcReturned = found;
5630
5631     return ret;
5632 }
5633
5634 /*****************************************************************************
5635  *          EnumPrinterDriversA  [WINSPOOL.@]
5636  *
5637  *    see function EnumPrinterDrivers for RETURNS, BUGS
5638  */
5639 BOOL WINAPI EnumPrinterDriversA(LPSTR pName, LPSTR pEnvironment, DWORD Level,
5640                                 LPBYTE pDriverInfo, DWORD cbBuf,
5641                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
5642 {
5643     BOOL ret;
5644     UNICODE_STRING pNameW, pEnvironmentW;
5645     PWSTR pwstrNameW, pwstrEnvironmentW;
5646     LPBYTE buf = NULL;
5647
5648     if (cbBuf)
5649         buf = HeapAlloc(GetProcessHeap(), 0, cbBuf);
5650
5651     pwstrNameW = asciitounicode(&pNameW, pName);
5652     pwstrEnvironmentW = asciitounicode(&pEnvironmentW, pEnvironment);
5653
5654     ret = EnumPrinterDriversW(pwstrNameW, pwstrEnvironmentW, Level,
5655                                 buf, cbBuf, pcbNeeded, pcReturned);
5656     if (ret)
5657         convert_driverinfo_W_to_A(pDriverInfo, buf, Level, cbBuf, *pcReturned);
5658
5659     HeapFree(GetProcessHeap(), 0, buf);
5660
5661     RtlFreeUnicodeString(&pNameW);
5662     RtlFreeUnicodeString(&pEnvironmentW);
5663
5664     return ret;
5665 }
5666
5667 /******************************************************************************
5668  *              EnumPortsA   (WINSPOOL.@)
5669  *
5670  * See EnumPortsW.
5671  *
5672  */
5673 BOOL WINAPI EnumPortsA( LPSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf,
5674                         LPDWORD pcbNeeded, LPDWORD pcReturned)
5675 {
5676     BOOL    res;
5677     LPBYTE  bufferW = NULL;
5678     LPWSTR  nameW = NULL;
5679     DWORD   needed = 0;
5680     DWORD   numentries = 0;
5681     INT     len;
5682
5683     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), Level, pPorts,
5684           cbBuf, pcbNeeded, pcReturned);
5685
5686     /* convert servername to unicode */
5687     if (pName) {
5688         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
5689         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5690         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
5691     }
5692     /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the Ports */
5693     needed = cbBuf * sizeof(WCHAR);    
5694     if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
5695     res = EnumPortsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5696
5697     if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
5698         if (pcbNeeded) needed = *pcbNeeded;
5699         /* HeapReAlloc return NULL, when bufferW was NULL */
5700         bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
5701                               HeapAlloc(GetProcessHeap(), 0, needed);
5702
5703         /* Try again with the large Buffer */
5704         res = EnumPortsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5705     }
5706     needed = pcbNeeded ? *pcbNeeded : 0;
5707     numentries = pcReturned ? *pcReturned : 0;
5708
5709     /*
5710        W2k require the buffersize from EnumPortsW also for EnumPortsA.
5711        We use the smaller Ansi-Size to avoid conflicts with fixed Buffers of old Apps.
5712      */
5713     if (res) {
5714         /* EnumPortsW collected all Data. Parse them to calculate ANSI-Size */
5715         DWORD   entrysize = 0;
5716         DWORD   index;
5717         LPSTR   ptr;
5718         LPPORT_INFO_2W pi2w;
5719         LPPORT_INFO_2A pi2a;
5720
5721         needed = 0;
5722         entrysize = (Level == 1) ? sizeof(PORT_INFO_1A) : sizeof(PORT_INFO_2A);
5723
5724         /* First pass: calculate the size for all Entries */
5725         pi2w = (LPPORT_INFO_2W) bufferW;
5726         pi2a = (LPPORT_INFO_2A) pPorts;
5727         index = 0;
5728         while (index < numentries) {
5729             index++;
5730             needed += entrysize;    /* PORT_INFO_?A */
5731             TRACE("%p: parsing #%d (%s)\n", pi2w, index, debugstr_w(pi2w->pPortName));
5732
5733             needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pPortName, -1,
5734                                             NULL, 0, NULL, NULL);
5735             if (Level > 1) {
5736                 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pMonitorName, -1,
5737                                                 NULL, 0, NULL, NULL);
5738                 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pDescription, -1,
5739                                                 NULL, 0, NULL, NULL);
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         /* check for errors and quit on failure */
5747         if (cbBuf < needed) {
5748             SetLastError(ERROR_INSUFFICIENT_BUFFER);
5749             res = FALSE;
5750             goto cleanup;
5751         }
5752         len = entrysize * numentries;       /* room for all PORT_INFO_?A */
5753         ptr = (LPSTR) &pPorts[len];         /* room for strings */
5754         cbBuf -= len ;                      /* free Bytes in the user-Buffer */
5755         pi2w = (LPPORT_INFO_2W) bufferW;
5756         pi2a = (LPPORT_INFO_2A) pPorts;
5757         index = 0;
5758         /* Second Pass: Fill the User Buffer (if we have one) */
5759         while ((index < numentries) && pPorts) {
5760             index++;
5761             TRACE("%p: writing PORT_INFO_%dA #%d\n", pi2a, Level, index);
5762             pi2a->pPortName = ptr;
5763             len = WideCharToMultiByte(CP_ACP, 0, pi2w->pPortName, -1,
5764                                             ptr, cbBuf , NULL, NULL);
5765             ptr += len;
5766             cbBuf -= len;
5767             if (Level > 1) {
5768                 pi2a->pMonitorName = ptr;
5769                 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pMonitorName, -1,
5770                                             ptr, cbBuf, NULL, NULL);
5771                 ptr += len;
5772                 cbBuf -= len;
5773
5774                 pi2a->pDescription = ptr;
5775                 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pDescription, -1,
5776                                             ptr, cbBuf, NULL, NULL);
5777                 ptr += len;
5778                 cbBuf -= len;
5779
5780                 pi2a->fPortType = pi2w->fPortType;
5781                 pi2a->Reserved = 0;              /* documented: "must be zero" */
5782                 
5783             }
5784             /* use LPBYTE with entrysize to avoid double code (PORT_INFO_1 + PORT_INFO_2) */
5785             pi2w = (LPPORT_INFO_2W) (((LPBYTE)pi2w) + entrysize);
5786             pi2a = (LPPORT_INFO_2A) (((LPBYTE)pi2a) + entrysize);
5787         }
5788     }
5789
5790 cleanup:
5791     if (pcbNeeded)  *pcbNeeded = needed;
5792     if (pcReturned) *pcReturned = (res) ? numentries : 0;
5793
5794     HeapFree(GetProcessHeap(), 0, nameW);
5795     HeapFree(GetProcessHeap(), 0, bufferW);
5796
5797     TRACE("returning %d with %d (%d byte for %d of %d entries)\n", 
5798             (res), GetLastError(), needed, (res)? numentries : 0, numentries);
5799
5800     return (res);
5801
5802 }
5803
5804 /******************************************************************************
5805  *      EnumPortsW   (WINSPOOL.@)
5806  *
5807  * Enumerate available Ports
5808  *
5809  * PARAMS
5810  *  pName      [I] Servername or NULL (local Computer)
5811  *  Level      [I] Structure-Level (1 or 2)
5812  *  pPorts     [O] PTR to Buffer that receives the Result
5813  *  cbBuf      [I] Size of Buffer at pPorts
5814  *  pcbNeeded  [O] PTR to DWORD that receives the size in Bytes used / required for pPorts
5815  *  pcReturned [O] PTR to DWORD that receives the number of Ports in pPorts
5816  *
5817  * RETURNS
5818  *  Success: TRUE
5819  *  Failure: FALSE and in pcbNeeded the Bytes required for pPorts, if cbBuf is too small
5820  *
5821  */
5822 BOOL WINAPI EnumPortsW(LPWSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
5823 {
5824
5825     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pPorts,
5826           cbBuf, pcbNeeded, pcReturned);
5827
5828     if ((backend == NULL)  && !load_backend()) return FALSE;
5829
5830     /* Level is not checked in win9x */
5831     if (!Level || (Level > 2)) {
5832         WARN("level (%d) is ignored in win9x\n", Level);
5833         SetLastError(ERROR_INVALID_LEVEL);
5834         return FALSE;
5835     }
5836     if (!pcbNeeded || (!pPorts && (cbBuf > 0))) {
5837         SetLastError(RPC_X_NULL_REF_POINTER);
5838         return FALSE;
5839     }
5840
5841     return backend->fpEnumPorts(pName, Level, pPorts, cbBuf, pcbNeeded, pcReturned);
5842 }
5843
5844 /******************************************************************************
5845  *              GetDefaultPrinterW   (WINSPOOL.@)
5846  *
5847  * FIXME
5848  *      This function must read the value from data 'device' of key
5849  *      HCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows
5850  */
5851 BOOL WINAPI GetDefaultPrinterW(LPWSTR name, LPDWORD namesize)
5852 {
5853     BOOL  retval = TRUE;
5854     DWORD insize, len;
5855     WCHAR *buffer, *ptr;
5856
5857     if (!namesize)
5858     {
5859         SetLastError(ERROR_INVALID_PARAMETER);
5860         return FALSE;
5861     }
5862
5863     /* make the buffer big enough for the stuff from the profile/registry,
5864      * the content must fit into the local buffer to compute the correct
5865      * size even if the extern buffer is too small or not given.
5866      * (20 for ,driver,port) */
5867     insize = *namesize;
5868     len = max(100, (insize + 20));
5869     buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
5870
5871     if (!GetProfileStringW(windowsW, deviceW, emptyStringW, buffer, len))
5872     {
5873         SetLastError (ERROR_FILE_NOT_FOUND);
5874         retval = FALSE;
5875         goto end;
5876     }
5877     TRACE("%s\n", debugstr_w(buffer));
5878
5879     if ((ptr = strchrW(buffer, ',')) == NULL)
5880     {
5881         SetLastError(ERROR_INVALID_NAME);
5882         retval = FALSE;
5883         goto end;
5884     }
5885
5886     *ptr = 0;
5887     *namesize = strlenW(buffer) + 1;
5888     if(!name || (*namesize > insize))
5889     {
5890         SetLastError(ERROR_INSUFFICIENT_BUFFER);
5891         retval = FALSE;
5892         goto end;
5893     }
5894     strcpyW(name, buffer);
5895
5896 end:
5897     HeapFree( GetProcessHeap(), 0, buffer);
5898     return retval;
5899 }
5900
5901
5902 /******************************************************************************
5903  *              GetDefaultPrinterA   (WINSPOOL.@)
5904  */
5905 BOOL WINAPI GetDefaultPrinterA(LPSTR name, LPDWORD namesize)
5906 {
5907     BOOL  retval = TRUE;
5908     DWORD insize = 0;
5909     WCHAR *bufferW = NULL;
5910
5911     if (!namesize)
5912     {
5913         SetLastError(ERROR_INVALID_PARAMETER);
5914         return FALSE;
5915     }
5916
5917     if(name && *namesize) {
5918         insize = *namesize;
5919         bufferW = HeapAlloc( GetProcessHeap(), 0, insize * sizeof(WCHAR));
5920     }
5921
5922     if(!GetDefaultPrinterW( bufferW, namesize)) {
5923         retval = FALSE;
5924         goto end;
5925     }
5926
5927     *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, name, insize,
5928                                     NULL, NULL);
5929     if (!*namesize)
5930     {
5931         *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
5932         retval = FALSE;
5933     }
5934     TRACE("0x%08x/0x%08x:%s\n", *namesize, insize, debugstr_w(bufferW));
5935
5936 end:
5937     HeapFree( GetProcessHeap(), 0, bufferW);
5938     return retval;
5939 }
5940
5941
5942 /******************************************************************************
5943  *              SetDefaultPrinterW   (WINSPOOL.204)
5944  *
5945  * Set the Name of the Default Printer
5946  *
5947  * PARAMS
5948  *  pszPrinter [I] Name of the Printer or NULL
5949  *
5950  * RETURNS
5951  *  Success:    True
5952  *  Failure:    FALSE
5953  *
5954  * NOTES
5955  *  When the Parameter is NULL or points to an Empty String and
5956  *  a Default Printer was already present, then this Function changes nothing.
5957  *  Without a Default Printer and NULL (or an Empty String) as Parameter,
5958  *  the First enumerated local Printer is used.
5959  *
5960  */
5961 BOOL WINAPI SetDefaultPrinterW(LPCWSTR pszPrinter)
5962 {
5963     WCHAR   default_printer[MAX_PATH];
5964     LPWSTR  buffer = NULL;
5965     HKEY    hreg;
5966     DWORD   size;
5967     DWORD   namelen;
5968     LONG    lres;
5969
5970     TRACE("(%s)\n", debugstr_w(pszPrinter));
5971     if ((pszPrinter == NULL) || (pszPrinter[0] == '\0')) {
5972
5973         default_printer[0] = '\0';
5974         size = sizeof(default_printer)/sizeof(WCHAR);
5975
5976         /* if we have a default Printer, do nothing. */
5977         if (GetDefaultPrinterW(default_printer, &size))
5978             return TRUE;
5979
5980         pszPrinter = NULL;
5981         /* we have no default Printer: search local Printers and use the first */
5982         if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE, PrintersW, 0, KEY_READ, &hreg)) {
5983
5984             default_printer[0] = '\0';
5985             size = sizeof(default_printer)/sizeof(WCHAR);
5986             if (!RegEnumKeyExW(hreg, 0, default_printer, &size, NULL, NULL, NULL, NULL)) {
5987
5988                 pszPrinter = default_printer;
5989                 TRACE("using %s\n", debugstr_w(pszPrinter));
5990             }
5991             RegCloseKey(hreg);
5992         }
5993
5994         if (pszPrinter == NULL) {
5995             TRACE("no local printer found\n");
5996             SetLastError(ERROR_FILE_NOT_FOUND);
5997             return FALSE;
5998         }
5999     }
6000
6001     /* "pszPrinter" is never empty or NULL here. */
6002     namelen = lstrlenW(pszPrinter);
6003     size = namelen + (MAX_PATH * 2) + 3; /* printer,driver,port and a 0 */
6004     buffer = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
6005     if (!buffer ||
6006         (RegOpenKeyExW(HKEY_CURRENT_USER, user_printers_reg_key, 0, KEY_READ, &hreg) != ERROR_SUCCESS)) {
6007         HeapFree(GetProcessHeap(), 0, buffer);
6008         SetLastError(ERROR_FILE_NOT_FOUND);
6009         return FALSE;
6010     }
6011
6012     /* read the devices entry for the printer (driver,port) to build the string for the
6013        default device entry (printer,driver,port) */
6014     memcpy(buffer, pszPrinter, namelen * sizeof(WCHAR));
6015     buffer[namelen] = ',';
6016     namelen++; /* move index to the start of the driver */
6017
6018     size = ((MAX_PATH * 2) + 2) * sizeof(WCHAR); /* driver,port and a 0 */
6019     lres = RegQueryValueExW(hreg, pszPrinter, NULL, NULL, (LPBYTE) (&buffer[namelen]), &size);
6020     if (!lres) {
6021         TRACE("set device to %s\n", debugstr_w(buffer));
6022
6023         if (!WriteProfileStringW(windowsW, deviceW, buffer)) {
6024             TRACE("failed to set the device entry: %d\n", GetLastError());
6025             lres = ERROR_INVALID_PRINTER_NAME;
6026         }
6027
6028         /* remove the next section, when INIFileMapping is implemented */
6029         {
6030             HKEY hdev;
6031             if (!RegCreateKeyW(HKEY_CURRENT_USER, user_default_reg_key, &hdev)) {
6032                 RegSetValueExW(hdev, deviceW, 0, REG_SZ, (LPBYTE)buffer, (lstrlenW(buffer) + 1) * sizeof(WCHAR));
6033                 RegCloseKey(hdev);
6034             }
6035         }
6036     }
6037     else
6038     {
6039         if (lres != ERROR_FILE_NOT_FOUND)
6040             FIXME("RegQueryValueExW failed with %d for %s\n", lres, debugstr_w(pszPrinter));
6041
6042         SetLastError(ERROR_INVALID_PRINTER_NAME);
6043     }
6044
6045     RegCloseKey(hreg);
6046     HeapFree(GetProcessHeap(), 0, buffer);
6047     return (lres == ERROR_SUCCESS);
6048 }
6049
6050 /******************************************************************************
6051  *              SetDefaultPrinterA   (WINSPOOL.202)
6052  *
6053  * See SetDefaultPrinterW.
6054  *
6055  */
6056 BOOL WINAPI SetDefaultPrinterA(LPCSTR pszPrinter)
6057 {
6058     LPWSTR  bufferW = NULL;
6059     BOOL    res;
6060
6061     TRACE("(%s)\n", debugstr_a(pszPrinter));
6062     if(pszPrinter) {
6063         INT len = MultiByteToWideChar(CP_ACP, 0, pszPrinter, -1, NULL, 0);
6064         bufferW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6065         if (bufferW) MultiByteToWideChar(CP_ACP, 0, pszPrinter, -1, bufferW, len);
6066     }
6067     res = SetDefaultPrinterW(bufferW);
6068     HeapFree(GetProcessHeap(), 0, bufferW);
6069     return res;
6070 }
6071
6072 /******************************************************************************
6073  *              SetPrinterDataExA   (WINSPOOL.@)
6074  */
6075 DWORD WINAPI SetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
6076                                LPCSTR pValueName, DWORD Type,
6077                                LPBYTE pData, DWORD cbData)
6078 {
6079     HKEY hkeyPrinter, hkeySubkey;
6080     DWORD ret;
6081
6082     TRACE("(%p, %s, %s %08x, %p, %08x)\n", hPrinter, debugstr_a(pKeyName),
6083           debugstr_a(pValueName), Type, pData, cbData);
6084
6085     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
6086        != ERROR_SUCCESS)
6087         return ret;
6088
6089     if((ret = RegCreateKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
6090        != ERROR_SUCCESS) {
6091         ERR("Can't create subkey %s\n", debugstr_a(pKeyName));
6092         RegCloseKey(hkeyPrinter);
6093         return ret;
6094     }
6095     ret = RegSetValueExA(hkeySubkey, pValueName, 0, Type, pData, cbData);
6096     RegCloseKey(hkeySubkey);
6097     RegCloseKey(hkeyPrinter);
6098     return ret;
6099 }
6100
6101 /******************************************************************************
6102  *              SetPrinterDataExW   (WINSPOOL.@)
6103  */
6104 DWORD WINAPI SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
6105                                LPCWSTR pValueName, DWORD Type,
6106                                LPBYTE pData, DWORD cbData)
6107 {
6108     HKEY hkeyPrinter, hkeySubkey;
6109     DWORD ret;
6110
6111     TRACE("(%p, %s, %s %08x, %p, %08x)\n", hPrinter, debugstr_w(pKeyName),
6112           debugstr_w(pValueName), Type, pData, cbData);
6113
6114     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
6115        != ERROR_SUCCESS)
6116         return ret;
6117
6118     if((ret = RegCreateKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
6119        != ERROR_SUCCESS) {
6120         ERR("Can't create subkey %s\n", debugstr_w(pKeyName));
6121         RegCloseKey(hkeyPrinter);
6122         return ret;
6123     }
6124     ret = RegSetValueExW(hkeySubkey, pValueName, 0, Type, pData, cbData);
6125     RegCloseKey(hkeySubkey);
6126     RegCloseKey(hkeyPrinter);
6127     return ret;
6128 }
6129
6130 /******************************************************************************
6131  *              SetPrinterDataA   (WINSPOOL.@)
6132  */
6133 DWORD WINAPI SetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, DWORD Type,
6134                                LPBYTE pData, DWORD cbData)
6135 {
6136     return SetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, Type,
6137                              pData, cbData);
6138 }
6139
6140 /******************************************************************************
6141  *              SetPrinterDataW   (WINSPOOL.@)
6142  */
6143 DWORD WINAPI SetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, DWORD Type,
6144                              LPBYTE pData, DWORD cbData)
6145 {
6146     return SetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, Type,
6147                              pData, cbData);
6148 }
6149
6150 /******************************************************************************
6151  *              GetPrinterDataExA   (WINSPOOL.@)
6152  */
6153 DWORD WINAPI GetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
6154                                LPCSTR pValueName, LPDWORD pType,
6155                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
6156 {
6157     opened_printer_t *printer;
6158     HKEY hkeyPrinters, hkeyPrinter = 0, hkeySubkey = 0;
6159     DWORD ret;
6160
6161     TRACE("(%p, %s, %s, %p, %p, %u, %p)\n", hPrinter, debugstr_a(pKeyName),
6162             debugstr_a(pValueName), pType, pData, nSize, pcbNeeded);
6163
6164     printer = get_opened_printer(hPrinter);
6165     if(!printer) return ERROR_INVALID_HANDLE;
6166
6167     ret = RegOpenKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters);
6168     if (ret) return ret;
6169
6170     TRACE("printer->name: %s\n", debugstr_w(printer->name));
6171
6172     if (printer->name) {
6173
6174         ret = RegOpenKeyW(hkeyPrinters, printer->name, &hkeyPrinter);
6175         if (ret) {
6176             RegCloseKey(hkeyPrinters);
6177             return ret;
6178         }
6179         if((ret = RegOpenKeyA(hkeyPrinter, pKeyName, &hkeySubkey)) != ERROR_SUCCESS) {
6180             WARN("Can't open subkey %s: %d\n", debugstr_a(pKeyName), ret);
6181             RegCloseKey(hkeyPrinter);
6182             RegCloseKey(hkeyPrinters);
6183             return ret;
6184         }
6185     }
6186     *pcbNeeded = nSize;
6187     ret = RegQueryValueExA(printer->name ? hkeySubkey : hkeyPrinters, pValueName,
6188                           0, pType, pData, pcbNeeded);
6189
6190     if (!ret && !pData) ret = ERROR_MORE_DATA;
6191
6192     RegCloseKey(hkeySubkey);
6193     RegCloseKey(hkeyPrinter);
6194     RegCloseKey(hkeyPrinters);
6195
6196     TRACE("--> %d\n", ret);
6197     return ret;
6198 }
6199
6200 /******************************************************************************
6201  *              GetPrinterDataExW   (WINSPOOL.@)
6202  */
6203 DWORD WINAPI GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
6204                                LPCWSTR pValueName, LPDWORD pType,
6205                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
6206 {
6207     opened_printer_t *printer;
6208     HKEY hkeyPrinters, hkeyPrinter = 0, hkeySubkey = 0;
6209     DWORD ret;
6210
6211     TRACE("(%p, %s, %s, %p, %p, %u, %p)\n", hPrinter, debugstr_w(pKeyName),
6212             debugstr_w(pValueName), pType, pData, nSize, pcbNeeded);
6213
6214     printer = get_opened_printer(hPrinter);
6215     if(!printer) return ERROR_INVALID_HANDLE;
6216
6217     ret = RegOpenKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters);
6218     if (ret) return ret;
6219
6220     TRACE("printer->name: %s\n", debugstr_w(printer->name));
6221
6222     if (printer->name) {
6223
6224         ret = RegOpenKeyW(hkeyPrinters, printer->name, &hkeyPrinter);
6225         if (ret) {
6226             RegCloseKey(hkeyPrinters);
6227             return ret;
6228         }
6229         if((ret = RegOpenKeyW(hkeyPrinter, pKeyName, &hkeySubkey)) != ERROR_SUCCESS) {
6230             WARN("Can't open subkey %s: %d\n", debugstr_w(pKeyName), ret);
6231             RegCloseKey(hkeyPrinter);
6232             RegCloseKey(hkeyPrinters);
6233             return ret;
6234         }
6235     }
6236     *pcbNeeded = nSize;
6237     ret = RegQueryValueExW(printer->name ? hkeySubkey : hkeyPrinters, pValueName,
6238                           0, pType, pData, pcbNeeded);
6239
6240     if (!ret && !pData) ret = ERROR_MORE_DATA;
6241
6242     RegCloseKey(hkeySubkey);
6243     RegCloseKey(hkeyPrinter);
6244     RegCloseKey(hkeyPrinters);
6245
6246     TRACE("--> %d\n", ret);
6247     return ret;
6248 }
6249
6250 /******************************************************************************
6251  *              GetPrinterDataA   (WINSPOOL.@)
6252  */
6253 DWORD WINAPI GetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, LPDWORD pType,
6254                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
6255 {
6256     return GetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, pType,
6257                              pData, nSize, pcbNeeded);
6258 }
6259
6260 /******************************************************************************
6261  *              GetPrinterDataW   (WINSPOOL.@)
6262  */
6263 DWORD WINAPI GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType,
6264                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
6265 {
6266     return GetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, pType,
6267                              pData, nSize, pcbNeeded);
6268 }
6269
6270 /*******************************************************************************
6271  *              EnumPrinterDataExW      [WINSPOOL.@]
6272  */
6273 DWORD WINAPI EnumPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
6274                                 LPBYTE pEnumValues, DWORD cbEnumValues,
6275                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
6276 {
6277     HKEY                    hkPrinter, hkSubKey;
6278     DWORD                   r, ret, dwIndex, cValues, cbMaxValueNameLen,
6279                             cbValueNameLen, cbMaxValueLen, cbValueLen,
6280                             cbBufSize, dwType;
6281     LPWSTR                  lpValueName;
6282     HANDLE                  hHeap;
6283     PBYTE                   lpValue;
6284     PPRINTER_ENUM_VALUESW   ppev;
6285
6286     TRACE ("%p %s\n", hPrinter, debugstr_w (pKeyName));
6287
6288     if (pKeyName == NULL || *pKeyName == 0)
6289         return ERROR_INVALID_PARAMETER;
6290
6291     ret = WINSPOOL_GetOpenedPrinterRegKey (hPrinter, &hkPrinter);
6292     if (ret != ERROR_SUCCESS)
6293     {
6294         TRACE ("WINSPOOL_GetOpenedPrinterRegKey (%p) returned %i\n",
6295                 hPrinter, ret);
6296         return ret;
6297     }
6298
6299     ret = RegOpenKeyExW (hkPrinter, pKeyName, 0, KEY_READ, &hkSubKey);
6300     if (ret != ERROR_SUCCESS)
6301     {
6302         r = RegCloseKey (hkPrinter);
6303         if (r != ERROR_SUCCESS)
6304             WARN ("RegCloseKey returned %i\n", r);
6305         TRACE ("RegOpenKeyExW (%p, %s) returned %i\n", hPrinter,
6306                 debugstr_w (pKeyName), ret);
6307         return ret;
6308     }
6309
6310     ret = RegCloseKey (hkPrinter);
6311     if (ret != ERROR_SUCCESS)
6312     {
6313         ERR ("RegCloseKey returned %i\n", ret);
6314         r = RegCloseKey (hkSubKey);
6315         if (r != ERROR_SUCCESS)
6316             WARN ("RegCloseKey returned %i\n", r);
6317         return ret;
6318     }
6319
6320     ret = RegQueryInfoKeyW (hkSubKey, NULL, NULL, NULL, NULL, NULL, NULL,
6321             &cValues, &cbMaxValueNameLen, &cbMaxValueLen, NULL, NULL);
6322     if (ret != ERROR_SUCCESS)
6323     {
6324         r = RegCloseKey (hkSubKey);
6325         if (r != ERROR_SUCCESS)
6326             WARN ("RegCloseKey returned %i\n", r);
6327         TRACE ("RegQueryInfoKeyW (%p) returned %i\n", hkSubKey, ret);
6328         return ret;
6329     }
6330
6331     TRACE ("RegQueryInfoKeyW returned cValues = %i, cbMaxValueNameLen = %i, "
6332             "cbMaxValueLen = %i\n", cValues, cbMaxValueNameLen, cbMaxValueLen);
6333
6334     if (cValues == 0)                   /* empty key */
6335     {
6336         r = RegCloseKey (hkSubKey);
6337         if (r != ERROR_SUCCESS)
6338             WARN ("RegCloseKey returned %i\n", r);
6339         *pcbEnumValues = *pnEnumValues = 0;
6340         return ERROR_SUCCESS;
6341     }
6342
6343     ++cbMaxValueNameLen;                        /* allow for trailing '\0' */
6344
6345     hHeap = GetProcessHeap ();
6346     if (hHeap == NULL)
6347     {
6348         ERR ("GetProcessHeap failed\n");
6349         r = RegCloseKey (hkSubKey);
6350         if (r != ERROR_SUCCESS)
6351             WARN ("RegCloseKey returned %i\n", r);
6352         return ERROR_OUTOFMEMORY;
6353     }
6354
6355     lpValueName = HeapAlloc (hHeap, 0, cbMaxValueNameLen * sizeof (WCHAR));
6356     if (lpValueName == NULL)
6357     {
6358         ERR ("Failed to allocate %i WCHARs from process heap\n", cbMaxValueNameLen);
6359         r = RegCloseKey (hkSubKey);
6360         if (r != ERROR_SUCCESS)
6361             WARN ("RegCloseKey returned %i\n", r);
6362         return ERROR_OUTOFMEMORY;
6363     }
6364
6365     lpValue = HeapAlloc (hHeap, 0, cbMaxValueLen);
6366     if (lpValue == NULL)
6367     {
6368         ERR ("Failed to allocate %i bytes from process heap\n", cbMaxValueLen);
6369         if (HeapFree (hHeap, 0, lpValueName) == 0)
6370             WARN ("HeapFree failed with code %i\n", GetLastError ());
6371         r = RegCloseKey (hkSubKey);
6372         if (r != ERROR_SUCCESS)
6373             WARN ("RegCloseKey returned %i\n", r);
6374         return ERROR_OUTOFMEMORY;
6375     }
6376
6377     TRACE ("pass 1: calculating buffer required for all names and values\n");
6378
6379     cbBufSize = cValues * sizeof (PRINTER_ENUM_VALUESW);
6380
6381     TRACE ("%i bytes required for %i headers\n", cbBufSize, cValues);
6382
6383     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
6384     {
6385         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
6386         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
6387                 NULL, NULL, lpValue, &cbValueLen);
6388         if (ret != ERROR_SUCCESS)
6389         {
6390             if (HeapFree (hHeap, 0, lpValue) == 0)
6391                 WARN ("HeapFree failed with code %i\n", GetLastError ());
6392             if (HeapFree (hHeap, 0, lpValueName) == 0)
6393                 WARN ("HeapFree failed with code %i\n", GetLastError ());
6394             r = RegCloseKey (hkSubKey);
6395             if (r != ERROR_SUCCESS)
6396                 WARN ("RegCloseKey returned %i\n", r);
6397             TRACE ("RegEnumValueW (%i) returned %i\n", dwIndex, ret);
6398             return ret;
6399         }
6400
6401         TRACE ("%s [%i]: name needs %i WCHARs, data needs %i bytes\n",
6402                 debugstr_w (lpValueName), dwIndex,
6403                 cbValueNameLen + 1, cbValueLen);
6404
6405         cbBufSize += (cbValueNameLen + 1) * sizeof (WCHAR);
6406         cbBufSize += cbValueLen;
6407     }
6408
6409     TRACE ("%i bytes required for all %i values\n", cbBufSize, cValues);
6410
6411     *pcbEnumValues = cbBufSize;
6412     *pnEnumValues = cValues;
6413
6414     if (cbEnumValues < cbBufSize)       /* buffer too small */
6415     {
6416         if (HeapFree (hHeap, 0, lpValue) == 0)
6417             WARN ("HeapFree failed with code %i\n", GetLastError ());
6418         if (HeapFree (hHeap, 0, lpValueName) == 0)
6419             WARN ("HeapFree failed with code %i\n", GetLastError ());
6420         r = RegCloseKey (hkSubKey);
6421         if (r != ERROR_SUCCESS)
6422             WARN ("RegCloseKey returned %i\n", r);
6423         TRACE ("%i byte buffer is not large enough\n", cbEnumValues);
6424         return ERROR_MORE_DATA;
6425     }
6426
6427     TRACE ("pass 2: copying all names and values to buffer\n");
6428
6429     ppev = (PPRINTER_ENUM_VALUESW) pEnumValues;         /* array of structs */
6430     pEnumValues += cValues * sizeof (PRINTER_ENUM_VALUESW);
6431
6432     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
6433     {
6434         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
6435         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
6436                 NULL, &dwType, lpValue, &cbValueLen);
6437         if (ret != ERROR_SUCCESS)
6438         {
6439             if (HeapFree (hHeap, 0, lpValue) == 0)
6440                 WARN ("HeapFree failed with code %i\n", GetLastError ());
6441             if (HeapFree (hHeap, 0, lpValueName) == 0)
6442                 WARN ("HeapFree failed with code %i\n", GetLastError ());
6443             r = RegCloseKey (hkSubKey);
6444             if (r != ERROR_SUCCESS)
6445                 WARN ("RegCloseKey returned %i\n", r);
6446             TRACE ("RegEnumValueW (%i) returned %i\n", dwIndex, ret);
6447             return ret;
6448         }
6449
6450         cbValueNameLen = (cbValueNameLen + 1) * sizeof (WCHAR);
6451         memcpy (pEnumValues, lpValueName, cbValueNameLen);
6452         ppev[dwIndex].pValueName = (LPWSTR) pEnumValues;
6453         pEnumValues += cbValueNameLen;
6454
6455         /* return # of *bytes* (including trailing \0), not # of chars */
6456         ppev[dwIndex].cbValueName = cbValueNameLen;
6457
6458         ppev[dwIndex].dwType = dwType;
6459
6460         memcpy (pEnumValues, lpValue, cbValueLen);
6461         ppev[dwIndex].pData = pEnumValues;
6462         pEnumValues += cbValueLen;
6463
6464         ppev[dwIndex].cbData = cbValueLen;
6465
6466         TRACE ("%s [%i]: copied name (%i bytes) and data (%i bytes)\n",
6467                 debugstr_w (lpValueName), dwIndex, cbValueNameLen, cbValueLen);
6468     }
6469
6470     if (HeapFree (hHeap, 0, lpValue) == 0)
6471     {
6472         ret = GetLastError ();
6473         ERR ("HeapFree failed with code %i\n", ret);
6474         if (HeapFree (hHeap, 0, lpValueName) == 0)
6475             WARN ("HeapFree failed with code %i\n", GetLastError ());
6476         r = RegCloseKey (hkSubKey);
6477         if (r != ERROR_SUCCESS)
6478             WARN ("RegCloseKey returned %i\n", r);
6479         return ret;
6480     }
6481
6482     if (HeapFree (hHeap, 0, lpValueName) == 0)
6483     {
6484         ret = GetLastError ();
6485         ERR ("HeapFree failed with code %i\n", ret);
6486         r = RegCloseKey (hkSubKey);
6487         if (r != ERROR_SUCCESS)
6488             WARN ("RegCloseKey returned %i\n", r);
6489         return ret;
6490     }
6491
6492     ret = RegCloseKey (hkSubKey);
6493     if (ret != ERROR_SUCCESS)
6494     {
6495         ERR ("RegCloseKey returned %i\n", ret);
6496         return ret;
6497     }
6498
6499     return ERROR_SUCCESS;
6500 }
6501
6502 /*******************************************************************************
6503  *              EnumPrinterDataExA      [WINSPOOL.@]
6504  *
6505  * This functions returns value names and REG_SZ, REG_EXPAND_SZ, and
6506  * REG_MULTI_SZ values as ASCII strings in Unicode-sized buffers.  This is
6507  * what Windows 2000 SP1 does.
6508  *
6509  */
6510 DWORD WINAPI EnumPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
6511                                 LPBYTE pEnumValues, DWORD cbEnumValues,
6512                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
6513 {
6514     INT     len;
6515     LPWSTR  pKeyNameW;
6516     DWORD   ret, dwIndex, dwBufSize;
6517     HANDLE  hHeap;
6518     LPSTR   pBuffer;
6519
6520     TRACE ("%p %s\n", hPrinter, pKeyName);
6521
6522     if (pKeyName == NULL || *pKeyName == 0)
6523         return ERROR_INVALID_PARAMETER;
6524
6525     len = MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, NULL, 0);
6526     if (len == 0)
6527     {
6528         ret = GetLastError ();
6529         ERR ("MultiByteToWideChar failed with code %i\n", ret);
6530         return ret;
6531     }
6532
6533     hHeap = GetProcessHeap ();
6534     if (hHeap == NULL)
6535     {
6536         ERR ("GetProcessHeap failed\n");
6537         return ERROR_OUTOFMEMORY;
6538     }
6539
6540     pKeyNameW = HeapAlloc (hHeap, 0, len * sizeof (WCHAR));
6541     if (pKeyNameW == NULL)
6542     {
6543         ERR ("Failed to allocate %i bytes from process heap\n",
6544              (LONG)(len * sizeof (WCHAR)));
6545         return ERROR_OUTOFMEMORY;
6546     }
6547
6548     if (MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, pKeyNameW, len) == 0)
6549     {
6550         ret = GetLastError ();
6551         ERR ("MultiByteToWideChar failed with code %i\n", ret);
6552         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
6553             WARN ("HeapFree failed with code %i\n", GetLastError ());
6554         return ret;
6555     }
6556
6557     ret = EnumPrinterDataExW (hPrinter, pKeyNameW, pEnumValues, cbEnumValues,
6558             pcbEnumValues, pnEnumValues);
6559     if (ret != ERROR_SUCCESS)
6560     {
6561         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
6562             WARN ("HeapFree failed with code %i\n", GetLastError ());
6563         TRACE ("EnumPrinterDataExW returned %i\n", ret);
6564         return ret;
6565     }
6566
6567     if (HeapFree (hHeap, 0, pKeyNameW) == 0)
6568     {
6569         ret = GetLastError ();
6570         ERR ("HeapFree failed with code %i\n", ret);
6571         return ret;
6572     }
6573
6574     if (*pnEnumValues == 0)     /* empty key */
6575         return ERROR_SUCCESS;
6576
6577     dwBufSize = 0;
6578     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
6579     {
6580         PPRINTER_ENUM_VALUESW ppev =
6581                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
6582
6583         if (dwBufSize < ppev->cbValueName)
6584             dwBufSize = ppev->cbValueName;
6585
6586         if (dwBufSize < ppev->cbData && (ppev->dwType == REG_SZ ||
6587                 ppev->dwType == REG_EXPAND_SZ || ppev->dwType == REG_MULTI_SZ))
6588             dwBufSize = ppev->cbData;
6589     }
6590
6591     TRACE ("Largest Unicode name or value is %i bytes\n", dwBufSize);
6592
6593     pBuffer = HeapAlloc (hHeap, 0, dwBufSize);
6594     if (pBuffer == NULL)
6595     {
6596         ERR ("Failed to allocate %i bytes from process heap\n", dwBufSize);
6597         return ERROR_OUTOFMEMORY;
6598     }
6599
6600     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
6601     {
6602         PPRINTER_ENUM_VALUESW ppev =
6603                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
6604
6605         len = WideCharToMultiByte (CP_ACP, 0, ppev->pValueName,
6606                 ppev->cbValueName / sizeof (WCHAR), pBuffer, dwBufSize, NULL,
6607                 NULL);
6608         if (len == 0)
6609         {
6610             ret = GetLastError ();
6611             ERR ("WideCharToMultiByte failed with code %i\n", ret);
6612             if (HeapFree (hHeap, 0, pBuffer) == 0)
6613                 WARN ("HeapFree failed with code %i\n", GetLastError ());
6614             return ret;
6615         }
6616
6617         memcpy (ppev->pValueName, pBuffer, len);
6618
6619         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
6620
6621         if (ppev->dwType != REG_SZ && ppev->dwType != REG_EXPAND_SZ &&
6622                 ppev->dwType != REG_MULTI_SZ)
6623             continue;
6624
6625         len = WideCharToMultiByte (CP_ACP, 0, (LPWSTR) ppev->pData,
6626                 ppev->cbData / sizeof (WCHAR), pBuffer, dwBufSize, NULL, NULL);
6627         if (len == 0)
6628         {
6629             ret = GetLastError ();
6630             ERR ("WideCharToMultiByte failed with code %i\n", ret);
6631             if (HeapFree (hHeap, 0, pBuffer) == 0)
6632                 WARN ("HeapFree failed with code %i\n", GetLastError ());
6633             return ret;
6634         }
6635
6636         memcpy (ppev->pData, pBuffer, len);
6637
6638         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
6639         TRACE ("  (only first string of REG_MULTI_SZ printed)\n");
6640     }
6641
6642     if (HeapFree (hHeap, 0, pBuffer) == 0)
6643     {
6644         ret = GetLastError ();
6645         ERR ("HeapFree failed with code %i\n", ret);
6646         return ret;
6647     }
6648
6649     return ERROR_SUCCESS;
6650 }
6651
6652 /******************************************************************************
6653  *      AbortPrinter (WINSPOOL.@)
6654  */
6655 BOOL WINAPI AbortPrinter( HANDLE hPrinter )
6656 {
6657     FIXME("(%p), stub!\n", hPrinter);
6658     return TRUE;
6659 }
6660
6661 /******************************************************************************
6662  *              AddPortA (WINSPOOL.@)
6663  *
6664  * See AddPortW.
6665  *
6666  */
6667 BOOL WINAPI AddPortA(LPSTR pName, HWND hWnd, LPSTR pMonitorName)
6668 {
6669     LPWSTR  nameW = NULL;
6670     LPWSTR  monitorW = NULL;
6671     DWORD   len;
6672     BOOL    res;
6673
6674     TRACE("(%s, %p, %s)\n",debugstr_a(pName), hWnd, debugstr_a(pMonitorName));
6675
6676     if (pName) {
6677         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6678         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6679         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6680     }
6681
6682     if (pMonitorName) {
6683         len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
6684         monitorW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6685         MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, monitorW, len);
6686     }
6687     res = AddPortW(nameW, hWnd, monitorW);
6688     HeapFree(GetProcessHeap(), 0, nameW);
6689     HeapFree(GetProcessHeap(), 0, monitorW);
6690     return res;
6691 }
6692
6693 /******************************************************************************
6694  *      AddPortW (WINSPOOL.@)
6695  *
6696  * Add a Port for a specific Monitor
6697  *
6698  * PARAMS
6699  *  pName        [I] Servername or NULL (local Computer)
6700  *  hWnd         [I] Handle to parent Window for the Dialog-Box
6701  *  pMonitorName [I] Name of the Monitor that manage the Port
6702  *
6703  * RETURNS
6704  *  Success: TRUE
6705  *  Failure: FALSE
6706  *
6707  */
6708 BOOL WINAPI AddPortW(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
6709 {
6710     TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pMonitorName));
6711
6712     if ((backend == NULL)  && !load_backend()) return FALSE;
6713
6714     if (!pMonitorName) {
6715         SetLastError(RPC_X_NULL_REF_POINTER);
6716         return FALSE;
6717     }
6718
6719     return backend->fpAddPort(pName, hWnd, pMonitorName);
6720 }
6721
6722 /******************************************************************************
6723  *             AddPortExA (WINSPOOL.@)
6724  *
6725  * See AddPortExW.
6726  *
6727  */
6728 BOOL WINAPI AddPortExA(LPSTR pName, DWORD level, LPBYTE pBuffer, LPSTR pMonitorName)
6729 {
6730     PORT_INFO_2W   pi2W;
6731     PORT_INFO_2A * pi2A;
6732     LPWSTR  nameW = NULL;
6733     LPWSTR  monitorW = NULL;
6734     DWORD   len;
6735     BOOL    res;
6736
6737     pi2A = (PORT_INFO_2A *) pBuffer;
6738
6739     TRACE("(%s, %d, %p, %s): %s\n", debugstr_a(pName), level, pBuffer,
6740             debugstr_a(pMonitorName), debugstr_a(pi2A ? pi2A->pPortName : NULL));
6741
6742     if ((level < 1) || (level > 2)) {
6743         SetLastError(ERROR_INVALID_LEVEL);
6744         return FALSE;
6745     }
6746
6747     if (!pi2A) {
6748         SetLastError(ERROR_INVALID_PARAMETER);
6749         return FALSE;
6750     }
6751
6752     if (pName) {
6753         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6754         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6755         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6756     }
6757
6758     if (pMonitorName) {
6759         len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
6760         monitorW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6761         MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, monitorW, len);
6762     }
6763
6764     ZeroMemory(&pi2W, sizeof(PORT_INFO_2W));
6765
6766     if (pi2A->pPortName) {
6767         len = MultiByteToWideChar(CP_ACP, 0, pi2A->pPortName, -1, NULL, 0);
6768         pi2W.pPortName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6769         MultiByteToWideChar(CP_ACP, 0, pi2A->pPortName, -1, pi2W.pPortName, len);
6770     }
6771
6772     if (level > 1) {
6773         if (pi2A->pMonitorName) {
6774             len = MultiByteToWideChar(CP_ACP, 0, pi2A->pMonitorName, -1, NULL, 0);
6775             pi2W.pMonitorName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6776             MultiByteToWideChar(CP_ACP, 0, pi2A->pMonitorName, -1, pi2W.pMonitorName, len);
6777         }
6778
6779         if (pi2A->pDescription) {
6780             len = MultiByteToWideChar(CP_ACP, 0, pi2A->pDescription, -1, NULL, 0);
6781             pi2W.pDescription = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6782             MultiByteToWideChar(CP_ACP, 0, pi2A->pDescription, -1, pi2W.pDescription, len);
6783         }
6784         pi2W.fPortType = pi2A->fPortType;
6785         pi2W.Reserved = pi2A->Reserved;
6786     }
6787
6788     res = AddPortExW(nameW, level, (LPBYTE) &pi2W, monitorW);
6789
6790     HeapFree(GetProcessHeap(), 0, nameW);
6791     HeapFree(GetProcessHeap(), 0, monitorW);
6792     HeapFree(GetProcessHeap(), 0, pi2W.pPortName);
6793     HeapFree(GetProcessHeap(), 0, pi2W.pMonitorName);
6794     HeapFree(GetProcessHeap(), 0, pi2W.pDescription);
6795     return res;
6796
6797 }
6798
6799 /******************************************************************************
6800  *             AddPortExW (WINSPOOL.@)
6801  *
6802  * Add a Port for a specific Monitor, without presenting a user interface
6803  *
6804  * PARAMS
6805  *  pName         [I] Servername or NULL (local Computer)
6806  *  level         [I] Structure-Level (1 or 2) for pBuffer
6807  *  pBuffer       [I] PTR to: PORT_INFO_1 or PORT_INFO_2
6808  *  pMonitorName  [I] Name of the Monitor that manage the Port
6809  *
6810  * RETURNS
6811  *  Success: TRUE
6812  *  Failure: FALSE
6813  *
6814  */
6815 BOOL WINAPI AddPortExW(LPWSTR pName, DWORD level, LPBYTE pBuffer, LPWSTR pMonitorName)
6816 {
6817     PORT_INFO_2W * pi2;
6818
6819     pi2 = (PORT_INFO_2W *) pBuffer;
6820
6821     TRACE("(%s, %d, %p, %s): %s %s %s\n", debugstr_w(pName), level, pBuffer,
6822             debugstr_w(pMonitorName), debugstr_w(pi2 ? pi2->pPortName : NULL),
6823             debugstr_w(((level > 1) && pi2) ? pi2->pMonitorName : NULL),
6824             debugstr_w(((level > 1) && pi2) ? pi2->pDescription : NULL));
6825
6826     if ((backend == NULL)  && !load_backend()) return FALSE;
6827
6828     if ((!pi2) || (!pMonitorName) || (!pMonitorName[0])) {
6829         SetLastError(ERROR_INVALID_PARAMETER);
6830         return FALSE;
6831     }
6832
6833     return backend->fpAddPortEx(pName, level, pBuffer, pMonitorName);
6834 }
6835
6836 /******************************************************************************
6837  *      AddPrinterConnectionA (WINSPOOL.@)
6838  */
6839 BOOL WINAPI AddPrinterConnectionA( LPSTR pName )
6840 {
6841     FIXME("%s\n", debugstr_a(pName));
6842     return FALSE;
6843 }
6844
6845 /******************************************************************************
6846  *      AddPrinterConnectionW (WINSPOOL.@)
6847  */
6848 BOOL WINAPI AddPrinterConnectionW( LPWSTR pName )
6849 {
6850     FIXME("%s\n", debugstr_w(pName));
6851     return FALSE;
6852 }
6853
6854 /******************************************************************************
6855  *  AddPrinterDriverExW (WINSPOOL.@)
6856  *
6857  * Install a Printer Driver with the Option to upgrade / downgrade the Files
6858  *
6859  * PARAMS
6860  *  pName           [I] Servername or NULL (local Computer)
6861  *  level           [I] Level for the supplied DRIVER_INFO_*W struct
6862  *  pDriverInfo     [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter
6863  *  dwFileCopyFlags [I] How to Copy / Upgrade / Downgrade the needed Files
6864  *
6865  * RESULTS
6866  *  Success: TRUE
6867  *  Failure: FALSE
6868  *
6869  */
6870 BOOL WINAPI AddPrinterDriverExW( LPWSTR pName, DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
6871 {
6872     TRACE("(%s, %d, %p, 0x%x)\n", debugstr_w(pName), level, pDriverInfo, dwFileCopyFlags);
6873
6874     if ((backend == NULL)  && !load_backend()) return FALSE;
6875
6876     if (level < 2 || level == 5 || level == 7 || level > 8) {
6877         SetLastError(ERROR_INVALID_LEVEL);
6878         return FALSE;
6879     }
6880
6881     if (!pDriverInfo) {
6882         SetLastError(ERROR_INVALID_PARAMETER);
6883         return FALSE;
6884     }
6885
6886     return backend->fpAddPrinterDriverEx(pName, level, pDriverInfo, dwFileCopyFlags);
6887 }
6888
6889 /******************************************************************************
6890  *  AddPrinterDriverExA (WINSPOOL.@)
6891  *
6892  * See AddPrinterDriverExW.
6893  *
6894  */
6895 BOOL WINAPI AddPrinterDriverExA(LPSTR pName, DWORD Level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
6896 {
6897     DRIVER_INFO_8A  *diA;
6898     DRIVER_INFO_8W   diW;
6899     LPWSTR  nameW = NULL;
6900     DWORD   lenA;
6901     DWORD   len;
6902     DWORD   res = FALSE;
6903
6904     TRACE("(%s, %d, %p, 0x%x)\n", debugstr_a(pName), Level, pDriverInfo, dwFileCopyFlags);
6905
6906     diA = (DRIVER_INFO_8A  *) pDriverInfo;
6907     ZeroMemory(&diW, sizeof(diW));
6908
6909     if (Level < 2 || Level == 5 || Level == 7 || Level > 8) {
6910         SetLastError(ERROR_INVALID_LEVEL);
6911         return FALSE;
6912     }
6913
6914     if (diA == NULL) {
6915         SetLastError(ERROR_INVALID_PARAMETER);
6916         return FALSE;
6917     }
6918
6919     /* convert servername to unicode */
6920     if (pName) {
6921         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6922         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6923         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6924     }
6925
6926     /* common fields */
6927     diW.cVersion = diA->cVersion;
6928
6929     if (diA->pName) {
6930         len = MultiByteToWideChar(CP_ACP, 0, diA->pName, -1, NULL, 0);
6931         diW.pName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6932         MultiByteToWideChar(CP_ACP, 0, diA->pName, -1, diW.pName, len);
6933     }
6934
6935     if (diA->pEnvironment) {
6936         len = MultiByteToWideChar(CP_ACP, 0, diA->pEnvironment, -1, NULL, 0);
6937         diW.pEnvironment = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6938         MultiByteToWideChar(CP_ACP, 0, diA->pEnvironment, -1, diW.pEnvironment, len);
6939     }
6940
6941     if (diA->pDriverPath) {
6942         len = MultiByteToWideChar(CP_ACP, 0, diA->pDriverPath, -1, NULL, 0);
6943         diW.pDriverPath = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6944         MultiByteToWideChar(CP_ACP, 0, diA->pDriverPath, -1, diW.pDriverPath, len);
6945     }
6946
6947     if (diA->pDataFile) {
6948         len = MultiByteToWideChar(CP_ACP, 0, diA->pDataFile, -1, NULL, 0);
6949         diW.pDataFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6950         MultiByteToWideChar(CP_ACP, 0, diA->pDataFile, -1, diW.pDataFile, len);
6951     }
6952
6953     if (diA->pConfigFile) {
6954         len = MultiByteToWideChar(CP_ACP, 0, diA->pConfigFile, -1, NULL, 0);
6955         diW.pConfigFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6956         MultiByteToWideChar(CP_ACP, 0, diA->pConfigFile, -1, diW.pConfigFile, len);
6957     }
6958
6959     if ((Level > 2) && diA->pDependentFiles) {
6960         lenA = multi_sz_lenA(diA->pDependentFiles);
6961         len = MultiByteToWideChar(CP_ACP, 0, diA->pDependentFiles, lenA, NULL, 0);
6962         diW.pDependentFiles = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6963         MultiByteToWideChar(CP_ACP, 0, diA->pDependentFiles, lenA, diW.pDependentFiles, len);
6964     }
6965
6966     if ((Level > 2) && diA->pMonitorName) {
6967         len = MultiByteToWideChar(CP_ACP, 0, diA->pMonitorName, -1, NULL, 0);
6968         diW.pMonitorName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6969         MultiByteToWideChar(CP_ACP, 0, diA->pMonitorName, -1, diW.pMonitorName, len);
6970     }
6971
6972     if ((Level > 3) && diA->pDefaultDataType) {
6973         len = MultiByteToWideChar(CP_ACP, 0, diA->pDefaultDataType, -1, NULL, 0);
6974         diW.pDefaultDataType = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6975         MultiByteToWideChar(CP_ACP, 0, diA->pDefaultDataType, -1, diW.pDefaultDataType, len);
6976     }
6977
6978     if ((Level > 3) && diA->pszzPreviousNames) {
6979         lenA = multi_sz_lenA(diA->pszzPreviousNames);
6980         len = MultiByteToWideChar(CP_ACP, 0, diA->pszzPreviousNames, lenA, NULL, 0);
6981         diW.pszzPreviousNames = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6982         MultiByteToWideChar(CP_ACP, 0, diA->pszzPreviousNames, lenA, diW.pszzPreviousNames, len);
6983     }
6984
6985     if ((Level > 5) && diA->pszMfgName) {
6986         len = MultiByteToWideChar(CP_ACP, 0, diA->pszMfgName, -1, NULL, 0);
6987         diW.pszMfgName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6988         MultiByteToWideChar(CP_ACP, 0, diA->pszMfgName, -1, diW.pszMfgName, len);
6989     }
6990
6991     if ((Level > 5) && diA->pszOEMUrl) {
6992         len = MultiByteToWideChar(CP_ACP, 0, diA->pszOEMUrl, -1, NULL, 0);
6993         diW.pszOEMUrl = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6994         MultiByteToWideChar(CP_ACP, 0, diA->pszOEMUrl, -1, diW.pszOEMUrl, len);
6995     }
6996
6997     if ((Level > 5) && diA->pszHardwareID) {
6998         len = MultiByteToWideChar(CP_ACP, 0, diA->pszHardwareID, -1, NULL, 0);
6999         diW.pszHardwareID = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7000         MultiByteToWideChar(CP_ACP, 0, diA->pszHardwareID, -1, diW.pszHardwareID, len);
7001     }
7002
7003     if ((Level > 5) && diA->pszProvider) {
7004         len = MultiByteToWideChar(CP_ACP, 0, diA->pszProvider, -1, NULL, 0);
7005         diW.pszProvider = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7006         MultiByteToWideChar(CP_ACP, 0, diA->pszProvider, -1, diW.pszProvider, len);
7007     }
7008
7009     if (Level > 7) {
7010         FIXME("level %u is incomplete\n", Level);
7011     }
7012
7013     res = AddPrinterDriverExW(nameW, Level, (LPBYTE) &diW, dwFileCopyFlags);
7014     TRACE("got %u with %u\n", res, GetLastError());
7015     HeapFree(GetProcessHeap(), 0, nameW);
7016     HeapFree(GetProcessHeap(), 0, diW.pName);
7017     HeapFree(GetProcessHeap(), 0, diW.pEnvironment);
7018     HeapFree(GetProcessHeap(), 0, diW.pDriverPath);
7019     HeapFree(GetProcessHeap(), 0, diW.pDataFile);
7020     HeapFree(GetProcessHeap(), 0, diW.pConfigFile);
7021     HeapFree(GetProcessHeap(), 0, diW.pDependentFiles);
7022     HeapFree(GetProcessHeap(), 0, diW.pMonitorName);
7023     HeapFree(GetProcessHeap(), 0, diW.pDefaultDataType);
7024     HeapFree(GetProcessHeap(), 0, diW.pszzPreviousNames);
7025     HeapFree(GetProcessHeap(), 0, diW.pszMfgName);
7026     HeapFree(GetProcessHeap(), 0, diW.pszOEMUrl);
7027     HeapFree(GetProcessHeap(), 0, diW.pszHardwareID);
7028     HeapFree(GetProcessHeap(), 0, diW.pszProvider);
7029
7030     TRACE("=> %u with %u\n", res, GetLastError());
7031     return res;
7032 }
7033
7034 /******************************************************************************
7035  *      ConfigurePortA (WINSPOOL.@)
7036  *
7037  * See ConfigurePortW.
7038  *
7039  */
7040 BOOL WINAPI ConfigurePortA(LPSTR pName, HWND hWnd, LPSTR pPortName)
7041 {
7042     LPWSTR  nameW = NULL;
7043     LPWSTR  portW = NULL;
7044     INT     len;
7045     DWORD   res;
7046
7047     TRACE("(%s, %p, %s)\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
7048
7049     /* convert servername to unicode */
7050     if (pName) {
7051         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
7052         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7053         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
7054     }
7055
7056     /* convert portname to unicode */
7057     if (pPortName) {
7058         len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
7059         portW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7060         MultiByteToWideChar(CP_ACP, 0, pPortName, -1, portW, len);
7061     }
7062
7063     res = ConfigurePortW(nameW, hWnd, portW);
7064     HeapFree(GetProcessHeap(), 0, nameW);
7065     HeapFree(GetProcessHeap(), 0, portW);
7066     return res;
7067 }
7068
7069 /******************************************************************************
7070  *      ConfigurePortW (WINSPOOL.@)
7071  *
7072  * Display the Configuration-Dialog for a specific Port
7073  *
7074  * PARAMS
7075  *  pName     [I] Servername or NULL (local Computer)
7076  *  hWnd      [I] Handle to parent Window for the Dialog-Box
7077  *  pPortName [I] Name of the Port, that should be configured
7078  *
7079  * RETURNS
7080  *  Success: TRUE
7081  *  Failure: FALSE
7082  *
7083  */
7084 BOOL WINAPI ConfigurePortW(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
7085 {
7086
7087     TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
7088
7089     if ((backend == NULL)  && !load_backend()) return FALSE;
7090
7091     if (!pPortName) {
7092         SetLastError(RPC_X_NULL_REF_POINTER);
7093         return FALSE;
7094     }
7095
7096     return backend->fpConfigurePort(pName, hWnd, pPortName);
7097 }
7098
7099 /******************************************************************************
7100  *      ConnectToPrinterDlg (WINSPOOL.@)
7101  */
7102 HANDLE WINAPI ConnectToPrinterDlg( HWND hWnd, DWORD Flags )
7103 {
7104     FIXME("%p %x\n", hWnd, Flags);
7105     return NULL;
7106 }
7107
7108 /******************************************************************************
7109  *      DeletePrinterConnectionA (WINSPOOL.@)
7110  */
7111 BOOL WINAPI DeletePrinterConnectionA( LPSTR pName )
7112 {
7113     FIXME("%s\n", debugstr_a(pName));
7114     return TRUE;
7115 }
7116
7117 /******************************************************************************
7118  *      DeletePrinterConnectionW (WINSPOOL.@)
7119  */
7120 BOOL WINAPI DeletePrinterConnectionW( LPWSTR pName )
7121 {
7122     FIXME("%s\n", debugstr_w(pName));
7123     return TRUE;
7124 }
7125
7126 /******************************************************************************
7127  *              DeletePrinterDriverExW (WINSPOOL.@)
7128  */
7129 BOOL WINAPI DeletePrinterDriverExW( LPWSTR pName, LPWSTR pEnvironment,
7130     LPWSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
7131 {
7132     HKEY hkey_drivers;
7133     BOOL ret = FALSE;
7134
7135     TRACE("%s %s %s %x %x\n", debugstr_w(pName), debugstr_w(pEnvironment),
7136           debugstr_w(pDriverName), dwDeleteFlag, dwVersionFlag);
7137
7138     if(pName && pName[0])
7139     {
7140         FIXME("pName = %s - unsupported\n", debugstr_w(pName));
7141         SetLastError(ERROR_INVALID_PARAMETER);
7142         return FALSE;
7143     }
7144
7145     if(dwDeleteFlag)
7146     {
7147         FIXME("dwDeleteFlag = %x - unsupported\n", dwDeleteFlag);
7148         SetLastError(ERROR_INVALID_PARAMETER);
7149         return FALSE;
7150     }
7151
7152     hkey_drivers = WINSPOOL_OpenDriverReg(pEnvironment);
7153
7154     if(!hkey_drivers)
7155     {
7156         ERR("Can't open drivers key\n");
7157         return FALSE;
7158     }
7159
7160     if(RegDeleteTreeW(hkey_drivers, pDriverName) == ERROR_SUCCESS)
7161         ret = TRUE;
7162
7163     RegCloseKey(hkey_drivers);
7164
7165     return ret;
7166 }
7167
7168 /******************************************************************************
7169  *              DeletePrinterDriverExA (WINSPOOL.@)
7170  */
7171 BOOL WINAPI DeletePrinterDriverExA( LPSTR pName, LPSTR pEnvironment,
7172     LPSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
7173 {
7174     UNICODE_STRING NameW, EnvW, DriverW;
7175     BOOL ret;
7176
7177     asciitounicode(&NameW, pName);
7178     asciitounicode(&EnvW, pEnvironment);
7179     asciitounicode(&DriverW, pDriverName);
7180
7181     ret = DeletePrinterDriverExW(NameW.Buffer, EnvW.Buffer, DriverW.Buffer, dwDeleteFlag, dwVersionFlag);
7182
7183     RtlFreeUnicodeString(&DriverW);
7184     RtlFreeUnicodeString(&EnvW);
7185     RtlFreeUnicodeString(&NameW);
7186
7187     return ret;
7188 }
7189
7190 /******************************************************************************
7191  *              DeletePrinterDataExW (WINSPOOL.@)
7192  */
7193 DWORD WINAPI DeletePrinterDataExW( HANDLE hPrinter, LPCWSTR pKeyName,
7194                                   LPCWSTR pValueName)
7195 {
7196     FIXME("%p %s %s\n", hPrinter, 
7197           debugstr_w(pKeyName), debugstr_w(pValueName));
7198     return ERROR_INVALID_PARAMETER;
7199 }
7200
7201 /******************************************************************************
7202  *              DeletePrinterDataExA (WINSPOOL.@)
7203  */
7204 DWORD WINAPI DeletePrinterDataExA( HANDLE hPrinter, LPCSTR pKeyName,
7205                                   LPCSTR pValueName)
7206 {
7207     FIXME("%p %s %s\n", hPrinter, 
7208           debugstr_a(pKeyName), debugstr_a(pValueName));
7209     return ERROR_INVALID_PARAMETER;
7210 }
7211
7212 /******************************************************************************
7213  *      DeletePrintProcessorA (WINSPOOL.@)
7214  */
7215 BOOL WINAPI DeletePrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProcessorName)
7216 {
7217     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
7218           debugstr_a(pPrintProcessorName));
7219     return TRUE;
7220 }
7221
7222 /******************************************************************************
7223  *      DeletePrintProcessorW (WINSPOOL.@)
7224  */
7225 BOOL WINAPI DeletePrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProcessorName)
7226 {
7227     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
7228           debugstr_w(pPrintProcessorName));
7229     return TRUE;
7230 }
7231
7232 /******************************************************************************
7233  *      DeletePrintProvidorA (WINSPOOL.@)
7234  */
7235 BOOL WINAPI DeletePrintProvidorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProviderName)
7236 {
7237     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
7238           debugstr_a(pPrintProviderName));
7239     return TRUE;
7240 }
7241
7242 /******************************************************************************
7243  *      DeletePrintProvidorW (WINSPOOL.@)
7244  */
7245 BOOL WINAPI DeletePrintProvidorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProviderName)
7246 {
7247     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
7248           debugstr_w(pPrintProviderName));
7249     return TRUE;
7250 }
7251
7252 /******************************************************************************
7253  *      EnumFormsA (WINSPOOL.@)
7254  */
7255 BOOL WINAPI EnumFormsA( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
7256     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
7257 {
7258     FIXME("%p %x %p %x %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
7259     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
7260     return FALSE;
7261 }
7262
7263 /******************************************************************************
7264  *      EnumFormsW (WINSPOOL.@)
7265  */
7266 BOOL WINAPI EnumFormsW( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
7267     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
7268 {
7269     FIXME("%p %x %p %x %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
7270     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
7271     return FALSE;
7272 }
7273
7274 /*****************************************************************************
7275  *          EnumMonitorsA [WINSPOOL.@]
7276  *
7277  * See EnumMonitorsW.
7278  *
7279  */
7280 BOOL WINAPI EnumMonitorsA(LPSTR pName, DWORD Level, LPBYTE pMonitors,
7281                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
7282 {
7283     BOOL    res;
7284     LPBYTE  bufferW = NULL;
7285     LPWSTR  nameW = NULL;
7286     DWORD   needed = 0;
7287     DWORD   numentries = 0;
7288     INT     len;
7289
7290     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), Level, pMonitors,
7291           cbBuf, pcbNeeded, pcReturned);
7292
7293     /* convert servername to unicode */
7294     if (pName) {
7295         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
7296         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7297         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
7298     }
7299     /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the monitors */
7300     needed = cbBuf * sizeof(WCHAR);    
7301     if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
7302     res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
7303
7304     if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
7305         if (pcbNeeded) needed = *pcbNeeded;
7306         /* HeapReAlloc return NULL, when bufferW was NULL */
7307         bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
7308                               HeapAlloc(GetProcessHeap(), 0, needed);
7309
7310         /* Try again with the large Buffer */
7311         res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
7312     }
7313     numentries = pcReturned ? *pcReturned : 0;
7314     needed = 0;
7315     /*
7316        W2k require the buffersize from EnumMonitorsW also for EnumMonitorsA.
7317        We use the smaller Ansi-Size to avoid conflicts with fixed Buffers of old Apps.
7318      */
7319     if (res) {
7320         /* EnumMonitorsW collected all Data. Parse them to calculate ANSI-Size */
7321         DWORD   entrysize = 0;
7322         DWORD   index;
7323         LPSTR   ptr;
7324         LPMONITOR_INFO_2W mi2w;
7325         LPMONITOR_INFO_2A mi2a;
7326
7327         /* MONITOR_INFO_*W and MONITOR_INFO_*A have the same size */
7328         entrysize = (Level == 1) ? sizeof(MONITOR_INFO_1A) : sizeof(MONITOR_INFO_2A);
7329
7330         /* First pass: calculate the size for all Entries */
7331         mi2w = (LPMONITOR_INFO_2W) bufferW;
7332         mi2a = (LPMONITOR_INFO_2A) pMonitors;
7333         index = 0;
7334         while (index < numentries) {
7335             index++;
7336             needed += entrysize;    /* MONITOR_INFO_?A */
7337             TRACE("%p: parsing #%d (%s)\n", mi2w, index, debugstr_w(mi2w->pName));
7338
7339             needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
7340                                             NULL, 0, NULL, NULL);
7341             if (Level > 1) {
7342                 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
7343                                                 NULL, 0, NULL, NULL);
7344                 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
7345                                                 NULL, 0, NULL, NULL);
7346             }
7347             /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
7348             mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
7349             mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
7350         }
7351
7352         /* check for errors and quit on failure */
7353         if (cbBuf < needed) {
7354             SetLastError(ERROR_INSUFFICIENT_BUFFER);
7355             res = FALSE;
7356             goto emA_cleanup;
7357         }
7358         len = entrysize * numentries;       /* room for all MONITOR_INFO_?A */
7359         ptr = (LPSTR) &pMonitors[len];      /* room for strings */
7360         cbBuf -= len ;                      /* free Bytes in the user-Buffer */
7361         mi2w = (LPMONITOR_INFO_2W) bufferW;
7362         mi2a = (LPMONITOR_INFO_2A) pMonitors;
7363         index = 0;
7364         /* Second Pass: Fill the User Buffer (if we have one) */
7365         while ((index < numentries) && pMonitors) {
7366             index++;
7367             TRACE("%p: writing MONITOR_INFO_%dA #%d\n", mi2a, Level, index);
7368             mi2a->pName = ptr;
7369             len = WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
7370                                             ptr, cbBuf , NULL, NULL);
7371             ptr += len;
7372             cbBuf -= len;
7373             if (Level > 1) {
7374                 mi2a->pEnvironment = ptr;
7375                 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
7376                                             ptr, cbBuf, NULL, NULL);
7377                 ptr += len;
7378                 cbBuf -= len;
7379
7380                 mi2a->pDLLName = ptr;
7381                 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
7382                                             ptr, cbBuf, NULL, NULL);
7383                 ptr += len;
7384                 cbBuf -= len;
7385             }
7386             /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
7387             mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
7388             mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
7389         }
7390     }
7391 emA_cleanup:
7392     if (pcbNeeded)  *pcbNeeded = needed;
7393     if (pcReturned) *pcReturned = (res) ? numentries : 0;
7394
7395     HeapFree(GetProcessHeap(), 0, nameW);
7396     HeapFree(GetProcessHeap(), 0, bufferW);
7397
7398     TRACE("returning %d with %d (%d byte for %d entries)\n", 
7399             (res), GetLastError(), needed, numentries);
7400
7401     return (res);
7402
7403 }
7404
7405 /*****************************************************************************
7406  *          EnumMonitorsW [WINSPOOL.@]
7407  *
7408  * Enumerate available Port-Monitors
7409  *
7410  * PARAMS
7411  *  pName       [I] Servername or NULL (local Computer)
7412  *  Level       [I] Structure-Level (1:Win9x+NT or 2:NT only)
7413  *  pMonitors   [O] PTR to Buffer that receives the Result
7414  *  cbBuf       [I] Size of Buffer at pMonitors
7415  *  pcbNeeded   [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
7416  *  pcReturned  [O] PTR to DWORD that receives the number of Monitors in pMonitors
7417  *
7418  * RETURNS
7419  *  Success: TRUE
7420  *  Failure: FALSE and in pcbNeeded the Bytes required for buffer, if cbBuf is too small
7421  *
7422  */
7423 BOOL WINAPI EnumMonitorsW(LPWSTR pName, DWORD Level, LPBYTE pMonitors,
7424                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
7425 {
7426
7427     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pMonitors,
7428           cbBuf, pcbNeeded, pcReturned);
7429
7430     if ((backend == NULL)  && !load_backend()) return FALSE;
7431
7432     if (!pcbNeeded || !pcReturned || (!pMonitors && (cbBuf > 0))) {
7433         SetLastError(RPC_X_NULL_REF_POINTER);
7434         return FALSE;
7435     }
7436
7437     return backend->fpEnumMonitors(pName, Level, pMonitors, cbBuf, pcbNeeded, pcReturned);
7438 }
7439
7440 /******************************************************************************
7441  * SpoolerInit (WINSPOOL.@)
7442  *
7443  * Initialize the Spooler
7444  *
7445  * RETURNS
7446  *  Success: TRUE
7447  *  Failure: FALSE
7448  *
7449  * NOTES
7450  *  The function fails on windows, when the spooler service is not running
7451  *
7452  */
7453 BOOL WINAPI SpoolerInit(void)
7454 {
7455
7456     if ((backend == NULL)  && !load_backend()) return FALSE;
7457     return TRUE;
7458 }
7459
7460 /******************************************************************************
7461  *              XcvDataW (WINSPOOL.@)
7462  *
7463  * Execute commands in the Printmonitor DLL
7464  *
7465  * PARAMS
7466  *  hXcv            [i] Handle from OpenPrinter (with XcvMonitor or XcvPort)
7467  *  pszDataName     [i] Name of the command to execute
7468  *  pInputData      [i] Buffer for extra Input Data (needed only for some commands)
7469  *  cbInputData     [i] Size in Bytes of Buffer at pInputData
7470  *  pOutputData     [o] Buffer to receive additional Data (needed only for some commands)
7471  *  cbOutputData    [i] Size in Bytes of Buffer at pOutputData
7472  *  pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
7473  *  pdwStatus       [o] PTR to receive the win32 error code from the Printmonitor DLL
7474  *
7475  * RETURNS
7476  *  Success: TRUE
7477  *  Failure: FALSE
7478  *
7479  * NOTES
7480  *  Returning "TRUE" does mean, that the Printmonitor DLL was called successful.
7481  *  The execution of the command can still fail (check pdwStatus for ERROR_SUCCESS).
7482  *
7483  *  Minimal List of commands, that a Printmonitor DLL should support:
7484  *
7485  *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
7486  *| "AddPort"   : Add a Port
7487  *| "DeletePort": Delete a Port
7488  *
7489  *  Many Printmonitors support additional commands. Examples for localspl.dll:
7490  *  "GetDefaultCommConfig", "SetDefaultCommConfig",
7491  *  "GetTransmissionRetryTimeout", "ConfigureLPTPortCommandOK"
7492  *
7493  */
7494 BOOL WINAPI XcvDataW( HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData,
7495     DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData,
7496     PDWORD pcbOutputNeeded, PDWORD pdwStatus)
7497 {
7498     opened_printer_t *printer;
7499
7500     TRACE("(%p, %s, %p, %d, %p, %d, %p, %p)\n", hXcv, debugstr_w(pszDataName),
7501           pInputData, cbInputData, pOutputData,
7502           cbOutputData, pcbOutputNeeded, pdwStatus);
7503
7504     if ((backend == NULL)  && !load_backend()) return FALSE;
7505
7506     printer = get_opened_printer(hXcv);
7507     if (!printer || (!printer->backend_printer)) {
7508         SetLastError(ERROR_INVALID_HANDLE);
7509         return FALSE;
7510     }
7511
7512     if (!pcbOutputNeeded) {
7513         SetLastError(ERROR_INVALID_PARAMETER);
7514         return FALSE;
7515     }
7516
7517     if (!pszDataName || !pdwStatus || (!pOutputData && (cbOutputData > 0))) {
7518         SetLastError(RPC_X_NULL_REF_POINTER);
7519         return FALSE;
7520     }
7521
7522     *pcbOutputNeeded = 0;
7523
7524     return backend->fpXcvData(printer->backend_printer, pszDataName, pInputData,
7525                     cbInputData, pOutputData, cbOutputData, pcbOutputNeeded, pdwStatus);
7526
7527 }
7528
7529 /*****************************************************************************
7530  *          EnumPrinterDataA [WINSPOOL.@]
7531  *
7532  */
7533 DWORD WINAPI EnumPrinterDataA( HANDLE hPrinter, DWORD dwIndex, LPSTR pValueName,
7534     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
7535     DWORD cbData, LPDWORD pcbData )
7536 {
7537     FIXME("%p %x %p %x %p %p %p %x %p\n", hPrinter, dwIndex, pValueName,
7538           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
7539     return ERROR_NO_MORE_ITEMS;
7540 }
7541
7542 /*****************************************************************************
7543  *          EnumPrinterDataW [WINSPOOL.@]
7544  *
7545  */
7546 DWORD WINAPI EnumPrinterDataW( HANDLE hPrinter, DWORD dwIndex, LPWSTR pValueName,
7547     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
7548     DWORD cbData, LPDWORD pcbData )
7549 {
7550     FIXME("%p %x %p %x %p %p %p %x %p\n", hPrinter, dwIndex, pValueName,
7551           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
7552     return ERROR_NO_MORE_ITEMS;
7553 }
7554
7555 /*****************************************************************************
7556  *          EnumPrintProcessorDatatypesA [WINSPOOL.@]
7557  *
7558  */
7559 BOOL WINAPI EnumPrintProcessorDatatypesA(LPSTR pName, LPSTR pPrintProcessorName,
7560                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
7561                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
7562 {
7563     FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_a(pName),
7564           debugstr_a(pPrintProcessorName), Level, pDatatypes, cbBuf,
7565           pcbNeeded, pcReturned);
7566     return FALSE;
7567 }
7568
7569 /*****************************************************************************
7570  *          EnumPrintProcessorDatatypesW [WINSPOOL.@]
7571  *
7572  */
7573 BOOL WINAPI EnumPrintProcessorDatatypesW(LPWSTR pName, LPWSTR pPrintProcessorName,
7574                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
7575                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
7576 {
7577     FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_w(pName),
7578           debugstr_w(pPrintProcessorName), Level, pDatatypes, cbBuf,
7579           pcbNeeded, pcReturned);
7580     return FALSE;
7581 }
7582
7583 /*****************************************************************************
7584  *          EnumPrintProcessorsA [WINSPOOL.@]
7585  *
7586  * See EnumPrintProcessorsW.
7587  *
7588  */
7589 BOOL WINAPI EnumPrintProcessorsA(LPSTR pName, LPSTR pEnvironment, DWORD Level, 
7590                             LPBYTE pPPInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
7591 {
7592     BOOL    res;
7593     LPBYTE  bufferW = NULL;
7594     LPWSTR  nameW = NULL;
7595     LPWSTR  envW = NULL;
7596     DWORD   needed = 0;
7597     DWORD   numentries = 0;
7598     INT     len;
7599
7600     TRACE("(%s, %s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), debugstr_a(pEnvironment),
7601                 Level, pPPInfo, cbBuf, pcbNeeded, pcReturned);
7602
7603     /* convert names to unicode */
7604     if (pName) {
7605         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
7606         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7607         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
7608     }
7609     if (pEnvironment) {
7610         len = MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, NULL, 0);
7611         envW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7612         MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, envW, len);
7613     }
7614
7615     /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the monitors */
7616     needed = cbBuf * sizeof(WCHAR);
7617     if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
7618     res = EnumPrintProcessorsW(nameW, envW, Level, bufferW, needed, pcbNeeded, pcReturned);
7619
7620     if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
7621         if (pcbNeeded) needed = *pcbNeeded;
7622         /* HeapReAlloc return NULL, when bufferW was NULL */
7623         bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
7624                               HeapAlloc(GetProcessHeap(), 0, needed);
7625
7626         /* Try again with the large Buffer */
7627         res = EnumPrintProcessorsW(nameW, envW, Level, bufferW, needed, pcbNeeded, pcReturned);
7628     }
7629     numentries = pcReturned ? *pcReturned : 0;
7630     needed = 0;
7631
7632     if (res) {
7633         /* EnumPrintProcessorsW collected all Data. Parse them to calculate ANSI-Size */
7634         DWORD   index;
7635         LPSTR   ptr;
7636         PPRINTPROCESSOR_INFO_1W ppiw;
7637         PPRINTPROCESSOR_INFO_1A ppia;
7638
7639         /* First pass: calculate the size for all Entries */
7640         ppiw = (PPRINTPROCESSOR_INFO_1W) bufferW;
7641         ppia = (PPRINTPROCESSOR_INFO_1A) pPPInfo;
7642         index = 0;
7643         while (index < numentries) {
7644             index++;
7645             needed += sizeof(PRINTPROCESSOR_INFO_1A);
7646             TRACE("%p: parsing #%d (%s)\n", ppiw, index, debugstr_w(ppiw->pName));
7647
7648             needed += WideCharToMultiByte(CP_ACP, 0, ppiw->pName, -1,
7649                                             NULL, 0, NULL, NULL);
7650
7651             ppiw = (PPRINTPROCESSOR_INFO_1W) (((LPBYTE)ppiw) + sizeof(PRINTPROCESSOR_INFO_1W));
7652             ppia = (PPRINTPROCESSOR_INFO_1A) (((LPBYTE)ppia) + sizeof(PRINTPROCESSOR_INFO_1A));
7653         }
7654
7655         /* check for errors and quit on failure */
7656         if (cbBuf < needed) {
7657             SetLastError(ERROR_INSUFFICIENT_BUFFER);
7658             res = FALSE;
7659             goto epp_cleanup;
7660         }
7661
7662         len = numentries * sizeof(PRINTPROCESSOR_INFO_1A); /* room for structs */
7663         ptr = (LPSTR) &pPPInfo[len];        /* start of strings */
7664         cbBuf -= len ;                      /* free Bytes in the user-Buffer */
7665         ppiw = (PPRINTPROCESSOR_INFO_1W) bufferW;
7666         ppia = (PPRINTPROCESSOR_INFO_1A) pPPInfo;
7667         index = 0;
7668         /* Second Pass: Fill the User Buffer (if we have one) */
7669         while ((index < numentries) && pPPInfo) {
7670             index++;
7671             TRACE("%p: writing PRINTPROCESSOR_INFO_1A #%d\n", ppia, index);
7672             ppia->pName = ptr;
7673             len = WideCharToMultiByte(CP_ACP, 0, ppiw->pName, -1,
7674                                             ptr, cbBuf , NULL, NULL);
7675             ptr += len;
7676             cbBuf -= len;
7677
7678             ppiw = (PPRINTPROCESSOR_INFO_1W) (((LPBYTE)ppiw) + sizeof(PRINTPROCESSOR_INFO_1W));
7679             ppia = (PPRINTPROCESSOR_INFO_1A) (((LPBYTE)ppia) + sizeof(PRINTPROCESSOR_INFO_1A));
7680
7681         }
7682     }
7683 epp_cleanup:
7684     if (pcbNeeded)  *pcbNeeded = needed;
7685     if (pcReturned) *pcReturned = (res) ? numentries : 0;
7686
7687     HeapFree(GetProcessHeap(), 0, nameW);
7688     HeapFree(GetProcessHeap(), 0, envW);
7689     HeapFree(GetProcessHeap(), 0, bufferW);
7690
7691     TRACE("returning %d with %d (%d byte for %d entries)\n",
7692             (res), GetLastError(), needed, numentries);
7693
7694     return (res);
7695 }
7696
7697 /*****************************************************************************
7698  *          EnumPrintProcessorsW [WINSPOOL.@]
7699  *
7700  * Enumerate available Print Processors
7701  *
7702  * PARAMS
7703  *  pName        [I] Servername or NULL (local Computer)
7704  *  pEnvironment [I] Printing-Environment or NULL (Default)
7705  *  Level        [I] Structure-Level (Only 1 is allowed)
7706  *  pPPInfo      [O] PTR to Buffer that receives the Result
7707  *  cbBuf        [I] Size of Buffer at pPPInfo
7708  *  pcbNeeded    [O] PTR to DWORD that receives the size in Bytes used / required for pPPInfo
7709  *  pcReturned   [O] PTR to DWORD that receives the number of Print Processors in pPPInfo
7710  *
7711  * RETURNS
7712  *  Success: TRUE
7713  *  Failure: FALSE and in pcbNeeded the Bytes required for pPPInfo, if cbBuf is too small
7714  *
7715  */
7716 BOOL WINAPI EnumPrintProcessorsW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
7717                             LPBYTE pPPInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
7718 {
7719
7720     TRACE("(%s, %s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), debugstr_w(pEnvironment),
7721                                 Level, pPPInfo, cbBuf, pcbNeeded, pcReturned);
7722
7723     if ((backend == NULL)  && !load_backend()) return FALSE;
7724
7725     if (!pcbNeeded || !pcReturned) {
7726         SetLastError(RPC_X_NULL_REF_POINTER);
7727         return FALSE;
7728     }
7729
7730     if (!pPPInfo && (cbBuf > 0)) {
7731         SetLastError(ERROR_INVALID_USER_BUFFER);
7732         return FALSE;
7733     }
7734
7735     return backend->fpEnumPrintProcessors(pName, pEnvironment, Level, pPPInfo,
7736                                           cbBuf, pcbNeeded, pcReturned);
7737 }
7738
7739 /*****************************************************************************
7740  *          ExtDeviceMode [WINSPOOL.@]
7741  *
7742  */
7743 LONG WINAPI ExtDeviceMode( HWND hWnd, HANDLE hInst, LPDEVMODEA pDevModeOutput,
7744     LPSTR pDeviceName, LPSTR pPort, LPDEVMODEA pDevModeInput, LPSTR pProfile,
7745     DWORD fMode)
7746 {
7747     FIXME("Stub: %p %p %p %s %s %p %s %x\n", hWnd, hInst, pDevModeOutput,
7748           debugstr_a(pDeviceName), debugstr_a(pPort), pDevModeInput,
7749           debugstr_a(pProfile), fMode);
7750     return -1;
7751 }
7752
7753 /*****************************************************************************
7754  *          FindClosePrinterChangeNotification [WINSPOOL.@]
7755  *
7756  */
7757 BOOL WINAPI FindClosePrinterChangeNotification( HANDLE hChange )
7758 {
7759     FIXME("Stub: %p\n", hChange);
7760     return TRUE;
7761 }
7762
7763 /*****************************************************************************
7764  *          FindFirstPrinterChangeNotification [WINSPOOL.@]
7765  *
7766  */
7767 HANDLE WINAPI FindFirstPrinterChangeNotification( HANDLE hPrinter,
7768     DWORD fdwFlags, DWORD fdwOptions, LPVOID pPrinterNotifyOptions )
7769 {
7770     FIXME("Stub: %p %x %x %p\n",
7771           hPrinter, fdwFlags, fdwOptions, pPrinterNotifyOptions);
7772     return INVALID_HANDLE_VALUE;
7773 }
7774
7775 /*****************************************************************************
7776  *          FindNextPrinterChangeNotification [WINSPOOL.@]
7777  *
7778  */
7779 BOOL WINAPI FindNextPrinterChangeNotification( HANDLE hChange, PDWORD pdwChange,
7780     LPVOID pPrinterNotifyOptions, LPVOID *ppPrinterNotifyInfo )
7781 {
7782     FIXME("Stub: %p %p %p %p\n",
7783           hChange, pdwChange, pPrinterNotifyOptions, ppPrinterNotifyInfo);
7784     return FALSE;
7785 }
7786
7787 /*****************************************************************************
7788  *          FreePrinterNotifyInfo [WINSPOOL.@]
7789  *
7790  */
7791 BOOL WINAPI FreePrinterNotifyInfo( PPRINTER_NOTIFY_INFO pPrinterNotifyInfo )
7792 {
7793     FIXME("Stub: %p\n", pPrinterNotifyInfo);
7794     return TRUE;
7795 }
7796
7797 /*****************************************************************************
7798  *          string_to_buf
7799  *
7800  * Copies a unicode string into a buffer.  The buffer will either contain unicode or
7801  * ansi depending on the unicode parameter.
7802  */
7803 static BOOL string_to_buf(LPCWSTR str, LPBYTE ptr, DWORD cb, DWORD *size, BOOL unicode)
7804 {
7805     if(!str)
7806     {
7807         *size = 0;
7808         return TRUE;
7809     }
7810
7811     if(unicode)
7812     {
7813         *size = (strlenW(str) + 1) * sizeof(WCHAR);
7814         if(*size <= cb)
7815         {
7816             memcpy(ptr, str, *size);
7817             return TRUE;
7818         }
7819         return FALSE;
7820     }
7821     else
7822     {
7823         *size = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
7824         if(*size <= cb)
7825         {
7826             WideCharToMultiByte(CP_ACP, 0, str, -1, (LPSTR)ptr, *size, NULL, NULL);
7827             return TRUE;
7828         }
7829         return FALSE;
7830     }
7831 }
7832
7833 /*****************************************************************************
7834  *          get_job_info_1
7835  */
7836 static BOOL get_job_info_1(job_t *job, JOB_INFO_1W *ji1, LPBYTE buf, DWORD cbBuf,
7837                            LPDWORD pcbNeeded, BOOL unicode)
7838 {
7839     DWORD size, left = cbBuf;
7840     BOOL space = (cbBuf > 0);
7841     LPBYTE ptr = buf;
7842
7843     *pcbNeeded = 0;
7844
7845     if(space)
7846     {
7847         ji1->JobId = job->job_id;
7848     }
7849
7850     string_to_buf(job->document_title, ptr, left, &size, unicode);
7851     if(space && size <= left)
7852     {
7853         ji1->pDocument = (LPWSTR)ptr;
7854         ptr += size;
7855         left -= size;
7856     }
7857     else
7858         space = FALSE;
7859     *pcbNeeded += size;
7860
7861     if (job->printer_name)
7862     {
7863         string_to_buf(job->printer_name, ptr, left, &size, unicode);
7864         if(space && size <= left)
7865         {
7866             ji1->pPrinterName = (LPWSTR)ptr;
7867             ptr += size;
7868             left -= size;
7869         }
7870         else
7871             space = FALSE;
7872         *pcbNeeded += size;
7873     }
7874
7875     return space;
7876 }
7877
7878 /*****************************************************************************
7879  *          get_job_info_2
7880  */
7881 static BOOL get_job_info_2(job_t *job, JOB_INFO_2W *ji2, LPBYTE buf, DWORD cbBuf,
7882                            LPDWORD pcbNeeded, BOOL unicode)
7883 {
7884     DWORD size, left = cbBuf;
7885     DWORD shift;
7886     BOOL space = (cbBuf > 0);
7887     LPBYTE ptr = buf;
7888     LPDEVMODEA  dmA = NULL;
7889     LPDEVMODEW  devmode;
7890
7891     *pcbNeeded = 0;
7892
7893     if(space)
7894     {
7895         ji2->JobId = job->job_id;
7896     }
7897
7898     string_to_buf(job->document_title, ptr, left, &size, unicode);
7899     if(space && size <= left)
7900     {
7901         ji2->pDocument = (LPWSTR)ptr;
7902         ptr += size;
7903         left -= size;
7904     }
7905     else
7906         space = FALSE;
7907     *pcbNeeded += size;
7908
7909     if (job->printer_name)
7910     {
7911         string_to_buf(job->printer_name, ptr, left, &size, unicode);
7912         if(space && size <= left)
7913         {
7914             ji2->pPrinterName = (LPWSTR)ptr;
7915             ptr += size;
7916             left -= size;
7917         }
7918         else
7919             space = FALSE;
7920         *pcbNeeded += size;
7921     }
7922
7923     if (job->devmode)
7924     {
7925         if (!unicode)
7926         {
7927             dmA = DEVMODEdupWtoA(job->devmode);
7928             devmode = (LPDEVMODEW) dmA;
7929             if (dmA) size = dmA->dmSize + dmA->dmDriverExtra;
7930         }
7931         else
7932         {
7933             devmode = job->devmode;
7934             size = devmode->dmSize + devmode->dmDriverExtra;
7935         }
7936
7937         if (!devmode)
7938              FIXME("Can't convert DEVMODE W to A\n");
7939         else
7940         {
7941             /* align DEVMODE to a DWORD boundary */
7942             shift = (4 - (*pcbNeeded & 3)) & 3;
7943             size += shift;
7944
7945             if (size <= left)
7946             {
7947                 ptr += shift;
7948                 memcpy(ptr, devmode, size-shift);
7949                 ji2->pDevMode = (LPDEVMODEW)ptr;
7950                 if (!unicode) HeapFree(GetProcessHeap(), 0, dmA);
7951                 ptr += size-shift;
7952                 left -= size;
7953             }
7954             else
7955                 space = FALSE;
7956             *pcbNeeded +=size;
7957         }
7958     }
7959
7960     return space;
7961 }
7962
7963 /*****************************************************************************
7964  *          get_job_info
7965  */
7966 static BOOL get_job_info(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7967                          DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
7968 {
7969     BOOL ret = FALSE;
7970     DWORD needed = 0, size;
7971     job_t *job;
7972     LPBYTE ptr = pJob;
7973
7974     TRACE("%p %d %d %p %d %p\n", hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
7975
7976     EnterCriticalSection(&printer_handles_cs);
7977     job = get_job(hPrinter, JobId);
7978     if(!job)
7979         goto end;
7980
7981     switch(Level)
7982     {
7983     case 1:
7984         size = sizeof(JOB_INFO_1W);
7985         if(cbBuf >= size)
7986         {
7987             cbBuf -= size;
7988             ptr += size;
7989             memset(pJob, 0, size);
7990         }
7991         else
7992             cbBuf = 0;
7993         ret = get_job_info_1(job, (JOB_INFO_1W *)pJob, ptr, cbBuf, &needed, unicode);
7994         needed += size;
7995         break;
7996
7997     case 2:
7998         size = sizeof(JOB_INFO_2W);
7999         if(cbBuf >= size)
8000         {
8001             cbBuf -= size;
8002             ptr += size;
8003             memset(pJob, 0, size);
8004         }
8005         else
8006             cbBuf = 0;
8007         ret = get_job_info_2(job, (JOB_INFO_2W *)pJob, ptr, cbBuf, &needed, unicode);
8008         needed += size;
8009         break;
8010
8011     case 3:
8012         size = sizeof(JOB_INFO_3);
8013         if(cbBuf >= size)
8014         {
8015             cbBuf -= size;
8016             memset(pJob, 0, size);
8017             ret = TRUE;
8018         }
8019         else
8020             cbBuf = 0;
8021         needed = size;
8022         break;
8023
8024     default:
8025         SetLastError(ERROR_INVALID_LEVEL);
8026         goto end;
8027     }
8028     if(pcbNeeded)
8029         *pcbNeeded = needed;
8030 end:
8031     LeaveCriticalSection(&printer_handles_cs);
8032     return ret;
8033 }
8034
8035 /*****************************************************************************
8036  *          GetJobA [WINSPOOL.@]
8037  *
8038  */
8039 BOOL WINAPI GetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
8040                     DWORD cbBuf, LPDWORD pcbNeeded)
8041 {
8042     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, FALSE);
8043 }
8044
8045 /*****************************************************************************
8046  *          GetJobW [WINSPOOL.@]
8047  *
8048  */
8049 BOOL WINAPI GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
8050                     DWORD cbBuf, LPDWORD pcbNeeded)
8051 {
8052     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, TRUE);
8053 }
8054
8055 /*****************************************************************************
8056  *          schedule_pipe
8057  */
8058 static BOOL schedule_pipe(LPCWSTR cmd, LPCWSTR filename)
8059 {
8060 #ifdef HAVE_FORK
8061     char *unixname, *cmdA;
8062     DWORD len;
8063     int fds[2] = {-1, -1}, file_fd = -1, no_read;
8064     BOOL ret = FALSE;
8065     char buf[1024];
8066     pid_t pid, wret;
8067     int status;
8068
8069     if(!(unixname = wine_get_unix_file_name(filename)))
8070         return FALSE;
8071
8072     len = WideCharToMultiByte(CP_UNIXCP, 0, cmd, -1, NULL, 0, NULL, NULL);
8073     cmdA = HeapAlloc(GetProcessHeap(), 0, len);
8074     WideCharToMultiByte(CP_UNIXCP, 0, cmd, -1, cmdA, len, NULL, NULL);
8075
8076     TRACE("printing with: %s\n", cmdA);
8077
8078     if((file_fd = open(unixname, O_RDONLY)) == -1)
8079         goto end;
8080
8081     if (pipe(fds))
8082     {
8083         ERR("pipe() failed!\n");
8084         goto end;
8085     }
8086
8087     if ((pid = fork()) == 0)
8088     {
8089         close(0);
8090         dup2(fds[0], 0);
8091         close(fds[1]);
8092
8093         /* reset signals that we previously set to SIG_IGN */
8094         signal(SIGPIPE, SIG_DFL);
8095
8096         execl("/bin/sh", "/bin/sh", "-c", cmdA, NULL);
8097         _exit(1);
8098     }
8099     else if (pid == -1)
8100     {
8101         ERR("fork() failed!\n");
8102         goto end;
8103     }
8104
8105     close(fds[0]);
8106     fds[0] = -1;
8107     while((no_read = read(file_fd, buf, sizeof(buf))) > 0)
8108         write(fds[1], buf, no_read);
8109
8110     close(fds[1]);
8111     fds[1] = -1;
8112
8113     /* reap child */
8114     do {
8115         wret = waitpid(pid, &status, 0);
8116     } while (wret < 0 && errno == EINTR);
8117     if (wret < 0)
8118     {
8119         ERR("waitpid() failed!\n");
8120         goto end;
8121     }
8122     if (!WIFEXITED(status) || WEXITSTATUS(status))
8123     {
8124         ERR("child process failed! %d\n", status);
8125         goto end;
8126     }
8127
8128     ret = TRUE;
8129
8130 end:
8131     if(file_fd != -1) close(file_fd);
8132     if(fds[0] != -1) close(fds[0]);
8133     if(fds[1] != -1) close(fds[1]);
8134
8135     HeapFree(GetProcessHeap(), 0, cmdA);
8136     HeapFree(GetProcessHeap(), 0, unixname);
8137     return ret;
8138 #else
8139     return FALSE;
8140 #endif
8141 }
8142
8143 /*****************************************************************************
8144  *          schedule_lpr
8145  */
8146 static BOOL schedule_lpr(LPCWSTR printer_name, LPCWSTR filename)
8147 {
8148     WCHAR *cmd;
8149     const WCHAR fmtW[] = {'l','p','r',' ','-','P','\'','%','s','\'',0};
8150     BOOL r;
8151
8152     cmd = HeapAlloc(GetProcessHeap(), 0, strlenW(printer_name) * sizeof(WCHAR) + sizeof(fmtW));
8153     sprintfW(cmd, fmtW, printer_name);
8154
8155     r = schedule_pipe(cmd, filename);
8156
8157     HeapFree(GetProcessHeap(), 0, cmd);
8158     return r;
8159 }
8160
8161 #ifdef SONAME_LIBCUPS
8162 /*****************************************************************************
8163  *          get_cups_jobs_ticket_options
8164  *
8165  * Explicitly set CUPS options based on any %cupsJobTicket lines.
8166  * The CUPS scheduler only looks for these in Print-File requests, and since
8167  * cupsPrintFile uses Create-Job / Send-Document, the ticket lines don't get
8168  * parsed.
8169  */
8170 static int get_cups_job_ticket_options( const char *file, int num_options, cups_option_t **options )
8171 {
8172     FILE *fp = fopen( file, "r" );
8173     char buf[257]; /* DSC max of 256 + '\0' */
8174     const char *ps_adobe = "%!PS-Adobe-";
8175     const char *cups_job = "%cupsJobTicket:";
8176
8177     if (!fp) return num_options;
8178     if (!fgets( buf, sizeof(buf), fp )) goto end;
8179     if (strncmp( buf, ps_adobe, strlen( ps_adobe ) )) goto end;
8180     while (fgets( buf, sizeof(buf), fp ))
8181     {
8182         if (strncmp( buf, cups_job, strlen( cups_job ) )) break;
8183         num_options = pcupsParseOptions( buf + strlen( cups_job ), num_options, options );
8184     }
8185
8186 end:
8187     fclose( fp );
8188     return num_options;
8189 }
8190 #endif
8191
8192 /*****************************************************************************
8193  *          schedule_cups
8194  */
8195 static BOOL schedule_cups(LPCWSTR printer_name, LPCWSTR filename, LPCWSTR document_title)
8196 {
8197 #ifdef SONAME_LIBCUPS
8198     if(pcupsPrintFile)
8199     {
8200         char *unixname, *queue, *unix_doc_title;
8201         DWORD len;
8202         BOOL ret;
8203         int num_options = 0, i;
8204         cups_option_t *options = NULL;
8205
8206         if(!(unixname = wine_get_unix_file_name(filename)))
8207             return FALSE;
8208
8209         len = WideCharToMultiByte(CP_UNIXCP, 0, printer_name, -1, NULL, 0, NULL, NULL);
8210         queue = HeapAlloc(GetProcessHeap(), 0, len);
8211         WideCharToMultiByte(CP_UNIXCP, 0, printer_name, -1, queue, len, NULL, NULL);
8212
8213         len = WideCharToMultiByte(CP_UNIXCP, 0, document_title, -1, NULL, 0, NULL, NULL);
8214         unix_doc_title = HeapAlloc(GetProcessHeap(), 0, len);
8215         WideCharToMultiByte(CP_UNIXCP, 0, document_title, -1, unix_doc_title, len, NULL, NULL);
8216
8217         num_options = get_cups_job_ticket_options( unixname, num_options, &options );
8218
8219         TRACE( "printing via cups with options:\n" );
8220         for (i = 0; i < num_options; i++)
8221             TRACE( "\t%d: %s = %s\n", i, options[i].name, options[i].value );
8222
8223         ret = pcupsPrintFile( queue, unixname, unix_doc_title, num_options, options );
8224
8225         pcupsFreeOptions( num_options, options );
8226
8227         HeapFree(GetProcessHeap(), 0, unix_doc_title);
8228         HeapFree(GetProcessHeap(), 0, queue);
8229         HeapFree(GetProcessHeap(), 0, unixname);
8230         return ret;
8231     }
8232     else
8233 #endif
8234     {
8235         return schedule_lpr(printer_name, filename);
8236     }
8237 }
8238
8239 static INT_PTR CALLBACK file_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
8240 {
8241     LPWSTR filename;
8242
8243     switch(msg)
8244     {
8245     case WM_INITDIALOG:
8246         SetWindowLongPtrW(hwnd, DWLP_USER, lparam);
8247         return TRUE;
8248
8249     case WM_COMMAND:
8250         if(HIWORD(wparam) == BN_CLICKED)
8251         {
8252             if(LOWORD(wparam) == IDOK)
8253             {
8254                 HANDLE hf;
8255                 DWORD len = SendDlgItemMessageW(hwnd, EDITBOX, WM_GETTEXTLENGTH, 0, 0);
8256                 LPWSTR *output;
8257
8258                 filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
8259                 GetDlgItemTextW(hwnd, EDITBOX, filename, len + 1);
8260
8261                 if(GetFileAttributesW(filename) != INVALID_FILE_ATTRIBUTES)
8262                 {
8263                     WCHAR caption[200], message[200];
8264                     int mb_ret;
8265
8266                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
8267                     LoadStringW(WINSPOOL_hInstance, IDS_FILE_EXISTS, message, sizeof(message) / sizeof(WCHAR));
8268                     mb_ret = MessageBoxW(hwnd, message, caption, MB_OKCANCEL | MB_ICONEXCLAMATION);
8269                     if(mb_ret == IDCANCEL)
8270                     {
8271                         HeapFree(GetProcessHeap(), 0, filename);
8272                         return TRUE;
8273                     }
8274                 }
8275                 hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
8276                 if(hf == INVALID_HANDLE_VALUE)
8277                 {
8278                     WCHAR caption[200], message[200];
8279
8280                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
8281                     LoadStringW(WINSPOOL_hInstance, IDS_CANNOT_OPEN, message, sizeof(message) / sizeof(WCHAR));
8282                     MessageBoxW(hwnd, message, caption, MB_OK | MB_ICONEXCLAMATION);
8283                     HeapFree(GetProcessHeap(), 0, filename);
8284                     return TRUE;
8285                 }
8286                 CloseHandle(hf);
8287                 DeleteFileW(filename);
8288                 output = (LPWSTR *)GetWindowLongPtrW(hwnd, DWLP_USER);
8289                 *output = filename;
8290                 EndDialog(hwnd, IDOK);
8291                 return TRUE;
8292             }
8293             if(LOWORD(wparam) == IDCANCEL)
8294             {
8295                 EndDialog(hwnd, IDCANCEL);
8296                 return TRUE;
8297             }
8298         }
8299         return FALSE;
8300     }
8301     return FALSE;
8302 }
8303
8304 /*****************************************************************************
8305  *          get_filename
8306  */
8307 static BOOL get_filename(LPWSTR *filename)
8308 {
8309     return DialogBoxParamW(WINSPOOL_hInstance, MAKEINTRESOURCEW(FILENAME_DIALOG), GetForegroundWindow(),
8310                            file_dlg_proc, (LPARAM)filename) == IDOK;
8311 }
8312
8313 /*****************************************************************************
8314  *          schedule_file
8315  */
8316 static BOOL schedule_file(LPCWSTR filename)
8317 {
8318     LPWSTR output = NULL;
8319
8320     if(get_filename(&output))
8321     {
8322         BOOL r;
8323         TRACE("copy to %s\n", debugstr_w(output));
8324         r = CopyFileW(filename, output, FALSE);
8325         HeapFree(GetProcessHeap(), 0, output);
8326         return r;
8327     }
8328     return FALSE;
8329 }
8330
8331 /*****************************************************************************
8332  *          schedule_unixfile
8333  */
8334 static BOOL schedule_unixfile(LPCWSTR output, LPCWSTR filename)
8335 {
8336     int in_fd, out_fd, no_read;
8337     char buf[1024];
8338     BOOL ret = FALSE;
8339     char *unixname, *outputA;
8340     DWORD len;
8341
8342     if(!(unixname = wine_get_unix_file_name(filename)))
8343         return FALSE;
8344
8345     len = WideCharToMultiByte(CP_UNIXCP, 0, output, -1, NULL, 0, NULL, NULL);
8346     outputA = HeapAlloc(GetProcessHeap(), 0, len);
8347     WideCharToMultiByte(CP_UNIXCP, 0, output, -1, outputA, len, NULL, NULL);
8348     
8349     out_fd = open(outputA, O_CREAT | O_TRUNC | O_WRONLY, 0666);
8350     in_fd = open(unixname, O_RDONLY);
8351     if(out_fd == -1 || in_fd == -1)
8352         goto end;
8353
8354     while((no_read = read(in_fd, buf, sizeof(buf))) > 0)
8355         write(out_fd, buf, no_read);
8356
8357     ret = TRUE;
8358 end:
8359     if(in_fd != -1) close(in_fd);
8360     if(out_fd != -1) close(out_fd);
8361     HeapFree(GetProcessHeap(), 0, outputA);
8362     HeapFree(GetProcessHeap(), 0, unixname);
8363     return ret;
8364 }
8365
8366 /*****************************************************************************
8367  *          ScheduleJob [WINSPOOL.@]
8368  *
8369  */
8370 BOOL WINAPI ScheduleJob( HANDLE hPrinter, DWORD dwJobID )
8371 {
8372     opened_printer_t *printer;
8373     BOOL ret = FALSE;
8374     struct list *cursor, *cursor2;
8375
8376     TRACE("(%p, %x)\n", hPrinter, dwJobID);
8377     EnterCriticalSection(&printer_handles_cs);
8378     printer = get_opened_printer(hPrinter);
8379     if(!printer)
8380         goto end;
8381
8382     LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
8383     {
8384         job_t *job = LIST_ENTRY(cursor, job_t, entry);
8385         HANDLE hf;
8386
8387         if(job->job_id != dwJobID) continue;
8388
8389         hf = CreateFileW(job->filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
8390         if(hf != INVALID_HANDLE_VALUE)
8391         {
8392             PRINTER_INFO_5W *pi5 = NULL;
8393             LPWSTR portname = job->portname;
8394             DWORD needed;
8395             HKEY hkey;
8396             WCHAR output[1024];
8397             static const WCHAR spooler_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
8398                                                 'P','r','i','n','t','i','n','g','\\','S','p','o','o','l','e','r',0};
8399
8400             if (!portname)
8401             {
8402                 GetPrinterW(hPrinter, 5, NULL, 0, &needed);
8403                 pi5 = HeapAlloc(GetProcessHeap(), 0, needed);
8404                 GetPrinterW(hPrinter, 5, (LPBYTE)pi5, needed, &needed);
8405                 portname = pi5->pPortName;
8406             }
8407             TRACE("need to schedule job %d filename %s to port %s\n", job->job_id, debugstr_w(job->filename),
8408                   debugstr_w(portname));
8409             
8410             output[0] = 0;
8411
8412             /* @@ Wine registry key: HKCU\Software\Wine\Printing\Spooler */
8413             if(RegOpenKeyW(HKEY_CURRENT_USER, spooler_key, &hkey) == ERROR_SUCCESS)
8414             {
8415                 DWORD type, count = sizeof(output);
8416                 RegQueryValueExW(hkey, portname, NULL, &type, (LPBYTE)output, &count);
8417                 RegCloseKey(hkey);
8418             }
8419             if(output[0] == '|')
8420             {
8421                 ret = schedule_pipe(output + 1, job->filename);
8422             }
8423             else if(output[0])
8424             {
8425                 ret = schedule_unixfile(output, job->filename);
8426             }
8427             else if(!strncmpW(portname, LPR_Port, strlenW(LPR_Port)))
8428             {
8429                 ret = schedule_lpr(portname + strlenW(LPR_Port), job->filename);
8430             }
8431             else if(!strncmpW(portname, CUPS_Port, strlenW(CUPS_Port)))
8432             {
8433                 ret = schedule_cups(portname + strlenW(CUPS_Port), job->filename, job->document_title);
8434             }
8435             else if(!strncmpW(portname, FILE_Port, strlenW(FILE_Port)))
8436             {
8437                 ret = schedule_file(job->filename);
8438             }
8439             else
8440             {
8441                 FIXME("can't schedule to port %s\n", debugstr_w(portname));
8442             }
8443             HeapFree(GetProcessHeap(), 0, pi5);
8444             CloseHandle(hf);
8445             DeleteFileW(job->filename);
8446         }
8447         list_remove(cursor);
8448         HeapFree(GetProcessHeap(), 0, job->document_title);
8449         HeapFree(GetProcessHeap(), 0, job->printer_name);
8450         HeapFree(GetProcessHeap(), 0, job->portname);
8451         HeapFree(GetProcessHeap(), 0, job->filename);
8452         HeapFree(GetProcessHeap(), 0, job->devmode);
8453         HeapFree(GetProcessHeap(), 0, job);
8454         break;
8455     }
8456 end:
8457     LeaveCriticalSection(&printer_handles_cs);
8458     return ret;
8459 }
8460
8461 /*****************************************************************************
8462  *          StartDocDlgA [WINSPOOL.@]
8463  */
8464 LPSTR WINAPI StartDocDlgA( HANDLE hPrinter, DOCINFOA *doc )
8465 {
8466     UNICODE_STRING usBuffer;
8467     DOCINFOW docW;
8468     LPWSTR retW;
8469     LPWSTR docnameW = NULL, outputW = NULL, datatypeW = NULL;
8470     LPSTR ret = NULL;
8471
8472     docW.cbSize = sizeof(docW);
8473     if (doc->lpszDocName)
8474     {
8475         docnameW = asciitounicode(&usBuffer, doc->lpszDocName);
8476         if (!(docW.lpszDocName = docnameW)) return NULL;
8477     }
8478     if (doc->lpszOutput)
8479     {
8480         outputW = asciitounicode(&usBuffer, doc->lpszOutput);
8481         if (!(docW.lpszOutput = outputW)) return NULL;
8482     }
8483     if (doc->lpszDatatype)
8484     {
8485         datatypeW = asciitounicode(&usBuffer, doc->lpszDatatype);
8486         if (!(docW.lpszDatatype = datatypeW)) return NULL;
8487     }
8488     docW.fwType = doc->fwType;
8489
8490     retW = StartDocDlgW(hPrinter, &docW);
8491
8492     if(retW)
8493     {
8494         DWORD len = WideCharToMultiByte(CP_ACP, 0, retW, -1, NULL, 0, NULL, NULL);
8495         ret = HeapAlloc(GetProcessHeap(), 0, len);
8496         WideCharToMultiByte(CP_ACP, 0, retW, -1, ret, len, NULL, NULL);
8497         HeapFree(GetProcessHeap(), 0, retW);
8498     }
8499
8500     HeapFree(GetProcessHeap(), 0, datatypeW);
8501     HeapFree(GetProcessHeap(), 0, outputW);
8502     HeapFree(GetProcessHeap(), 0, docnameW);
8503
8504     return ret;
8505 }
8506
8507 /*****************************************************************************
8508  *          StartDocDlgW [WINSPOOL.@]
8509  *
8510  * Undocumented: Apparently used by gdi32:StartDocW() to popup the file dialog
8511  * when lpszOutput is "FILE:" or if lpszOutput is NULL and the default printer
8512  * port is "FILE:". Also returns the full path if passed a relative path.
8513  *
8514  * The caller should free the returned string from the process heap.
8515  */
8516 LPWSTR WINAPI StartDocDlgW( HANDLE hPrinter, DOCINFOW *doc )
8517 {
8518     LPWSTR ret = NULL;
8519     DWORD len, attr;
8520
8521     if(doc->lpszOutput == NULL) /* Check whether default port is FILE: */
8522     {
8523         PRINTER_INFO_5W *pi5;
8524         GetPrinterW(hPrinter, 5, NULL, 0, &len);
8525         if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
8526             return NULL;
8527         pi5 = HeapAlloc(GetProcessHeap(), 0, len);
8528         GetPrinterW(hPrinter, 5, (LPBYTE)pi5, len, &len);
8529         if(!pi5->pPortName || strcmpW(pi5->pPortName, FILE_Port))
8530         {
8531             HeapFree(GetProcessHeap(), 0, pi5);
8532             return NULL;
8533         }
8534         HeapFree(GetProcessHeap(), 0, pi5);
8535     }
8536
8537     if(doc->lpszOutput == NULL || !strcmpW(doc->lpszOutput, FILE_Port))
8538     {
8539         LPWSTR name;
8540
8541         if (get_filename(&name))
8542         {
8543             if(!(len = GetFullPathNameW(name, 0, NULL, NULL)))
8544             {
8545                 HeapFree(GetProcessHeap(), 0, name);
8546                 return NULL;
8547             }
8548             ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
8549             GetFullPathNameW(name, len, ret, NULL);
8550             HeapFree(GetProcessHeap(), 0, name);
8551         }
8552         return ret;
8553     }
8554
8555     if(!(len = GetFullPathNameW(doc->lpszOutput, 0, NULL, NULL)))
8556         return NULL;
8557
8558     ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
8559     GetFullPathNameW(doc->lpszOutput, len, ret, NULL);
8560         
8561     attr = GetFileAttributesW(ret);
8562     if(attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY))
8563     {
8564         HeapFree(GetProcessHeap(), 0, ret);
8565         ret = NULL;
8566     }
8567     return ret;
8568 }