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