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