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