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