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