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