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