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