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