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