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