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