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