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