wintrust/tests: Fix compilation on systems that don't support nameless unions.
[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     monitor_t * pm = NULL;
2275     LPMONITOR_INFO_2W mi2w;
2276     HKEY    hroot = NULL;
2277     HKEY    hentry = NULL;
2278     DWORD   disposition;
2279     BOOL    res = FALSE;
2280
2281     mi2w = (LPMONITOR_INFO_2W) pMonitors;
2282     TRACE("(%s, %d, %p) :  %s %s %s\n", debugstr_w(pName), Level, pMonitors,
2283           debugstr_w(mi2w ? mi2w->pName : NULL),
2284           debugstr_w(mi2w ? mi2w->pEnvironment : NULL),
2285           debugstr_w(mi2w ? mi2w->pDLLName : NULL));
2286
2287     if (Level != 2) {
2288         SetLastError(ERROR_INVALID_LEVEL);
2289         return FALSE;
2290     }
2291
2292     /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
2293     if (mi2w == NULL) {
2294         return FALSE;
2295     }
2296
2297     if (pName && (pName[0])) {
2298         FIXME("for server %s not implemented\n", debugstr_w(pName));
2299         SetLastError(ERROR_ACCESS_DENIED);
2300         return FALSE;
2301     }
2302
2303
2304     if (!mi2w->pName || (! mi2w->pName[0])) {
2305         WARN("pName not valid : %s\n", debugstr_w(mi2w->pName));
2306         SetLastError(ERROR_INVALID_PARAMETER);
2307         return FALSE;
2308     }
2309     if (!mi2w->pEnvironment || lstrcmpW(mi2w->pEnvironment, envname_x86W)) {
2310         WARN("Environment %s requested (we support only %s)\n", 
2311                 debugstr_w(mi2w->pEnvironment), debugstr_w(envname_x86W));
2312         SetLastError(ERROR_INVALID_ENVIRONMENT);
2313         return FALSE;
2314     }
2315
2316     if (!mi2w->pDLLName || (! mi2w->pDLLName[0])) {
2317         WARN("pDLLName not valid : %s\n", debugstr_w(mi2w->pDLLName));
2318         SetLastError(ERROR_INVALID_PARAMETER);
2319         return FALSE;
2320     }
2321
2322     /* Load and initialize the monitor. SetLastError() is called on failure */
2323     if ((pm = monitor_load(mi2w->pName, mi2w->pDLLName)) == NULL) {
2324         return FALSE;
2325     }
2326     monitor_unload(pm);
2327
2328     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) != ERROR_SUCCESS) {
2329         ERR("unable to create key %s\n", debugstr_w(MonitorsW));
2330         return FALSE;
2331     }
2332
2333     if(RegCreateKeyExW( hroot, mi2w->pName, 0, NULL, REG_OPTION_NON_VOLATILE,
2334                         KEY_WRITE | KEY_QUERY_VALUE, NULL, &hentry,
2335                         &disposition) == ERROR_SUCCESS) {
2336
2337         /* Some installers set options for the port before calling AddMonitor.
2338            We query the "Driver" entry to verify that the monitor is installed,
2339            before we return an error.
2340            When a user installs two print monitors at the same time with the
2341            same name but with a different driver DLL and a task switch comes
2342            between RegQueryValueExW and RegSetValueExW, a race condition
2343            is possible but silently ignored. */
2344
2345         DWORD   namesize = 0;
2346
2347         if ((disposition == REG_OPENED_EXISTING_KEY) &&
2348             (RegQueryValueExW(hentry, DriverW, NULL, NULL, NULL,
2349                               &namesize) == ERROR_SUCCESS)) {
2350             TRACE("monitor %s already exists\n", debugstr_w(mi2w->pName));
2351             /* NT: ERROR_PRINT_MONITOR_ALREADY_INSTALLED (3006)
2352                9x: ERROR_ALREADY_EXISTS (183) */
2353             SetLastError(ERROR_PRINT_MONITOR_ALREADY_INSTALLED);
2354         }
2355         else
2356         {
2357                INT len;
2358                len = (lstrlenW(mi2w->pDLLName) +1) * sizeof(WCHAR);
2359                res = (RegSetValueExW(hentry, DriverW, 0,
2360                       REG_SZ, (LPBYTE) mi2w->pDLLName, len) == ERROR_SUCCESS);
2361         }
2362         RegCloseKey(hentry);
2363     }
2364
2365     RegCloseKey(hroot);
2366     return (res);
2367 }
2368
2369 /******************************************************************
2370  *              DeletePrinterDriverA        [WINSPOOL.@]
2371  *
2372  */
2373 BOOL WINAPI DeletePrinterDriverA (LPSTR pName, LPSTR pEnvironment, LPSTR pDriverName)
2374 {
2375     return DeletePrinterDriverExA(pName, pEnvironment, pDriverName, 0, 0);
2376 }
2377
2378 /******************************************************************
2379  *              DeletePrinterDriverW        [WINSPOOL.@]
2380  *
2381  */
2382 BOOL WINAPI DeletePrinterDriverW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pDriverName)
2383 {
2384     return DeletePrinterDriverExW(pName, pEnvironment, pDriverName, 0, 0);
2385 }
2386
2387 /******************************************************************
2388  *              DeleteMonitorA        [WINSPOOL.@]
2389  *
2390  * See DeleteMonitorW.
2391  *
2392  */
2393 BOOL WINAPI DeleteMonitorA (LPSTR pName, LPSTR pEnvironment, LPSTR pMonitorName)
2394 {
2395     LPWSTR  nameW = NULL;
2396     LPWSTR  EnvironmentW = NULL;
2397     LPWSTR  MonitorNameW = NULL;
2398     BOOL    res;
2399     INT     len;
2400
2401     if (pName) {
2402         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2403         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2404         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2405     }
2406
2407     if (pEnvironment) {
2408         len = MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, NULL, 0);
2409         EnvironmentW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2410         MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, EnvironmentW, len);
2411     }
2412     if (pMonitorName) {
2413         len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
2414         MonitorNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2415         MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, MonitorNameW, len);
2416     }
2417
2418     res = DeleteMonitorW(nameW, EnvironmentW, MonitorNameW);
2419
2420     HeapFree(GetProcessHeap(), 0, MonitorNameW); 
2421     HeapFree(GetProcessHeap(), 0, EnvironmentW);
2422     HeapFree(GetProcessHeap(), 0, nameW); 
2423     return (res);
2424 }
2425
2426 /******************************************************************
2427  *              DeleteMonitorW        [WINSPOOL.@]
2428  *
2429  * Delete a specific Printmonitor from a Printing-Environment
2430  *
2431  * PARAMS
2432  *  pName        [I] Servername or NULL (local Computer)
2433  *  pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
2434  *  pMonitorName [I] Name of the Monitor, that should be deleted
2435  *
2436  * RETURNS
2437  *  Success: TRUE
2438  *  Failure: FALSE
2439  *
2440  * NOTES
2441  *  pEnvironment is ignored in Windows for the local Computer.
2442  *
2443  */
2444 BOOL WINAPI DeleteMonitorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
2445 {
2446
2447     TRACE("(%s, %s, %s)\n",debugstr_w(pName),debugstr_w(pEnvironment),
2448            debugstr_w(pMonitorName));
2449
2450     if ((backend == NULL)  && !load_backend()) return FALSE;
2451
2452     return backend->fpDeleteMonitor(pName, pEnvironment, pMonitorName);
2453 }
2454
2455
2456 /******************************************************************
2457  *              DeletePortA        [WINSPOOL.@]
2458  *
2459  * See DeletePortW.
2460  *
2461  */
2462 BOOL WINAPI DeletePortA (LPSTR pName, HWND hWnd, LPSTR pPortName)
2463 {
2464     LPWSTR  nameW = NULL;
2465     LPWSTR  portW = NULL;
2466     INT     len;
2467     DWORD   res;
2468
2469     TRACE("(%s, %p, %s)\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
2470
2471     /* convert servername to unicode */
2472     if (pName) {
2473         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2474         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2475         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2476     }
2477
2478     /* convert portname to unicode */
2479     if (pPortName) {
2480         len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
2481         portW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2482         MultiByteToWideChar(CP_ACP, 0, pPortName, -1, portW, len);
2483     }
2484
2485     res = DeletePortW(nameW, hWnd, portW);
2486     HeapFree(GetProcessHeap(), 0, nameW);
2487     HeapFree(GetProcessHeap(), 0, portW);
2488     return res;
2489 }
2490
2491 /******************************************************************
2492  *              DeletePortW        [WINSPOOL.@]
2493  *
2494  * Delete a specific Port
2495  *
2496  * PARAMS
2497  *  pName     [I] Servername or NULL (local Computer)
2498  *  hWnd      [I] Handle to parent Window for the Dialog-Box
2499  *  pPortName [I] Name of the Port, that should be deleted
2500  *
2501  * RETURNS
2502  *  Success: TRUE
2503  *  Failure: FALSE
2504  *
2505  */
2506 BOOL WINAPI DeletePortW (LPWSTR pName, HWND hWnd, LPWSTR pPortName)
2507 {
2508     monitor_t * pm;
2509     monitor_t * pui;
2510     DWORD       res;
2511
2512     TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
2513
2514     if (pName && pName[0]) {
2515         SetLastError(ERROR_INVALID_PARAMETER);
2516         return FALSE;
2517     }
2518
2519     if (!pPortName) {
2520         SetLastError(RPC_X_NULL_REF_POINTER);
2521         return FALSE;
2522     }
2523
2524     /* an empty Portname is Invalid */
2525     if (!pPortName[0]) {
2526         SetLastError(ERROR_NOT_SUPPORTED);
2527         return FALSE;
2528     }
2529
2530     pm = monitor_load_by_port(pPortName);
2531     if (pm && pm->monitor && pm->monitor->pfnDeletePort) {
2532         TRACE("Using %s for %s (%p: %s)\n", debugstr_w(pm->name), debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
2533         res = pm->monitor->pfnDeletePort(pName, hWnd, pPortName);
2534         TRACE("got %d with %u\n", res, GetLastError());
2535     }
2536     else
2537     {
2538         pui = monitor_loadui(pm);
2539         if (pui && pui->monitorUI && pui->monitorUI->pfnDeletePortUI) {
2540             TRACE("use %s for %s (%p: %s)\n", debugstr_w(pui->name), debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
2541             res = pui->monitorUI->pfnDeletePortUI(pName, hWnd, pPortName);
2542             TRACE("got %d with %u\n", res, GetLastError());
2543         }
2544         else
2545         {
2546             FIXME("not implemented for %s (%p: %s => %p: %s)\n", debugstr_w(pPortName),
2547                   pm, debugstr_w(pm ? pm->dllname : NULL), pui, debugstr_w(pui ? pui->dllname : NULL));
2548
2549             /* XP: ERROR_NOT_SUPPORTED, NT351,9x: ERROR_INVALID_PARAMETER */
2550             SetLastError(ERROR_NOT_SUPPORTED);
2551             res = FALSE;
2552         }
2553         monitor_unload(pui);
2554     }
2555     monitor_unload(pm);
2556
2557     TRACE("returning %d with %u\n", res, GetLastError());
2558     return res;
2559 }
2560
2561 /******************************************************************************
2562  *    SetPrinterW  [WINSPOOL.@]
2563  */
2564 BOOL WINAPI SetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD Command)
2565 {
2566     FIXME("(%p, %d, %p, %d): stub\n", hPrinter, Level, pPrinter, Command);
2567     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2568     return FALSE;
2569 }
2570
2571 /******************************************************************************
2572  *    WritePrinter  [WINSPOOL.@]
2573  */
2574 BOOL WINAPI WritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
2575 {
2576     opened_printer_t *printer;
2577     BOOL ret = FALSE;
2578
2579     TRACE("(%p, %p, %d, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
2580
2581     EnterCriticalSection(&printer_handles_cs);
2582     printer = get_opened_printer(hPrinter);
2583     if(!printer)
2584     {
2585         SetLastError(ERROR_INVALID_HANDLE);
2586         goto end;
2587     }
2588
2589     if(!printer->doc)
2590     {
2591         SetLastError(ERROR_SPL_NO_STARTDOC);
2592         goto end;
2593     }
2594
2595     ret = WriteFile(printer->doc->hf, pBuf, cbBuf, pcWritten, NULL);
2596 end:
2597     LeaveCriticalSection(&printer_handles_cs);
2598     return ret;
2599 }
2600
2601 /*****************************************************************************
2602  *          AddFormA  [WINSPOOL.@]
2603  */
2604 BOOL WINAPI AddFormA(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
2605 {
2606     FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
2607     return 1;
2608 }
2609
2610 /*****************************************************************************
2611  *          AddFormW  [WINSPOOL.@]
2612  */
2613 BOOL WINAPI AddFormW(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
2614 {
2615     FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
2616     return 1;
2617 }
2618
2619 /*****************************************************************************
2620  *          AddJobA  [WINSPOOL.@]
2621  */
2622 BOOL WINAPI AddJobA(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
2623 {
2624     BOOL ret;
2625     BYTE buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
2626     DWORD needed;
2627
2628     if(Level != 1) {
2629         SetLastError(ERROR_INVALID_LEVEL);
2630         return FALSE;
2631     }
2632
2633     ret = AddJobW(hPrinter, Level, buf, sizeof(buf), &needed);
2634
2635     if(ret) {
2636         ADDJOB_INFO_1W *addjobW = (ADDJOB_INFO_1W*)buf;
2637         DWORD len = WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, NULL, 0, NULL, NULL);
2638         *pcbNeeded = len + sizeof(ADDJOB_INFO_1A);
2639         if(*pcbNeeded > cbBuf) {
2640             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2641             ret = FALSE;
2642         } else {
2643             ADDJOB_INFO_1A *addjobA = (ADDJOB_INFO_1A*)pData;
2644             addjobA->JobId = addjobW->JobId;
2645             addjobA->Path = (char *)(addjobA + 1);
2646             WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, addjobA->Path, len, NULL, NULL);
2647         }
2648     }
2649     return ret;
2650 }
2651
2652 /*****************************************************************************
2653  *          AddJobW  [WINSPOOL.@]
2654  */
2655 BOOL WINAPI AddJobW(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
2656 {
2657     opened_printer_t *printer;
2658     job_t *job;
2659     BOOL ret = FALSE;
2660     static const WCHAR spool_path[] = {'s','p','o','o','l','\\','P','R','I','N','T','E','R','S','\\',0};
2661     static const WCHAR fmtW[] = {'%','s','%','0','5','d','.','S','P','L',0};
2662     WCHAR path[MAX_PATH], filename[MAX_PATH];
2663     DWORD len;
2664     ADDJOB_INFO_1W *addjob;
2665
2666     TRACE("(%p,%d,%p,%d,%p)\n", hPrinter, Level, pData, cbBuf, pcbNeeded);
2667     
2668     EnterCriticalSection(&printer_handles_cs);
2669
2670     printer = get_opened_printer(hPrinter);
2671
2672     if(!printer) {
2673         SetLastError(ERROR_INVALID_HANDLE);
2674         goto end;
2675     }
2676
2677     if(Level != 1) {
2678         SetLastError(ERROR_INVALID_LEVEL);
2679         goto end;
2680     }
2681
2682     job = HeapAlloc(GetProcessHeap(), 0, sizeof(*job));
2683     if(!job)
2684         goto end;
2685
2686     job->job_id = InterlockedIncrement(&next_job_id);
2687
2688     len = GetSystemDirectoryW(path, sizeof(path) / sizeof(WCHAR));
2689     if(path[len - 1] != '\\')
2690         path[len++] = '\\';
2691     memcpy(path + len, spool_path, sizeof(spool_path));    
2692     sprintfW(filename, fmtW, path, job->job_id);
2693
2694     len = strlenW(filename);
2695     job->filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
2696     memcpy(job->filename, filename, (len + 1) * sizeof(WCHAR));
2697     job->document_title = strdupW(default_doc_title);
2698     list_add_tail(&printer->queue->jobs, &job->entry);
2699
2700     *pcbNeeded = (len + 1) * sizeof(WCHAR) + sizeof(*addjob);
2701     if(*pcbNeeded <= cbBuf) {
2702         addjob = (ADDJOB_INFO_1W*)pData;
2703         addjob->JobId = job->job_id;
2704         addjob->Path = (WCHAR *)(addjob + 1);
2705         memcpy(addjob->Path, filename, (len + 1) * sizeof(WCHAR));
2706         ret = TRUE;
2707     } else 
2708         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2709
2710 end:
2711     LeaveCriticalSection(&printer_handles_cs);
2712     return ret;
2713 }
2714
2715 /*****************************************************************************
2716  *          GetPrintProcessorDirectoryA  [WINSPOOL.@]
2717  *
2718  * Return the PATH for the Print-Processors
2719  *
2720  * See GetPrintProcessorDirectoryW.
2721  *
2722  *
2723  */
2724 BOOL WINAPI GetPrintProcessorDirectoryA(LPSTR server, LPSTR env,
2725                                         DWORD level,  LPBYTE Info,
2726                                         DWORD cbBuf,  LPDWORD pcbNeeded)
2727 {
2728     LPWSTR  serverW = NULL;
2729     LPWSTR  envW = NULL;
2730     BOOL    ret;
2731     INT     len;
2732
2733     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_a(server), 
2734           debugstr_a(env), level, Info, cbBuf, pcbNeeded);
2735  
2736
2737     if (server) {
2738         len = MultiByteToWideChar(CP_ACP, 0, server, -1, NULL, 0);
2739         serverW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2740         MultiByteToWideChar(CP_ACP, 0, server, -1, serverW, len);
2741     }
2742
2743     if (env) {
2744         len = MultiByteToWideChar(CP_ACP, 0, env, -1, NULL, 0);
2745         envW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2746         MultiByteToWideChar(CP_ACP, 0, env, -1, envW, len);
2747     }
2748
2749     /* NT requires the buffersize from GetPrintProcessorDirectoryW also
2750        for GetPrintProcessorDirectoryA and WC2MB is done in-place.
2751      */
2752     ret = GetPrintProcessorDirectoryW(serverW, envW, level, Info, 
2753                                       cbBuf, pcbNeeded);
2754
2755     if (ret) ret = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)Info, -1, (LPSTR)Info,
2756                                        cbBuf, NULL, NULL) > 0;
2757
2758
2759     TRACE(" required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
2760     HeapFree(GetProcessHeap(), 0, envW);
2761     HeapFree(GetProcessHeap(), 0, serverW);
2762     return ret;
2763 }
2764
2765 /*****************************************************************************
2766  *          GetPrintProcessorDirectoryW  [WINSPOOL.@]
2767  *
2768  * Return the PATH for the Print-Processors
2769  *
2770  * PARAMS
2771  *   server     [I] Servername (NT only) or NULL (local Computer)
2772  *   env        [I] Printing-Environment (see below) or NULL (Default)
2773  *   level      [I] Structure-Level (must be 1)
2774  *   Info       [O] PTR to Buffer that receives the Result
2775  *   cbBuf      [I] Size of Buffer at "Info"
2776  *   pcbNeeded  [O] PTR to DWORD that receives the size in Bytes used / 
2777  *                  required for the Buffer at "Info"
2778  *
2779  * RETURNS
2780  *   Success: TRUE  and in pcbNeeded the Bytes used in Info
2781  *   Failure: FALSE and in pcbNeeded the Bytes required for Info,
2782  *   if cbBuf is too small
2783  * 
2784  *   Native Values returned in Info on Success:
2785  *|  NT(Windows NT x86):  "%winsysdir%\\spool\\PRTPROCS\\w32x86" 
2786  *|  NT(Windows 4.0):     "%winsysdir%\\spool\\PRTPROCS\\win40" 
2787  *|  win9x(Windows 4.0):  "%winsysdir%" 
2788  *
2789  *   "%winsysdir%" is the Value from GetSystemDirectoryW()
2790  *
2791  * BUGS
2792  *  Only NULL or "" is supported for server
2793  *
2794  */
2795 BOOL WINAPI GetPrintProcessorDirectoryW(LPWSTR server, LPWSTR env,
2796                                         DWORD level,  LPBYTE Info,
2797                                         DWORD cbBuf,  LPDWORD pcbNeeded)
2798 {
2799     DWORD needed;
2800     const printenv_t * env_t;
2801
2802     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(server),
2803             debugstr_w(env), level, Info, cbBuf, pcbNeeded);
2804
2805     if(server != NULL && server[0]) {
2806         FIXME("server not supported: %s\n", debugstr_w(server));
2807         SetLastError(ERROR_INVALID_PARAMETER);
2808         return FALSE;
2809     }
2810
2811     env_t = validate_envW(env);
2812     if(!env_t) return FALSE;  /* environment invalid or unsupported */
2813
2814     if(level != 1) {
2815         WARN("(Level: %d) is ignored in win9x\n", level);
2816         SetLastError(ERROR_INVALID_LEVEL);
2817         return FALSE;
2818     }
2819
2820     /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
2821     needed = GetSystemDirectoryW(NULL, 0);
2822     /* add the Size for the Subdirectories */
2823     needed += lstrlenW(spoolprtprocsW);
2824     needed += lstrlenW(env_t->subdir);
2825     needed *= sizeof(WCHAR);  /* return-value is size in Bytes */
2826
2827     if(pcbNeeded) *pcbNeeded = needed;
2828     TRACE ("required: 0x%x/%d\n", needed, needed);
2829     if (needed > cbBuf) {
2830         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2831         return FALSE;
2832     }
2833     if(pcbNeeded == NULL) {
2834         /* NT: RPC_X_NULL_REF_POINTER, 9x: ignored */
2835         WARN("(pcbNeeded == NULL) is ignored in win9x\n");
2836         SetLastError(RPC_X_NULL_REF_POINTER);
2837         return FALSE;
2838     }
2839     if(Info == NULL) {
2840         /* NT: RPC_X_NULL_REF_POINTER, 9x: ERROR_INVALID_PARAMETER */
2841         SetLastError(RPC_X_NULL_REF_POINTER);
2842         return FALSE;
2843     }
2844     
2845     GetSystemDirectoryW((LPWSTR) Info, cbBuf/sizeof(WCHAR));
2846     /* add the Subdirectories */
2847     lstrcatW((LPWSTR) Info, spoolprtprocsW);
2848     lstrcatW((LPWSTR) Info, env_t->subdir);
2849     TRACE(" => %s\n", debugstr_w((LPWSTR) Info));
2850     return TRUE;
2851 }
2852
2853 /*****************************************************************************
2854  *          WINSPOOL_OpenDriverReg [internal]
2855  *
2856  * opens the registry for the printer drivers depending on the given input
2857  * variable pEnvironment
2858  *
2859  * RETURNS:
2860  *    the opened hkey on success
2861  *    NULL on error
2862  */
2863 static HKEY WINSPOOL_OpenDriverReg( LPCVOID pEnvironment, BOOL unicode)
2864 {   
2865     HKEY  retval = NULL;
2866     LPWSTR buffer;
2867     const printenv_t * env;
2868
2869     TRACE("(%s, %d)\n",
2870           (unicode) ? debugstr_w(pEnvironment) : debugstr_a(pEnvironment), unicode);
2871
2872     if (!pEnvironment || unicode) {
2873         /* pEnvironment was NULL or an Unicode-String: use it direct */
2874         env = validate_envW(pEnvironment);
2875     }
2876     else
2877     {
2878         /* pEnvironment was an ANSI-String: convert to unicode first */
2879         LPWSTR  buffer;
2880         INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, NULL, 0);
2881         buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2882         if (buffer) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, buffer, len);
2883         env = validate_envW(buffer);
2884         HeapFree(GetProcessHeap(), 0, buffer);
2885     }
2886     if (!env) return NULL;
2887
2888     buffer = HeapAlloc( GetProcessHeap(), 0,
2889                 (strlenW(DriversW) + strlenW(env->envname) + 
2890                  strlenW(env->versionregpath) + 1) * sizeof(WCHAR));
2891     if(buffer) {
2892         wsprintfW(buffer, DriversW, env->envname, env->versionregpath);
2893         RegCreateKeyW(HKEY_LOCAL_MACHINE, buffer, &retval);
2894         HeapFree(GetProcessHeap(), 0, buffer);
2895     }
2896     return retval;
2897 }
2898
2899 /*****************************************************************************
2900  *          AddPrinterW  [WINSPOOL.@]
2901  */
2902 HANDLE WINAPI AddPrinterW(LPWSTR pName, DWORD Level, LPBYTE pPrinter)
2903 {
2904     PRINTER_INFO_2W *pi = (PRINTER_INFO_2W *) pPrinter;
2905     LPDEVMODEA dmA;
2906     LPDEVMODEW dmW;
2907     HANDLE retval;
2908     HKEY hkeyPrinter, hkeyPrinters, hkeyDriver, hkeyDrivers;
2909     LONG size;
2910     static const WCHAR attributesW[]      = {'A','t','t','r','i','b','u','t','e','s',0},
2911                        default_devmodeW[] = {'D','e','f','a','u','l','t',' ','D','e','v','M','o','d','e',0},
2912                        priorityW[]        = {'P','r','i','o','r','i','t','y',0},
2913                        start_timeW[]      = {'S','t','a','r','t','T','i','m','e',0},
2914                        statusW[]          = {'S','t','a','t','u','s',0},
2915                        until_timeW[]      = {'U','n','t','i','l','T','i','m','e',0};
2916
2917     TRACE("(%s,%d,%p)\n", debugstr_w(pName), Level, pPrinter);
2918
2919     if(pName != NULL) {
2920         ERR("pName = %s - unsupported\n", debugstr_w(pName));
2921         SetLastError(ERROR_INVALID_PARAMETER);
2922         return 0;
2923     }
2924     if(Level != 2) {
2925         ERR("Level = %d, unsupported!\n", Level);
2926         SetLastError(ERROR_INVALID_LEVEL);
2927         return 0;
2928     }
2929     if(!pPrinter) {
2930         SetLastError(ERROR_INVALID_PARAMETER);
2931         return 0;
2932     }
2933     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
2934        ERROR_SUCCESS) {
2935         ERR("Can't create Printers key\n");
2936         return 0;
2937     }
2938     if(!RegOpenKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter)) {
2939         if (!RegQueryValueW(hkeyPrinter, attributesW, NULL, NULL)) {
2940             SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
2941             RegCloseKey(hkeyPrinter);
2942             RegCloseKey(hkeyPrinters);
2943             return 0;
2944         }
2945         RegCloseKey(hkeyPrinter);
2946     }
2947     hkeyDrivers = WINSPOOL_OpenDriverReg( NULL, TRUE);
2948     if(!hkeyDrivers) {
2949         ERR("Can't create Drivers key\n");
2950         RegCloseKey(hkeyPrinters);
2951         return 0;
2952     }
2953     if(RegOpenKeyW(hkeyDrivers, pi->pDriverName, &hkeyDriver) !=
2954        ERROR_SUCCESS) {
2955         WARN("Can't find driver %s\n", debugstr_w(pi->pDriverName));
2956         RegCloseKey(hkeyPrinters);
2957         RegCloseKey(hkeyDrivers);
2958         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER);
2959         return 0;
2960     }
2961     RegCloseKey(hkeyDriver);
2962     RegCloseKey(hkeyDrivers);
2963
2964     if(lstrcmpiW(pi->pPrintProcessor, WinPrintW)) {  /* FIXME */
2965         FIXME("Can't find processor %s\n", debugstr_w(pi->pPrintProcessor));
2966         SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR);
2967         RegCloseKey(hkeyPrinters);
2968         return 0;
2969     }
2970
2971     if(RegCreateKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter) !=
2972        ERROR_SUCCESS) {
2973         FIXME("Can't create printer %s\n", debugstr_w(pi->pPrinterName));
2974         SetLastError(ERROR_INVALID_PRINTER_NAME);
2975         RegCloseKey(hkeyPrinters);
2976         return 0;
2977     }
2978     RegSetValueExW(hkeyPrinter, attributesW, 0, REG_DWORD,
2979                    (LPBYTE)&pi->Attributes, sizeof(DWORD));
2980     set_reg_szW(hkeyPrinter, DatatypeW, pi->pDatatype);
2981
2982     /* See if we can load the driver.  We may need the devmode structure anyway
2983      *
2984      * FIXME:
2985      * Note that DocumentPropertiesW will briefly try to open the printer we
2986      * just create to find a DEVMODEA struct (it will use the WINEPS default
2987      * one in case it is not there, so we are ok).
2988      */
2989     size = DocumentPropertiesW(0, 0, pi->pPrinterName, NULL, NULL, 0);
2990
2991     if(size < 0) {
2992         FIXME("DocumentPropertiesW on printer %s fails\n", debugstr_w(pi->pPrinterName));
2993         size = sizeof(DEVMODEW);
2994     }
2995     if(pi->pDevMode)
2996         dmW = pi->pDevMode;
2997     else
2998     {
2999         dmW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
3000         dmW->dmSize = size;
3001         if (0>DocumentPropertiesW(0,0,pi->pPrinterName,dmW,NULL,DM_OUT_BUFFER))
3002         {
3003             WARN("DocumentPropertiesW on printer %s failed!\n", debugstr_w(pi->pPrinterName));
3004             HeapFree(GetProcessHeap(),0,dmW);
3005             dmW=NULL;
3006         }
3007         else
3008         {
3009             /* set devmode to printer name */
3010             lstrcpynW(dmW->dmDeviceName, pi->pPrinterName, CCHDEVICENAME);
3011         }
3012     }
3013
3014     /* Write DEVMODEA not DEVMODEW into reg.  This is what win9x does
3015        and we support these drivers.  NT writes DEVMODEW so somehow
3016        we'll need to distinguish between these when we support NT
3017        drivers */
3018     if (dmW)
3019     {
3020         dmA = DEVMODEdupWtoA(dmW);
3021         RegSetValueExW(hkeyPrinter, default_devmodeW, 0, REG_BINARY,
3022                        (LPBYTE)dmA, dmA->dmSize + dmA->dmDriverExtra);
3023         HeapFree(GetProcessHeap(), 0, dmA);
3024         if(!pi->pDevMode)
3025             HeapFree(GetProcessHeap(), 0, dmW);
3026     }
3027     set_reg_szW(hkeyPrinter, DescriptionW, pi->pComment);
3028     set_reg_szW(hkeyPrinter, LocationW, pi->pLocation);
3029     set_reg_szW(hkeyPrinter, NameW, pi->pPrinterName);
3030     set_reg_szW(hkeyPrinter, ParametersW, pi->pParameters);
3031
3032     set_reg_szW(hkeyPrinter, PortW, pi->pPortName);
3033     set_reg_szW(hkeyPrinter, Print_ProcessorW, pi->pPrintProcessor);
3034     set_reg_szW(hkeyPrinter, Printer_DriverW, pi->pDriverName);
3035     RegSetValueExW(hkeyPrinter, priorityW, 0, REG_DWORD,
3036                    (LPBYTE)&pi->Priority, sizeof(DWORD));
3037     set_reg_szW(hkeyPrinter, Separator_FileW, pi->pSepFile);
3038     set_reg_szW(hkeyPrinter, Share_NameW, pi->pShareName);
3039     RegSetValueExW(hkeyPrinter, start_timeW, 0, REG_DWORD,
3040                    (LPBYTE)&pi->StartTime, sizeof(DWORD));
3041     RegSetValueExW(hkeyPrinter, statusW, 0, REG_DWORD,
3042                    (LPBYTE)&pi->Status, sizeof(DWORD));
3043     RegSetValueExW(hkeyPrinter, until_timeW, 0, REG_DWORD,
3044                    (LPBYTE)&pi->UntilTime, sizeof(DWORD));
3045
3046     RegCloseKey(hkeyPrinter);
3047     RegCloseKey(hkeyPrinters);
3048     if(!OpenPrinterW(pi->pPrinterName, &retval, NULL)) {
3049         ERR("OpenPrinter failing\n");
3050         return 0;
3051     }
3052     return retval;
3053 }
3054
3055 /*****************************************************************************
3056  *          AddPrinterA  [WINSPOOL.@]
3057  */
3058 HANDLE WINAPI AddPrinterA(LPSTR pName, DWORD Level, LPBYTE pPrinter)
3059 {
3060     UNICODE_STRING pNameW;
3061     PWSTR pwstrNameW;
3062     PRINTER_INFO_2W *piW;
3063     PRINTER_INFO_2A *piA = (PRINTER_INFO_2A*)pPrinter;
3064     HANDLE ret;
3065
3066     TRACE("(%s,%d,%p): stub\n", debugstr_a(pName), Level, pPrinter);
3067     if(Level != 2) {
3068         ERR("Level = %d, unsupported!\n", Level);
3069         SetLastError(ERROR_INVALID_LEVEL);
3070         return 0;
3071     }
3072     pwstrNameW = asciitounicode(&pNameW,pName);
3073     piW = PRINTER_INFO_2AtoW(GetProcessHeap(), piA);
3074
3075     ret = AddPrinterW(pwstrNameW, Level, (LPBYTE)piW);
3076
3077     FREE_PRINTER_INFO_2W(GetProcessHeap(), piW);
3078     RtlFreeUnicodeString(&pNameW);
3079     return ret;
3080 }
3081
3082
3083 /*****************************************************************************
3084  *          ClosePrinter  [WINSPOOL.@]
3085  */
3086 BOOL WINAPI ClosePrinter(HANDLE hPrinter)
3087 {
3088     UINT_PTR i = (UINT_PTR)hPrinter;
3089     opened_printer_t *printer = NULL;
3090     BOOL ret = FALSE;
3091
3092     TRACE("(%p)\n", hPrinter);
3093
3094     EnterCriticalSection(&printer_handles_cs);
3095
3096     if ((i > 0) && (i <= nb_printer_handles))
3097         printer = printer_handles[i - 1];
3098
3099
3100     if(printer)
3101     {
3102         struct list *cursor, *cursor2;
3103
3104         TRACE("%p: %s (hXcv: %p) for %s (doc: %p)\n", printer->pm,
3105                 debugstr_w(printer->pm ? printer->pm->dllname : NULL),
3106                 printer->hXcv, debugstr_w(printer->name), printer->doc );
3107
3108         if(printer->doc)
3109             EndDocPrinter(hPrinter);
3110
3111         if(InterlockedDecrement(&printer->queue->ref) == 0)
3112         {
3113             LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
3114             {
3115                 job_t *job = LIST_ENTRY(cursor, job_t, entry);
3116                 ScheduleJob(hPrinter, job->job_id);
3117             }
3118             HeapFree(GetProcessHeap(), 0, printer->queue);
3119         }
3120         if (printer->hXcv) printer->pm->monitor->pfnXcvClosePort(printer->hXcv);
3121         monitor_unload(printer->pm);
3122         HeapFree(GetProcessHeap(), 0, printer->printername);
3123         HeapFree(GetProcessHeap(), 0, printer->name);
3124         HeapFree(GetProcessHeap(), 0, printer);
3125         printer_handles[i - 1] = NULL;
3126         ret = TRUE;
3127     }
3128     LeaveCriticalSection(&printer_handles_cs);
3129     return ret;
3130 }
3131
3132 /*****************************************************************************
3133  *          DeleteFormA  [WINSPOOL.@]
3134  */
3135 BOOL WINAPI DeleteFormA(HANDLE hPrinter, LPSTR pFormName)
3136 {
3137     FIXME("(%p,%s): stub\n", hPrinter, pFormName);
3138     return 1;
3139 }
3140
3141 /*****************************************************************************
3142  *          DeleteFormW  [WINSPOOL.@]
3143  */
3144 BOOL WINAPI DeleteFormW(HANDLE hPrinter, LPWSTR pFormName)
3145 {
3146     FIXME("(%p,%s): stub\n", hPrinter, debugstr_w(pFormName));
3147     return 1;
3148 }
3149
3150 /*****************************************************************************
3151  *          DeletePrinter  [WINSPOOL.@]
3152  */
3153 BOOL WINAPI DeletePrinter(HANDLE hPrinter)
3154 {
3155     LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
3156     HKEY hkeyPrinters, hkey;
3157
3158     if(!lpNameW) {
3159         SetLastError(ERROR_INVALID_HANDLE);
3160         return FALSE;
3161     }
3162     if(RegOpenKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
3163         RegDeleteTreeW(hkeyPrinters, lpNameW);
3164         RegCloseKey(hkeyPrinters);
3165     }
3166     WriteProfileStringW(devicesW, lpNameW, NULL);
3167     WriteProfileStringW(PrinterPortsW, lpNameW, NULL);
3168
3169     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
3170         RegDeleteValueW(hkey, lpNameW);
3171         RegCloseKey(hkey);
3172     }
3173
3174     if(RegCreateKeyW(HKEY_CURRENT_USER, WinNT_CV_PrinterPortsW, &hkey) == ERROR_SUCCESS) {
3175         RegDeleteValueW(hkey, lpNameW);
3176         RegCloseKey(hkey);
3177     }
3178     return TRUE;
3179 }
3180
3181 /*****************************************************************************
3182  *          SetPrinterA  [WINSPOOL.@]
3183  */
3184 BOOL WINAPI SetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
3185                            DWORD Command)
3186 {
3187     FIXME("(%p,%d,%p,%d): stub\n",hPrinter,Level,pPrinter,Command);
3188     return FALSE;
3189 }
3190
3191 /*****************************************************************************
3192  *          SetJobA  [WINSPOOL.@]
3193  */
3194 BOOL WINAPI SetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level,
3195                     LPBYTE pJob, DWORD Command)
3196 {
3197     BOOL ret;
3198     LPBYTE JobW;
3199     UNICODE_STRING usBuffer;
3200
3201     TRACE("(%p, %d, %d, %p, %d)\n",hPrinter, JobId, Level, pJob, Command);
3202
3203     /* JobId, pPrinterName, pMachineName, pDriverName, Size, Submitted, Time and TotalPages
3204        are all ignored by SetJob, so we don't bother copying them */
3205     switch(Level)
3206     {
3207     case 0:
3208         JobW = NULL;
3209         break;
3210     case 1:
3211       {
3212         JOB_INFO_1W *info1W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info1W));
3213         JOB_INFO_1A *info1A = (JOB_INFO_1A*)pJob;
3214
3215         JobW = (LPBYTE)info1W;
3216         info1W->pUserName = asciitounicode(&usBuffer, info1A->pUserName);
3217         info1W->pDocument = asciitounicode(&usBuffer, info1A->pDocument);
3218         info1W->pDatatype = asciitounicode(&usBuffer, info1A->pDatatype);
3219         info1W->pStatus = asciitounicode(&usBuffer, info1A->pStatus);
3220         info1W->Status = info1A->Status;
3221         info1W->Priority = info1A->Priority;
3222         info1W->Position = info1A->Position;
3223         info1W->PagesPrinted = info1A->PagesPrinted;
3224         break;
3225       }
3226     case 2:
3227       {
3228         JOB_INFO_2W *info2W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info2W));
3229         JOB_INFO_2A *info2A = (JOB_INFO_2A*)pJob;
3230
3231         JobW = (LPBYTE)info2W;
3232         info2W->pUserName = asciitounicode(&usBuffer, info2A->pUserName);
3233         info2W->pDocument = asciitounicode(&usBuffer, info2A->pDocument);
3234         info2W->pNotifyName = asciitounicode(&usBuffer, info2A->pNotifyName);
3235         info2W->pDatatype = asciitounicode(&usBuffer, info2A->pDatatype);
3236         info2W->pPrintProcessor = asciitounicode(&usBuffer, info2A->pPrintProcessor);
3237         info2W->pParameters = asciitounicode(&usBuffer, info2A->pParameters);
3238         info2W->pDevMode = info2A->pDevMode ? GdiConvertToDevmodeW(info2A->pDevMode) : NULL;
3239         info2W->pStatus = asciitounicode(&usBuffer, info2A->pStatus);
3240         info2W->pSecurityDescriptor = info2A->pSecurityDescriptor;
3241         info2W->Status = info2A->Status;
3242         info2W->Priority = info2A->Priority;
3243         info2W->Position = info2A->Position;
3244         info2W->StartTime = info2A->StartTime;
3245         info2W->UntilTime = info2A->UntilTime;
3246         info2W->PagesPrinted = info2A->PagesPrinted;
3247         break;
3248       }
3249     case 3:
3250         JobW = HeapAlloc(GetProcessHeap(), 0, sizeof(JOB_INFO_3));
3251         memcpy(JobW, pJob, sizeof(JOB_INFO_3));
3252         break;
3253     default:
3254         SetLastError(ERROR_INVALID_LEVEL);
3255         return FALSE;
3256     }
3257
3258     ret = SetJobW(hPrinter, JobId, Level, JobW, Command);
3259
3260     switch(Level)
3261     {
3262     case 1:
3263       {
3264         JOB_INFO_1W *info1W = (JOB_INFO_1W*)JobW;
3265         HeapFree(GetProcessHeap(), 0, info1W->pUserName);
3266         HeapFree(GetProcessHeap(), 0, info1W->pDocument); 
3267         HeapFree(GetProcessHeap(), 0, info1W->pDatatype);
3268         HeapFree(GetProcessHeap(), 0, info1W->pStatus);
3269         break;
3270       }
3271     case 2:
3272       {
3273         JOB_INFO_2W *info2W = (JOB_INFO_2W*)JobW;
3274         HeapFree(GetProcessHeap(), 0, info2W->pUserName);
3275         HeapFree(GetProcessHeap(), 0, info2W->pDocument); 
3276         HeapFree(GetProcessHeap(), 0, info2W->pNotifyName);
3277         HeapFree(GetProcessHeap(), 0, info2W->pDatatype);
3278         HeapFree(GetProcessHeap(), 0, info2W->pPrintProcessor);
3279         HeapFree(GetProcessHeap(), 0, info2W->pParameters);
3280         HeapFree(GetProcessHeap(), 0, info2W->pDevMode);
3281         HeapFree(GetProcessHeap(), 0, info2W->pStatus);
3282         break;
3283       }
3284     }
3285     HeapFree(GetProcessHeap(), 0, JobW);
3286
3287     return ret;
3288 }
3289
3290 /*****************************************************************************
3291  *          SetJobW  [WINSPOOL.@]
3292  */
3293 BOOL WINAPI SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level,
3294                     LPBYTE pJob, DWORD Command)
3295 {
3296     BOOL ret = FALSE;
3297     job_t *job;
3298
3299     TRACE("(%p, %d, %d, %p, %d)\n", hPrinter, JobId, Level, pJob, Command);
3300     FIXME("Ignoring everything other than document title\n");
3301
3302     EnterCriticalSection(&printer_handles_cs);
3303     job = get_job(hPrinter, JobId);
3304     if(!job)
3305         goto end;
3306
3307     switch(Level)
3308     {
3309     case 0:
3310         break;
3311     case 1:
3312       {
3313         JOB_INFO_1W *info1 = (JOB_INFO_1W*)pJob;
3314         HeapFree(GetProcessHeap(), 0, job->document_title);
3315         job->document_title = strdupW(info1->pDocument);
3316         break;
3317       }
3318     case 2:
3319       {
3320         JOB_INFO_2W *info2 = (JOB_INFO_2W*)pJob;
3321         HeapFree(GetProcessHeap(), 0, job->document_title);
3322         job->document_title = strdupW(info2->pDocument);
3323         break;
3324       }
3325     case 3:
3326         break;
3327     default:
3328         SetLastError(ERROR_INVALID_LEVEL);
3329         goto end;
3330     }
3331     ret = TRUE;
3332 end:
3333     LeaveCriticalSection(&printer_handles_cs);
3334     return ret;
3335 }
3336
3337 /*****************************************************************************
3338  *          EndDocPrinter  [WINSPOOL.@]
3339  */
3340 BOOL WINAPI EndDocPrinter(HANDLE hPrinter)
3341 {
3342     opened_printer_t *printer;
3343     BOOL ret = FALSE;
3344     TRACE("(%p)\n", hPrinter);
3345
3346     EnterCriticalSection(&printer_handles_cs);
3347
3348     printer = get_opened_printer(hPrinter);
3349     if(!printer)
3350     {
3351         SetLastError(ERROR_INVALID_HANDLE);
3352         goto end;
3353     }
3354
3355     if(!printer->doc)    
3356     {
3357         SetLastError(ERROR_SPL_NO_STARTDOC);
3358         goto end;
3359     }
3360
3361     CloseHandle(printer->doc->hf);
3362     ScheduleJob(hPrinter, printer->doc->job_id);
3363     HeapFree(GetProcessHeap(), 0, printer->doc);
3364     printer->doc = NULL;
3365     ret = TRUE;
3366 end:
3367     LeaveCriticalSection(&printer_handles_cs);
3368     return ret;
3369 }
3370
3371 /*****************************************************************************
3372  *          EndPagePrinter  [WINSPOOL.@]
3373  */
3374 BOOL WINAPI EndPagePrinter(HANDLE hPrinter)
3375 {
3376     FIXME("(%p): stub\n", hPrinter);
3377     return TRUE;
3378 }
3379
3380 /*****************************************************************************
3381  *          StartDocPrinterA  [WINSPOOL.@]
3382  */
3383 DWORD WINAPI StartDocPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
3384 {
3385     UNICODE_STRING usBuffer;
3386     DOC_INFO_2W doc2W;
3387     DOC_INFO_2A *doc2 = (DOC_INFO_2A*)pDocInfo;
3388     DWORD ret;
3389
3390     /* DOC_INFO_1, 2 and 3 all have the strings in the same place with either two (DOC_INFO_2)
3391        or one (DOC_INFO_3) extra DWORDs */
3392
3393     switch(Level) {
3394     case 2:
3395         doc2W.JobId = doc2->JobId;
3396         /* fall through */
3397     case 3:
3398         doc2W.dwMode = doc2->dwMode;
3399         /* fall through */
3400     case 1:
3401         doc2W.pDocName = asciitounicode(&usBuffer, doc2->pDocName);
3402         doc2W.pOutputFile = asciitounicode(&usBuffer, doc2->pOutputFile);
3403         doc2W.pDatatype = asciitounicode(&usBuffer, doc2->pDatatype);
3404         break;
3405
3406     default:
3407         SetLastError(ERROR_INVALID_LEVEL);
3408         return FALSE;
3409     }
3410
3411     ret = StartDocPrinterW(hPrinter, Level, (LPBYTE)&doc2W);
3412
3413     HeapFree(GetProcessHeap(), 0, doc2W.pDatatype);
3414     HeapFree(GetProcessHeap(), 0, doc2W.pOutputFile);
3415     HeapFree(GetProcessHeap(), 0, doc2W.pDocName);
3416
3417     return ret;
3418 }
3419
3420 /*****************************************************************************
3421  *          StartDocPrinterW  [WINSPOOL.@]
3422  */
3423 DWORD WINAPI StartDocPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
3424 {
3425     DOC_INFO_2W *doc = (DOC_INFO_2W *)pDocInfo;
3426     opened_printer_t *printer;
3427     BYTE addjob_buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
3428     ADDJOB_INFO_1W *addjob = (ADDJOB_INFO_1W*) addjob_buf;
3429     JOB_INFO_1W job_info;
3430     DWORD needed, ret = 0;
3431     HANDLE hf;
3432     WCHAR *filename;
3433
3434     TRACE("(hPrinter = %p, Level = %d, pDocInfo = %p {pDocName = %s, pOutputFile = %s, pDatatype = %s}):\n",
3435           hPrinter, Level, doc, debugstr_w(doc->pDocName), debugstr_w(doc->pOutputFile),
3436           debugstr_w(doc->pDatatype));
3437
3438     if(Level < 1 || Level > 3)
3439     {
3440         SetLastError(ERROR_INVALID_LEVEL);
3441         return 0;
3442     }
3443
3444     EnterCriticalSection(&printer_handles_cs);
3445     printer = get_opened_printer(hPrinter);
3446     if(!printer)
3447     {
3448         SetLastError(ERROR_INVALID_HANDLE);
3449         goto end;
3450     }
3451
3452     if(printer->doc)
3453     {
3454         SetLastError(ERROR_INVALID_PRINTER_STATE);
3455         goto end;
3456     }
3457
3458     /* Even if we're printing to a file we still add a print job, we'll
3459        just ignore the spool file name */
3460
3461     if(!AddJobW(hPrinter, 1, addjob_buf, sizeof(addjob_buf), &needed))
3462     {
3463         ERR("AddJob failed gle %u\n", GetLastError());
3464         goto end;
3465     }
3466
3467     if(doc->pOutputFile)
3468         filename = doc->pOutputFile;
3469     else
3470         filename = addjob->Path;
3471
3472     hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3473     if(hf == INVALID_HANDLE_VALUE)
3474         goto end;
3475
3476     memset(&job_info, 0, sizeof(job_info));
3477     job_info.pDocument = doc->pDocName;
3478     SetJobW(hPrinter, addjob->JobId, 1, (LPBYTE)&job_info, 0);
3479
3480     printer->doc = HeapAlloc(GetProcessHeap(), 0, sizeof(*printer->doc));
3481     printer->doc->hf = hf;
3482     ret = printer->doc->job_id = addjob->JobId;
3483 end:
3484     LeaveCriticalSection(&printer_handles_cs);
3485
3486     return ret;
3487 }
3488
3489 /*****************************************************************************
3490  *          StartPagePrinter  [WINSPOOL.@]
3491  */
3492 BOOL WINAPI StartPagePrinter(HANDLE hPrinter)
3493 {
3494     FIXME("(%p): stub\n", hPrinter);
3495     return TRUE;
3496 }
3497
3498 /*****************************************************************************
3499  *          GetFormA  [WINSPOOL.@]
3500  */
3501 BOOL WINAPI GetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
3502                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
3503 {
3504     FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,pFormName,
3505          Level,pForm,cbBuf,pcbNeeded);
3506     return FALSE;
3507 }
3508
3509 /*****************************************************************************
3510  *          GetFormW  [WINSPOOL.@]
3511  */
3512 BOOL WINAPI GetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
3513                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
3514 {
3515     FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,
3516           debugstr_w(pFormName),Level,pForm,cbBuf,pcbNeeded);
3517     return FALSE;
3518 }
3519
3520 /*****************************************************************************
3521  *          SetFormA  [WINSPOOL.@]
3522  */
3523 BOOL WINAPI SetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
3524                         LPBYTE pForm)
3525 {
3526     FIXME("(%p,%s,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
3527     return FALSE;
3528 }
3529
3530 /*****************************************************************************
3531  *          SetFormW  [WINSPOOL.@]
3532  */
3533 BOOL WINAPI SetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
3534                         LPBYTE pForm)
3535 {
3536     FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
3537     return FALSE;
3538 }
3539
3540 /*****************************************************************************
3541  *          ReadPrinter  [WINSPOOL.@]
3542  */
3543 BOOL WINAPI ReadPrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf,
3544                            LPDWORD pNoBytesRead)
3545 {
3546     FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pBuf,cbBuf,pNoBytesRead);
3547     return FALSE;
3548 }
3549
3550 /*****************************************************************************
3551  *          ResetPrinterA  [WINSPOOL.@]
3552  */
3553 BOOL WINAPI ResetPrinterA(HANDLE hPrinter, LPPRINTER_DEFAULTSA pDefault)
3554 {
3555     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
3556     return FALSE;
3557 }
3558
3559 /*****************************************************************************
3560  *          ResetPrinterW  [WINSPOOL.@]
3561  */
3562 BOOL WINAPI ResetPrinterW(HANDLE hPrinter, LPPRINTER_DEFAULTSW pDefault)
3563 {
3564     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
3565     return FALSE;
3566 }
3567
3568 /*****************************************************************************
3569  *    WINSPOOL_GetDWORDFromReg
3570  *
3571  * Return DWORD associated with ValueName from hkey.
3572  */
3573 static DWORD WINSPOOL_GetDWORDFromReg(HKEY hkey, LPCSTR ValueName)
3574 {
3575     DWORD sz = sizeof(DWORD), type, value = 0;
3576     LONG ret;
3577
3578     ret = RegQueryValueExA(hkey, ValueName, 0, &type, (LPBYTE)&value, &sz);
3579
3580     if(ret != ERROR_SUCCESS) {
3581         WARN("Got ret = %d on name %s\n", ret, ValueName);
3582         return 0;
3583     }
3584     if(type != REG_DWORD) {
3585         ERR("Got type %d\n", type);
3586         return 0;
3587     }
3588     return value;
3589 }
3590
3591
3592 /*****************************************************************************
3593  * get_filename_from_reg [internal]
3594  *
3595  * Get ValueName from hkey storing result in out
3596  * when the Value in the registry has only a filename, use driverdir as prefix
3597  * outlen is space left in out
3598  * String is stored either as unicode or ascii
3599  *
3600  */
3601
3602 static BOOL get_filename_from_reg(HKEY hkey, LPCWSTR driverdir, DWORD dirlen, LPCWSTR ValueName,
3603                                   LPBYTE out, DWORD outlen, LPDWORD needed, BOOL unicode)
3604 {
3605     WCHAR   filename[MAX_PATH];
3606     DWORD   size;
3607     DWORD   type;
3608     LONG    ret;
3609     LPWSTR  buffer = filename;
3610     LPWSTR  ptr;
3611
3612     *needed = 0;
3613     size = sizeof(filename);
3614     buffer[0] = '\0';
3615     ret = RegQueryValueExW(hkey, ValueName, NULL, &type, (LPBYTE) buffer, &size);
3616     if (ret == ERROR_MORE_DATA) {
3617         TRACE("need dynamic buffer: %u\n", size);
3618         buffer = HeapAlloc(GetProcessHeap(), 0, size);
3619         if (!buffer) {
3620             /* No Memory is bad */
3621             return FALSE;
3622         }
3623         buffer[0] = '\0';
3624         ret = RegQueryValueExW(hkey, ValueName, NULL, &type, (LPBYTE) buffer, &size);
3625     }
3626
3627     if ((ret != ERROR_SUCCESS) || (!buffer[0])) {
3628         if (buffer != filename) HeapFree(GetProcessHeap(), 0, buffer);
3629         return FALSE;
3630     }
3631
3632     ptr = buffer;
3633     while (ptr) {
3634         /* do we have a full path ? */
3635         ret = (((buffer[0] == '\\') && (buffer[1] == '\\')) ||
3636                 (buffer[0] && (buffer[1] == ':') && (buffer[2] == '\\')) );
3637
3638         if (!ret) {
3639             /* we must build the full Path */
3640             *needed += dirlen;
3641             if ((out) && (outlen > dirlen)) {
3642                 if (unicode) {
3643                     lstrcpyW((LPWSTR)out, driverdir);
3644                 }
3645                 else
3646                 {
3647                     WideCharToMultiByte(CP_ACP, 0, driverdir, -1, (LPSTR)out, outlen, NULL, NULL);
3648                 }
3649                 out += dirlen;
3650                 outlen -= dirlen;
3651             }
3652             else
3653                 out = NULL;
3654         }
3655
3656         /* write the filename */
3657         if (unicode) {
3658             size = (lstrlenW(ptr) + 1) * sizeof(WCHAR);
3659             if ((out) && (outlen >= size)) {
3660                 lstrcpyW((LPWSTR)out, ptr);
3661                 out += size;
3662                 outlen -= size;
3663             }
3664             else
3665                 out = NULL;
3666         }
3667         else
3668         {
3669             size = WideCharToMultiByte(CP_ACP, 0, ptr, -1, NULL, 0, NULL, NULL);
3670             if ((out) && (outlen >= size)) {
3671                 WideCharToMultiByte(CP_ACP, 0, ptr, -1, (LPSTR)out, outlen, NULL, NULL);
3672                 out += size;
3673                 outlen -= size;
3674             }
3675             else
3676                 out = NULL;
3677         }
3678         *needed += size;
3679         ptr +=  lstrlenW(ptr)+1;
3680         if ((type != REG_MULTI_SZ) || (!ptr[0]))  ptr = NULL;
3681     }
3682
3683     if (buffer != filename) HeapFree(GetProcessHeap(), 0, buffer);
3684
3685     /* write the multisz-termination */
3686     if (type == REG_MULTI_SZ) {
3687         size = (unicode) ? sizeof(WCHAR) : 1;
3688
3689         *needed += size;
3690         if (out && (outlen >= size)) {
3691             memset (out, 0, size);
3692         }
3693     }
3694     return TRUE;
3695 }
3696
3697 /*****************************************************************************
3698  *    WINSPOOL_GetStringFromReg
3699  *
3700  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
3701  * String is stored either as unicode or ascii.
3702  * Bit of a hack here to get the ValueName if we want ascii.
3703  */
3704 static BOOL WINSPOOL_GetStringFromReg(HKEY hkey, LPCWSTR ValueName, LPBYTE ptr,
3705                                       DWORD buflen, DWORD *needed,
3706                                       BOOL unicode)
3707 {
3708     DWORD sz = buflen, type;
3709     LONG ret;
3710
3711     if(unicode)
3712         ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3713     else {
3714         LPSTR ValueNameA = strdupWtoA(ValueName);
3715         ret = RegQueryValueExA(hkey, ValueNameA, 0, &type, ptr, &sz);
3716         HeapFree(GetProcessHeap(),0,ValueNameA);
3717     }
3718     if(ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) {
3719         WARN("Got ret = %d\n", ret);
3720         *needed = 0;
3721         return FALSE;
3722     }
3723     /* add space for terminating '\0' */
3724     sz += unicode ? sizeof(WCHAR) : 1;
3725     *needed = sz;
3726
3727     if (ptr)
3728         TRACE("%s: %s\n", debugstr_w(ValueName), unicode ? debugstr_w((LPCWSTR)ptr) : debugstr_a((LPCSTR)ptr));
3729
3730     return TRUE;
3731 }
3732
3733 /*****************************************************************************
3734  *    WINSPOOL_GetDefaultDevMode
3735  *
3736  * Get a default DevMode values for wineps.
3737  * FIXME - use ppd.
3738  */
3739
3740 static void WINSPOOL_GetDefaultDevMode(
3741         LPBYTE ptr,
3742         DWORD buflen, DWORD *needed,
3743         BOOL unicode)
3744 {
3745     DEVMODEA    dm;
3746     static const char szwps[] = "wineps.drv";
3747
3748         /* fill default DEVMODE - should be read from ppd... */
3749         ZeroMemory( &dm, sizeof(dm) );
3750         memcpy(dm.dmDeviceName,szwps,sizeof szwps);
3751         dm.dmSpecVersion = DM_SPECVERSION;
3752         dm.dmDriverVersion = 1;
3753         dm.dmSize = sizeof(DEVMODEA);
3754         dm.dmDriverExtra = 0;
3755         dm.dmFields =
3756                 DM_ORIENTATION | DM_PAPERSIZE |
3757                 DM_PAPERLENGTH | DM_PAPERWIDTH |
3758                 DM_SCALE |
3759                 DM_COPIES |
3760                 DM_DEFAULTSOURCE | DM_PRINTQUALITY |
3761                 DM_YRESOLUTION | DM_TTOPTION;
3762
3763         dm.u1.s1.dmOrientation = DMORIENT_PORTRAIT;
3764         dm.u1.s1.dmPaperSize = DMPAPER_A4;
3765         dm.u1.s1.dmPaperLength = 2970;
3766         dm.u1.s1.dmPaperWidth = 2100;
3767
3768         dm.u1.s1.dmScale = 100;
3769         dm.u1.s1.dmCopies = 1;
3770         dm.u1.s1.dmDefaultSource = DMBIN_AUTO;
3771         dm.u1.s1.dmPrintQuality = DMRES_MEDIUM;
3772         /* dm.dmColor */
3773         /* dm.dmDuplex */
3774         dm.dmYResolution = 300; /* 300dpi */
3775         dm.dmTTOption = DMTT_BITMAP;
3776         /* dm.dmCollate */
3777         /* dm.dmFormName */
3778         /* dm.dmLogPixels */
3779         /* dm.dmBitsPerPel */
3780         /* dm.dmPelsWidth */
3781         /* dm.dmPelsHeight */
3782         /* dm.u2.dmDisplayFlags */
3783         /* dm.dmDisplayFrequency */
3784         /* dm.dmICMMethod */
3785         /* dm.dmICMIntent */
3786         /* dm.dmMediaType */
3787         /* dm.dmDitherType */
3788         /* dm.dmReserved1 */
3789         /* dm.dmReserved2 */
3790         /* dm.dmPanningWidth */
3791         /* dm.dmPanningHeight */
3792
3793     if(unicode) {
3794         if(buflen >= sizeof(DEVMODEW)) {
3795             DEVMODEW *pdmW = GdiConvertToDevmodeW(&dm);
3796             memcpy(ptr, pdmW, sizeof(DEVMODEW));
3797             HeapFree(GetProcessHeap(),0,pdmW);
3798         }
3799         *needed = sizeof(DEVMODEW);
3800     }
3801     else
3802     {
3803         if(buflen >= sizeof(DEVMODEA)) {
3804             memcpy(ptr, &dm, sizeof(DEVMODEA));
3805         }
3806         *needed = sizeof(DEVMODEA);
3807     }
3808 }
3809
3810 /*****************************************************************************
3811  *    WINSPOOL_GetDevModeFromReg
3812  *
3813  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
3814  * DevMode is stored either as unicode or ascii.
3815  */
3816 static BOOL WINSPOOL_GetDevModeFromReg(HKEY hkey, LPCWSTR ValueName,
3817                                        LPBYTE ptr,
3818                                        DWORD buflen, DWORD *needed,
3819                                        BOOL unicode)
3820 {
3821     DWORD sz = buflen, type;
3822     LONG ret;
3823
3824     if (ptr && buflen>=sizeof(DEVMODEA)) memset(ptr, 0, sizeof(DEVMODEA));
3825     ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3826     if ((ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA)) sz = 0;
3827     if (sz < sizeof(DEVMODEA))
3828     {
3829         TRACE("corrupted registry for %s ( size %d)\n",debugstr_w(ValueName),sz);
3830         return FALSE;
3831     }
3832     /* ensures that dmSize is not erratically bogus if registry is invalid */
3833     if (ptr && ((DEVMODEA*)ptr)->dmSize < sizeof(DEVMODEA))
3834         ((DEVMODEA*)ptr)->dmSize = sizeof(DEVMODEA);
3835     if(unicode) {
3836         sz += (CCHDEVICENAME + CCHFORMNAME);
3837         if(buflen >= sz) {
3838             DEVMODEW *dmW = GdiConvertToDevmodeW((DEVMODEA*)ptr);
3839             memcpy(ptr, dmW, sz);
3840             HeapFree(GetProcessHeap(),0,dmW);
3841         }
3842     }
3843     *needed = sz;
3844     return TRUE;
3845 }
3846
3847 /*********************************************************************
3848  *    WINSPOOL_GetPrinter_1
3849  *
3850  * Fills out a PRINTER_INFO_1A|W struct storing the strings in buf.
3851  * The strings are either stored as unicode or ascii.
3852  */
3853 static BOOL WINSPOOL_GetPrinter_1(HKEY hkeyPrinter, PRINTER_INFO_1W *pi1,
3854                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3855                                   BOOL unicode)
3856 {
3857     DWORD size, left = cbBuf;
3858     BOOL space = (cbBuf > 0);
3859     LPBYTE ptr = buf;
3860
3861     *pcbNeeded = 0;
3862
3863     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3864                                  unicode)) {
3865         if(space && size <= left) {
3866             pi1->pName = (LPWSTR)ptr;
3867             ptr += size;
3868             left -= size;
3869         } else
3870             space = FALSE;
3871         *pcbNeeded += size;
3872     }
3873
3874     /* FIXME: pDescription should be something like "Name,Driver_Name,". */
3875     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3876                                  unicode)) {
3877         if(space && size <= left) {
3878             pi1->pDescription = (LPWSTR)ptr;
3879             ptr += size;
3880             left -= size;
3881         } else
3882             space = FALSE;
3883         *pcbNeeded += size;
3884     }
3885
3886     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size,
3887                                  unicode)) {
3888         if(space && size <= left) {
3889             pi1->pComment = (LPWSTR)ptr;
3890             ptr += size;
3891             left -= size;
3892         } else
3893             space = FALSE;
3894         *pcbNeeded += size;
3895     }
3896
3897     if(pi1) pi1->Flags = PRINTER_ENUM_ICON8; /* We're a printer */
3898
3899     if(!space && pi1) /* zero out pi1 if we can't completely fill buf */
3900         memset(pi1, 0, sizeof(*pi1));
3901
3902     return space;
3903 }
3904 /*********************************************************************
3905  *    WINSPOOL_GetPrinter_2
3906  *
3907  * Fills out a PRINTER_INFO_2A|W struct storing the strings in buf.
3908  * The strings are either stored as unicode or ascii.
3909  */
3910 static BOOL WINSPOOL_GetPrinter_2(HKEY hkeyPrinter, PRINTER_INFO_2W *pi2,
3911                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3912                                   BOOL unicode)
3913 {
3914     DWORD size, left = cbBuf;
3915     BOOL space = (cbBuf > 0);
3916     LPBYTE ptr = buf;
3917
3918     *pcbNeeded = 0;
3919
3920     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3921                                  unicode)) {
3922         if(space && size <= left) {
3923             pi2->pPrinterName = (LPWSTR)ptr;
3924             ptr += size;
3925             left -= size;
3926         } else
3927             space = FALSE;
3928         *pcbNeeded += size;
3929     }
3930     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Share_NameW, ptr, left, &size,
3931                                  unicode)) {
3932         if(space && size <= left) {
3933             pi2->pShareName = (LPWSTR)ptr;
3934             ptr += size;
3935             left -= size;
3936         } else
3937             space = FALSE;
3938         *pcbNeeded += size;
3939     }
3940     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
3941                                  unicode)) {
3942         if(space && size <= left) {
3943             pi2->pPortName = (LPWSTR)ptr;
3944             ptr += size;
3945             left -= size;
3946         } else
3947             space = FALSE;
3948         *pcbNeeded += size;
3949     }
3950     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Printer_DriverW, ptr, left,
3951                                  &size, unicode)) {
3952         if(space && size <= left) {
3953             pi2->pDriverName = (LPWSTR)ptr;
3954             ptr += size;
3955             left -= size;
3956         } else
3957             space = FALSE;
3958         *pcbNeeded += size;
3959     }
3960     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size,
3961                                  unicode)) {
3962         if(space && size <= left) {
3963             pi2->pComment = (LPWSTR)ptr;
3964             ptr += size;
3965             left -= size;
3966         } else
3967             space = FALSE;
3968         *pcbNeeded += size;
3969     }
3970     if(WINSPOOL_GetStringFromReg(hkeyPrinter, LocationW, ptr, left, &size,
3971                                  unicode)) {
3972         if(space && size <= left) {
3973             pi2->pLocation = (LPWSTR)ptr;
3974             ptr += size;
3975             left -= size;
3976         } else
3977             space = FALSE;
3978         *pcbNeeded += size;
3979     }
3980     if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, ptr, left,
3981                                   &size, unicode)) {
3982         if(space && size <= left) {
3983             pi2->pDevMode = (LPDEVMODEW)ptr;
3984             ptr += size;
3985             left -= size;
3986         } else
3987             space = FALSE;
3988         *pcbNeeded += size;
3989     }
3990     else
3991     {
3992         WINSPOOL_GetDefaultDevMode(ptr, left, &size, unicode);
3993         if(space && size <= left) {
3994             pi2->pDevMode = (LPDEVMODEW)ptr;
3995             ptr += size;
3996             left -= size;
3997         } else
3998             space = FALSE;
3999         *pcbNeeded += size;
4000     }
4001     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Separator_FileW, ptr, left,
4002                                  &size, unicode)) {
4003         if(space && size <= left) {
4004             pi2->pSepFile = (LPWSTR)ptr;
4005             ptr += size;
4006             left -= size;
4007         } else
4008             space = FALSE;
4009         *pcbNeeded += size;
4010     }
4011     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Print_ProcessorW, ptr, left,
4012                                  &size, unicode)) {
4013         if(space && size <= left) {
4014             pi2->pPrintProcessor = (LPWSTR)ptr;
4015             ptr += size;
4016             left -= size;
4017         } else
4018             space = FALSE;
4019         *pcbNeeded += size;
4020     }
4021     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DatatypeW, ptr, left,
4022                                  &size, unicode)) {
4023         if(space && size <= left) {
4024             pi2->pDatatype = (LPWSTR)ptr;
4025             ptr += size;
4026             left -= size;
4027         } else
4028             space = FALSE;
4029         *pcbNeeded += size;
4030     }
4031     if(WINSPOOL_GetStringFromReg(hkeyPrinter, ParametersW, ptr, left,
4032                                  &size, unicode)) {
4033         if(space && size <= left) {
4034             pi2->pParameters = (LPWSTR)ptr;
4035             ptr += size;
4036             left -= size;
4037         } else
4038             space = FALSE;
4039         *pcbNeeded += size;
4040     }
4041     if(pi2) {
4042         pi2->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
4043         pi2->Priority = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Priority");
4044         pi2->DefaultPriority = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
4045                                                         "Default Priority");
4046         pi2->StartTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "StartTime");
4047         pi2->UntilTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "UntilTime");
4048     }
4049
4050     if(!space && pi2) /* zero out pi2 if we can't completely fill buf */
4051         memset(pi2, 0, sizeof(*pi2));
4052
4053     return space;
4054 }
4055
4056 /*********************************************************************
4057  *    WINSPOOL_GetPrinter_4
4058  *
4059  * Fills out a PRINTER_INFO_4 struct storing the strings in buf.
4060  */
4061 static BOOL WINSPOOL_GetPrinter_4(HKEY hkeyPrinter, PRINTER_INFO_4W *pi4,
4062                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
4063                                   BOOL unicode)
4064 {
4065     DWORD size, left = cbBuf;
4066     BOOL space = (cbBuf > 0);
4067     LPBYTE ptr = buf;
4068
4069     *pcbNeeded = 0;
4070
4071     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
4072                                  unicode)) {
4073         if(space && size <= left) {
4074             pi4->pPrinterName = (LPWSTR)ptr;
4075             ptr += size;
4076             left -= size;
4077         } else
4078             space = FALSE;
4079         *pcbNeeded += size;
4080     }
4081     if(pi4) {
4082         pi4->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
4083     }
4084
4085     if(!space && pi4) /* zero out pi4 if we can't completely fill buf */
4086         memset(pi4, 0, sizeof(*pi4));
4087
4088     return space;
4089 }
4090
4091 /*********************************************************************
4092  *    WINSPOOL_GetPrinter_5
4093  *
4094  * Fills out a PRINTER_INFO_5 struct storing the strings in buf.
4095  */
4096 static BOOL WINSPOOL_GetPrinter_5(HKEY hkeyPrinter, PRINTER_INFO_5W *pi5,
4097                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
4098                                   BOOL unicode)
4099 {
4100     DWORD size, left = cbBuf;
4101     BOOL space = (cbBuf > 0);
4102     LPBYTE ptr = buf;
4103
4104     *pcbNeeded = 0;
4105
4106     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
4107                                  unicode)) {
4108         if(space && size <= left) {
4109             pi5->pPrinterName = (LPWSTR)ptr;
4110             ptr += size;
4111             left -= size;
4112         } else
4113             space = FALSE;
4114         *pcbNeeded += size;
4115     }
4116     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
4117                                  unicode)) {
4118         if(space && size <= left) {
4119             pi5->pPortName = (LPWSTR)ptr;
4120             ptr += size;
4121             left -= size;
4122         } else
4123             space = FALSE;
4124         *pcbNeeded += size;
4125     }
4126     if(pi5) {
4127         pi5->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
4128         pi5->DeviceNotSelectedTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
4129                                                                 "dnsTimeout");
4130         pi5->TransmissionRetryTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
4131                                                                  "txTimeout");
4132     }
4133
4134     if(!space && pi5) /* zero out pi5 if we can't completely fill buf */
4135         memset(pi5, 0, sizeof(*pi5));
4136
4137     return space;
4138 }
4139
4140 /*********************************************************************
4141  *    WINSPOOL_GetPrinter_7
4142  *
4143  * Fills out a PRINTER_INFO_7 struct storing the strings in buf.
4144  */
4145 static BOOL WINSPOOL_GetPrinter_7(HKEY hkeyPrinter, PRINTER_INFO_7W *pi7, LPBYTE buf,
4146                                   DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
4147 {
4148     DWORD size, left = cbBuf;
4149     BOOL space = (cbBuf > 0);
4150     LPBYTE ptr = buf;
4151
4152     *pcbNeeded = 0;
4153
4154     if (WINSPOOL_GetStringFromReg(hkeyPrinter, ObjectGUIDW, ptr, left, &size, unicode))
4155     {
4156         if (space && size <= left) {
4157             pi7->pszObjectGUID = (LPWSTR)ptr;
4158             ptr += size;
4159             left -= size;
4160         } else
4161             space = FALSE;
4162         *pcbNeeded += size;
4163     }
4164     if (pi7) {
4165         /* We do not have a Directory Service */
4166         pi7->dwAction = DSPRINT_UNPUBLISH;
4167     }
4168
4169     if (!space && pi7) /* zero out pi7 if we can't completely fill buf */
4170         memset(pi7, 0, sizeof(*pi7));
4171
4172     return space;
4173 }
4174
4175 /*********************************************************************
4176  *    WINSPOOL_GetPrinter_9
4177  *
4178  * Fills out a PRINTER_INFO_9A|W struct storing the strings in buf.
4179  * The strings are either stored as unicode or ascii.
4180  */
4181 static BOOL WINSPOOL_GetPrinter_9(HKEY hkeyPrinter, PRINTER_INFO_9W *pi9, LPBYTE buf,
4182                                   DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
4183 {
4184     DWORD size;
4185     BOOL space = (cbBuf > 0);
4186
4187     *pcbNeeded = 0;
4188
4189     if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, buf, cbBuf, &size, unicode)) {
4190         if(space && size <= cbBuf) {
4191             pi9->pDevMode = (LPDEVMODEW)buf;
4192         } else
4193             space = FALSE;
4194         *pcbNeeded += size;
4195     }
4196     else
4197     {
4198         WINSPOOL_GetDefaultDevMode(buf, cbBuf, &size, unicode);
4199         if(space && size <= cbBuf) {
4200             pi9->pDevMode = (LPDEVMODEW)buf;
4201         } else
4202             space = FALSE;
4203         *pcbNeeded += size;
4204     }
4205
4206     if(!space && pi9) /* zero out pi9 if we can't completely fill buf */
4207         memset(pi9, 0, sizeof(*pi9));
4208
4209     return space;
4210 }
4211
4212 /*****************************************************************************
4213  *          WINSPOOL_GetPrinter
4214  *
4215  *    Implementation of GetPrinterA|W.  Relies on PRINTER_INFO_*W being
4216  *    essentially the same as PRINTER_INFO_*A. i.e. the structure itself is
4217  *    just a collection of pointers to strings.
4218  */
4219 static BOOL WINSPOOL_GetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
4220                                 DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
4221 {
4222     LPCWSTR name;
4223     DWORD size, needed = 0;
4224     LPBYTE ptr = NULL;
4225     HKEY hkeyPrinter, hkeyPrinters;
4226     BOOL ret;
4227
4228     TRACE("(%p,%d,%p,%d,%p)\n",hPrinter,Level,pPrinter,cbBuf, pcbNeeded);
4229
4230     if (!(name = get_opened_printer_name(hPrinter))) {
4231         SetLastError(ERROR_INVALID_HANDLE);
4232         return FALSE;
4233     }
4234
4235     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
4236        ERROR_SUCCESS) {
4237         ERR("Can't create Printers key\n");
4238         return FALSE;
4239     }
4240     if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter) != ERROR_SUCCESS)
4241     {
4242         ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
4243         RegCloseKey(hkeyPrinters);
4244         SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
4245         return FALSE;
4246     }
4247
4248     switch(Level) {
4249     case 2:
4250       {
4251         PRINTER_INFO_2W *pi2 = (PRINTER_INFO_2W *)pPrinter;
4252
4253         size = sizeof(PRINTER_INFO_2W);
4254         if(size <= cbBuf) {
4255             ptr = pPrinter + size;
4256             cbBuf -= size;
4257             memset(pPrinter, 0, size);
4258         } else {
4259             pi2 = NULL;
4260             cbBuf = 0;
4261         }
4262         ret = WINSPOOL_GetPrinter_2(hkeyPrinter, pi2, ptr, cbBuf, &needed,
4263                                     unicode);
4264         needed += size;
4265         break;
4266       }
4267
4268     case 4:
4269       {
4270         PRINTER_INFO_4W *pi4 = (PRINTER_INFO_4W *)pPrinter;
4271
4272         size = sizeof(PRINTER_INFO_4W);
4273         if(size <= cbBuf) {
4274             ptr = pPrinter + size;
4275             cbBuf -= size;
4276             memset(pPrinter, 0, size);
4277         } else {
4278             pi4 = NULL;
4279             cbBuf = 0;
4280         }
4281         ret = WINSPOOL_GetPrinter_4(hkeyPrinter, pi4, ptr, cbBuf, &needed,
4282                                     unicode);
4283         needed += size;
4284         break;
4285       }
4286
4287
4288     case 5:
4289       {
4290         PRINTER_INFO_5W *pi5 = (PRINTER_INFO_5W *)pPrinter;
4291
4292         size = sizeof(PRINTER_INFO_5W);
4293         if(size <= cbBuf) {
4294             ptr = pPrinter + size;
4295             cbBuf -= size;
4296             memset(pPrinter, 0, size);
4297         } else {
4298             pi5 = NULL;
4299             cbBuf = 0;
4300         }
4301
4302         ret = WINSPOOL_GetPrinter_5(hkeyPrinter, pi5, ptr, cbBuf, &needed,
4303                                     unicode);
4304         needed += size;
4305         break;
4306       }
4307
4308
4309     case 6:
4310       {
4311         PRINTER_INFO_6 *pi6 = (PRINTER_INFO_6 *) pPrinter;
4312
4313         size = sizeof(PRINTER_INFO_6);
4314         if (size <= cbBuf) {
4315             /* FIXME: We do not update the status yet */
4316             pi6->dwStatus = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Status");
4317             ret = TRUE;
4318         } else {
4319             ret = FALSE;
4320         }
4321
4322         needed += size;
4323         break;
4324       }
4325
4326     case 7:
4327       {
4328         PRINTER_INFO_7W *pi7 = (PRINTER_INFO_7W *) pPrinter;
4329
4330         size = sizeof(PRINTER_INFO_7W);
4331         if (size <= cbBuf) {
4332             ptr = pPrinter + size;
4333             cbBuf -= size;
4334             memset(pPrinter, 0, size);
4335         } else {
4336             pi7 = NULL;
4337             cbBuf = 0;
4338         }
4339
4340         ret = WINSPOOL_GetPrinter_7(hkeyPrinter, pi7, ptr, cbBuf, &needed, unicode);
4341         needed += size;
4342         break;
4343       }
4344
4345
4346     case 9:
4347       {
4348         PRINTER_INFO_9W *pi9 = (PRINTER_INFO_9W *)pPrinter;
4349
4350         size = sizeof(PRINTER_INFO_9W);
4351         if(size <= cbBuf) {
4352             ptr = pPrinter + size;
4353             cbBuf -= size;
4354             memset(pPrinter, 0, size);
4355         } else {
4356             pi9 = NULL;
4357             cbBuf = 0;
4358         }
4359
4360         ret = WINSPOOL_GetPrinter_9(hkeyPrinter, pi9, ptr, cbBuf, &needed, unicode);
4361         needed += size;
4362         break;
4363       }
4364
4365
4366     default:
4367         FIXME("Unimplemented level %d\n", Level);
4368         SetLastError(ERROR_INVALID_LEVEL);
4369         RegCloseKey(hkeyPrinters);
4370         RegCloseKey(hkeyPrinter);
4371         return FALSE;
4372     }
4373
4374     RegCloseKey(hkeyPrinter);
4375     RegCloseKey(hkeyPrinters);
4376
4377     TRACE("returning %d needed = %d\n", ret, needed);
4378     if(pcbNeeded) *pcbNeeded = needed;
4379     if(!ret)
4380         SetLastError(ERROR_INSUFFICIENT_BUFFER);
4381     return ret;
4382 }
4383
4384 /*****************************************************************************
4385  *          GetPrinterW  [WINSPOOL.@]
4386  */
4387 BOOL WINAPI GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
4388                         DWORD cbBuf, LPDWORD pcbNeeded)
4389 {
4390     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
4391                                TRUE);
4392 }
4393
4394 /*****************************************************************************
4395  *          GetPrinterA  [WINSPOOL.@]
4396  */
4397 BOOL WINAPI GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
4398                     DWORD cbBuf, LPDWORD pcbNeeded)
4399 {
4400     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
4401                                FALSE);
4402 }
4403
4404 /*****************************************************************************
4405  *          WINSPOOL_EnumPrinters
4406  *
4407  *    Implementation of EnumPrintersA|W
4408  */
4409 static BOOL WINSPOOL_EnumPrinters(DWORD dwType, LPWSTR lpszName,
4410                                   DWORD dwLevel, LPBYTE lpbPrinters,
4411                                   DWORD cbBuf, LPDWORD lpdwNeeded,
4412                                   LPDWORD lpdwReturned, BOOL unicode)
4413
4414 {
4415     HKEY hkeyPrinters, hkeyPrinter;
4416     WCHAR PrinterName[255];
4417     DWORD needed = 0, number = 0;
4418     DWORD used, i, left;
4419     PBYTE pi, buf;
4420
4421     if(lpbPrinters)
4422         memset(lpbPrinters, 0, cbBuf);
4423     if(lpdwReturned)
4424         *lpdwReturned = 0;
4425     if(lpdwNeeded)
4426         *lpdwNeeded = 0;
4427
4428     /* PRINTER_ENUM_DEFAULT is only supported under win9x, we behave like NT */
4429     if(dwType == PRINTER_ENUM_DEFAULT)
4430         return TRUE;
4431
4432     if (dwType & PRINTER_ENUM_CONNECTIONS) {
4433         TRACE("ignoring PRINTER_ENUM_CONNECTIONS\n");
4434         dwType &= ~PRINTER_ENUM_CONNECTIONS; /* we don't handle that */
4435         if (!dwType) {
4436             FIXME("We don't handle PRINTER_ENUM_CONNECTIONS\n");
4437             *lpdwNeeded = 0;
4438             *lpdwReturned = 0;
4439             return TRUE;
4440         }
4441
4442     }
4443
4444     if (!((dwType & PRINTER_ENUM_LOCAL) || (dwType & PRINTER_ENUM_NAME))) {
4445         FIXME("dwType = %08x\n", dwType);
4446         SetLastError(ERROR_INVALID_FLAGS);
4447         return FALSE;
4448     }
4449
4450     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
4451        ERROR_SUCCESS) {
4452         ERR("Can't create Printers key\n");
4453         return FALSE;
4454     }
4455
4456     if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &number, NULL, NULL,
4457                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
4458         RegCloseKey(hkeyPrinters);
4459         ERR("Can't query Printers key\n");
4460         return FALSE;
4461     }
4462     TRACE("Found %d printers\n", number);
4463
4464     switch(dwLevel) {
4465     case 1:
4466         used = number * sizeof(PRINTER_INFO_1W);
4467         break;
4468     case 2:
4469         used = number * sizeof(PRINTER_INFO_2W);
4470         break;
4471     case 4:
4472         used = number * sizeof(PRINTER_INFO_4W);
4473         break;
4474     case 5:
4475         used = number * sizeof(PRINTER_INFO_5W);
4476         break;
4477
4478     default:
4479         SetLastError(ERROR_INVALID_LEVEL);
4480         RegCloseKey(hkeyPrinters);
4481         return FALSE;
4482     }
4483     pi = (used <= cbBuf) ? lpbPrinters : NULL;
4484
4485     for(i = 0; i < number; i++) {
4486         if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)/sizeof(PrinterName[0])) !=
4487            ERROR_SUCCESS) {
4488             ERR("Can't enum key number %d\n", i);
4489             RegCloseKey(hkeyPrinters);
4490             return FALSE;
4491         }
4492         TRACE("Printer %d is %s\n", i, debugstr_w(PrinterName));
4493         if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkeyPrinter) !=
4494            ERROR_SUCCESS) {
4495             ERR("Can't open key %s\n", debugstr_w(PrinterName));
4496             RegCloseKey(hkeyPrinters);
4497             return FALSE;
4498         }
4499
4500         if(cbBuf > used) {
4501             buf = lpbPrinters + used;
4502             left = cbBuf - used;
4503         } else {
4504             buf = NULL;
4505             left = 0;
4506         }
4507
4508         switch(dwLevel) {
4509         case 1:
4510             WINSPOOL_GetPrinter_1(hkeyPrinter, (PRINTER_INFO_1W *)pi, buf,
4511                                   left, &needed, unicode);
4512             used += needed;
4513             if(pi) pi += sizeof(PRINTER_INFO_1W);
4514             break;
4515         case 2:
4516             WINSPOOL_GetPrinter_2(hkeyPrinter, (PRINTER_INFO_2W *)pi, buf,
4517                                   left, &needed, unicode);
4518             used += needed;
4519             if(pi) pi += sizeof(PRINTER_INFO_2W);
4520             break;
4521         case 4:
4522             WINSPOOL_GetPrinter_4(hkeyPrinter, (PRINTER_INFO_4W *)pi, buf,
4523                                   left, &needed, unicode);
4524             used += needed;
4525             if(pi) pi += sizeof(PRINTER_INFO_4W);
4526             break;
4527         case 5:
4528             WINSPOOL_GetPrinter_5(hkeyPrinter, (PRINTER_INFO_5W *)pi, buf,
4529                                   left, &needed, unicode);
4530             used += needed;
4531             if(pi) pi += sizeof(PRINTER_INFO_5W);
4532             break;
4533         default:
4534             ERR("Shouldn't be here!\n");
4535             RegCloseKey(hkeyPrinter);
4536             RegCloseKey(hkeyPrinters);
4537             return FALSE;
4538         }
4539         RegCloseKey(hkeyPrinter);
4540     }
4541     RegCloseKey(hkeyPrinters);
4542
4543     if(lpdwNeeded)
4544         *lpdwNeeded = used;
4545
4546     if(used > cbBuf) {
4547         if(lpbPrinters)
4548             memset(lpbPrinters, 0, cbBuf);
4549         SetLastError(ERROR_INSUFFICIENT_BUFFER);
4550         return FALSE;
4551     }
4552     if(lpdwReturned)
4553         *lpdwReturned = number;
4554     SetLastError(ERROR_SUCCESS);
4555     return TRUE;
4556 }
4557
4558
4559 /******************************************************************
4560  *              EnumPrintersW        [WINSPOOL.@]
4561  *
4562  *    Enumerates the available printers, print servers and print
4563  *    providers, depending on the specified flags, name and level.
4564  *
4565  * RETURNS:
4566  *
4567  *    If level is set to 1:
4568  *      Returns an array of PRINTER_INFO_1 data structures in the
4569  *      lpbPrinters buffer.
4570  *
4571  *    If level is set to 2:
4572  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
4573  *      Returns an array of PRINTER_INFO_2 data structures in the
4574  *      lpbPrinters buffer. Note that according to MSDN also an
4575  *      OpenPrinter should be performed on every remote printer.
4576  *
4577  *    If level is set to 4 (officially WinNT only):
4578  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
4579  *      Fast: Only the registry is queried to retrieve printer names,
4580  *      no connection to the driver is made.
4581  *      Returns an array of PRINTER_INFO_4 data structures in the
4582  *      lpbPrinters buffer.
4583  *
4584  *    If level is set to 5 (officially WinNT4/Win9x only):
4585  *      Fast: Only the registry is queried to retrieve printer names,
4586  *      no connection to the driver is made.
4587  *      Returns an array of PRINTER_INFO_5 data structures in the
4588  *      lpbPrinters buffer.
4589  *
4590  *    If level set to 3 or 6+:
4591  *          returns zero (failure!)
4592  *
4593  *    Returns nonzero (TRUE) on success, or zero on failure, use GetLastError
4594  *    for information.
4595  *
4596  * BUGS:
4597  *    - Only PRINTER_ENUM_LOCAL and PRINTER_ENUM_NAME are implemented.
4598  *    - Only levels 2, 4 and 5 are implemented at the moment.
4599  *    - 16-bit printer drivers are not enumerated.
4600  *    - Returned amount of bytes used/needed does not match the real Windoze
4601  *      implementation (as in this implementation, all strings are part
4602  *      of the buffer, whereas Win32 keeps them somewhere else)
4603  *    - At level 2, EnumPrinters should also call OpenPrinter for remote printers.
4604  *
4605  * NOTE:
4606  *    - In a regular Wine installation, no registry settings for printers
4607  *      exist, which makes this function return an empty list.
4608  */
4609 BOOL  WINAPI EnumPrintersW(
4610                 DWORD dwType,        /* [in] Types of print objects to enumerate */
4611                 LPWSTR lpszName,     /* [in] name of objects to enumerate */
4612                 DWORD dwLevel,       /* [in] type of printer info structure */
4613                 LPBYTE lpbPrinters,  /* [out] buffer which receives info */
4614                 DWORD cbBuf,         /* [in] max size of buffer in bytes */
4615                 LPDWORD lpdwNeeded,  /* [out] pointer to var: # bytes used/needed */
4616                 LPDWORD lpdwReturned /* [out] number of entries returned */
4617                 )
4618 {
4619     return WINSPOOL_EnumPrinters(dwType, lpszName, dwLevel, lpbPrinters, cbBuf,
4620                                  lpdwNeeded, lpdwReturned, TRUE);
4621 }
4622
4623 /******************************************************************
4624  * EnumPrintersA    [WINSPOOL.@]
4625  *
4626  * See EnumPrintersW
4627  *
4628  */
4629 BOOL WINAPI EnumPrintersA(DWORD flags, LPSTR pName, DWORD level, LPBYTE pPrinters,
4630                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
4631 {
4632     BOOL ret;
4633     UNICODE_STRING pNameU;
4634     LPWSTR pNameW;
4635     LPBYTE pPrintersW;
4636
4637     TRACE("(0x%x, %s, %u, %p, %d, %p, %p)\n", flags, debugstr_a(pName), level,
4638                                               pPrinters, cbBuf, pcbNeeded, pcReturned);
4639
4640     pNameW = asciitounicode(&pNameU, pName);
4641
4642     /* Request a buffer with a size, that is big enough for EnumPrintersW.
4643        MS Office need this */
4644     pPrintersW = (pPrinters && cbBuf) ? HeapAlloc(GetProcessHeap(), 0, cbBuf) : NULL;
4645
4646     ret = EnumPrintersW(flags, pNameW, level, pPrintersW, cbBuf, pcbNeeded, pcReturned);
4647
4648     RtlFreeUnicodeString(&pNameU);
4649     if (ret) {
4650         convert_printerinfo_W_to_A(pPrinters, pPrintersW, level, *pcbNeeded, *pcReturned);
4651     }
4652     HeapFree(GetProcessHeap(), 0, pPrintersW);
4653     return ret;
4654 }
4655
4656 /*****************************************************************************
4657  *          WINSPOOL_GetDriverInfoFromReg [internal]
4658  *
4659  *    Enters the information from the registry into the DRIVER_INFO struct
4660  *
4661  * RETURNS
4662  *    zero if the printer driver does not exist in the registry
4663  *    (only if Level > 1) otherwise nonzero
4664  */
4665 static BOOL WINSPOOL_GetDriverInfoFromReg(
4666                             HKEY    hkeyDrivers,
4667                             LPWSTR  DriverName,
4668                             const printenv_t * env,
4669                             DWORD   Level,
4670                             LPBYTE  ptr,            /* DRIVER_INFO */
4671                             LPBYTE  pDriverStrings, /* strings buffer */
4672                             DWORD   cbBuf,          /* size of string buffer */
4673                             LPDWORD pcbNeeded,      /* space needed for str. */
4674                             BOOL    unicode)        /* type of strings */
4675 {
4676     DWORD  size, tmp;
4677     HKEY   hkeyDriver;
4678     WCHAR  driverdir[MAX_PATH];
4679     DWORD  dirlen;
4680     LPBYTE strPtr = pDriverStrings;
4681     LPDRIVER_INFO_8W di = (LPDRIVER_INFO_8W) ptr;
4682
4683     TRACE("(%p, %s, %p, %d, %p, %p, %d, %d)\n", hkeyDrivers,
4684           debugstr_w(DriverName), env,
4685           Level, di, pDriverStrings, cbBuf, unicode);
4686
4687     if (di) ZeroMemory(di, di_sizeof[Level]);
4688
4689     if (unicode) {
4690         *pcbNeeded = (lstrlenW(DriverName) + 1) * sizeof(WCHAR);
4691         if (*pcbNeeded <= cbBuf)
4692            strcpyW((LPWSTR)strPtr, DriverName);
4693     }
4694     else
4695     {
4696         *pcbNeeded = WideCharToMultiByte(CP_ACP, 0, DriverName, -1, NULL, 0, NULL, NULL);
4697         if (*pcbNeeded <= cbBuf)
4698             WideCharToMultiByte(CP_ACP, 0, DriverName, -1, (LPSTR)strPtr, *pcbNeeded, NULL, NULL);
4699     }
4700
4701     /* pName for level 1 has a different offset! */
4702     if (Level == 1) {
4703        if (di) ((LPDRIVER_INFO_1W) di)->pName = (LPWSTR) strPtr;
4704        return TRUE;
4705     }
4706
4707     /* .cVersion and .pName for level > 1 */
4708     if (di) {
4709         di->cVersion = env->driverversion;
4710         di->pName = (LPWSTR) strPtr;
4711         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4712     }
4713
4714     /* Reserve Space for the largest subdir and a Backslash*/
4715     size = sizeof(driverdir) - sizeof(Version3_SubdirW) - sizeof(WCHAR);
4716     if (!GetPrinterDriverDirectoryW(NULL, (LPWSTR) env->envname, 1, (LPBYTE) driverdir, size, &size)) {
4717         /* Should never Fail */
4718         return FALSE;
4719     }
4720     lstrcatW(driverdir, env->versionsubdir);
4721     lstrcatW(driverdir, backslashW);
4722
4723     /* dirlen must not include the terminating zero */
4724     dirlen = (unicode) ? lstrlenW(driverdir) * sizeof(WCHAR) :
4725              WideCharToMultiByte(CP_ACP, 0, driverdir, -1, NULL, 0, NULL, NULL) -1;
4726
4727     if (!DriverName[0] || RegOpenKeyW(hkeyDrivers, DriverName, &hkeyDriver) != ERROR_SUCCESS) {
4728         ERR("Can't find driver %s in registry\n", debugstr_w(DriverName));
4729         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER); /* ? */
4730         return FALSE;
4731     }
4732
4733     /* pEnvironment */
4734     if (unicode)
4735         size = (lstrlenW(env->envname) + 1) * sizeof(WCHAR);
4736     else
4737         size = WideCharToMultiByte(CP_ACP, 0, env->envname, -1, NULL, 0, NULL, NULL);
4738
4739     *pcbNeeded += size;
4740     if (*pcbNeeded <= cbBuf) {
4741         if (unicode) {
4742             lstrcpyW((LPWSTR)strPtr, env->envname);
4743         }
4744         else
4745         {
4746             WideCharToMultiByte(CP_ACP, 0, env->envname, -1, (LPSTR)strPtr, size, NULL, NULL);
4747         }
4748         if (di) di->pEnvironment = (LPWSTR)strPtr;
4749         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4750     }
4751
4752     /* .pDriverPath is the Graphics rendering engine.
4753         The full Path is required to avoid a crash in some apps */
4754     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, DriverW, strPtr, 0, &size, unicode)) {
4755         *pcbNeeded += size;
4756         if (*pcbNeeded <= cbBuf)
4757             get_filename_from_reg(hkeyDriver, driverdir, dirlen, DriverW, strPtr, size, &tmp, unicode);
4758
4759         if (di) di->pDriverPath = (LPWSTR)strPtr;
4760         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4761     }
4762
4763     /* .pDataFile: For postscript-drivers, this is the ppd-file */
4764     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Data_FileW, strPtr, 0, &size, unicode)) {
4765         *pcbNeeded += size;
4766         if (*pcbNeeded <= cbBuf)
4767             get_filename_from_reg(hkeyDriver, driverdir, dirlen, Data_FileW, strPtr, size, &size, unicode);
4768
4769         if (di) di->pDataFile = (LPWSTR)strPtr;
4770         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4771     }
4772
4773     /* .pConfigFile is the Driver user Interface */
4774     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Configuration_FileW, strPtr, 0, &size, unicode)) {
4775         *pcbNeeded += size;
4776         if (*pcbNeeded <= cbBuf)
4777             get_filename_from_reg(hkeyDriver, driverdir, dirlen, Configuration_FileW, strPtr, size, &size, unicode);
4778
4779         if (di) di->pConfigFile = (LPWSTR)strPtr;
4780         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4781     }
4782
4783     if (Level == 2 ) {
4784         RegCloseKey(hkeyDriver);
4785         TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4786         return TRUE;
4787     }
4788
4789     if (Level == 5 ) {
4790         RegCloseKey(hkeyDriver);
4791         FIXME("level 5: incomplete\n");
4792         return TRUE;
4793     }
4794
4795     /* .pHelpFile */
4796     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Help_FileW, strPtr, 0, &size, unicode)) {
4797         *pcbNeeded += size;
4798         if (*pcbNeeded <= cbBuf)
4799             get_filename_from_reg(hkeyDriver, driverdir, dirlen, Help_FileW, strPtr, size, &size, unicode);
4800
4801         if (di) di->pHelpFile = (LPWSTR)strPtr;
4802         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4803     }
4804
4805     /* .pDependentFiles */
4806     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Dependent_FilesW, strPtr, 0, &size, unicode)) {
4807         *pcbNeeded += size;
4808         if (*pcbNeeded <= cbBuf)
4809             get_filename_from_reg(hkeyDriver, driverdir, dirlen, Dependent_FilesW, strPtr, size, &size, unicode);
4810
4811         if (di) di->pDependentFiles = (LPWSTR)strPtr;
4812         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4813     }
4814     else if (GetVersion() & 0x80000000) {
4815         /* PowerPoint XP expects that pDependentFiles is always valid on win9x */
4816         size = 2 * ((unicode) ? sizeof(WCHAR) : 1);
4817         *pcbNeeded += size;
4818         if ((*pcbNeeded <= cbBuf) && strPtr) ZeroMemory(strPtr, size);
4819
4820         if (di) di->pDependentFiles = (LPWSTR)strPtr;
4821         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4822     }
4823
4824     /* .pMonitorName is the optional Language Monitor */
4825     if (WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, 0, &size, unicode)) {
4826         *pcbNeeded += size;
4827         if (*pcbNeeded <= cbBuf)
4828             WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, size, &size, unicode);
4829
4830         if (di) di->pMonitorName = (LPWSTR)strPtr;
4831         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4832     }
4833
4834     /* .pDefaultDataType */
4835     if (WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, 0, &size, unicode)) {
4836         *pcbNeeded += size;
4837         if(*pcbNeeded <= cbBuf)
4838             WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, size, &size, unicode);
4839
4840         if (di) di->pDefaultDataType = (LPWSTR)strPtr;
4841         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4842     }
4843
4844     if (Level == 3 ) {
4845         RegCloseKey(hkeyDriver);
4846         TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4847         return TRUE;
4848     }
4849
4850     /* .pszzPreviousNames */
4851     if (WINSPOOL_GetStringFromReg(hkeyDriver, Previous_NamesW, strPtr, 0, &size, unicode)) {
4852         *pcbNeeded += size;
4853         if(*pcbNeeded <= cbBuf)
4854             WINSPOOL_GetStringFromReg(hkeyDriver, Previous_NamesW, strPtr, size, &size, unicode);
4855
4856         if (di) di->pszzPreviousNames = (LPWSTR)strPtr;
4857         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4858     }
4859
4860     if (Level == 4 ) {
4861         RegCloseKey(hkeyDriver);
4862         TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4863         return TRUE;
4864     }
4865
4866     /* support is missing, but not important enough for a FIXME */
4867     TRACE("%s: DriverDate + DriverVersion not supported\n", debugstr_w(DriverName));
4868
4869     /* .pszMfgName */
4870     if (WINSPOOL_GetStringFromReg(hkeyDriver, ManufacturerW, strPtr, 0, &size, unicode)) {
4871         *pcbNeeded += size;
4872         if(*pcbNeeded <= cbBuf)
4873             WINSPOOL_GetStringFromReg(hkeyDriver, ManufacturerW, strPtr, size, &size, unicode);
4874
4875         if (di) di->pszMfgName = (LPWSTR)strPtr;
4876         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4877     }
4878
4879     /* .pszOEMUrl */
4880     if (WINSPOOL_GetStringFromReg(hkeyDriver, OEM_UrlW, strPtr, 0, &size, unicode)) {
4881         *pcbNeeded += size;
4882         if(*pcbNeeded <= cbBuf)
4883             WINSPOOL_GetStringFromReg(hkeyDriver, OEM_UrlW, strPtr, size, &size, unicode);
4884
4885         if (di) di->pszOEMUrl = (LPWSTR)strPtr;
4886         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4887     }
4888
4889     /* .pszHardwareID */
4890     if (WINSPOOL_GetStringFromReg(hkeyDriver, HardwareIDW, strPtr, 0, &size, unicode)) {
4891         *pcbNeeded += size;
4892         if(*pcbNeeded <= cbBuf)
4893             WINSPOOL_GetStringFromReg(hkeyDriver, HardwareIDW, strPtr, size, &size, unicode);
4894
4895         if (di) di->pszHardwareID = (LPWSTR)strPtr;
4896         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4897     }
4898
4899     /* .pszProvider */
4900     if (WINSPOOL_GetStringFromReg(hkeyDriver, ProviderW, strPtr, 0, &size, unicode)) {
4901         *pcbNeeded += size;
4902         if(*pcbNeeded <= cbBuf)
4903             WINSPOOL_GetStringFromReg(hkeyDriver, ProviderW, strPtr, size, &size, unicode);
4904
4905         if (di) di->pszProvider = (LPWSTR)strPtr;
4906         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4907     }
4908
4909     if (Level == 6 ) {
4910         RegCloseKey(hkeyDriver);
4911         return TRUE;
4912     }
4913
4914     /* support is missing, but not important enough for a FIXME */
4915     TRACE("level 8: incomplete\n");
4916
4917     TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4918     RegCloseKey(hkeyDriver);
4919     return TRUE;
4920 }
4921
4922 /*****************************************************************************
4923  *          WINSPOOL_GetPrinterDriver
4924  */
4925 static BOOL WINSPOOL_GetPrinterDriver(HANDLE hPrinter, LPCWSTR pEnvironment,
4926                                       DWORD Level, LPBYTE pDriverInfo,
4927                                       DWORD cbBuf, LPDWORD pcbNeeded,
4928                                       BOOL unicode)
4929 {
4930     LPCWSTR name;
4931     WCHAR DriverName[100];
4932     DWORD ret, type, size, needed = 0;
4933     LPBYTE ptr = NULL;
4934     HKEY hkeyPrinter, hkeyPrinters, hkeyDrivers;
4935     const printenv_t * env;
4936
4937     TRACE("(%p,%s,%d,%p,%d,%p)\n",hPrinter,debugstr_w(pEnvironment),
4938           Level,pDriverInfo,cbBuf, pcbNeeded);
4939
4940
4941     if (!(name = get_opened_printer_name(hPrinter))) {
4942         SetLastError(ERROR_INVALID_HANDLE);
4943         return FALSE;
4944     }
4945
4946     if (Level < 1 || Level == 7 || Level > 8) {
4947         SetLastError(ERROR_INVALID_LEVEL);
4948         return FALSE;
4949     }
4950
4951     env = validate_envW(pEnvironment);
4952     if (!env) return FALSE;     /* SetLastError() is in validate_envW */
4953
4954     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
4955        ERROR_SUCCESS) {
4956         ERR("Can't create Printers key\n");
4957         return FALSE;
4958     }
4959     if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter)
4960        != ERROR_SUCCESS) {
4961         ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
4962         RegCloseKey(hkeyPrinters);
4963         SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
4964         return FALSE;
4965     }
4966     size = sizeof(DriverName);
4967     DriverName[0] = 0;
4968     ret = RegQueryValueExW(hkeyPrinter, Printer_DriverW, 0, &type,
4969                            (LPBYTE)DriverName, &size);
4970     RegCloseKey(hkeyPrinter);
4971     RegCloseKey(hkeyPrinters);
4972     if(ret != ERROR_SUCCESS) {
4973         ERR("Can't get DriverName for printer %s\n", debugstr_w(name));
4974         return FALSE;
4975     }
4976
4977     hkeyDrivers = WINSPOOL_OpenDriverReg( pEnvironment, TRUE);
4978     if(!hkeyDrivers) {
4979         ERR("Can't create Drivers key\n");
4980         return FALSE;
4981     }
4982
4983     size = di_sizeof[Level];
4984     if ((size <= cbBuf) && pDriverInfo)
4985         ptr = pDriverInfo + size;
4986
4987     if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverName,
4988                          env, Level, pDriverInfo, ptr,
4989                          (cbBuf < size) ? 0 : cbBuf - size,
4990                          &needed, unicode)) {
4991             RegCloseKey(hkeyDrivers);
4992             return FALSE;
4993     }
4994
4995     RegCloseKey(hkeyDrivers);
4996
4997     if(pcbNeeded) *pcbNeeded = size + needed;
4998     TRACE("buffer space %d required %d\n", cbBuf, size + needed);
4999     if(cbBuf >= needed) return TRUE;
5000     SetLastError(ERROR_INSUFFICIENT_BUFFER);
5001     return FALSE;
5002 }
5003
5004 /*****************************************************************************
5005  *          GetPrinterDriverA  [WINSPOOL.@]
5006  */
5007 BOOL WINAPI GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment,
5008                               DWORD Level, LPBYTE pDriverInfo,
5009                               DWORD cbBuf, LPDWORD pcbNeeded)
5010 {
5011     BOOL ret;
5012     UNICODE_STRING pEnvW;
5013     PWSTR pwstrEnvW;
5014     
5015     pwstrEnvW = asciitounicode(&pEnvW, pEnvironment);
5016     ret = WINSPOOL_GetPrinterDriver(hPrinter, pwstrEnvW, Level, pDriverInfo,
5017                                     cbBuf, pcbNeeded, FALSE);
5018     RtlFreeUnicodeString(&pEnvW);
5019     return ret;
5020 }
5021 /*****************************************************************************
5022  *          GetPrinterDriverW  [WINSPOOL.@]
5023  */
5024 BOOL WINAPI GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment,
5025                                   DWORD Level, LPBYTE pDriverInfo,
5026                                   DWORD cbBuf, LPDWORD pcbNeeded)
5027 {
5028     return WINSPOOL_GetPrinterDriver(hPrinter, pEnvironment, Level,
5029                                      pDriverInfo, cbBuf, pcbNeeded, TRUE);
5030 }
5031
5032 /*****************************************************************************
5033  *       GetPrinterDriverDirectoryW  [WINSPOOL.@]
5034  *
5035  * Return the PATH for the Printer-Drivers (UNICODE)
5036  *
5037  * PARAMS
5038  *   pName            [I] Servername (NT only) or NULL (local Computer)
5039  *   pEnvironment     [I] Printing-Environment (see below) or NULL (Default)
5040  *   Level            [I] Structure-Level (must be 1)
5041  *   pDriverDirectory [O] PTR to Buffer that receives the Result
5042  *   cbBuf            [I] Size of Buffer at pDriverDirectory
5043  *   pcbNeeded        [O] PTR to DWORD that receives the size in Bytes used / 
5044  *                        required for pDriverDirectory
5045  *
5046  * RETURNS
5047  *   Success: TRUE  and in pcbNeeded the Bytes used in pDriverDirectory
5048  *   Failure: FALSE and in pcbNeeded the Bytes required for pDriverDirectory,
5049  *   if cbBuf is too small
5050  * 
5051  *   Native Values returned in pDriverDirectory on Success:
5052  *|  NT(Windows NT x86):  "%winsysdir%\\spool\\DRIVERS\\w32x86" 
5053  *|  NT(Windows 4.0):     "%winsysdir%\\spool\\DRIVERS\\win40" 
5054  *|  win9x(Windows 4.0):  "%winsysdir%" 
5055  *
5056  *   "%winsysdir%" is the Value from GetSystemDirectoryW()
5057  *
5058  * FIXME
5059  *-  Only NULL or "" is supported for pName
5060  *
5061  */
5062 BOOL WINAPI GetPrinterDriverDirectoryW(LPWSTR pName, LPWSTR pEnvironment,
5063                                        DWORD Level, LPBYTE pDriverDirectory,
5064                                        DWORD cbBuf, LPDWORD pcbNeeded)
5065 {
5066     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName), 
5067           debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
5068
5069     if ((backend == NULL)  && !load_backend()) return FALSE;
5070
5071     if (Level != 1) {
5072         /* (Level != 1) is ignored in win9x */
5073         SetLastError(ERROR_INVALID_LEVEL);
5074         return FALSE;
5075     }
5076     if (pcbNeeded == NULL) {
5077         /* (pcbNeeded == NULL) is ignored in win9x */
5078         SetLastError(RPC_X_NULL_REF_POINTER);
5079         return FALSE;
5080     }
5081
5082     return backend->fpGetPrinterDriverDirectory(pName, pEnvironment, Level,
5083                                                 pDriverDirectory, cbBuf, pcbNeeded);
5084
5085 }
5086
5087
5088 /*****************************************************************************
5089  *       GetPrinterDriverDirectoryA  [WINSPOOL.@]
5090  *
5091  * Return the PATH for the Printer-Drivers (ANSI)
5092  *
5093  * See GetPrinterDriverDirectoryW.
5094  *
5095  * NOTES
5096  * On NT, pDriverDirectory need the same Size as the Unicode-Version
5097  *
5098  */
5099 BOOL WINAPI GetPrinterDriverDirectoryA(LPSTR pName, LPSTR pEnvironment,
5100                                        DWORD Level, LPBYTE pDriverDirectory,
5101                                        DWORD cbBuf, LPDWORD pcbNeeded)
5102 {
5103     UNICODE_STRING nameW, environmentW;
5104     BOOL ret;
5105     DWORD pcbNeededW;
5106     INT len = cbBuf * sizeof(WCHAR)/sizeof(CHAR);
5107     WCHAR *driverDirectoryW = NULL;
5108
5109     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_a(pName), 
5110           debugstr_a(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
5111  
5112     if (len) driverDirectoryW = HeapAlloc( GetProcessHeap(), 0, len );
5113
5114     if(pName) RtlCreateUnicodeStringFromAsciiz(&nameW, pName);
5115     else nameW.Buffer = NULL;
5116     if(pEnvironment) RtlCreateUnicodeStringFromAsciiz(&environmentW, pEnvironment);
5117     else environmentW.Buffer = NULL;
5118
5119     ret = GetPrinterDriverDirectoryW( nameW.Buffer, environmentW.Buffer, Level,
5120                                       (LPBYTE)driverDirectoryW, len, &pcbNeededW );
5121     if (ret) {
5122         DWORD needed;
5123         needed =  WideCharToMultiByte( CP_ACP, 0, driverDirectoryW, -1, 
5124                                    (LPSTR)pDriverDirectory, cbBuf, NULL, NULL);
5125         if(pcbNeeded)
5126             *pcbNeeded = needed;
5127         ret = (needed <= cbBuf) ? TRUE : FALSE;
5128     } else 
5129         if(pcbNeeded) *pcbNeeded = pcbNeededW * sizeof(CHAR)/sizeof(WCHAR);
5130
5131     TRACE("required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
5132
5133     HeapFree( GetProcessHeap(), 0, driverDirectoryW );
5134     RtlFreeUnicodeString(&environmentW);
5135     RtlFreeUnicodeString(&nameW);
5136
5137     return ret;
5138 }
5139
5140 /*****************************************************************************
5141  *          AddPrinterDriverA  [WINSPOOL.@]
5142  *
5143  * See AddPrinterDriverW.
5144  *
5145  */
5146 BOOL WINAPI AddPrinterDriverA(LPSTR pName, DWORD level, LPBYTE pDriverInfo)
5147 {
5148     TRACE("(%s, %d, %p)\n", debugstr_a(pName), level, pDriverInfo);
5149     return AddPrinterDriverExA(pName, level, pDriverInfo, APD_COPY_NEW_FILES);
5150 }
5151
5152 /******************************************************************************
5153  *  AddPrinterDriverW (WINSPOOL.@)
5154  *
5155  * Install a Printer Driver
5156  *
5157  * PARAMS
5158  *  pName           [I] Servername or NULL (local Computer)
5159  *  level           [I] Level for the supplied DRIVER_INFO_*W struct
5160  *  pDriverInfo     [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter
5161  *
5162  * RESULTS
5163  *  Success: TRUE
5164  *  Failure: FALSE
5165  *
5166  */
5167 BOOL WINAPI AddPrinterDriverW(LPWSTR pName, DWORD level, LPBYTE pDriverInfo)
5168 {
5169     TRACE("(%s, %d, %p)\n", debugstr_w(pName), level, pDriverInfo);
5170     return AddPrinterDriverExW(pName, level, pDriverInfo, APD_COPY_NEW_FILES);
5171 }
5172
5173 /*****************************************************************************
5174  *          AddPrintProcessorA  [WINSPOOL.@]
5175  */
5176 BOOL WINAPI AddPrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPathName,
5177                                LPSTR pPrintProcessorName)
5178 {
5179     FIXME("(%s,%s,%s,%s): stub\n", debugstr_a(pName), debugstr_a(pEnvironment),
5180           debugstr_a(pPathName), debugstr_a(pPrintProcessorName));
5181     return FALSE;
5182 }
5183
5184 /*****************************************************************************
5185  *          AddPrintProcessorW  [WINSPOOL.@]
5186  */
5187 BOOL WINAPI AddPrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPathName,
5188                                LPWSTR pPrintProcessorName)
5189 {
5190     FIXME("(%s,%s,%s,%s): stub\n", debugstr_w(pName), debugstr_w(pEnvironment),
5191           debugstr_w(pPathName), debugstr_w(pPrintProcessorName));
5192     return FALSE;
5193 }
5194
5195 /*****************************************************************************
5196  *          AddPrintProvidorA  [WINSPOOL.@]
5197  */
5198 BOOL WINAPI AddPrintProvidorA(LPSTR pName, DWORD Level, LPBYTE pProviderInfo)
5199 {
5200     FIXME("(%s,0x%08x,%p): stub\n", debugstr_a(pName), Level, pProviderInfo);
5201     return FALSE;
5202 }
5203
5204 /*****************************************************************************
5205  *          AddPrintProvidorW  [WINSPOOL.@]
5206  */
5207 BOOL WINAPI AddPrintProvidorW(LPWSTR pName, DWORD Level, LPBYTE pProviderInfo)
5208 {
5209     FIXME("(%s,0x%08x,%p): stub\n", debugstr_w(pName), Level, pProviderInfo);
5210     return FALSE;
5211 }
5212
5213 /*****************************************************************************
5214  *          AdvancedDocumentPropertiesA  [WINSPOOL.@]
5215  */
5216 LONG WINAPI AdvancedDocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName,
5217                                         PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput)
5218 {
5219     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_a(pDeviceName),
5220           pDevModeOutput, pDevModeInput);
5221     return 0;
5222 }
5223
5224 /*****************************************************************************
5225  *          AdvancedDocumentPropertiesW  [WINSPOOL.@]
5226  */
5227 LONG WINAPI AdvancedDocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName,
5228                                         PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput)
5229 {
5230     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_w(pDeviceName),
5231           pDevModeOutput, pDevModeInput);
5232     return 0;
5233 }
5234
5235 /*****************************************************************************
5236  *          PrinterProperties  [WINSPOOL.@]
5237  *
5238  *     Displays a dialog to set the properties of the printer.
5239  *
5240  * RETURNS
5241  *     nonzero on success or zero on failure
5242  *
5243  * BUGS
5244  *         implemented as stub only
5245  */
5246 BOOL WINAPI PrinterProperties(HWND hWnd,      /* [in] handle to parent window */
5247                               HANDLE hPrinter /* [in] handle to printer object */
5248 ){
5249     FIXME("(%p,%p): stub\n", hWnd, hPrinter);
5250     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5251     return FALSE;
5252 }
5253
5254 /*****************************************************************************
5255  *          EnumJobsA [WINSPOOL.@]
5256  *
5257  */
5258 BOOL WINAPI EnumJobsA(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
5259                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
5260                       LPDWORD pcReturned)
5261 {
5262     FIXME("(%p,first=%d,no=%d,level=%d,job=%p,cb=%d,%p,%p), stub!\n",
5263         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
5264     );
5265     if(pcbNeeded) *pcbNeeded = 0;
5266     if(pcReturned) *pcReturned = 0;
5267     return FALSE;
5268 }
5269
5270
5271 /*****************************************************************************
5272  *          EnumJobsW [WINSPOOL.@]
5273  *
5274  */
5275 BOOL WINAPI EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
5276                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
5277                       LPDWORD pcReturned)
5278 {
5279     FIXME("(%p,first=%d,no=%d,level=%d,job=%p,cb=%d,%p,%p), stub!\n",
5280         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
5281     );
5282     if(pcbNeeded) *pcbNeeded = 0;
5283     if(pcReturned) *pcReturned = 0;
5284     return FALSE;
5285 }
5286
5287 /*****************************************************************************
5288  *          WINSPOOL_EnumPrinterDrivers [internal]
5289  *
5290  *    Delivers information about all printer drivers installed on the
5291  *    localhost or a given server
5292  *
5293  * RETURNS
5294  *    nonzero on success or zero on failure. If the buffer for the returned
5295  *    information is too small the function will return an error
5296  *
5297  * BUGS
5298  *    - only implemented for localhost, foreign hosts will return an error
5299  */
5300 static BOOL WINSPOOL_EnumPrinterDrivers(LPWSTR pName, LPCWSTR pEnvironment,
5301                                         DWORD Level, LPBYTE pDriverInfo,
5302                                         DWORD cbBuf, LPDWORD pcbNeeded,
5303                                         LPDWORD pcReturned, BOOL unicode)
5304
5305 {   HKEY  hkeyDrivers;
5306     DWORD i, needed, number = 0, size = 0;
5307     WCHAR DriverNameW[255];
5308     PBYTE ptr;
5309     const printenv_t * env;
5310
5311     TRACE("%s,%s,%d,%p,%d,%d\n",
5312           debugstr_w(pName), debugstr_w(pEnvironment),
5313           Level, pDriverInfo, cbBuf, unicode);
5314
5315     /* check for local drivers */
5316     if((pName) && (pName[0])) {
5317         FIXME("remote drivers (%s) not supported!\n", debugstr_w(pName));
5318         SetLastError(ERROR_ACCESS_DENIED);
5319         return FALSE;
5320     }
5321
5322     env = validate_envW(pEnvironment);
5323     if (!env) return FALSE;     /* SetLastError() is in validate_envW */
5324
5325     /* check input parameter */
5326     if ((Level < 1) || (Level == 7) || (Level > 8)) {
5327         SetLastError(ERROR_INVALID_LEVEL);
5328         return FALSE;
5329     }
5330
5331     if ((pcbNeeded == NULL) || (pcReturned == NULL)) {
5332         SetLastError(RPC_X_NULL_REF_POINTER);
5333         return FALSE;
5334     }
5335
5336     /* initialize return values */
5337     if(pDriverInfo)
5338         memset( pDriverInfo, 0, cbBuf);
5339     *pcbNeeded  = 0;
5340     *pcReturned = 0;
5341
5342     hkeyDrivers = WINSPOOL_OpenDriverReg(pEnvironment, TRUE);
5343     if(!hkeyDrivers) {
5344         ERR("Can't open Drivers key\n");
5345         return FALSE;
5346     }
5347
5348     if(RegQueryInfoKeyA(hkeyDrivers, NULL, NULL, NULL, &number, NULL, NULL,
5349                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
5350         RegCloseKey(hkeyDrivers);
5351         ERR("Can't query Drivers key\n");
5352         return FALSE;
5353     }
5354     TRACE("Found %d Drivers\n", number);
5355
5356     /* get size of single struct
5357      * unicode and ascii structure have the same size
5358      */
5359     size = di_sizeof[Level];
5360
5361     /* calculate required buffer size */
5362     *pcbNeeded = size * number;
5363
5364     for( i = 0,  ptr = (pDriverInfo && (cbBuf >= size)) ? pDriverInfo : NULL ;
5365          i < number;
5366          i++, ptr = (ptr && (cbBuf >= size * i)) ? ptr + size : NULL) {
5367         if(RegEnumKeyW(hkeyDrivers, i, DriverNameW, sizeof(DriverNameW)/sizeof(DriverNameW[0]))
5368                        != ERROR_SUCCESS) {
5369             ERR("Can't enum key number %d\n", i);
5370             RegCloseKey(hkeyDrivers);
5371             return FALSE;
5372         }
5373         if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverNameW,
5374                          env, Level, ptr,
5375                          (cbBuf < *pcbNeeded) ? NULL : pDriverInfo + *pcbNeeded,
5376                          (cbBuf < *pcbNeeded) ? 0 : cbBuf - *pcbNeeded,
5377                          &needed, unicode)) {
5378             RegCloseKey(hkeyDrivers);
5379             return FALSE;
5380         }
5381         (*pcbNeeded) += needed;
5382     }
5383
5384     RegCloseKey(hkeyDrivers);
5385
5386     if(cbBuf < *pcbNeeded){
5387         SetLastError(ERROR_INSUFFICIENT_BUFFER);
5388         return FALSE;
5389     }
5390
5391     *pcReturned = number;
5392     return TRUE;
5393 }
5394
5395 /*****************************************************************************
5396  *          EnumPrinterDriversW  [WINSPOOL.@]
5397  *
5398  *    see function EnumPrinterDrivers for RETURNS, BUGS
5399  */
5400 BOOL WINAPI EnumPrinterDriversW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
5401                                 LPBYTE pDriverInfo, DWORD cbBuf,
5402                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
5403 {
5404     return WINSPOOL_EnumPrinterDrivers(pName, pEnvironment, Level, pDriverInfo,
5405                                        cbBuf, pcbNeeded, pcReturned, TRUE);
5406 }
5407
5408 /*****************************************************************************
5409  *          EnumPrinterDriversA  [WINSPOOL.@]
5410  *
5411  *    see function EnumPrinterDrivers for RETURNS, BUGS
5412  */
5413 BOOL WINAPI EnumPrinterDriversA(LPSTR pName, LPSTR pEnvironment, DWORD Level,
5414                                 LPBYTE pDriverInfo, DWORD cbBuf,
5415                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
5416 {   BOOL ret;
5417     UNICODE_STRING pNameW, pEnvironmentW;
5418     PWSTR pwstrNameW, pwstrEnvironmentW;
5419
5420     pwstrNameW = asciitounicode(&pNameW, pName);
5421     pwstrEnvironmentW = asciitounicode(&pEnvironmentW, pEnvironment);
5422
5423     ret = WINSPOOL_EnumPrinterDrivers(pwstrNameW, pwstrEnvironmentW,
5424                                       Level, pDriverInfo, cbBuf, pcbNeeded,
5425                                       pcReturned, FALSE);
5426     RtlFreeUnicodeString(&pNameW);
5427     RtlFreeUnicodeString(&pEnvironmentW);
5428
5429     return ret;
5430 }
5431
5432 /******************************************************************************
5433  *              EnumPortsA   (WINSPOOL.@)
5434  *
5435  * See EnumPortsW.
5436  *
5437  */
5438 BOOL WINAPI EnumPortsA( LPSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf,
5439                         LPDWORD pcbNeeded, LPDWORD pcReturned)
5440 {
5441     BOOL    res;
5442     LPBYTE  bufferW = NULL;
5443     LPWSTR  nameW = NULL;
5444     DWORD   needed = 0;
5445     DWORD   numentries = 0;
5446     INT     len;
5447
5448     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), Level, pPorts,
5449           cbBuf, pcbNeeded, pcReturned);
5450
5451     /* convert servername to unicode */
5452     if (pName) {
5453         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
5454         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5455         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
5456     }
5457     /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the Ports */
5458     needed = cbBuf * sizeof(WCHAR);    
5459     if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
5460     res = EnumPortsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5461
5462     if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
5463         if (pcbNeeded) needed = *pcbNeeded;
5464         /* HeapReAlloc return NULL, when bufferW was NULL */
5465         bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
5466                               HeapAlloc(GetProcessHeap(), 0, needed);
5467
5468         /* Try again with the large Buffer */
5469         res = EnumPortsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5470     }
5471     needed = pcbNeeded ? *pcbNeeded : 0;
5472     numentries = pcReturned ? *pcReturned : 0;
5473
5474     /*
5475        W2k require the buffersize from EnumPortsW also for EnumPortsA.
5476        We use the smaller Ansi-Size to avoid conflicts with fixed Buffers of old Apps.
5477      */
5478     if (res) {
5479         /* EnumPortsW collected all Data. Parse them to calculate ANSI-Size */
5480         DWORD   entrysize = 0;
5481         DWORD   index;
5482         LPSTR   ptr;
5483         LPPORT_INFO_2W pi2w;
5484         LPPORT_INFO_2A pi2a;
5485
5486         needed = 0;
5487         entrysize = (Level == 1) ? sizeof(PORT_INFO_1A) : sizeof(PORT_INFO_2A);
5488
5489         /* First pass: calculate the size for all Entries */
5490         pi2w = (LPPORT_INFO_2W) bufferW;
5491         pi2a = (LPPORT_INFO_2A) pPorts;
5492         index = 0;
5493         while (index < numentries) {
5494             index++;
5495             needed += entrysize;    /* PORT_INFO_?A */
5496             TRACE("%p: parsing #%d (%s)\n", pi2w, index, debugstr_w(pi2w->pPortName));
5497
5498             needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pPortName, -1,
5499                                             NULL, 0, NULL, NULL);
5500             if (Level > 1) {
5501                 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pMonitorName, -1,
5502                                                 NULL, 0, NULL, NULL);
5503                 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pDescription, -1,
5504                                                 NULL, 0, NULL, NULL);
5505             }
5506             /* use LPBYTE with entrysize to avoid double code (PORT_INFO_1 + PORT_INFO_2) */
5507             pi2w = (LPPORT_INFO_2W) (((LPBYTE)pi2w) + entrysize);
5508             pi2a = (LPPORT_INFO_2A) (((LPBYTE)pi2a) + entrysize);
5509         }
5510
5511         /* check for errors and quit on failure */
5512         if (cbBuf < needed) {
5513             SetLastError(ERROR_INSUFFICIENT_BUFFER);
5514             res = FALSE;
5515             goto cleanup;
5516         }
5517         len = entrysize * numentries;       /* room for all PORT_INFO_?A */
5518         ptr = (LPSTR) &pPorts[len];         /* room for strings */
5519         cbBuf -= len ;                      /* free Bytes in the user-Buffer */
5520         pi2w = (LPPORT_INFO_2W) bufferW;
5521         pi2a = (LPPORT_INFO_2A) pPorts;
5522         index = 0;
5523         /* Second Pass: Fill the User Buffer (if we have one) */
5524         while ((index < numentries) && pPorts) {
5525             index++;
5526             TRACE("%p: writing PORT_INFO_%dA #%d\n", pi2a, Level, index);
5527             pi2a->pPortName = ptr;
5528             len = WideCharToMultiByte(CP_ACP, 0, pi2w->pPortName, -1,
5529                                             ptr, cbBuf , NULL, NULL);
5530             ptr += len;
5531             cbBuf -= len;
5532             if (Level > 1) {
5533                 pi2a->pMonitorName = ptr;
5534                 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pMonitorName, -1,
5535                                             ptr, cbBuf, NULL, NULL);
5536                 ptr += len;
5537                 cbBuf -= len;
5538
5539                 pi2a->pDescription = ptr;
5540                 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pDescription, -1,
5541                                             ptr, cbBuf, NULL, NULL);
5542                 ptr += len;
5543                 cbBuf -= len;
5544
5545                 pi2a->fPortType = pi2w->fPortType;
5546                 pi2a->Reserved = 0;              /* documented: "must be zero" */
5547                 
5548             }
5549             /* use LPBYTE with entrysize to avoid double code (PORT_INFO_1 + PORT_INFO_2) */
5550             pi2w = (LPPORT_INFO_2W) (((LPBYTE)pi2w) + entrysize);
5551             pi2a = (LPPORT_INFO_2A) (((LPBYTE)pi2a) + entrysize);
5552         }
5553     }
5554
5555 cleanup:
5556     if (pcbNeeded)  *pcbNeeded = needed;
5557     if (pcReturned) *pcReturned = (res) ? numentries : 0;
5558
5559     HeapFree(GetProcessHeap(), 0, nameW);
5560     HeapFree(GetProcessHeap(), 0, bufferW);
5561
5562     TRACE("returning %d with %d (%d byte for %d of %d entries)\n", 
5563             (res), GetLastError(), needed, (res)? numentries : 0, numentries);
5564
5565     return (res);
5566
5567 }
5568
5569 /******************************************************************************
5570  *      EnumPortsW   (WINSPOOL.@)
5571  *
5572  * Enumerate available Ports
5573  *
5574  * PARAMS
5575  *  name        [I] Servername or NULL (local Computer)
5576  *  level       [I] Structure-Level (1 or 2)
5577  *  buffer      [O] PTR to Buffer that receives the Result
5578  *  bufsize     [I] Size of Buffer at buffer
5579  *  bufneeded   [O] PTR to DWORD that receives the size in Bytes used / required for buffer
5580  *  bufreturned [O] PTR to DWORD that receives the number of Ports in buffer
5581  *
5582  * RETURNS
5583  *  Success: TRUE
5584  *  Failure: FALSE and in bufneeded the Bytes required for buffer, if bufsize is too small
5585  *
5586  */
5587
5588 BOOL WINAPI EnumPortsW(LPWSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
5589 {
5590     DWORD   needed = 0;
5591     DWORD   numentries = 0;
5592     BOOL    res = FALSE;
5593
5594     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pPorts,
5595           cbBuf, pcbNeeded, pcReturned);
5596
5597     if (pName && (pName[0])) {
5598         FIXME("not implemented for Server %s\n", debugstr_w(pName));
5599         SetLastError(ERROR_ACCESS_DENIED);
5600         goto emP_cleanup;
5601     }
5602
5603     /* Level is not checked in win9x */
5604     if (!Level || (Level > 2)) {
5605         WARN("level (%d) is ignored in win9x\n", Level);
5606         SetLastError(ERROR_INVALID_LEVEL);
5607         goto emP_cleanup;
5608     }
5609     if (!pcbNeeded) {
5610         SetLastError(RPC_X_NULL_REF_POINTER);
5611         goto emP_cleanup;
5612     }
5613
5614     EnterCriticalSection(&monitor_handles_cs);
5615     monitor_loadall();
5616
5617     /* Scan all local Ports */
5618     numentries = 0;
5619     needed = get_ports_from_all_monitors(Level, NULL, 0, &numentries);
5620
5621     /* we calculated the needed buffersize. now do the error-checks */
5622     if (cbBuf < needed) {
5623         monitor_unloadall();
5624         SetLastError(ERROR_INSUFFICIENT_BUFFER);
5625         goto emP_cleanup_cs;
5626     }
5627     else if (!pPorts || !pcReturned) {
5628         monitor_unloadall();
5629         SetLastError(RPC_X_NULL_REF_POINTER);
5630         goto emP_cleanup_cs;
5631     }
5632
5633     /* Fill the Buffer */
5634     needed = get_ports_from_all_monitors(Level, pPorts, cbBuf, &numentries);
5635     res = TRUE;
5636     monitor_unloadall();
5637
5638 emP_cleanup_cs:
5639     LeaveCriticalSection(&monitor_handles_cs);
5640
5641 emP_cleanup:
5642     if (pcbNeeded)  *pcbNeeded = needed;
5643     if (pcReturned) *pcReturned = (res) ? numentries : 0;
5644
5645     TRACE("returning %d with %d (%d byte for %d of %d entries)\n", 
5646             (res), GetLastError(), needed, (res)? numentries : 0, numentries);
5647
5648     return (res);
5649 }
5650
5651 /******************************************************************************
5652  *              GetDefaultPrinterW   (WINSPOOL.@)
5653  *
5654  * FIXME
5655  *      This function must read the value from data 'device' of key
5656  *      HCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows
5657  */
5658 BOOL WINAPI GetDefaultPrinterW(LPWSTR name, LPDWORD namesize)
5659 {
5660     BOOL  retval = TRUE;
5661     DWORD insize, len;
5662     WCHAR *buffer, *ptr;
5663
5664     if (!namesize)
5665     {
5666         SetLastError(ERROR_INVALID_PARAMETER);
5667         return FALSE;
5668     }
5669
5670     /* make the buffer big enough for the stuff from the profile/registry,
5671      * the content must fit into the local buffer to compute the correct
5672      * size even if the extern buffer is too small or not given.
5673      * (20 for ,driver,port) */
5674     insize = *namesize;
5675     len = max(100, (insize + 20));
5676     buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
5677
5678     if (!GetProfileStringW(windowsW, deviceW, emptyStringW, buffer, len))
5679     {
5680         SetLastError (ERROR_FILE_NOT_FOUND);
5681         retval = FALSE;
5682         goto end;
5683     }
5684     TRACE("%s\n", debugstr_w(buffer));
5685
5686     if ((ptr = strchrW(buffer, ',')) == NULL)
5687     {
5688         SetLastError(ERROR_INVALID_NAME);
5689         retval = FALSE;
5690         goto end;
5691     }
5692
5693     *ptr = 0;
5694     *namesize = strlenW(buffer) + 1;
5695     if(!name || (*namesize > insize))
5696     {
5697         SetLastError(ERROR_INSUFFICIENT_BUFFER);
5698         retval = FALSE;
5699         goto end;
5700     }
5701     strcpyW(name, buffer);
5702
5703 end:
5704     HeapFree( GetProcessHeap(), 0, buffer);
5705     return retval;
5706 }
5707
5708
5709 /******************************************************************************
5710  *              GetDefaultPrinterA   (WINSPOOL.@)
5711  */
5712 BOOL WINAPI GetDefaultPrinterA(LPSTR name, LPDWORD namesize)
5713 {
5714     BOOL  retval = TRUE;
5715     DWORD insize = 0;
5716     WCHAR *bufferW = NULL;
5717
5718     if (!namesize)
5719     {
5720         SetLastError(ERROR_INVALID_PARAMETER);
5721         return FALSE;
5722     }
5723
5724     if(name && *namesize) {
5725         insize = *namesize;
5726         bufferW = HeapAlloc( GetProcessHeap(), 0, insize * sizeof(WCHAR));
5727     }
5728
5729     if(!GetDefaultPrinterW( bufferW, namesize)) {
5730         retval = FALSE;
5731         goto end;
5732     }
5733
5734     *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, name, insize,
5735                                     NULL, NULL);
5736     if (!*namesize)
5737     {
5738         *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
5739         retval = FALSE;
5740     }
5741     TRACE("0x%08x/0x%08x:%s\n", *namesize, insize, debugstr_w(bufferW));
5742
5743 end:
5744     HeapFree( GetProcessHeap(), 0, bufferW);
5745     return retval;
5746 }
5747
5748
5749 /******************************************************************************
5750  *              SetDefaultPrinterW   (WINSPOOL.204)
5751  *
5752  * Set the Name of the Default Printer
5753  *
5754  * PARAMS
5755  *  pszPrinter [I] Name of the Printer or NULL
5756  *
5757  * RETURNS
5758  *  Success:    True
5759  *  Failure:    FALSE
5760  *
5761  * NOTES
5762  *  When the Parameter is NULL or points to an Empty String and
5763  *  a Default Printer was already present, then this Function changes nothing.
5764  *  Without a Default Printer and NULL (or an Empty String) as Parameter,
5765  *  the First enumerated local Printer is used.
5766  *
5767  */
5768 BOOL WINAPI SetDefaultPrinterW(LPCWSTR pszPrinter)
5769 {
5770
5771     TRACE("(%s)\n", debugstr_w(pszPrinter));
5772
5773     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5774     return FALSE;
5775 }
5776
5777 /******************************************************************************
5778  *              SetDefaultPrinterA   (WINSPOOL.202)
5779  *
5780  * See SetDefaultPrinterW.
5781  *
5782  */
5783 BOOL WINAPI SetDefaultPrinterA(LPCSTR pszPrinter)
5784 {
5785
5786     TRACE("(%s)\n", debugstr_a(pszPrinter));
5787
5788     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5789     return FALSE;
5790 }
5791
5792
5793 /******************************************************************************
5794  *              SetPrinterDataExA   (WINSPOOL.@)
5795  */
5796 DWORD WINAPI SetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5797                                LPCSTR pValueName, DWORD Type,
5798                                LPBYTE pData, DWORD cbData)
5799 {
5800     HKEY hkeyPrinter, hkeySubkey;
5801     DWORD ret;
5802
5803     TRACE("(%p, %s, %s %08x, %p, %08x)\n", hPrinter, debugstr_a(pKeyName),
5804           debugstr_a(pValueName), Type, pData, cbData);
5805
5806     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5807        != ERROR_SUCCESS)
5808         return ret;
5809
5810     if((ret = RegCreateKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
5811        != ERROR_SUCCESS) {
5812         ERR("Can't create subkey %s\n", debugstr_a(pKeyName));
5813         RegCloseKey(hkeyPrinter);
5814         return ret;
5815     }
5816     ret = RegSetValueExA(hkeySubkey, pValueName, 0, Type, pData, cbData);
5817     RegCloseKey(hkeySubkey);
5818     RegCloseKey(hkeyPrinter);
5819     return ret;
5820 }
5821
5822 /******************************************************************************
5823  *              SetPrinterDataExW   (WINSPOOL.@)
5824  */
5825 DWORD WINAPI SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5826                                LPCWSTR pValueName, DWORD Type,
5827                                LPBYTE pData, DWORD cbData)
5828 {
5829     HKEY hkeyPrinter, hkeySubkey;
5830     DWORD ret;
5831
5832     TRACE("(%p, %s, %s %08x, %p, %08x)\n", hPrinter, debugstr_w(pKeyName),
5833           debugstr_w(pValueName), Type, pData, cbData);
5834
5835     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5836        != ERROR_SUCCESS)
5837         return ret;
5838
5839     if((ret = RegCreateKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
5840        != ERROR_SUCCESS) {
5841         ERR("Can't create subkey %s\n", debugstr_w(pKeyName));
5842         RegCloseKey(hkeyPrinter);
5843         return ret;
5844     }
5845     ret = RegSetValueExW(hkeySubkey, pValueName, 0, Type, pData, cbData);
5846     RegCloseKey(hkeySubkey);
5847     RegCloseKey(hkeyPrinter);
5848     return ret;
5849 }
5850
5851 /******************************************************************************
5852  *              SetPrinterDataA   (WINSPOOL.@)
5853  */
5854 DWORD WINAPI SetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, DWORD Type,
5855                                LPBYTE pData, DWORD cbData)
5856 {
5857     return SetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, Type,
5858                              pData, cbData);
5859 }
5860
5861 /******************************************************************************
5862  *              SetPrinterDataW   (WINSPOOL.@)
5863  */
5864 DWORD WINAPI SetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, DWORD Type,
5865                              LPBYTE pData, DWORD cbData)
5866 {
5867     return SetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, Type,
5868                              pData, cbData);
5869 }
5870
5871 /******************************************************************************
5872  *              GetPrinterDataExA   (WINSPOOL.@)
5873  */
5874 DWORD WINAPI GetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5875                                LPCSTR pValueName, LPDWORD pType,
5876                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5877 {
5878     HKEY hkeyPrinter, hkeySubkey;
5879     DWORD ret;
5880
5881     TRACE("(%p, %s, %s %p, %p, %08x, %p)\n", hPrinter,
5882           debugstr_a(pKeyName), debugstr_a(pValueName), pType, pData, nSize,
5883           pcbNeeded);
5884
5885     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5886        != ERROR_SUCCESS)
5887         return ret;
5888
5889     if((ret = RegOpenKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
5890        != ERROR_SUCCESS) {
5891         WARN("Can't open subkey %s\n", debugstr_a(pKeyName));
5892         RegCloseKey(hkeyPrinter);
5893         return ret;
5894     }
5895     *pcbNeeded = nSize;
5896     ret = RegQueryValueExA(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
5897     RegCloseKey(hkeySubkey);
5898     RegCloseKey(hkeyPrinter);
5899     return ret;
5900 }
5901
5902 /******************************************************************************
5903  *              GetPrinterDataExW   (WINSPOOL.@)
5904  */
5905 DWORD WINAPI GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5906                                LPCWSTR pValueName, LPDWORD pType,
5907                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5908 {
5909     HKEY hkeyPrinter, hkeySubkey;
5910     DWORD ret;
5911
5912     TRACE("(%p, %s, %s %p, %p, %08x, %p)\n", hPrinter,
5913           debugstr_w(pKeyName), debugstr_w(pValueName), pType, pData, nSize,
5914           pcbNeeded);
5915
5916     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5917        != ERROR_SUCCESS)
5918         return ret;
5919
5920     if((ret = RegOpenKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
5921        != ERROR_SUCCESS) {
5922         WARN("Can't open subkey %s\n", debugstr_w(pKeyName));
5923         RegCloseKey(hkeyPrinter);
5924         return ret;
5925     }
5926     *pcbNeeded = nSize;
5927     ret = RegQueryValueExW(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
5928     RegCloseKey(hkeySubkey);
5929     RegCloseKey(hkeyPrinter);
5930     return ret;
5931 }
5932
5933 /******************************************************************************
5934  *              GetPrinterDataA   (WINSPOOL.@)
5935  */
5936 DWORD WINAPI GetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, LPDWORD pType,
5937                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5938 {
5939     return GetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, pType,
5940                              pData, nSize, pcbNeeded);
5941 }
5942
5943 /******************************************************************************
5944  *              GetPrinterDataW   (WINSPOOL.@)
5945  */
5946 DWORD WINAPI GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType,
5947                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5948 {
5949     return GetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, pType,
5950                              pData, nSize, pcbNeeded);
5951 }
5952
5953 /*******************************************************************************
5954  *              EnumPrinterDataExW      [WINSPOOL.@]
5955  */
5956 DWORD WINAPI EnumPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5957                                 LPBYTE pEnumValues, DWORD cbEnumValues,
5958                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
5959 {
5960     HKEY                    hkPrinter, hkSubKey;
5961     DWORD                   r, ret, dwIndex, cValues, cbMaxValueNameLen,
5962                             cbValueNameLen, cbMaxValueLen, cbValueLen,
5963                             cbBufSize, dwType;
5964     LPWSTR                  lpValueName;
5965     HANDLE                  hHeap;
5966     PBYTE                   lpValue;
5967     PPRINTER_ENUM_VALUESW   ppev;
5968
5969     TRACE ("%p %s\n", hPrinter, debugstr_w (pKeyName));
5970
5971     if (pKeyName == NULL || *pKeyName == 0)
5972         return ERROR_INVALID_PARAMETER;
5973
5974     ret = WINSPOOL_GetOpenedPrinterRegKey (hPrinter, &hkPrinter);
5975     if (ret != ERROR_SUCCESS)
5976     {
5977         TRACE ("WINSPOOL_GetOpenedPrinterRegKey (%p) returned %i\n",
5978                 hPrinter, ret);
5979         return ret;
5980     }
5981
5982     ret = RegOpenKeyExW (hkPrinter, pKeyName, 0, KEY_READ, &hkSubKey);
5983     if (ret != ERROR_SUCCESS)
5984     {
5985         r = RegCloseKey (hkPrinter);
5986         if (r != ERROR_SUCCESS)
5987             WARN ("RegCloseKey returned %i\n", r);
5988         TRACE ("RegOpenKeyExW (%p, %s) returned %i\n", hPrinter,
5989                 debugstr_w (pKeyName), ret);
5990         return ret;
5991     }
5992
5993     ret = RegCloseKey (hkPrinter);
5994     if (ret != ERROR_SUCCESS)
5995     {
5996         ERR ("RegCloseKey returned %i\n", ret);
5997         r = RegCloseKey (hkSubKey);
5998         if (r != ERROR_SUCCESS)
5999             WARN ("RegCloseKey returned %i\n", r);
6000         return ret;
6001     }
6002
6003     ret = RegQueryInfoKeyW (hkSubKey, NULL, NULL, NULL, NULL, NULL, NULL,
6004             &cValues, &cbMaxValueNameLen, &cbMaxValueLen, NULL, NULL);
6005     if (ret != ERROR_SUCCESS)
6006     {
6007         r = RegCloseKey (hkSubKey);
6008         if (r != ERROR_SUCCESS)
6009             WARN ("RegCloseKey returned %i\n", r);
6010         TRACE ("RegQueryInfoKeyW (%p) returned %i\n", hkSubKey, ret);
6011         return ret;
6012     }
6013
6014     TRACE ("RegQueryInfoKeyW returned cValues = %i, cbMaxValueNameLen = %i, "
6015             "cbMaxValueLen = %i\n", cValues, cbMaxValueNameLen, cbMaxValueLen);
6016
6017     if (cValues == 0)                   /* empty key */
6018     {
6019         r = RegCloseKey (hkSubKey);
6020         if (r != ERROR_SUCCESS)
6021             WARN ("RegCloseKey returned %i\n", r);
6022         *pcbEnumValues = *pnEnumValues = 0;
6023         return ERROR_SUCCESS;
6024     }
6025
6026     ++cbMaxValueNameLen;                        /* allow for trailing '\0' */
6027
6028     hHeap = GetProcessHeap ();
6029     if (hHeap == NULL)
6030     {
6031         ERR ("GetProcessHeap failed\n");
6032         r = RegCloseKey (hkSubKey);
6033         if (r != ERROR_SUCCESS)
6034             WARN ("RegCloseKey returned %i\n", r);
6035         return ERROR_OUTOFMEMORY;
6036     }
6037
6038     lpValueName = HeapAlloc (hHeap, 0, cbMaxValueNameLen * sizeof (WCHAR));
6039     if (lpValueName == NULL)
6040     {
6041         ERR ("Failed to allocate %i WCHARs from process heap\n", cbMaxValueNameLen);
6042         r = RegCloseKey (hkSubKey);
6043         if (r != ERROR_SUCCESS)
6044             WARN ("RegCloseKey returned %i\n", r);
6045         return ERROR_OUTOFMEMORY;
6046     }
6047
6048     lpValue = HeapAlloc (hHeap, 0, cbMaxValueLen);
6049     if (lpValue == NULL)
6050     {
6051         ERR ("Failed to allocate %i bytes from process heap\n", cbMaxValueLen);
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         return ERROR_OUTOFMEMORY;
6058     }
6059
6060     TRACE ("pass 1: calculating buffer required for all names and values\n");
6061
6062     cbBufSize = cValues * sizeof (PRINTER_ENUM_VALUESW);
6063
6064     TRACE ("%i bytes required for %i headers\n", cbBufSize, cValues);
6065
6066     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
6067     {
6068         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
6069         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
6070                 NULL, NULL, lpValue, &cbValueLen);
6071         if (ret != ERROR_SUCCESS)
6072         {
6073             if (HeapFree (hHeap, 0, lpValue) == 0)
6074                 WARN ("HeapFree failed with code %i\n", GetLastError ());
6075             if (HeapFree (hHeap, 0, lpValueName) == 0)
6076                 WARN ("HeapFree failed with code %i\n", GetLastError ());
6077             r = RegCloseKey (hkSubKey);
6078             if (r != ERROR_SUCCESS)
6079                 WARN ("RegCloseKey returned %i\n", r);
6080             TRACE ("RegEnumValueW (%i) returned %i\n", dwIndex, ret);
6081             return ret;
6082         }
6083
6084         TRACE ("%s [%i]: name needs %i WCHARs, data needs %i bytes\n",
6085                 debugstr_w (lpValueName), dwIndex,
6086                 cbValueNameLen + 1, cbValueLen);
6087
6088         cbBufSize += (cbValueNameLen + 1) * sizeof (WCHAR);
6089         cbBufSize += cbValueLen;
6090     }
6091
6092     TRACE ("%i bytes required for all %i values\n", cbBufSize, cValues);
6093
6094     *pcbEnumValues = cbBufSize;
6095     *pnEnumValues = cValues;
6096
6097     if (cbEnumValues < cbBufSize)       /* buffer too small */
6098     {
6099         if (HeapFree (hHeap, 0, lpValue) == 0)
6100             WARN ("HeapFree failed with code %i\n", GetLastError ());
6101         if (HeapFree (hHeap, 0, lpValueName) == 0)
6102             WARN ("HeapFree failed with code %i\n", GetLastError ());
6103         r = RegCloseKey (hkSubKey);
6104         if (r != ERROR_SUCCESS)
6105             WARN ("RegCloseKey returned %i\n", r);
6106         TRACE ("%i byte buffer is not large enough\n", cbEnumValues);
6107         return ERROR_MORE_DATA;
6108     }
6109
6110     TRACE ("pass 2: copying all names and values to buffer\n");
6111
6112     ppev = (PPRINTER_ENUM_VALUESW) pEnumValues;         /* array of structs */
6113     pEnumValues += cValues * sizeof (PRINTER_ENUM_VALUESW);
6114
6115     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
6116     {
6117         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
6118         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
6119                 NULL, &dwType, lpValue, &cbValueLen);
6120         if (ret != ERROR_SUCCESS)
6121         {
6122             if (HeapFree (hHeap, 0, lpValue) == 0)
6123                 WARN ("HeapFree failed with code %i\n", GetLastError ());
6124             if (HeapFree (hHeap, 0, lpValueName) == 0)
6125                 WARN ("HeapFree failed with code %i\n", GetLastError ());
6126             r = RegCloseKey (hkSubKey);
6127             if (r != ERROR_SUCCESS)
6128                 WARN ("RegCloseKey returned %i\n", r);
6129             TRACE ("RegEnumValueW (%i) returned %i\n", dwIndex, ret);
6130             return ret;
6131         }
6132
6133         cbValueNameLen = (cbValueNameLen + 1) * sizeof (WCHAR);
6134         memcpy (pEnumValues, lpValueName, cbValueNameLen);
6135         ppev[dwIndex].pValueName = (LPWSTR) pEnumValues;
6136         pEnumValues += cbValueNameLen;
6137
6138         /* return # of *bytes* (including trailing \0), not # of chars */
6139         ppev[dwIndex].cbValueName = cbValueNameLen;
6140
6141         ppev[dwIndex].dwType = dwType;
6142
6143         memcpy (pEnumValues, lpValue, cbValueLen);
6144         ppev[dwIndex].pData = pEnumValues;
6145         pEnumValues += cbValueLen;
6146
6147         ppev[dwIndex].cbData = cbValueLen;
6148
6149         TRACE ("%s [%i]: copied name (%i bytes) and data (%i bytes)\n",
6150                 debugstr_w (lpValueName), dwIndex, cbValueNameLen, cbValueLen);
6151     }
6152
6153     if (HeapFree (hHeap, 0, lpValue) == 0)
6154     {
6155         ret = GetLastError ();
6156         ERR ("HeapFree failed with code %i\n", ret);
6157         if (HeapFree (hHeap, 0, lpValueName) == 0)
6158             WARN ("HeapFree failed with code %i\n", GetLastError ());
6159         r = RegCloseKey (hkSubKey);
6160         if (r != ERROR_SUCCESS)
6161             WARN ("RegCloseKey returned %i\n", r);
6162         return ret;
6163     }
6164
6165     if (HeapFree (hHeap, 0, lpValueName) == 0)
6166     {
6167         ret = GetLastError ();
6168         ERR ("HeapFree failed with code %i\n", ret);
6169         r = RegCloseKey (hkSubKey);
6170         if (r != ERROR_SUCCESS)
6171             WARN ("RegCloseKey returned %i\n", r);
6172         return ret;
6173     }
6174
6175     ret = RegCloseKey (hkSubKey);
6176     if (ret != ERROR_SUCCESS)
6177     {
6178         ERR ("RegCloseKey returned %i\n", ret);
6179         return ret;
6180     }
6181
6182     return ERROR_SUCCESS;
6183 }
6184
6185 /*******************************************************************************
6186  *              EnumPrinterDataExA      [WINSPOOL.@]
6187  *
6188  * This functions returns value names and REG_SZ, REG_EXPAND_SZ, and
6189  * REG_MULTI_SZ values as ASCII strings in Unicode-sized buffers.  This is
6190  * what Windows 2000 SP1 does.
6191  *
6192  */
6193 DWORD WINAPI EnumPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
6194                                 LPBYTE pEnumValues, DWORD cbEnumValues,
6195                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
6196 {
6197     INT     len;
6198     LPWSTR  pKeyNameW;
6199     DWORD   ret, dwIndex, dwBufSize;
6200     HANDLE  hHeap;
6201     LPSTR   pBuffer;
6202
6203     TRACE ("%p %s\n", hPrinter, pKeyName);
6204
6205     if (pKeyName == NULL || *pKeyName == 0)
6206         return ERROR_INVALID_PARAMETER;
6207
6208     len = MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, NULL, 0);
6209     if (len == 0)
6210     {
6211         ret = GetLastError ();
6212         ERR ("MultiByteToWideChar failed with code %i\n", ret);
6213         return ret;
6214     }
6215
6216     hHeap = GetProcessHeap ();
6217     if (hHeap == NULL)
6218     {
6219         ERR ("GetProcessHeap failed\n");
6220         return ERROR_OUTOFMEMORY;
6221     }
6222
6223     pKeyNameW = HeapAlloc (hHeap, 0, len * sizeof (WCHAR));
6224     if (pKeyNameW == NULL)
6225     {
6226         ERR ("Failed to allocate %i bytes from process heap\n",
6227              (LONG)(len * sizeof (WCHAR)));
6228         return ERROR_OUTOFMEMORY;
6229     }
6230
6231     if (MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, pKeyNameW, len) == 0)
6232     {
6233         ret = GetLastError ();
6234         ERR ("MultiByteToWideChar failed with code %i\n", ret);
6235         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
6236             WARN ("HeapFree failed with code %i\n", GetLastError ());
6237         return ret;
6238     }
6239
6240     ret = EnumPrinterDataExW (hPrinter, pKeyNameW, pEnumValues, cbEnumValues,
6241             pcbEnumValues, pnEnumValues);
6242     if (ret != ERROR_SUCCESS)
6243     {
6244         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
6245             WARN ("HeapFree failed with code %i\n", GetLastError ());
6246         TRACE ("EnumPrinterDataExW returned %i\n", ret);
6247         return ret;
6248     }
6249
6250     if (HeapFree (hHeap, 0, pKeyNameW) == 0)
6251     {
6252         ret = GetLastError ();
6253         ERR ("HeapFree failed with code %i\n", ret);
6254         return ret;
6255     }
6256
6257     if (*pnEnumValues == 0)     /* empty key */
6258         return ERROR_SUCCESS;
6259
6260     dwBufSize = 0;
6261     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
6262     {
6263         PPRINTER_ENUM_VALUESW ppev =
6264                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
6265
6266         if (dwBufSize < ppev->cbValueName)
6267             dwBufSize = ppev->cbValueName;
6268
6269         if (dwBufSize < ppev->cbData && (ppev->dwType == REG_SZ ||
6270                 ppev->dwType == REG_EXPAND_SZ || ppev->dwType == REG_MULTI_SZ))
6271             dwBufSize = ppev->cbData;
6272     }
6273
6274     TRACE ("Largest Unicode name or value is %i bytes\n", dwBufSize);
6275
6276     pBuffer = HeapAlloc (hHeap, 0, dwBufSize);
6277     if (pBuffer == NULL)
6278     {
6279         ERR ("Failed to allocate %i bytes from process heap\n", dwBufSize);
6280         return ERROR_OUTOFMEMORY;
6281     }
6282
6283     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
6284     {
6285         PPRINTER_ENUM_VALUESW ppev =
6286                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
6287
6288         len = WideCharToMultiByte (CP_ACP, 0, ppev->pValueName,
6289                 ppev->cbValueName / sizeof (WCHAR), pBuffer, dwBufSize, NULL,
6290                 NULL);
6291         if (len == 0)
6292         {
6293             ret = GetLastError ();
6294             ERR ("WideCharToMultiByte failed with code %i\n", ret);
6295             if (HeapFree (hHeap, 0, pBuffer) == 0)
6296                 WARN ("HeapFree failed with code %i\n", GetLastError ());
6297             return ret;
6298         }
6299
6300         memcpy (ppev->pValueName, pBuffer, len);
6301
6302         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
6303
6304         if (ppev->dwType != REG_SZ && ppev->dwType != REG_EXPAND_SZ &&
6305                 ppev->dwType != REG_MULTI_SZ)
6306             continue;
6307
6308         len = WideCharToMultiByte (CP_ACP, 0, (LPWSTR) ppev->pData,
6309                 ppev->cbData / sizeof (WCHAR), pBuffer, dwBufSize, NULL, NULL);
6310         if (len == 0)
6311         {
6312             ret = GetLastError ();
6313             ERR ("WideCharToMultiByte failed with code %i\n", ret);
6314             if (HeapFree (hHeap, 0, pBuffer) == 0)
6315                 WARN ("HeapFree failed with code %i\n", GetLastError ());
6316             return ret;
6317         }
6318
6319         memcpy (ppev->pData, pBuffer, len);
6320
6321         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
6322         TRACE ("  (only first string of REG_MULTI_SZ printed)\n");
6323     }
6324
6325     if (HeapFree (hHeap, 0, pBuffer) == 0)
6326     {
6327         ret = GetLastError ();
6328         ERR ("HeapFree failed with code %i\n", ret);
6329         return ret;
6330     }
6331
6332     return ERROR_SUCCESS;
6333 }
6334
6335 /******************************************************************************
6336  *      AbortPrinter (WINSPOOL.@)
6337  */
6338 BOOL WINAPI AbortPrinter( HANDLE hPrinter )
6339 {
6340     FIXME("(%p), stub!\n", hPrinter);
6341     return TRUE;
6342 }
6343
6344 /******************************************************************************
6345  *              AddPortA (WINSPOOL.@)
6346  *
6347  * See AddPortW.
6348  *
6349  */
6350 BOOL WINAPI AddPortA(LPSTR pName, HWND hWnd, LPSTR pMonitorName)
6351 {
6352     LPWSTR  nameW = NULL;
6353     LPWSTR  monitorW = NULL;
6354     DWORD   len;
6355     BOOL    res;
6356
6357     TRACE("(%s, %p, %s)\n",debugstr_a(pName), hWnd, debugstr_a(pMonitorName));
6358
6359     if (pName) {
6360         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6361         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6362         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6363     }
6364
6365     if (pMonitorName) {
6366         len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
6367         monitorW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6368         MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, monitorW, len);
6369     }
6370     res = AddPortW(nameW, hWnd, monitorW);
6371     HeapFree(GetProcessHeap(), 0, nameW);
6372     HeapFree(GetProcessHeap(), 0, monitorW);
6373     return res;
6374 }
6375
6376 /******************************************************************************
6377  *      AddPortW (WINSPOOL.@)
6378  *
6379  * Add a Port for a specific Monitor
6380  *
6381  * PARAMS
6382  *  pName        [I] Servername or NULL (local Computer)
6383  *  hWnd         [I] Handle to parent Window for the Dialog-Box
6384  *  pMonitorName [I] Name of the Monitor that manage the Port
6385  *
6386  * RETURNS
6387  *  Success: TRUE
6388  *  Failure: FALSE
6389  *
6390  */
6391 BOOL WINAPI AddPortW(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
6392 {
6393     monitor_t * pm;
6394     monitor_t * pui;
6395     DWORD       res;
6396
6397     TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pMonitorName));
6398
6399     if (pName && pName[0]) {
6400         SetLastError(ERROR_INVALID_PARAMETER);
6401         return FALSE;
6402     }
6403
6404     if (!pMonitorName) {
6405         SetLastError(RPC_X_NULL_REF_POINTER);
6406         return FALSE;
6407     }
6408
6409     /* an empty Monitorname is Invalid */
6410     if (!pMonitorName[0]) {
6411         SetLastError(ERROR_NOT_SUPPORTED);
6412         return FALSE;
6413     }
6414
6415     pm = monitor_load(pMonitorName, NULL);
6416     if (pm && pm->monitor && pm->monitor->pfnAddPort) {
6417         res = pm->monitor->pfnAddPort(pName, hWnd, pMonitorName);
6418         TRACE("got %d with %u\n", res, GetLastError());
6419         res = TRUE;
6420     }
6421     else
6422     {
6423         pui = monitor_loadui(pm);
6424         if (pui && pui->monitorUI && pui->monitorUI->pfnAddPortUI) {
6425             TRACE("use %p: %s\n", pui, debugstr_w(pui->dllname));
6426             res = pui->monitorUI->pfnAddPortUI(pName, hWnd, pMonitorName, NULL);
6427             TRACE("got %d with %u\n", res, GetLastError());
6428             res = TRUE;
6429         }
6430         else
6431         {
6432             FIXME("not implemented for %s (%p: %s => %p: %s)\n", debugstr_w(pMonitorName),
6433                   pm, debugstr_w(pm ? pm->dllname : NULL), pui, debugstr_w(pui ? pui->dllname : NULL));
6434
6435             /* XP: ERROR_NOT_SUPPORTED, NT351,9x: ERROR_INVALID_PARAMETER */
6436             SetLastError(ERROR_NOT_SUPPORTED);
6437             res = FALSE;
6438         }
6439         monitor_unload(pui);
6440     }
6441     monitor_unload(pm);
6442     TRACE("returning %d with %u\n", res, GetLastError());
6443     return res;
6444 }
6445
6446 /******************************************************************************
6447  *             AddPortExA (WINSPOOL.@)
6448  *
6449  * See AddPortExW.
6450  *
6451  */
6452 BOOL WINAPI AddPortExA(LPSTR pName, DWORD level, LPBYTE pBuffer, LPSTR pMonitorName)
6453 {
6454     PORT_INFO_2W   pi2W;
6455     PORT_INFO_2A * pi2A;
6456     LPWSTR  nameW = NULL;
6457     LPWSTR  monitorW = NULL;
6458     DWORD   len;
6459     BOOL    res;
6460
6461     pi2A = (PORT_INFO_2A *) pBuffer;
6462
6463     TRACE("(%s, %d, %p, %s): %s\n", debugstr_a(pName), level, pBuffer,
6464             debugstr_a(pMonitorName), debugstr_a(pi2A ? pi2A->pPortName : NULL));
6465
6466     if ((level < 1) || (level > 2)) {
6467         SetLastError(ERROR_INVALID_LEVEL);
6468         return FALSE;
6469     }
6470
6471     if (!pi2A) {
6472         SetLastError(ERROR_INVALID_PARAMETER);
6473         return FALSE;
6474     }
6475
6476     if (pName) {
6477         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6478         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6479         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6480     }
6481
6482     if (pMonitorName) {
6483         len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
6484         monitorW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6485         MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, monitorW, len);
6486     }
6487
6488     ZeroMemory(&pi2W, sizeof(PORT_INFO_2W));
6489
6490     if (pi2A->pPortName) {
6491         len = MultiByteToWideChar(CP_ACP, 0, pi2A->pPortName, -1, NULL, 0);
6492         pi2W.pPortName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6493         MultiByteToWideChar(CP_ACP, 0, pi2A->pPortName, -1, pi2W.pPortName, len);
6494     }
6495
6496     if (level > 1) {
6497         if (pi2A->pMonitorName) {
6498             len = MultiByteToWideChar(CP_ACP, 0, pi2A->pMonitorName, -1, NULL, 0);
6499             pi2W.pMonitorName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6500             MultiByteToWideChar(CP_ACP, 0, pi2A->pMonitorName, -1, pi2W.pMonitorName, len);
6501         }
6502
6503         if (pi2A->pDescription) {
6504             len = MultiByteToWideChar(CP_ACP, 0, pi2A->pDescription, -1, NULL, 0);
6505             pi2W.pDescription = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6506             MultiByteToWideChar(CP_ACP, 0, pi2A->pDescription, -1, pi2W.pDescription, len);
6507         }
6508         pi2W.fPortType = pi2A->fPortType;
6509         pi2W.Reserved = pi2A->Reserved;
6510     }
6511
6512     res = AddPortExW(nameW, level, (LPBYTE) &pi2W, monitorW);
6513
6514     HeapFree(GetProcessHeap(), 0, nameW);
6515     HeapFree(GetProcessHeap(), 0, monitorW);
6516     HeapFree(GetProcessHeap(), 0, pi2W.pPortName);
6517     HeapFree(GetProcessHeap(), 0, pi2W.pMonitorName);
6518     HeapFree(GetProcessHeap(), 0, pi2W.pDescription);
6519     return res;
6520
6521 }
6522
6523 /******************************************************************************
6524  *             AddPortExW (WINSPOOL.@)
6525  *
6526  * Add a Port for a specific Monitor, without presenting a user interface
6527  *
6528  * PARAMS
6529  *  pName         [I] Servername or NULL (local Computer)
6530  *  level         [I] Structure-Level (1 or 2) for pBuffer
6531  *  pBuffer       [I] PTR to: PORT_INFO_1 or PORT_INFO_2
6532  *  pMonitorName  [I] Name of the Monitor that manage the Port
6533  *
6534  * RETURNS
6535  *  Success: TRUE
6536  *  Failure: FALSE
6537  *
6538  */
6539 BOOL WINAPI AddPortExW(LPWSTR pName, DWORD level, LPBYTE pBuffer, LPWSTR pMonitorName)
6540 {
6541     PORT_INFO_2W * pi2;
6542     monitor_t * pm;
6543     DWORD       res = FALSE;
6544
6545     pi2 = (PORT_INFO_2W *) pBuffer;
6546
6547     TRACE("(%s, %d, %p, %s): %s %s %s\n", debugstr_w(pName), level, pBuffer,
6548             debugstr_w(pMonitorName), debugstr_w(pi2 ? pi2->pPortName : NULL),
6549             debugstr_w(((level > 1) && pi2) ? pi2->pMonitorName : NULL),
6550             debugstr_w(((level > 1) && pi2) ? pi2->pDescription : NULL));
6551
6552
6553     if ((level < 1) || (level > 2)) {
6554         SetLastError(ERROR_INVALID_LEVEL);
6555         return FALSE;
6556     }
6557
6558     if ((!pi2) || (!pMonitorName) || (!pMonitorName[0])) {
6559         SetLastError(ERROR_INVALID_PARAMETER);
6560         return FALSE;
6561     }
6562
6563     /* load the Monitor */
6564     pm = monitor_load(pMonitorName, NULL);
6565     if (!pm) {
6566         SetLastError(ERROR_INVALID_PARAMETER);
6567         return FALSE;
6568     }
6569
6570     if (pm->monitor && pm->monitor->pfnAddPortEx) {
6571         res = pm->monitor->pfnAddPortEx(pName, level, pBuffer, pMonitorName);
6572         TRACE("got %u with %u\n", res, GetLastError());
6573     }
6574     else
6575     {
6576         FIXME("not implemented for %s (%p)\n", debugstr_w(pMonitorName), pm->monitor);
6577     }
6578     monitor_unload(pm);
6579     return res;
6580 }
6581
6582 /******************************************************************************
6583  *      AddPrinterConnectionA (WINSPOOL.@)
6584  */
6585 BOOL WINAPI AddPrinterConnectionA( LPSTR pName )
6586 {
6587     FIXME("%s\n", debugstr_a(pName));
6588     return FALSE;
6589 }
6590
6591 /******************************************************************************
6592  *      AddPrinterConnectionW (WINSPOOL.@)
6593  */
6594 BOOL WINAPI AddPrinterConnectionW( LPWSTR pName )
6595 {
6596     FIXME("%s\n", debugstr_w(pName));
6597     return FALSE;
6598 }
6599
6600 /******************************************************************************
6601  *  AddPrinterDriverExW (WINSPOOL.@)
6602  *
6603  * Install a Printer Driver with the Option to upgrade / downgrade the Files
6604  *
6605  * PARAMS
6606  *  pName           [I] Servername or NULL (local Computer)
6607  *  level           [I] Level for the supplied DRIVER_INFO_*W struct
6608  *  pDriverInfo     [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter
6609  *  dwFileCopyFlags [I] How to Copy / Upgrade / Downgrade the needed Files
6610  *
6611  * RESULTS
6612  *  Success: TRUE
6613  *  Failure: FALSE
6614  *
6615  */
6616 BOOL WINAPI AddPrinterDriverExW( LPWSTR pName, DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
6617 {
6618     TRACE("(%s, %d, %p, 0x%x)\n", debugstr_w(pName), level, pDriverInfo, dwFileCopyFlags);
6619
6620     if ((backend == NULL)  && !load_backend()) return FALSE;
6621
6622     if (level < 2 || level == 5 || level == 7 || level > 8) {
6623         SetLastError(ERROR_INVALID_LEVEL);
6624         return FALSE;
6625     }
6626
6627     if (!pDriverInfo) {
6628         SetLastError(ERROR_INVALID_PARAMETER);
6629         return FALSE;
6630     }
6631
6632     return backend->fpAddPrinterDriverEx(pName, level, pDriverInfo, dwFileCopyFlags);
6633 }
6634
6635 /******************************************************************************
6636  *  AddPrinterDriverExA (WINSPOOL.@)
6637  *
6638  * See AddPrinterDriverExW.
6639  *
6640  */
6641 BOOL WINAPI AddPrinterDriverExA(LPSTR pName, DWORD Level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
6642 {
6643     DRIVER_INFO_8A  *diA;
6644     DRIVER_INFO_8W   diW;
6645     LPWSTR  nameW = NULL;
6646     DWORD   lenA;
6647     DWORD   len;
6648     DWORD   res = FALSE;
6649
6650     TRACE("(%s, %d, %p, 0x%x)\n", debugstr_a(pName), Level, pDriverInfo, dwFileCopyFlags);
6651
6652     diA = (DRIVER_INFO_8A  *) pDriverInfo;
6653     ZeroMemory(&diW, sizeof(diW));
6654
6655     if (Level < 2 || Level == 5 || Level == 7 || Level > 8) {
6656         SetLastError(ERROR_INVALID_LEVEL);
6657         return FALSE;
6658     }
6659
6660     if (diA == NULL) {
6661         SetLastError(ERROR_INVALID_PARAMETER);
6662         return FALSE;
6663     }
6664
6665     /* convert servername to unicode */
6666     if (pName) {
6667         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6668         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6669         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6670     }
6671
6672     /* common fields */
6673     diW.cVersion = diA->cVersion;
6674
6675     if (diA->pName) {
6676         len = MultiByteToWideChar(CP_ACP, 0, diA->pName, -1, NULL, 0);
6677         diW.pName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6678         MultiByteToWideChar(CP_ACP, 0, diA->pName, -1, diW.pName, len);
6679     }
6680
6681     if (diA->pEnvironment) {
6682         len = MultiByteToWideChar(CP_ACP, 0, diA->pEnvironment, -1, NULL, 0);
6683         diW.pEnvironment = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6684         MultiByteToWideChar(CP_ACP, 0, diA->pEnvironment, -1, diW.pEnvironment, len);
6685     }
6686
6687     if (diA->pDriverPath) {
6688         len = MultiByteToWideChar(CP_ACP, 0, diA->pDriverPath, -1, NULL, 0);
6689         diW.pDriverPath = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6690         MultiByteToWideChar(CP_ACP, 0, diA->pDriverPath, -1, diW.pDriverPath, len);
6691     }
6692
6693     if (diA->pDataFile) {
6694         len = MultiByteToWideChar(CP_ACP, 0, diA->pDataFile, -1, NULL, 0);
6695         diW.pDataFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6696         MultiByteToWideChar(CP_ACP, 0, diA->pDataFile, -1, diW.pDataFile, len);
6697     }
6698
6699     if (diA->pConfigFile) {
6700         len = MultiByteToWideChar(CP_ACP, 0, diA->pConfigFile, -1, NULL, 0);
6701         diW.pConfigFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6702         MultiByteToWideChar(CP_ACP, 0, diA->pConfigFile, -1, diW.pConfigFile, len);
6703     }
6704
6705     if ((Level > 2) && diA->pDependentFiles) {
6706         lenA = multi_sz_lenA(diA->pDependentFiles);
6707         len = MultiByteToWideChar(CP_ACP, 0, diA->pDependentFiles, lenA, NULL, 0);
6708         diW.pDependentFiles = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6709         MultiByteToWideChar(CP_ACP, 0, diA->pDependentFiles, lenA, diW.pDependentFiles, len);
6710     }
6711
6712     if ((Level > 2) && diA->pMonitorName) {
6713         len = MultiByteToWideChar(CP_ACP, 0, diA->pMonitorName, -1, NULL, 0);
6714         diW.pMonitorName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6715         MultiByteToWideChar(CP_ACP, 0, diA->pMonitorName, -1, diW.pMonitorName, len);
6716     }
6717
6718     if ((Level > 3) && diA->pDefaultDataType) {
6719         len = MultiByteToWideChar(CP_ACP, 0, diA->pDefaultDataType, -1, NULL, 0);
6720         diW.pDefaultDataType = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6721         MultiByteToWideChar(CP_ACP, 0, diA->pDefaultDataType, -1, diW.pDefaultDataType, len);
6722     }
6723
6724     if ((Level > 3) && diA->pszzPreviousNames) {
6725         lenA = multi_sz_lenA(diA->pszzPreviousNames);
6726         len = MultiByteToWideChar(CP_ACP, 0, diA->pszzPreviousNames, lenA, NULL, 0);
6727         diW.pszzPreviousNames = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6728         MultiByteToWideChar(CP_ACP, 0, diA->pszzPreviousNames, lenA, diW.pszzPreviousNames, len);
6729     }
6730
6731     if ((Level > 5) && diA->pszMfgName) {
6732         len = MultiByteToWideChar(CP_ACP, 0, diA->pszMfgName, -1, NULL, 0);
6733         diW.pszMfgName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6734         MultiByteToWideChar(CP_ACP, 0, diA->pszMfgName, -1, diW.pszMfgName, len);
6735     }
6736
6737     if ((Level > 5) && diA->pszOEMUrl) {
6738         len = MultiByteToWideChar(CP_ACP, 0, diA->pszOEMUrl, -1, NULL, 0);
6739         diW.pszOEMUrl = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6740         MultiByteToWideChar(CP_ACP, 0, diA->pszOEMUrl, -1, diW.pszOEMUrl, len);
6741     }
6742
6743     if ((Level > 5) && diA->pszHardwareID) {
6744         len = MultiByteToWideChar(CP_ACP, 0, diA->pszHardwareID, -1, NULL, 0);
6745         diW.pszHardwareID = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6746         MultiByteToWideChar(CP_ACP, 0, diA->pszHardwareID, -1, diW.pszHardwareID, len);
6747     }
6748
6749     if ((Level > 5) && diA->pszProvider) {
6750         len = MultiByteToWideChar(CP_ACP, 0, diA->pszProvider, -1, NULL, 0);
6751         diW.pszProvider = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6752         MultiByteToWideChar(CP_ACP, 0, diA->pszProvider, -1, diW.pszProvider, len);
6753     }
6754
6755     if (Level > 7) {
6756         FIXME("level %u is incomplete\n", Level);
6757     }
6758
6759     res = AddPrinterDriverExW(nameW, Level, (LPBYTE) &diW, dwFileCopyFlags);
6760     TRACE("got %u with %u\n", res, GetLastError());
6761     HeapFree(GetProcessHeap(), 0, nameW);
6762     HeapFree(GetProcessHeap(), 0, diW.pName);
6763     HeapFree(GetProcessHeap(), 0, diW.pEnvironment);
6764     HeapFree(GetProcessHeap(), 0, diW.pDriverPath);
6765     HeapFree(GetProcessHeap(), 0, diW.pDataFile);
6766     HeapFree(GetProcessHeap(), 0, diW.pConfigFile);
6767     HeapFree(GetProcessHeap(), 0, diW.pDependentFiles);
6768     HeapFree(GetProcessHeap(), 0, diW.pMonitorName);
6769     HeapFree(GetProcessHeap(), 0, diW.pDefaultDataType);
6770     HeapFree(GetProcessHeap(), 0, diW.pszzPreviousNames);
6771     HeapFree(GetProcessHeap(), 0, diW.pszMfgName);
6772     HeapFree(GetProcessHeap(), 0, diW.pszOEMUrl);
6773     HeapFree(GetProcessHeap(), 0, diW.pszHardwareID);
6774     HeapFree(GetProcessHeap(), 0, diW.pszProvider);
6775
6776     TRACE("=> %u with %u\n", res, GetLastError());
6777     return res;
6778 }
6779
6780 /******************************************************************************
6781  *      ConfigurePortA (WINSPOOL.@)
6782  *
6783  * See ConfigurePortW.
6784  *
6785  */
6786 BOOL WINAPI ConfigurePortA(LPSTR pName, HWND hWnd, LPSTR pPortName)
6787 {
6788     LPWSTR  nameW = NULL;
6789     LPWSTR  portW = NULL;
6790     INT     len;
6791     DWORD   res;
6792
6793     TRACE("(%s, %p, %s)\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
6794
6795     /* convert servername to unicode */
6796     if (pName) {
6797         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6798         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6799         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6800     }
6801
6802     /* convert portname to unicode */
6803     if (pPortName) {
6804         len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
6805         portW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6806         MultiByteToWideChar(CP_ACP, 0, pPortName, -1, portW, len);
6807     }
6808
6809     res = ConfigurePortW(nameW, hWnd, portW);
6810     HeapFree(GetProcessHeap(), 0, nameW);
6811     HeapFree(GetProcessHeap(), 0, portW);
6812     return res;
6813 }
6814
6815 /******************************************************************************
6816  *      ConfigurePortW (WINSPOOL.@)
6817  *
6818  * Display the Configuration-Dialog for a specific Port
6819  *
6820  * PARAMS
6821  *  pName     [I] Servername or NULL (local Computer)
6822  *  hWnd      [I] Handle to parent Window for the Dialog-Box
6823  *  pPortName [I] Name of the Port, that should be configured
6824  *
6825  * RETURNS
6826  *  Success: TRUE
6827  *  Failure: FALSE
6828  *
6829  */
6830 BOOL WINAPI ConfigurePortW(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
6831 {
6832     monitor_t * pm;
6833     monitor_t * pui;
6834     DWORD       res;
6835
6836     TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
6837
6838     if (pName && pName[0]) {
6839         SetLastError(ERROR_INVALID_PARAMETER);
6840         return FALSE;
6841     }
6842
6843     if (!pPortName) {
6844         SetLastError(RPC_X_NULL_REF_POINTER);
6845         return FALSE;
6846     }
6847
6848     /* an empty Portname is Invalid, but can popup a Dialog */
6849     if (!pPortName[0]) {
6850         SetLastError(ERROR_NOT_SUPPORTED);
6851         return FALSE;
6852     }
6853
6854     pm = monitor_load_by_port(pPortName);
6855     if (pm && pm->monitor && pm->monitor->pfnConfigurePort) {
6856         TRACE("Using %s for %s (%p: %s)\n", debugstr_w(pm->name), debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
6857         res = pm->monitor->pfnConfigurePort(pName, hWnd, pPortName);
6858         TRACE("got %d with %u\n", res, GetLastError());
6859     }
6860     else
6861     {
6862         pui = monitor_loadui(pm);
6863         if (pui && pui->monitorUI && pui->monitorUI->pfnConfigurePortUI) {
6864             TRACE("Use %s for %s (%p: %s)\n", debugstr_w(pui->name), debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
6865             res = pui->monitorUI->pfnConfigurePortUI(pName, hWnd, pPortName);
6866             TRACE("got %d with %u\n", res, GetLastError());
6867         }
6868         else
6869         {
6870             FIXME("not implemented for %s (%p: %s => %p: %s)\n", debugstr_w(pPortName),
6871                   pm, debugstr_w(pm ? pm->dllname : NULL), pui, debugstr_w(pui ? pui->dllname : NULL));
6872
6873             /* XP: ERROR_NOT_SUPPORTED, NT351,9x: ERROR_INVALID_PARAMETER */
6874             SetLastError(ERROR_NOT_SUPPORTED);
6875             res = FALSE;
6876         }
6877         monitor_unload(pui);
6878     }
6879     monitor_unload(pm);
6880
6881     TRACE("returning %d with %u\n", res, GetLastError());
6882     return res;
6883 }
6884
6885 /******************************************************************************
6886  *      ConnectToPrinterDlg (WINSPOOL.@)
6887  */
6888 HANDLE WINAPI ConnectToPrinterDlg( HWND hWnd, DWORD Flags )
6889 {
6890     FIXME("%p %x\n", hWnd, Flags);
6891     return NULL;
6892 }
6893
6894 /******************************************************************************
6895  *      DeletePrinterConnectionA (WINSPOOL.@)
6896  */
6897 BOOL WINAPI DeletePrinterConnectionA( LPSTR pName )
6898 {
6899     FIXME("%s\n", debugstr_a(pName));
6900     return TRUE;
6901 }
6902
6903 /******************************************************************************
6904  *      DeletePrinterConnectionW (WINSPOOL.@)
6905  */
6906 BOOL WINAPI DeletePrinterConnectionW( LPWSTR pName )
6907 {
6908     FIXME("%s\n", debugstr_w(pName));
6909     return TRUE;
6910 }
6911
6912 /******************************************************************************
6913  *              DeletePrinterDriverExW (WINSPOOL.@)
6914  */
6915 BOOL WINAPI DeletePrinterDriverExW( LPWSTR pName, LPWSTR pEnvironment,
6916     LPWSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
6917 {
6918     HKEY hkey_drivers;
6919     BOOL ret = FALSE;
6920
6921     TRACE("%s %s %s %x %x\n", debugstr_w(pName), debugstr_w(pEnvironment),
6922           debugstr_w(pDriverName), dwDeleteFlag, dwVersionFlag);
6923
6924     if(pName && pName[0])
6925     {
6926         FIXME("pName = %s - unsupported\n", debugstr_w(pName));
6927         SetLastError(ERROR_INVALID_PARAMETER);
6928         return FALSE;
6929     }
6930
6931     if(dwDeleteFlag)
6932     {
6933         FIXME("dwDeleteFlag = %x - unsupported\n", dwDeleteFlag);
6934         SetLastError(ERROR_INVALID_PARAMETER);
6935         return FALSE;
6936     }
6937
6938     hkey_drivers = WINSPOOL_OpenDriverReg(pEnvironment, TRUE);
6939
6940     if(!hkey_drivers)
6941     {
6942         ERR("Can't open drivers key\n");
6943         return FALSE;
6944     }
6945
6946     if(RegDeleteTreeW(hkey_drivers, pDriverName) == ERROR_SUCCESS)
6947         ret = TRUE;
6948
6949     RegCloseKey(hkey_drivers);
6950
6951     return ret;
6952 }
6953
6954 /******************************************************************************
6955  *              DeletePrinterDriverExA (WINSPOOL.@)
6956  */
6957 BOOL WINAPI DeletePrinterDriverExA( LPSTR pName, LPSTR pEnvironment,
6958     LPSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
6959 {
6960     UNICODE_STRING NameW, EnvW, DriverW;
6961     BOOL ret;
6962
6963     asciitounicode(&NameW, pName);
6964     asciitounicode(&EnvW, pEnvironment);
6965     asciitounicode(&DriverW, pDriverName);
6966
6967     ret = DeletePrinterDriverExW(NameW.Buffer, EnvW.Buffer, DriverW.Buffer, dwDeleteFlag, dwVersionFlag);
6968
6969     RtlFreeUnicodeString(&DriverW);
6970     RtlFreeUnicodeString(&EnvW);
6971     RtlFreeUnicodeString(&NameW);
6972
6973     return ret;
6974 }
6975
6976 /******************************************************************************
6977  *              DeletePrinterDataExW (WINSPOOL.@)
6978  */
6979 DWORD WINAPI DeletePrinterDataExW( HANDLE hPrinter, LPCWSTR pKeyName,
6980                                   LPCWSTR pValueName)
6981 {
6982     FIXME("%p %s %s\n", hPrinter, 
6983           debugstr_w(pKeyName), debugstr_w(pValueName));
6984     return ERROR_INVALID_PARAMETER;
6985 }
6986
6987 /******************************************************************************
6988  *              DeletePrinterDataExA (WINSPOOL.@)
6989  */
6990 DWORD WINAPI DeletePrinterDataExA( HANDLE hPrinter, LPCSTR pKeyName,
6991                                   LPCSTR pValueName)
6992 {
6993     FIXME("%p %s %s\n", hPrinter, 
6994           debugstr_a(pKeyName), debugstr_a(pValueName));
6995     return ERROR_INVALID_PARAMETER;
6996 }
6997
6998 /******************************************************************************
6999  *      DeletePrintProcessorA (WINSPOOL.@)
7000  */
7001 BOOL WINAPI DeletePrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProcessorName)
7002 {
7003     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
7004           debugstr_a(pPrintProcessorName));
7005     return TRUE;
7006 }
7007
7008 /******************************************************************************
7009  *      DeletePrintProcessorW (WINSPOOL.@)
7010  */
7011 BOOL WINAPI DeletePrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProcessorName)
7012 {
7013     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
7014           debugstr_w(pPrintProcessorName));
7015     return TRUE;
7016 }
7017
7018 /******************************************************************************
7019  *      DeletePrintProvidorA (WINSPOOL.@)
7020  */
7021 BOOL WINAPI DeletePrintProvidorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProviderName)
7022 {
7023     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
7024           debugstr_a(pPrintProviderName));
7025     return TRUE;
7026 }
7027
7028 /******************************************************************************
7029  *      DeletePrintProvidorW (WINSPOOL.@)
7030  */
7031 BOOL WINAPI DeletePrintProvidorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProviderName)
7032 {
7033     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
7034           debugstr_w(pPrintProviderName));
7035     return TRUE;
7036 }
7037
7038 /******************************************************************************
7039  *      EnumFormsA (WINSPOOL.@)
7040  */
7041 BOOL WINAPI EnumFormsA( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
7042     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
7043 {
7044     FIXME("%p %x %p %x %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
7045     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
7046     return FALSE;
7047 }
7048
7049 /******************************************************************************
7050  *      EnumFormsW (WINSPOOL.@)
7051  */
7052 BOOL WINAPI EnumFormsW( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
7053     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
7054 {
7055     FIXME("%p %x %p %x %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
7056     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
7057     return FALSE;
7058 }
7059
7060 /*****************************************************************************
7061  *          EnumMonitorsA [WINSPOOL.@]
7062  *
7063  * See EnumMonitorsW.
7064  *
7065  */
7066 BOOL WINAPI EnumMonitorsA(LPSTR pName, DWORD Level, LPBYTE pMonitors,
7067                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
7068 {
7069     BOOL    res;
7070     LPBYTE  bufferW = NULL;
7071     LPWSTR  nameW = NULL;
7072     DWORD   needed = 0;
7073     DWORD   numentries = 0;
7074     INT     len;
7075
7076     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), Level, pMonitors,
7077           cbBuf, pcbNeeded, pcReturned);
7078
7079     /* convert servername to unicode */
7080     if (pName) {
7081         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
7082         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7083         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
7084     }
7085     /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the monitors */
7086     needed = cbBuf * sizeof(WCHAR);    
7087     if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
7088     res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
7089
7090     if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
7091         if (pcbNeeded) needed = *pcbNeeded;
7092         /* HeapReAlloc return NULL, when bufferW was NULL */
7093         bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
7094                               HeapAlloc(GetProcessHeap(), 0, needed);
7095
7096         /* Try again with the large Buffer */
7097         res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
7098     }
7099     numentries = pcReturned ? *pcReturned : 0;
7100     needed = 0;
7101     /*
7102        W2k require the buffersize from EnumMonitorsW also for EnumMonitorsA.
7103        We use the smaller Ansi-Size to avoid conflicts with fixed Buffers of old Apps.
7104      */
7105     if (res) {
7106         /* EnumMonitorsW collected all Data. Parse them to calculate ANSI-Size */
7107         DWORD   entrysize = 0;
7108         DWORD   index;
7109         LPSTR   ptr;
7110         LPMONITOR_INFO_2W mi2w;
7111         LPMONITOR_INFO_2A mi2a;
7112
7113         /* MONITOR_INFO_*W and MONITOR_INFO_*A have the same size */
7114         entrysize = (Level == 1) ? sizeof(MONITOR_INFO_1A) : sizeof(MONITOR_INFO_2A);
7115
7116         /* First pass: calculate the size for all Entries */
7117         mi2w = (LPMONITOR_INFO_2W) bufferW;
7118         mi2a = (LPMONITOR_INFO_2A) pMonitors;
7119         index = 0;
7120         while (index < numentries) {
7121             index++;
7122             needed += entrysize;    /* MONITOR_INFO_?A */
7123             TRACE("%p: parsing #%d (%s)\n", mi2w, index, debugstr_w(mi2w->pName));
7124
7125             needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
7126                                             NULL, 0, NULL, NULL);
7127             if (Level > 1) {
7128                 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
7129                                                 NULL, 0, NULL, NULL);
7130                 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
7131                                                 NULL, 0, NULL, NULL);
7132             }
7133             /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
7134             mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
7135             mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
7136         }
7137
7138         /* check for errors and quit on failure */
7139         if (cbBuf < needed) {
7140             SetLastError(ERROR_INSUFFICIENT_BUFFER);
7141             res = FALSE;
7142             goto emA_cleanup;
7143         }
7144         len = entrysize * numentries;       /* room for all MONITOR_INFO_?A */
7145         ptr = (LPSTR) &pMonitors[len];      /* room for strings */
7146         cbBuf -= len ;                      /* free Bytes in the user-Buffer */
7147         mi2w = (LPMONITOR_INFO_2W) bufferW;
7148         mi2a = (LPMONITOR_INFO_2A) pMonitors;
7149         index = 0;
7150         /* Second Pass: Fill the User Buffer (if we have one) */
7151         while ((index < numentries) && pMonitors) {
7152             index++;
7153             TRACE("%p: writing MONITOR_INFO_%dA #%d\n", mi2a, Level, index);
7154             mi2a->pName = ptr;
7155             len = WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
7156                                             ptr, cbBuf , NULL, NULL);
7157             ptr += len;
7158             cbBuf -= len;
7159             if (Level > 1) {
7160                 mi2a->pEnvironment = ptr;
7161                 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
7162                                             ptr, cbBuf, NULL, NULL);
7163                 ptr += len;
7164                 cbBuf -= len;
7165
7166                 mi2a->pDLLName = ptr;
7167                 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
7168                                             ptr, cbBuf, NULL, NULL);
7169                 ptr += len;
7170                 cbBuf -= len;
7171             }
7172             /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
7173             mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
7174             mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
7175         }
7176     }
7177 emA_cleanup:
7178     if (pcbNeeded)  *pcbNeeded = needed;
7179     if (pcReturned) *pcReturned = (res) ? numentries : 0;
7180
7181     HeapFree(GetProcessHeap(), 0, nameW);
7182     HeapFree(GetProcessHeap(), 0, bufferW);
7183
7184     TRACE("returning %d with %d (%d byte for %d entries)\n", 
7185             (res), GetLastError(), needed, numentries);
7186
7187     return (res);
7188
7189 }
7190
7191 /*****************************************************************************
7192  *          EnumMonitorsW [WINSPOOL.@]
7193  *
7194  * Enumerate available Port-Monitors
7195  *
7196  * PARAMS
7197  *  pName       [I] Servername or NULL (local Computer)
7198  *  Level       [I] Structure-Level (1:Win9x+NT or 2:NT only)
7199  *  pMonitors   [O] PTR to Buffer that receives the Result
7200  *  cbBuf       [I] Size of Buffer at pMonitors
7201  *  pcbNeeded   [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
7202  *  pcReturned  [O] PTR to DWORD that receives the number of Monitors in pMonitors
7203  *
7204  * RETURNS
7205  *  Success: TRUE
7206  *  Failure: FALSE and in pcbNeeded the Bytes required for buffer, if cbBuf is too small
7207  *
7208  */
7209 BOOL WINAPI EnumMonitorsW(LPWSTR pName, DWORD Level, LPBYTE pMonitors,
7210                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
7211 {
7212
7213     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pMonitors,
7214           cbBuf, pcbNeeded, pcReturned);
7215
7216     if ((backend == NULL)  && !load_backend()) return FALSE;
7217
7218     if (!pcbNeeded || !pcReturned || (!pMonitors && (cbBuf > 0))) {
7219         SetLastError(RPC_X_NULL_REF_POINTER);
7220         return FALSE;
7221     }
7222
7223     return backend->fpEnumMonitors(pName, Level, pMonitors, cbBuf, pcbNeeded, pcReturned);
7224 }
7225
7226 /******************************************************************************
7227  * SpoolerInit (WINSPOOL.@)
7228  *
7229  * Initialize the Spooler
7230  *
7231  * RETURNS
7232  *  Success: TRUE
7233  *  Failure: FALSE
7234  *
7235  * NOTES
7236  *  The function fails on windows, when the spooler service is not running
7237  *
7238  */
7239 BOOL WINAPI SpoolerInit(void)
7240 {
7241
7242     if ((backend == NULL)  && !load_backend()) return FALSE;
7243     return TRUE;
7244 }
7245
7246 /******************************************************************************
7247  *              XcvDataW (WINSPOOL.@)
7248  *
7249  * Execute commands in the Printmonitor DLL
7250  *
7251  * PARAMS
7252  *  hXcv            [i] Handle from OpenPrinter (with XcvMonitor or XcvPort)
7253  *  pszDataName     [i] Name of the command to execute
7254  *  pInputData      [i] Buffer for extra Input Data (needed only for some commands)
7255  *  cbInputData     [i] Size in Bytes of Buffer at pInputData
7256  *  pOutputData     [o] Buffer to receive additional Data (needed only for some commands)
7257  *  cbOutputData    [i] Size in Bytes of Buffer at pOutputData
7258  *  pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
7259  *  pdwStatus       [o] PTR to receive the win32 error code from the Printmonitor DLL
7260  *
7261  * RETURNS
7262  *  Success: TRUE
7263  *  Failure: FALSE
7264  *
7265  * NOTES
7266  *  Returning "TRUE" does mean, that the Printmonitor DLL was called successful.
7267  *  The execution of the command can still fail (check pdwStatus for ERROR_SUCCESS).
7268  *
7269  *  Minimal List of commands, that a Printmonitor DLL should support:
7270  *
7271  *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
7272  *| "AddPort"   : Add a Port
7273  *| "DeletePort": Delete a Port
7274  *
7275  *  Many Printmonitors support additional commands. Examples for localspl.dll:
7276  *  "GetDefaultCommConfig", "SetDefaultCommConfig",
7277  *  "GetTransmissionRetryTimeout", "ConfigureLPTPortCommandOK"
7278  *
7279  */
7280 BOOL WINAPI XcvDataW( HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData,
7281     DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData,
7282     PDWORD pcbOutputNeeded, PDWORD pdwStatus)
7283 {
7284     opened_printer_t *printer;
7285
7286     TRACE("(%p, %s, %p, %d, %p, %d, %p, %p)\n", hXcv, debugstr_w(pszDataName),
7287           pInputData, cbInputData, pOutputData,
7288           cbOutputData, pcbOutputNeeded, pdwStatus);
7289
7290     printer = get_opened_printer(hXcv);
7291     if (!printer || (!printer->hXcv)) {
7292         SetLastError(ERROR_INVALID_HANDLE);
7293         return FALSE;
7294     }
7295
7296     if (!pcbOutputNeeded) {
7297         SetLastError(ERROR_INVALID_PARAMETER);
7298         return FALSE;
7299     }
7300
7301     if (!pszDataName || !pdwStatus || (!pOutputData && (cbOutputData > 0))) {
7302         SetLastError(RPC_X_NULL_REF_POINTER);
7303         return FALSE;
7304     }
7305
7306     *pcbOutputNeeded = 0;
7307
7308     *pdwStatus = printer->pm->monitor->pfnXcvDataPort(printer->hXcv, pszDataName,
7309             pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
7310
7311     return TRUE;
7312 }
7313
7314 /*****************************************************************************
7315  *          EnumPrinterDataA [WINSPOOL.@]
7316  *
7317  */
7318 DWORD WINAPI EnumPrinterDataA( HANDLE hPrinter, DWORD dwIndex, LPSTR pValueName,
7319     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
7320     DWORD cbData, LPDWORD pcbData )
7321 {
7322     FIXME("%p %x %p %x %p %p %p %x %p\n", hPrinter, dwIndex, pValueName,
7323           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
7324     return ERROR_NO_MORE_ITEMS;
7325 }
7326
7327 /*****************************************************************************
7328  *          EnumPrinterDataW [WINSPOOL.@]
7329  *
7330  */
7331 DWORD WINAPI EnumPrinterDataW( HANDLE hPrinter, DWORD dwIndex, LPWSTR pValueName,
7332     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
7333     DWORD cbData, LPDWORD pcbData )
7334 {
7335     FIXME("%p %x %p %x %p %p %p %x %p\n", hPrinter, dwIndex, pValueName,
7336           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
7337     return ERROR_NO_MORE_ITEMS;
7338 }
7339
7340 /*****************************************************************************
7341  *          EnumPrintProcessorDatatypesA [WINSPOOL.@]
7342  *
7343  */
7344 BOOL WINAPI EnumPrintProcessorDatatypesA(LPSTR pName, LPSTR pPrintProcessorName,
7345                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
7346                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
7347 {
7348     FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_a(pName),
7349           debugstr_a(pPrintProcessorName), Level, pDatatypes, cbBuf,
7350           pcbNeeded, pcReturned);
7351     return FALSE;
7352 }
7353
7354 /*****************************************************************************
7355  *          EnumPrintProcessorDatatypesW [WINSPOOL.@]
7356  *
7357  */
7358 BOOL WINAPI EnumPrintProcessorDatatypesW(LPWSTR pName, LPWSTR pPrintProcessorName,
7359                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
7360                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
7361 {
7362     FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_w(pName),
7363           debugstr_w(pPrintProcessorName), Level, pDatatypes, cbBuf,
7364           pcbNeeded, pcReturned);
7365     return FALSE;
7366 }
7367
7368 /*****************************************************************************
7369  *          EnumPrintProcessorsA [WINSPOOL.@]
7370  *
7371  */
7372 BOOL WINAPI EnumPrintProcessorsA(LPSTR pName, LPSTR pEnvironment, DWORD Level, 
7373     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
7374 {
7375     FIXME("Stub: %s %s %d %p %d %p %p\n", pName, pEnvironment, Level,
7376         pPrintProcessorInfo, cbBuf, pcbNeeded, pcbReturned);
7377     return FALSE;
7378 }
7379
7380 /*****************************************************************************
7381  *          EnumPrintProcessorsW [WINSPOOL.@]
7382  *
7383  */
7384 BOOL WINAPI EnumPrintProcessorsW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
7385     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
7386 {
7387     FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_w(pName),
7388         debugstr_w(pEnvironment), Level, pPrintProcessorInfo,
7389         cbBuf, pcbNeeded, pcbReturned);
7390     return FALSE;
7391 }
7392
7393 /*****************************************************************************
7394  *          ExtDeviceMode [WINSPOOL.@]
7395  *
7396  */
7397 LONG WINAPI ExtDeviceMode( HWND hWnd, HANDLE hInst, LPDEVMODEA pDevModeOutput,
7398     LPSTR pDeviceName, LPSTR pPort, LPDEVMODEA pDevModeInput, LPSTR pProfile,
7399     DWORD fMode)
7400 {
7401     FIXME("Stub: %p %p %p %s %s %p %s %x\n", hWnd, hInst, pDevModeOutput,
7402           debugstr_a(pDeviceName), debugstr_a(pPort), pDevModeInput,
7403           debugstr_a(pProfile), fMode);
7404     return -1;
7405 }
7406
7407 /*****************************************************************************
7408  *          FindClosePrinterChangeNotification [WINSPOOL.@]
7409  *
7410  */
7411 BOOL WINAPI FindClosePrinterChangeNotification( HANDLE hChange )
7412 {
7413     FIXME("Stub: %p\n", hChange);
7414     return TRUE;
7415 }
7416
7417 /*****************************************************************************
7418  *          FindFirstPrinterChangeNotification [WINSPOOL.@]
7419  *
7420  */
7421 HANDLE WINAPI FindFirstPrinterChangeNotification( HANDLE hPrinter,
7422     DWORD fdwFlags, DWORD fdwOptions, LPVOID pPrinterNotifyOptions )
7423 {
7424     FIXME("Stub: %p %x %x %p\n",
7425           hPrinter, fdwFlags, fdwOptions, pPrinterNotifyOptions);
7426     return INVALID_HANDLE_VALUE;
7427 }
7428
7429 /*****************************************************************************
7430  *          FindNextPrinterChangeNotification [WINSPOOL.@]
7431  *
7432  */
7433 BOOL WINAPI FindNextPrinterChangeNotification( HANDLE hChange, PDWORD pdwChange,
7434     LPVOID pPrinterNotifyOptions, LPVOID *ppPrinterNotifyInfo )
7435 {
7436     FIXME("Stub: %p %p %p %p\n",
7437           hChange, pdwChange, pPrinterNotifyOptions, ppPrinterNotifyInfo);
7438     return FALSE;
7439 }
7440
7441 /*****************************************************************************
7442  *          FreePrinterNotifyInfo [WINSPOOL.@]
7443  *
7444  */
7445 BOOL WINAPI FreePrinterNotifyInfo( PPRINTER_NOTIFY_INFO pPrinterNotifyInfo )
7446 {
7447     FIXME("Stub: %p\n", pPrinterNotifyInfo);
7448     return TRUE;
7449 }
7450
7451 /*****************************************************************************
7452  *          string_to_buf
7453  *
7454  * Copies a unicode string into a buffer.  The buffer will either contain unicode or
7455  * ansi depending on the unicode parameter.
7456  */
7457 static BOOL string_to_buf(LPCWSTR str, LPBYTE ptr, DWORD cb, DWORD *size, BOOL unicode)
7458 {
7459     if(!str)
7460     {
7461         *size = 0;
7462         return TRUE;
7463     }
7464
7465     if(unicode)
7466     {
7467         *size = (strlenW(str) + 1) * sizeof(WCHAR);
7468         if(*size <= cb)
7469         {
7470             memcpy(ptr, str, *size);
7471             return TRUE;
7472         }
7473         return FALSE;
7474     }
7475     else
7476     {
7477         *size = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
7478         if(*size <= cb)
7479         {
7480             WideCharToMultiByte(CP_ACP, 0, str, -1, (LPSTR)ptr, *size, NULL, NULL);
7481             return TRUE;
7482         }
7483         return FALSE;
7484     }
7485 }
7486
7487 /*****************************************************************************
7488  *          get_job_info_1
7489  */
7490 static BOOL get_job_info_1(job_t *job, JOB_INFO_1W *ji1, LPBYTE buf, DWORD cbBuf,
7491                            LPDWORD pcbNeeded, BOOL unicode)
7492 {
7493     DWORD size, left = cbBuf;
7494     BOOL space = (cbBuf > 0);
7495     LPBYTE ptr = buf;
7496
7497     *pcbNeeded = 0;
7498
7499     if(space)
7500     {
7501         ji1->JobId = job->job_id;
7502     }
7503
7504     string_to_buf(job->document_title, ptr, left, &size, unicode);
7505     if(space && size <= left)
7506     {
7507         ji1->pDocument = (LPWSTR)ptr;
7508         ptr += size;
7509         left -= size;
7510     }
7511     else
7512         space = FALSE;
7513     *pcbNeeded += size;
7514
7515     return space;
7516 }
7517
7518 /*****************************************************************************
7519  *          get_job_info_2
7520  */
7521 static BOOL get_job_info_2(job_t *job, JOB_INFO_2W *ji2, LPBYTE buf, DWORD cbBuf,
7522                            LPDWORD pcbNeeded, BOOL unicode)
7523 {
7524     DWORD size, left = cbBuf;
7525     BOOL space = (cbBuf > 0);
7526     LPBYTE ptr = buf;
7527
7528     *pcbNeeded = 0;
7529
7530     if(space)
7531     {
7532         ji2->JobId = job->job_id;
7533     }
7534
7535     string_to_buf(job->document_title, ptr, left, &size, unicode);
7536     if(space && size <= left)
7537     {
7538         ji2->pDocument = (LPWSTR)ptr;
7539         ptr += size;
7540         left -= size;
7541     }
7542     else
7543         space = FALSE;
7544     *pcbNeeded += size;
7545
7546     return space;
7547 }
7548
7549 /*****************************************************************************
7550  *          get_job_info
7551  */
7552 static BOOL get_job_info(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7553                          DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
7554 {
7555     BOOL ret = FALSE;
7556     DWORD needed = 0, size;
7557     job_t *job;
7558     LPBYTE ptr = pJob;
7559
7560     TRACE("%p %d %d %p %d %p\n", hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
7561
7562     EnterCriticalSection(&printer_handles_cs);
7563     job = get_job(hPrinter, JobId);
7564     if(!job)
7565         goto end;
7566
7567     switch(Level)
7568     {
7569     case 1:
7570         size = sizeof(JOB_INFO_1W);
7571         if(cbBuf >= size)
7572         {
7573             cbBuf -= size;
7574             ptr += size;
7575             memset(pJob, 0, size);
7576         }
7577         else
7578             cbBuf = 0;
7579         ret = get_job_info_1(job, (JOB_INFO_1W *)pJob, ptr, cbBuf, &needed, unicode);
7580         needed += size;
7581         break;
7582
7583     case 2:
7584         size = sizeof(JOB_INFO_2W);
7585         if(cbBuf >= size)
7586         {
7587             cbBuf -= size;
7588             ptr += size;
7589             memset(pJob, 0, size);
7590         }
7591         else
7592             cbBuf = 0;
7593         ret = get_job_info_2(job, (JOB_INFO_2W *)pJob, ptr, cbBuf, &needed, unicode);
7594         needed += size;
7595         break;
7596
7597     case 3:
7598         size = sizeof(JOB_INFO_3);
7599         if(cbBuf >= size)
7600         {
7601             cbBuf -= size;
7602             memset(pJob, 0, size);
7603             ret = TRUE;
7604         }
7605         else
7606             cbBuf = 0;
7607         needed = size;
7608         break;
7609
7610     default:
7611         SetLastError(ERROR_INVALID_LEVEL);
7612         goto end;
7613     }
7614     if(pcbNeeded)
7615         *pcbNeeded = needed;
7616 end:
7617     LeaveCriticalSection(&printer_handles_cs);
7618     return ret;
7619 }
7620
7621 /*****************************************************************************
7622  *          GetJobA [WINSPOOL.@]
7623  *
7624  */
7625 BOOL WINAPI GetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7626                     DWORD cbBuf, LPDWORD pcbNeeded)
7627 {
7628     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, FALSE);
7629 }
7630
7631 /*****************************************************************************
7632  *          GetJobW [WINSPOOL.@]
7633  *
7634  */
7635 BOOL WINAPI GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7636                     DWORD cbBuf, LPDWORD pcbNeeded)
7637 {
7638     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, TRUE);
7639 }
7640
7641 /*****************************************************************************
7642  *          schedule_lpr
7643  */
7644 static BOOL schedule_lpr(LPCWSTR printer_name, LPCWSTR filename)
7645 {
7646     char *unixname, *queue, *cmd;
7647     char fmt[] = "lpr -P%s %s";
7648     DWORD len;
7649
7650     if(!(unixname = wine_get_unix_file_name(filename)))
7651         return FALSE;
7652
7653     len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
7654     queue = HeapAlloc(GetProcessHeap(), 0, len);
7655     WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
7656
7657     cmd = HeapAlloc(GetProcessHeap(), 0, strlen(unixname) + len + sizeof(fmt) - 5);
7658     sprintf(cmd, fmt, queue, unixname);
7659
7660     TRACE("printing with: %s\n", cmd);
7661     system(cmd);
7662
7663     HeapFree(GetProcessHeap(), 0, cmd);
7664     HeapFree(GetProcessHeap(), 0, queue);
7665     HeapFree(GetProcessHeap(), 0, unixname);
7666     return TRUE;
7667 }
7668
7669 /*****************************************************************************
7670  *          schedule_cups
7671  */
7672 static BOOL schedule_cups(LPCWSTR printer_name, LPCWSTR filename, LPCWSTR document_title)
7673 {
7674 #ifdef SONAME_LIBCUPS
7675     if(pcupsPrintFile)
7676     {
7677         char *unixname, *queue, *doc_titleA;
7678         DWORD len;
7679         BOOL ret;
7680
7681         if(!(unixname = wine_get_unix_file_name(filename)))
7682             return FALSE;
7683
7684         len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
7685         queue = HeapAlloc(GetProcessHeap(), 0, len);
7686         WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
7687
7688         len = WideCharToMultiByte(CP_ACP, 0, document_title, -1, NULL, 0, NULL, NULL);
7689         doc_titleA = HeapAlloc(GetProcessHeap(), 0, len);
7690         WideCharToMultiByte(CP_ACP, 0, document_title, -1, doc_titleA, len, NULL, NULL);
7691
7692         TRACE("printing via cups\n");
7693         ret = pcupsPrintFile(queue, unixname, doc_titleA, 0, NULL);
7694         HeapFree(GetProcessHeap(), 0, doc_titleA);
7695         HeapFree(GetProcessHeap(), 0, queue);
7696         HeapFree(GetProcessHeap(), 0, unixname);
7697         return ret;
7698     }
7699     else
7700 #endif
7701     {
7702         return schedule_lpr(printer_name, filename);
7703     }
7704 }
7705
7706 INT_PTR CALLBACK file_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
7707 {
7708     LPWSTR filename;
7709
7710     switch(msg)
7711     {
7712     case WM_INITDIALOG:
7713         SetWindowLongPtrW(hwnd, DWLP_USER, lparam);
7714         return TRUE;
7715
7716     case WM_COMMAND:
7717         if(HIWORD(wparam) == BN_CLICKED)
7718         {
7719             if(LOWORD(wparam) == IDOK)
7720             {
7721                 HANDLE hf;
7722                 DWORD len = SendDlgItemMessageW(hwnd, EDITBOX, WM_GETTEXTLENGTH, 0, 0);
7723                 LPWSTR *output;
7724
7725                 filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
7726                 GetDlgItemTextW(hwnd, EDITBOX, filename, len + 1);
7727
7728                 if(GetFileAttributesW(filename) != INVALID_FILE_ATTRIBUTES)
7729                 {
7730                     WCHAR caption[200], message[200];
7731                     int mb_ret;
7732
7733                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
7734                     LoadStringW(WINSPOOL_hInstance, IDS_FILE_EXISTS, message, sizeof(message) / sizeof(WCHAR));
7735                     mb_ret = MessageBoxW(hwnd, message, caption, MB_OKCANCEL | MB_ICONEXCLAMATION);
7736                     if(mb_ret == IDCANCEL)
7737                     {
7738                         HeapFree(GetProcessHeap(), 0, filename);
7739                         return TRUE;
7740                     }
7741                 }
7742                 hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
7743                 if(hf == INVALID_HANDLE_VALUE)
7744                 {
7745                     WCHAR caption[200], message[200];
7746
7747                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
7748                     LoadStringW(WINSPOOL_hInstance, IDS_CANNOT_OPEN, message, sizeof(message) / sizeof(WCHAR));
7749                     MessageBoxW(hwnd, message, caption, MB_OK | MB_ICONEXCLAMATION);
7750                     HeapFree(GetProcessHeap(), 0, filename);
7751                     return TRUE;
7752                 }
7753                 CloseHandle(hf);
7754                 DeleteFileW(filename);
7755                 output = (LPWSTR *)GetWindowLongPtrW(hwnd, DWLP_USER);
7756                 *output = filename;
7757                 EndDialog(hwnd, IDOK);
7758                 return TRUE;
7759             }
7760             if(LOWORD(wparam) == IDCANCEL)
7761             {
7762                 EndDialog(hwnd, IDCANCEL);
7763                 return TRUE;
7764             }
7765         }
7766         return FALSE;
7767     }
7768     return FALSE;
7769 }
7770
7771 /*****************************************************************************
7772  *          get_filename
7773  */
7774 static BOOL get_filename(LPWSTR *filename)
7775 {
7776     return DialogBoxParamW(WINSPOOL_hInstance, MAKEINTRESOURCEW(FILENAME_DIALOG), GetForegroundWindow(),
7777                            file_dlg_proc, (LPARAM)filename) == IDOK;
7778 }
7779
7780 /*****************************************************************************
7781  *          schedule_file
7782  */
7783 static BOOL schedule_file(LPCWSTR filename)
7784 {
7785     LPWSTR output = NULL;
7786
7787     if(get_filename(&output))
7788     {
7789         TRACE("copy to %s\n", debugstr_w(output));
7790         CopyFileW(filename, output, FALSE);
7791         HeapFree(GetProcessHeap(), 0, output);
7792         return TRUE;
7793     }
7794     return FALSE;
7795 }
7796
7797 /*****************************************************************************
7798  *          schedule_pipe
7799  */
7800 static BOOL schedule_pipe(LPCWSTR cmd, LPCWSTR filename)
7801 {
7802 #ifdef HAVE_FORK
7803     char *unixname, *cmdA;
7804     DWORD len;
7805     int fds[2] = {-1, -1}, file_fd = -1, no_read;
7806     BOOL ret = FALSE;
7807     char buf[1024];
7808
7809     if(!(unixname = wine_get_unix_file_name(filename)))
7810         return FALSE;
7811
7812     len = WideCharToMultiByte(CP_ACP, 0, cmd, -1, NULL, 0, NULL, NULL);
7813     cmdA = HeapAlloc(GetProcessHeap(), 0, len);
7814     WideCharToMultiByte(CP_ACP, 0, cmd, -1, cmdA, len, NULL, NULL);
7815
7816     TRACE("printing with: %s\n", cmdA);
7817
7818     if((file_fd = open(unixname, O_RDONLY)) == -1)
7819         goto end;
7820
7821     if (pipe(fds))
7822     {
7823         ERR("pipe() failed!\n"); 
7824         goto end;
7825     }
7826
7827     if (fork() == 0)
7828     {
7829         close(0);
7830         dup2(fds[0], 0);
7831         close(fds[1]);
7832
7833         /* reset signals that we previously set to SIG_IGN */
7834         signal(SIGPIPE, SIG_DFL);
7835         signal(SIGCHLD, SIG_DFL);
7836
7837         execl("/bin/sh", "/bin/sh", "-c", cmdA, (char*)0);
7838         _exit(1);
7839     }
7840
7841     while((no_read = read(file_fd, buf, sizeof(buf))) > 0)
7842         write(fds[1], buf, no_read);
7843
7844     ret = TRUE;
7845
7846 end:
7847     if(file_fd != -1) close(file_fd);
7848     if(fds[0] != -1) close(fds[0]);
7849     if(fds[1] != -1) close(fds[1]);
7850
7851     HeapFree(GetProcessHeap(), 0, cmdA);
7852     HeapFree(GetProcessHeap(), 0, unixname);
7853     return ret;
7854 #else
7855     return FALSE;
7856 #endif
7857 }
7858
7859 /*****************************************************************************
7860  *          schedule_unixfile
7861  */
7862 static BOOL schedule_unixfile(LPCWSTR output, LPCWSTR filename)
7863 {
7864     int in_fd, out_fd, no_read;
7865     char buf[1024];
7866     BOOL ret = FALSE;
7867     char *unixname, *outputA;
7868     DWORD len;
7869
7870     if(!(unixname = wine_get_unix_file_name(filename)))
7871         return FALSE;
7872
7873     len = WideCharToMultiByte(CP_ACP, 0, output, -1, NULL, 0, NULL, NULL);
7874     outputA = HeapAlloc(GetProcessHeap(), 0, len);
7875     WideCharToMultiByte(CP_ACP, 0, output, -1, outputA, len, NULL, NULL);
7876     
7877     out_fd = open(outputA, O_CREAT | O_TRUNC | O_WRONLY, 0666);
7878     in_fd = open(unixname, O_RDONLY);
7879     if(out_fd == -1 || in_fd == -1)
7880         goto end;
7881
7882     while((no_read = read(in_fd, buf, sizeof(buf))) > 0)
7883         write(out_fd, buf, no_read);
7884
7885     ret = TRUE;
7886 end:
7887     if(in_fd != -1) close(in_fd);
7888     if(out_fd != -1) close(out_fd);
7889     HeapFree(GetProcessHeap(), 0, outputA);
7890     HeapFree(GetProcessHeap(), 0, unixname);
7891     return ret;
7892 }
7893
7894 /*****************************************************************************
7895  *          ScheduleJob [WINSPOOL.@]
7896  *
7897  */
7898 BOOL WINAPI ScheduleJob( HANDLE hPrinter, DWORD dwJobID )
7899 {
7900     opened_printer_t *printer;
7901     BOOL ret = FALSE;
7902     struct list *cursor, *cursor2;
7903
7904     TRACE("(%p, %x)\n", hPrinter, dwJobID);
7905     EnterCriticalSection(&printer_handles_cs);
7906     printer = get_opened_printer(hPrinter);
7907     if(!printer)
7908         goto end;
7909
7910     LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
7911     {
7912         job_t *job = LIST_ENTRY(cursor, job_t, entry);
7913         HANDLE hf;
7914
7915         if(job->job_id != dwJobID) continue;
7916
7917         hf = CreateFileW(job->filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
7918         if(hf != INVALID_HANDLE_VALUE)
7919         {
7920             PRINTER_INFO_5W *pi5;
7921             DWORD needed;
7922             HKEY hkey;
7923             WCHAR output[1024];
7924             static const WCHAR spooler_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
7925                                                 'P','r','i','n','t','i','n','g','\\','S','p','o','o','l','e','r',0};
7926
7927             GetPrinterW(hPrinter, 5, NULL, 0, &needed);
7928             pi5 = HeapAlloc(GetProcessHeap(), 0, needed);
7929             GetPrinterW(hPrinter, 5, (LPBYTE)pi5, needed, &needed);
7930             TRACE("need to schedule job %d filename %s to port %s\n", job->job_id, debugstr_w(job->filename),
7931                   debugstr_w(pi5->pPortName));
7932             
7933             output[0] = 0;
7934
7935             /* @@ Wine registry key: HKCU\Software\Wine\Printing\Spooler */
7936             if(RegOpenKeyW(HKEY_CURRENT_USER, spooler_key, &hkey) == ERROR_SUCCESS)
7937             {
7938                 DWORD type, count = sizeof(output);
7939                 RegQueryValueExW(hkey, pi5->pPortName, NULL, &type, (LPBYTE)output, &count);
7940                 RegCloseKey(hkey);
7941             }
7942             if(output[0] == '|')
7943             {
7944                 schedule_pipe(output + 1, job->filename);
7945             }
7946             else if(output[0])
7947             {
7948                 schedule_unixfile(output, job->filename);
7949             }
7950             else if(!strncmpW(pi5->pPortName, LPR_Port, strlenW(LPR_Port)))
7951             {
7952                 schedule_lpr(pi5->pPortName + strlenW(LPR_Port), job->filename);
7953             }
7954             else if(!strncmpW(pi5->pPortName, CUPS_Port, strlenW(CUPS_Port)))
7955             {
7956                 schedule_cups(pi5->pPortName + strlenW(CUPS_Port), job->filename, job->document_title);
7957             }
7958             else if(!strncmpW(pi5->pPortName, FILE_Port, strlenW(FILE_Port)))
7959             {
7960                 schedule_file(job->filename);
7961             }
7962             else
7963             {
7964                 FIXME("can't schedule to port %s\n", debugstr_w(pi5->pPortName));
7965             }
7966             HeapFree(GetProcessHeap(), 0, pi5);
7967             CloseHandle(hf);
7968             DeleteFileW(job->filename);
7969         }
7970         list_remove(cursor);
7971         HeapFree(GetProcessHeap(), 0, job->document_title);
7972         HeapFree(GetProcessHeap(), 0, job->filename);
7973         HeapFree(GetProcessHeap(), 0, job);
7974         ret = TRUE;
7975         break;
7976     }
7977 end:
7978     LeaveCriticalSection(&printer_handles_cs);
7979     return ret;
7980 }
7981
7982 /*****************************************************************************
7983  *          StartDocDlgA [WINSPOOL.@]
7984  */
7985 LPSTR WINAPI StartDocDlgA( HANDLE hPrinter, DOCINFOA *doc )
7986 {
7987     UNICODE_STRING usBuffer;
7988     DOCINFOW docW;
7989     LPWSTR retW;
7990     LPWSTR docnameW = NULL, outputW = NULL, datatypeW = NULL;
7991     LPSTR ret = NULL;
7992
7993     docW.cbSize = sizeof(docW);
7994     if (doc->lpszDocName)
7995     {
7996         docnameW = asciitounicode(&usBuffer, doc->lpszDocName);
7997         if (!(docW.lpszDocName = docnameW)) return NULL;
7998     }
7999     if (doc->lpszOutput)
8000     {
8001         outputW = asciitounicode(&usBuffer, doc->lpszOutput);
8002         if (!(docW.lpszOutput = outputW)) return NULL;
8003     }
8004     if (doc->lpszDatatype)
8005     {
8006         datatypeW = asciitounicode(&usBuffer, doc->lpszDatatype);
8007         if (!(docW.lpszDatatype = datatypeW)) return NULL;
8008     }
8009     docW.fwType = doc->fwType;
8010
8011     retW = StartDocDlgW(hPrinter, &docW);
8012
8013     if(retW)
8014     {
8015         DWORD len = WideCharToMultiByte(CP_ACP, 0, retW, -1, NULL, 0, NULL, NULL);
8016         ret = HeapAlloc(GetProcessHeap(), 0, len);
8017         WideCharToMultiByte(CP_ACP, 0, retW, -1, ret, len, NULL, NULL);
8018         HeapFree(GetProcessHeap(), 0, retW);
8019     }
8020
8021     HeapFree(GetProcessHeap(), 0, datatypeW);
8022     HeapFree(GetProcessHeap(), 0, outputW);
8023     HeapFree(GetProcessHeap(), 0, docnameW);
8024
8025     return ret;
8026 }
8027
8028 /*****************************************************************************
8029  *          StartDocDlgW [WINSPOOL.@]
8030  *
8031  * Undocumented: Apparently used by gdi32:StartDocW() to popup the file dialog
8032  * when lpszOutput is "FILE:" or if lpszOutput is NULL and the default printer
8033  * port is "FILE:". Also returns the full path if passed a relative path.
8034  *
8035  * The caller should free the returned string from the process heap.
8036  */
8037 LPWSTR WINAPI StartDocDlgW( HANDLE hPrinter, DOCINFOW *doc )
8038 {
8039     LPWSTR ret = NULL;
8040     DWORD len, attr;
8041
8042     if(doc->lpszOutput == NULL) /* Check whether default port is FILE: */
8043     {
8044         PRINTER_INFO_5W *pi5;
8045         GetPrinterW(hPrinter, 5, NULL, 0, &len);
8046         if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
8047             return NULL;
8048         pi5 = HeapAlloc(GetProcessHeap(), 0, len);
8049         GetPrinterW(hPrinter, 5, (LPBYTE)pi5, len, &len);
8050         if(!pi5->pPortName || strcmpW(pi5->pPortName, FILE_Port))
8051         {
8052             HeapFree(GetProcessHeap(), 0, pi5);
8053             return NULL;
8054         }
8055         HeapFree(GetProcessHeap(), 0, pi5);
8056     }
8057
8058     if(doc->lpszOutput == NULL || !strcmpW(doc->lpszOutput, FILE_Port))
8059     {
8060         LPWSTR name;
8061
8062         if (get_filename(&name))
8063         {
8064             if(!(len = GetFullPathNameW(name, 0, NULL, NULL)))
8065             {
8066                 HeapFree(GetProcessHeap(), 0, name);
8067                 return NULL;
8068             }
8069             ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
8070             GetFullPathNameW(name, len, ret, NULL);
8071             HeapFree(GetProcessHeap(), 0, name);
8072         }
8073         return ret;
8074     }
8075
8076     if(!(len = GetFullPathNameW(doc->lpszOutput, 0, NULL, NULL)))
8077         return NULL;
8078
8079     ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
8080     GetFullPathNameW(doc->lpszOutput, len, ret, NULL);
8081         
8082     attr = GetFileAttributesW(ret);
8083     if(attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY))
8084     {
8085         HeapFree(GetProcessHeap(), 0, ret);
8086         ret = NULL;
8087     }
8088     return ret;
8089 }