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