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