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