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