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