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