msi: Fix the prototype of MsiDatabaseIsTablePersistent to match the SDK.
[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 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 # ifndef SONAME_LIBCUPS
42 #  define SONAME_LIBCUPS "libcups.so"
43 # endif
44 #endif
45
46 #define NONAMELESSUNION
47 #define NONAMELESSSTRUCT
48 #include "wine/library.h"
49 #include "windef.h"
50 #include "winbase.h"
51 #include "winuser.h"
52 #include "winerror.h"
53 #include "winreg.h"
54 #include "wingdi.h"
55 #include "winspool.h"
56 #include "winternl.h"
57 #include "wine/windef16.h"
58 #include "wine/unicode.h"
59 #include "wine/debug.h"
60 #include "wine/list.h"
61 #include "heap.h"
62 #include "winnls.h"
63
64 #include "wspool.h"
65
66 WINE_DEFAULT_DEBUG_CHANNEL(winspool);
67
68 static CRITICAL_SECTION printer_handles_cs;
69 static CRITICAL_SECTION_DEBUG printer_handles_cs_debug = 
70 {
71     0, 0, &printer_handles_cs,
72     { &printer_handles_cs_debug.ProcessLocksList, &printer_handles_cs_debug.ProcessLocksList },
73       0, 0, { (DWORD_PTR)(__FILE__ ": printer_handles_cs") }
74 };
75 static CRITICAL_SECTION printer_handles_cs = { &printer_handles_cs_debug, -1, 0, 0, 0, 0 };
76
77 typedef struct {
78     DWORD job_id;
79     HANDLE hf;
80 } started_doc_t;
81
82 typedef struct {
83     struct list jobs;
84     LONG ref;
85 } jobqueue_t;
86
87 typedef struct {
88     LPWSTR name;
89     jobqueue_t *queue;
90     started_doc_t *doc;
91 } opened_printer_t;
92
93 typedef struct {
94     struct list entry;
95     DWORD job_id;
96     WCHAR *filename;
97     WCHAR *document_title;
98 } job_t;
99
100
101 typedef struct {
102     LPCWSTR  envname;
103     LPCWSTR  subdir;
104     DWORD    driverversion;
105     LPCWSTR  versionregpath;
106     LPCWSTR  versionsubdir;
107 } printenv_t;
108
109 /* ############################### */
110
111 static opened_printer_t **printer_handles;
112 static int nb_printer_handles;
113 static LONG next_job_id = 1;
114
115 static DWORD (WINAPI *GDI_CallDeviceCapabilities16)( LPCSTR lpszDevice, LPCSTR lpszPort,
116                                                      WORD fwCapability, LPSTR lpszOutput,
117                                                      LPDEVMODEA lpdm );
118 static INT (WINAPI *GDI_CallExtDeviceMode16)( HWND hwnd, LPDEVMODEA lpdmOutput,
119                                               LPSTR lpszDevice, LPSTR lpszPort,
120                                               LPDEVMODEA lpdmInput, LPSTR lpszProfile,
121                                               DWORD fwMode );
122
123 static const WCHAR DriversW[] = { 'S','y','s','t','e','m','\\',
124                                   'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
125                                   'c','o','n','t','r','o','l','\\',
126                                   'P','r','i','n','t','\\',
127                                   'E','n','v','i','r','o','n','m','e','n','t','s','\\',
128                                   '%','s','\\','D','r','i','v','e','r','s','%','s',0 };
129
130 static const WCHAR MonitorsW[] =  { 'S','y','s','t','e','m','\\',
131                                   'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
132                                   'C','o','n','t','r','o','l','\\',
133                                   'P','r','i','n','t','\\',
134                                   'M','o','n','i','t','o','r','s',0};
135
136 static const WCHAR PrintersW[] = {'S','y','s','t','e','m','\\',
137                                   'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
138                                   'C','o','n','t','r','o','l','\\',
139                                   'P','r','i','n','t','\\',
140                                   'P','r','i','n','t','e','r','s',0};
141
142 static const WCHAR LocalPortW[] = {'L','o','c','a','l',' ','P','o','r','t',0};
143
144 static const WCHAR user_default_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
145                                               'M','i','c','r','o','s','o','f','t','\\',
146                                               'W','i','n','d','o','w','s',' ','N','T','\\',
147                                               'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
148                                               'W','i','n','d','o','w','s',0};
149
150 static const WCHAR user_printers_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
151                                                'M','i','c','r','o','s','o','f','t','\\',
152                                                'W','i','n','d','o','w','s',' ','N','T','\\',
153                                                'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
154                                                'D','e','v','i','c','e','s',0};
155
156 static const WCHAR DefaultEnvironmentW[] = {'W','i','n','e',0};
157 static const WCHAR envname_win40W[] = {'W','i','n','d','o','w','s',' ','4','.','0',0};
158 static const WCHAR envname_x86W[] =   {'W','i','n','d','o','w','s',' ','N','T',' ','x','8','6',0};
159 static const WCHAR subdir_win40W[] = {'w','i','n','4','0',0};
160 static const WCHAR subdir_x86W[] =   {'w','3','2','x','8','6',0};
161 static const WCHAR Version3_RegPathW[] = {'\\','V','e','r','s','i','o','n','-','3',0};
162 static const WCHAR Version3_SubdirW[] = {'\\','3',0};
163
164 static const WCHAR spooldriversW[] = {'\\','s','p','o','o','l','\\','d','r','i','v','e','r','s','\\',0};
165 static const WCHAR spoolprtprocsW[] = {'\\','s','p','o','o','l','\\','p','r','t','p','r','o','c','s','\\',0};
166
167 static const WCHAR Configuration_FileW[] = {'C','o','n','f','i','g','u','r','a','t',
168                                       'i','o','n',' ','F','i','l','e',0};
169 static const WCHAR DatatypeW[] = {'D','a','t','a','t','y','p','e',0};
170 static const WCHAR Data_FileW[] = {'D','a','t','a',' ','F','i','l','e',0};
171 static const WCHAR Default_DevModeW[] = {'D','e','f','a','u','l','t',' ','D','e','v',
172                                    'M','o','d','e',0};
173 static const WCHAR Dependent_FilesW[] = {'D','e','p','e','n','d','e','n','t',' ','F',
174                                    'i','l','e','s',0};
175 static const WCHAR DescriptionW[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
176 static const WCHAR DriverW[] = {'D','r','i','v','e','r',0};
177 static const WCHAR Help_FileW[] = {'H','e','l','p',' ','F','i','l','e',0};
178 static const WCHAR LocationW[] = {'L','o','c','a','t','i','o','n',0};
179 static const WCHAR MonitorW[] = {'M','o','n','i','t','o','r',0};
180 static const WCHAR NameW[] = {'N','a','m','e',0};
181 static const WCHAR ParametersW[] = {'P','a','r','a','m','e','t','e','r','s',0};
182 static const WCHAR PortW[] = {'P','o','r','t',0};
183 static const WCHAR Print_ProcessorW[] = {'P','r','i','n','t',' ','P','r','o','c','e',
184                                    's','s','o','r',0};
185 static const WCHAR Printer_DriverW[] = {'P','r','i','n','t','e','r',' ','D','r','i',
186                                   'v','e','r',0};
187 static const WCHAR PrinterDriverDataW[] = {'P','r','i','n','t','e','r','D','r','i',
188                                      'v','e','r','D','a','t','a',0};
189 static const WCHAR Separator_FileW[] = {'S','e','p','a','r','a','t','o','r',' ','F',
190                                   'i','l','e',0};
191 static const WCHAR Share_NameW[] = {'S','h','a','r','e',' ','N','a','m','e',0};
192 static const WCHAR WinPrintW[] = {'W','i','n','P','r','i','n','t',0};
193 static const WCHAR deviceW[]  = {'d','e','v','i','c','e',0};
194 static const WCHAR devicesW[] = {'d','e','v','i','c','e','s',0};
195 static const WCHAR windowsW[] = {'w','i','n','d','o','w','s',0};
196 static const WCHAR emptyStringW[] = {0};
197
198 static const WCHAR May_Delete_Value[] = {'W','i','n','e','M','a','y','D','e','l','e','t','e','M','e',0};
199
200 static const WCHAR CUPS_Port[] = {'C','U','P','S',':',0};
201 static const WCHAR FILE_Port[] = {'F','I','L','E',':',0};
202 static const WCHAR LPR_Port[] = {'L','P','R',':',0};
203
204 static const WCHAR default_doc_title[] = {'L','o','c','a','l',' ','D','o','w','n','l','e','v','e','l',' ',
205                                           'D','o','c','u','m','e','n','t',0};
206
207 static HKEY WINSPOOL_OpenDriverReg( LPVOID pEnvironment, BOOL unicode);
208 static BOOL WINSPOOL_GetPrinterDriver(HANDLE hPrinter, LPWSTR pEnvironment,
209                                       DWORD Level, LPBYTE pDriverInfo,
210                                       DWORD cbBuf, LPDWORD pcbNeeded,
211                                       BOOL unicode);
212 static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey);
213 static DWORD WINSPOOL_SHDeleteKeyW(HKEY hKey, LPCWSTR lpszSubKey);
214
215 /******************************************************************
216  *  validate the user-supplied printing-environment [internal]
217  *
218  * PARAMS
219  *  env  [I] PTR to Environment-String or NULL
220  *
221  * RETURNS
222  *  Failure:  NULL
223  *  Success:  PTR to printenv_t
224  *
225  * NOTES
226  *  An empty string is handled the same way as NULL.
227  *  SetLastEror(ERROR_INVALID_ENVIRONMENT) is called on Failure
228  *  
229  */
230
231 static const  printenv_t * validate_envW(LPCWSTR env)
232 {
233     static const printenv_t env_x86 =   {envname_x86W, subdir_x86W,
234                                          3, Version3_RegPathW, Version3_SubdirW};
235     static const printenv_t env_win40 = {envname_win40W, subdir_win40W,
236                                          0, emptyStringW, emptyStringW};
237     static const printenv_t * const all_printenv[]={&env_x86, &env_win40};
238
239     const printenv_t *result = NULL;
240     unsigned int i;
241
242     TRACE("testing %s\n", debugstr_w(env));
243     if (env && env[0])
244     {
245         for (i = 0; i < sizeof(all_printenv)/sizeof(all_printenv[0]); i++)
246         {
247             if (lstrcmpiW(env, all_printenv[i]->envname) == 0)
248             {
249                 result = all_printenv[i];
250                 break;
251             }
252         }
253
254         if (result == NULL) {
255             FIXME("unsupported Environment: %s\n", debugstr_w(env));
256             SetLastError(ERROR_INVALID_ENVIRONMENT);
257         }
258         /* on win9x, only "Windows 4.0" is allowed, but we ignore this */
259     }
260     else
261     {
262         result = (GetVersion() & 0x80000000) ? &env_win40 : &env_x86;
263     }
264     TRACE("using %p: %s\n", result, debugstr_w(result ? result->envname : NULL));
265
266     return result;
267 }
268
269
270 /* RtlCreateUnicodeStringFromAsciiz will return an empty string in the buffer
271    if passed a NULL string. This returns NULLs to the result. 
272 */
273 static inline PWSTR asciitounicode( UNICODE_STRING * usBufferPtr, LPCSTR src )
274 {
275     if ( (src) )
276     {
277         RtlCreateUnicodeStringFromAsciiz(usBufferPtr, src);
278         return usBufferPtr->Buffer;
279     }
280     usBufferPtr->Buffer = NULL; /* so that RtlFreeUnicodeString won't barf */
281     return NULL;
282 }
283             
284 static LPWSTR strdupW(LPCWSTR p)
285 {
286     LPWSTR ret;
287     DWORD len;
288
289     if(!p) return NULL;
290     len = (strlenW(p) + 1) * sizeof(WCHAR);
291     ret = HeapAlloc(GetProcessHeap(), 0, len);
292     memcpy(ret, p, len);
293     return ret;
294 }
295
296 static void
297 WINSPOOL_SetDefaultPrinter(const char *devname, const char *name,BOOL force) {
298     char qbuf[200];
299
300     /* If forcing, or no profile string entry for device yet, set the entry
301      *
302      * The always change entry if not WINEPS yet is discussable.
303      */
304     if (force                                                           ||
305         !GetProfileStringA("windows","device","*",qbuf,sizeof(qbuf))    ||
306         !strcmp(qbuf,"*")                                               ||
307         !strstr(qbuf,"WINEPS.DRV")
308     ) {
309         char *buf = HeapAlloc(GetProcessHeap(),0,strlen(name)+strlen(devname)+strlen(",WINEPS.DRV,LPR:")+1);
310         HKEY hkey;
311
312         sprintf(buf,"%s,WINEPS.DRV,LPR:%s",devname,name);
313         WriteProfileStringA("windows","device",buf);
314         if(RegCreateKeyW(HKEY_CURRENT_USER, user_default_reg_key, &hkey) == ERROR_SUCCESS) {
315             RegSetValueExA(hkey, "Device", 0, REG_SZ, (LPBYTE)buf, strlen(buf) + 1);
316             RegCloseKey(hkey);
317         }
318         HeapFree(GetProcessHeap(),0,buf);
319     }
320 }
321
322 #ifdef HAVE_CUPS_CUPS_H
323 static typeof(cupsGetDests)  *pcupsGetDests;
324 static typeof(cupsGetPPD)    *pcupsGetPPD;
325 static typeof(cupsPrintFile) *pcupsPrintFile;
326 static void *cupshandle;
327
328 static BOOL CUPS_LoadPrinters(void)
329 {
330     int                   i, nrofdests;
331     BOOL                  hadprinter = FALSE;
332     cups_dest_t          *dests;
333     PRINTER_INFO_2A       pinfo2a;
334     char   *port,*devline;
335     HKEY hkeyPrinter, hkeyPrinters, hkey;
336
337     cupshandle = wine_dlopen(SONAME_LIBCUPS, RTLD_NOW, NULL, 0);
338     if (!cupshandle) 
339         return FALSE;
340     TRACE("loaded %s\n", SONAME_LIBCUPS);
341
342 #define DYNCUPS(x)                                      \
343         p##x = wine_dlsym(cupshandle, #x, NULL,0);      \
344         if (!p##x) return FALSE;
345
346     DYNCUPS(cupsGetPPD);
347     DYNCUPS(cupsGetDests);
348     DYNCUPS(cupsPrintFile);
349 #undef DYNCUPS
350
351     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
352        ERROR_SUCCESS) {
353         ERR("Can't create Printers key\n");
354         return FALSE;
355     }
356
357     nrofdests = pcupsGetDests(&dests);
358     TRACE("Found %d CUPS %s:\n", nrofdests, (nrofdests == 1) ? "printer" : "printers");
359     for (i=0;i<nrofdests;i++) {
360         port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(dests[i].name)+1);
361         sprintf(port,"LPR:%s",dests[i].name);
362         devline=HeapAlloc(GetProcessHeap(),0,sizeof("WINEPS.DRV,")+strlen(port));
363         sprintf(devline,"WINEPS.DRV,%s",port);
364         WriteProfileStringA("devices",dests[i].name,devline);
365         if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
366             RegSetValueExA(hkey, dests[i].name, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
367             RegCloseKey(hkey);
368         }
369         HeapFree(GetProcessHeap(),0,devline);
370
371         TRACE("Printer %d: %s\n", i, dests[i].name);
372         if(RegOpenKeyA(hkeyPrinters, dests[i].name, &hkeyPrinter) == ERROR_SUCCESS) {
373             /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
374                and continue */
375             TRACE("Printer already exists\n");
376             RegDeleteValueW(hkeyPrinter, May_Delete_Value);
377             RegCloseKey(hkeyPrinter);
378         } else {
379             static CHAR data_type[] = "RAW",
380                     print_proc[]    = "WinPrint",
381                     driver_name[]   = "PS Driver",
382                     comment[]       = "WINEPS Printer using CUPS",
383                     location[]      = "<physical location of printer>",
384                     params[]        = "<parameters?>",
385                     share_name[]    = "<share name?>",
386                     sep_file[]      = "<sep file?>";
387
388             memset(&pinfo2a,0,sizeof(pinfo2a));
389             pinfo2a.pPrinterName    = dests[i].name;
390             pinfo2a.pDatatype       = data_type;
391             pinfo2a.pPrintProcessor = print_proc;
392             pinfo2a.pDriverName     = driver_name;
393             pinfo2a.pComment        = comment;
394             pinfo2a.pLocation       = location;
395             pinfo2a.pPortName       = port;
396             pinfo2a.pParameters     = params;
397             pinfo2a.pShareName      = share_name;
398             pinfo2a.pSepFile        = sep_file;
399
400             if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
401                 if (GetLastError() != ERROR_PRINTER_ALREADY_EXISTS)
402                     ERR("printer '%s' not added by AddPrinterA (error %ld)\n",dests[i].name,GetLastError());
403             }
404         }
405         HeapFree(GetProcessHeap(),0,port);
406
407         hadprinter = TRUE;
408         if (dests[i].is_default)
409             WINSPOOL_SetDefaultPrinter(dests[i].name, dests[i].name, TRUE);
410     }
411     RegCloseKey(hkeyPrinters);
412     return hadprinter;
413 }
414 #endif
415
416 static BOOL
417 PRINTCAP_ParseEntry(const char *pent, BOOL isfirst) {
418     PRINTER_INFO_2A     pinfo2a;
419     char                *e,*s,*name,*prettyname,*devname;
420     BOOL                ret = FALSE, set_default = FALSE;
421     char                *port,*devline,*env_default;
422     HKEY                hkeyPrinter, hkeyPrinters, hkey;
423
424     while (isspace(*pent)) pent++;
425     s = strchr(pent,':');
426     if(s) *s='\0';
427     name = HeapAlloc(GetProcessHeap(), 0, strlen(pent) + 1);
428     strcpy(name,pent);
429     if(s) {
430         *s=':';
431         pent = s;
432     } else
433         pent = "";
434
435     TRACE("name=%s entry=%s\n",name, pent);
436
437     if(ispunct(*name)) { /* a tc entry, not a real printer */
438         TRACE("skipping tc entry\n");
439         goto end;
440     }
441
442     if(strstr(pent,":server")) { /* server only version so skip */
443         TRACE("skipping server entry\n");
444         goto end;
445     }
446
447     /* Determine whether this is a postscript printer. */
448
449     ret = TRUE;
450     env_default = getenv("PRINTER");
451     prettyname = name;
452     /* Get longest name, usually the one at the right for later display. */
453     while((s=strchr(prettyname,'|'))) {
454         *s = '\0';
455         e = s;
456         while(isspace(*--e)) *e = '\0';
457         TRACE("\t%s\n", debugstr_a(prettyname));
458         if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
459         for(prettyname = s+1; isspace(*prettyname); prettyname++)
460             ;
461     }
462     e = prettyname + strlen(prettyname);
463     while(isspace(*--e)) *e = '\0';
464     TRACE("\t%s\n", debugstr_a(prettyname));
465     if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
466
467     /* prettyname must fit into the dmDeviceName member of DEVMODE struct,
468      * if it is too long, we use it as comment below. */
469     devname = prettyname;
470     if (strlen(devname)>=CCHDEVICENAME-1)
471          devname = name;
472     if (strlen(devname)>=CCHDEVICENAME-1) {
473         ret = FALSE;
474         goto end;
475     }
476
477     port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(name)+1);
478     sprintf(port,"LPR:%s",name);
479
480     devline=HeapAlloc(GetProcessHeap(),0,sizeof("WINEPS.DRV,")+strlen(port));
481     sprintf(devline,"WINEPS.DRV,%s",port);
482     WriteProfileStringA("devices",devname,devline);
483     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
484         RegSetValueExA(hkey, devname, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
485         RegCloseKey(hkey);
486     }
487     HeapFree(GetProcessHeap(),0,devline);
488     
489     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
490        ERROR_SUCCESS) {
491         ERR("Can't create Printers key\n");
492         ret = FALSE;
493         goto end;
494     }
495     if(RegOpenKeyA(hkeyPrinters, devname, &hkeyPrinter) == ERROR_SUCCESS) {
496         /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
497            and continue */
498         TRACE("Printer already exists\n");
499         RegDeleteValueW(hkeyPrinter, May_Delete_Value);
500         RegCloseKey(hkeyPrinter);
501     } else {
502         static CHAR data_type[]   = "RAW",
503                     print_proc[]  = "WinPrint",
504                     driver_name[] = "PS Driver",
505                     comment[]     = "WINEPS Printer using LPR",
506                     params[]      = "<parameters?>",
507                     share_name[]  = "<share name?>",
508                     sep_file[]    = "<sep file?>";
509
510         memset(&pinfo2a,0,sizeof(pinfo2a));
511         pinfo2a.pPrinterName    = devname;
512         pinfo2a.pDatatype       = data_type;
513         pinfo2a.pPrintProcessor = print_proc;
514         pinfo2a.pDriverName     = driver_name;
515         pinfo2a.pComment        = comment;
516         pinfo2a.pLocation       = prettyname;
517         pinfo2a.pPortName       = port;
518         pinfo2a.pParameters     = params;
519         pinfo2a.pShareName      = share_name;
520         pinfo2a.pSepFile        = sep_file;
521
522         if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
523             if (GetLastError()!=ERROR_PRINTER_ALREADY_EXISTS)
524                 ERR("%s not added by AddPrinterA (%ld)\n",name,GetLastError());
525         }
526     }
527     RegCloseKey(hkeyPrinters);
528
529     if (isfirst || set_default)
530         WINSPOOL_SetDefaultPrinter(devname,name,TRUE);
531
532     HeapFree(GetProcessHeap(), 0, port);
533  end:
534     HeapFree(GetProcessHeap(), 0, name);
535     return ret;
536 }
537
538 static BOOL
539 PRINTCAP_LoadPrinters(void) {
540     BOOL                hadprinter = FALSE;
541     char                buf[200];
542     FILE                *f;
543     char *pent = NULL;
544     BOOL had_bash = FALSE;
545
546     f = fopen("/etc/printcap","r");
547     if (!f)
548         return FALSE;
549
550     while(fgets(buf,sizeof(buf),f)) {
551         char *start, *end;
552
553         end=strchr(buf,'\n');
554         if (end) *end='\0';
555     
556         start = buf;
557         while(isspace(*start)) start++;
558         if(*start == '#' || *start == '\0')
559             continue;
560
561         if(pent && !had_bash && *start != ':' && *start != '|') { /* start of new entry, parse the previous one */
562             hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
563             HeapFree(GetProcessHeap(),0,pent);
564             pent = NULL;
565         }
566
567         if (end && *--end == '\\') {
568             *end = '\0';
569             had_bash = TRUE;
570         } else
571             had_bash = FALSE;
572
573         if (pent) {
574             pent=HeapReAlloc(GetProcessHeap(),0,pent,strlen(pent)+strlen(start)+1);
575             strcat(pent,start);
576         } else {
577             pent=HeapAlloc(GetProcessHeap(),0,strlen(start)+1);
578             strcpy(pent,start);
579         }
580
581     }
582     if(pent) {
583         hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
584         HeapFree(GetProcessHeap(),0,pent);
585     }
586     fclose(f);
587     return hadprinter;
588 }
589
590 static inline DWORD set_reg_szW(HKEY hkey, const WCHAR *keyname, const WCHAR *value)
591 {
592     if (value)
593         return RegSetValueExW(hkey, keyname, 0, REG_SZ, (const BYTE*)value,
594                    lstrlenW(value) * sizeof(WCHAR));
595     else
596         return ERROR_FILE_NOT_FOUND;
597 }
598
599 void WINSPOOL_LoadSystemPrinters(void)
600 {
601     HKEY                hkey, hkeyPrinters;
602     DRIVER_INFO_3A      di3a;
603     HANDLE              hprn;
604     DWORD               needed, num, i;
605     WCHAR               PrinterName[256];
606     BOOL                done = FALSE;
607     static CHAR         name[]              = "PS Driver",
608                         driver_path[]       = "wineps16",
609                         data_file[]         = "<datafile?>",
610                         config_file[]       = "wineps16",
611                         help_file[]         = "<helpfile?>",
612                         dep_file[]          = "<dependend files?>",
613                         monitor_name[]      = "<monitor name?>",
614                         default_data_type[] = "RAW";
615
616     di3a.cVersion = (GetVersion() & 0x80000000) ? 0 : 3; /* FIXME: add 1, 2 */
617     di3a.pName            = name;
618     di3a.pEnvironment     = NULL;      /* NULL means auto */
619     di3a.pDriverPath      = driver_path;
620     di3a.pDataFile        = data_file;
621     di3a.pConfigFile      = config_file;
622     di3a.pHelpFile        = help_file;
623     di3a.pDependentFiles  = dep_file;
624     di3a.pMonitorName     = monitor_name;
625     di3a.pDefaultDataType = default_data_type;
626
627     if (!AddPrinterDriverA(NULL,3,(LPBYTE)&di3a)) {
628         ERR("Failed adding PS Driver (%ld)\n",GetLastError());
629         return;
630     }
631
632     /* This ensures that all printer entries have a valid Name value.  If causes
633        problems later if they don't.  If one is found to be missed we create one
634        and set it equal to the name of the key */
635     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
636         if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &num, NULL, NULL,
637                             NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
638             for(i = 0; i < num; i++) {
639                 if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)) == ERROR_SUCCESS) {
640                     if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkey) == ERROR_SUCCESS) {
641                         if(RegQueryValueExW(hkey, NameW, 0, 0, 0, &needed) == ERROR_FILE_NOT_FOUND) {
642                             set_reg_szW(hkey, NameW, PrinterName);
643                         }
644                         RegCloseKey(hkey);
645                     }
646                 }
647             }
648         }
649         RegCloseKey(hkeyPrinters);
650     }
651
652     /* We want to avoid calling AddPrinter on printers as much as
653        possible, because on cups printers this will (eventually) lead
654        to a call to cupsGetPPD which takes forever, even with non-cups
655        printers AddPrinter takes a while.  So we'll tag all printers that
656        were automatically added last time around, if they still exist
657        we'll leave them be otherwise we'll delete them. */
658     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
659     if(needed) {
660         PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
661         if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
662             for(i = 0; i < num; i++) {
663                 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
664                     if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
665                         if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
666                             DWORD dw = 1;
667                             RegSetValueExW(hkey, May_Delete_Value, 0, REG_DWORD, (LPBYTE)&dw, sizeof(dw));
668                             RegCloseKey(hkey);
669                         }
670                         ClosePrinter(hprn);
671                     }
672                 }
673             }
674         }
675         HeapFree(GetProcessHeap(), 0, pi);
676     }
677
678
679 #ifdef HAVE_CUPS_CUPS_H
680     done = CUPS_LoadPrinters();
681 #endif
682
683     if(!done) { /* If we have any CUPS based printers, skip looking for printcap printers */
684         /* Check for [ppd] section in config file before parsing /etc/printcap */
685         /* @@ Wine registry key: HKCU\Software\Wine\Printing\PPD Files */
686         if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Printing\\PPD Files",
687                         &hkey) == ERROR_SUCCESS) {
688             RegCloseKey(hkey);
689             PRINTCAP_LoadPrinters();
690         }
691     }
692
693     /* Now enumerate the list again and delete any printers that a still tagged */
694     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
695     if(needed) {
696         PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
697         if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
698             for(i = 0; i < num; i++) {
699                 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
700                     if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
701                         if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
702                             DWORD dw, type, size = sizeof(dw);
703                             if(RegQueryValueExW(hkey, May_Delete_Value, NULL, &type, (LPBYTE)&dw, &size) == ERROR_SUCCESS) {
704                                 TRACE("Deleting old printer %s\n", pi[i].pPrinterName);
705                                 DeletePrinter(hprn);
706                             }
707                             RegCloseKey(hkey);
708                         }
709                         ClosePrinter(hprn);
710                     }
711                 }
712             }
713         }
714         HeapFree(GetProcessHeap(), 0, pi);
715     }
716
717     return;
718
719 }
720
721 /*****************************************************************************
722  * enumerate the local monitors (INTERNAL)  
723  *
724  * returns the needed size (in bytes) for pMonitors
725  * and  *lpreturned is set to number of entries returned in pMonitors
726  *
727  */
728 static DWORD get_local_monitors(DWORD level, LPBYTE pMonitors, DWORD cbBuf, LPDWORD lpreturned)
729 {
730     HKEY    hroot = NULL;
731     HKEY    hentry = NULL;
732     LPWSTR  ptr;
733     LPMONITOR_INFO_2W mi;
734     WCHAR   buffer[MAX_PATH];
735     WCHAR   dllname[MAX_PATH];
736     DWORD   dllsize;
737     DWORD   len;
738     DWORD   index = 0;
739     DWORD   needed = 0;
740     DWORD   numentries;
741     DWORD   entrysize;
742
743     entrysize = (level == 1) ? sizeof(MONITOR_INFO_1W) : sizeof(MONITOR_INFO_2W);
744
745     numentries = *lpreturned;       /* this is 0, when we scan the registry */
746     len = entrysize * numentries;
747     ptr = (LPWSTR) &pMonitors[len];
748
749     numentries = 0;
750     len = sizeof(buffer);
751     buffer[0] = '\0';
752
753     /* Windows creates the "Monitors"-Key on reboot / start "spooler" */
754     if (RegCreateKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) == ERROR_SUCCESS) {
755         /* Scan all Monitor-Registry-Keys */
756         while (RegEnumKeyExW(hroot, index, buffer, &len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
757             TRACE("Monitor_%ld: %s\n", numentries, debugstr_w(buffer));
758             dllsize = sizeof(dllname);
759             dllname[0] = '\0';
760
761             /* The Monitor must have a Driver-DLL */
762             if (RegOpenKeyExW(hroot, buffer, 0, KEY_READ, &hentry) == ERROR_SUCCESS) {
763                 if (RegQueryValueExW(hentry, DriverW, NULL, NULL, (LPBYTE) dllname, &dllsize) == ERROR_SUCCESS) {
764                     /* We found a valid DLL for this Monitor. */
765                     TRACE("using Driver: %s\n", debugstr_w(dllname));
766                 }
767                 RegCloseKey(hentry);
768             }
769
770             /* Windows returns only Port-Monitors here, but to simplify our code,
771                we do no filtering for Language-Monitors */
772             if (dllname[0]) {
773                 numentries++;
774                 needed += entrysize;
775                 needed += (len+1) * sizeof(WCHAR);  /* len is lstrlenW(monitorname) */
776                 if (level > 1) {
777                     /* we install and return only monitors for "Windows NT x86" */
778                     needed += (lstrlenW(envname_x86W) +1) * sizeof(WCHAR);
779                     needed += dllsize;
780                 }
781
782                 /* required size is calculated. Now fill the user-buffer */
783                 if (pMonitors && (cbBuf >= needed)){
784                     mi = (LPMONITOR_INFO_2W) pMonitors;
785                     pMonitors += entrysize;
786
787                     TRACE("%p: writing MONITOR_INFO_%ldW #%ld\n", mi, level, numentries);
788                     mi->pName = ptr;
789                     lstrcpyW(ptr, buffer);      /* Name of the Monitor */
790                     ptr += (len+1);               /* len is lstrlenW(monitorname) */
791                     if (level > 1) {
792                         mi->pEnvironment = ptr;
793                         lstrcpyW(ptr, envname_x86W); /* fixed to "Windows NT x86" */
794                         ptr += (lstrlenW(envname_x86W)+1);
795
796                         mi->pDLLName = ptr;
797                         lstrcpyW(ptr, dllname);         /* Name of the Driver-DLL */
798                         ptr += (dllsize / sizeof(WCHAR));
799                     }
800                 }
801             }
802             index++;
803             len = sizeof(buffer);
804             buffer[0] = '\0';
805         }
806         RegCloseKey(hroot);
807     }
808     *lpreturned = numentries;
809     TRACE("need %ld byte for %ld entries\n", needed, numentries);
810     return needed;
811 }
812
813 /******************************************************************
814  *  get_opened_printer_entry
815  *  Get the first place empty in the opened printer table
816  *
817  * ToDo:
818  *  - pDefault is ignored
819  */
820 static HANDLE get_opened_printer_entry(LPCWSTR name, LPPRINTER_DEFAULTSW pDefault)
821 {
822     UINT_PTR handle = nb_printer_handles, i;
823     jobqueue_t *queue = NULL;
824     opened_printer_t *printer = NULL;
825
826     EnterCriticalSection(&printer_handles_cs);
827
828     for (i = 0; i < nb_printer_handles; i++)
829     {
830         if (!printer_handles[i])
831         {
832             if(handle == nb_printer_handles)
833                 handle = i;
834         }
835         else
836         {
837             if(!queue && (name) && !lstrcmpW(name, printer_handles[i]->name))
838                 queue = printer_handles[i]->queue;
839         }
840     }
841
842     if (handle >= nb_printer_handles)
843     {
844         opened_printer_t **new_array;
845         if (printer_handles)
846             new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, printer_handles,
847                                      (nb_printer_handles + 16) * sizeof(*new_array) );
848         else
849             new_array = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
850                                    (nb_printer_handles + 16) * sizeof(*new_array) );
851
852         if (!new_array)
853         {
854             handle = 0;
855             goto end;
856         }
857         printer_handles = new_array;
858         nb_printer_handles += 16;
859     }
860
861     if (!(printer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*printer))))
862     {
863         handle = 0;
864         goto end;
865     }
866
867     if(name) {
868         printer->name = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
869         if (!printer->name) {
870             handle = 0;
871             goto end;
872         }
873         strcpyW(printer->name, name);
874     }
875
876     if(queue)
877         printer->queue = queue;
878     else
879     {
880         printer->queue = HeapAlloc(GetProcessHeap(), 0, sizeof(*queue));
881         if (!printer->queue) {
882             handle = 0;
883             goto end;
884         }
885         list_init(&printer->queue->jobs);
886         printer->queue->ref = 0;
887     }
888     InterlockedIncrement(&printer->queue->ref);
889
890     printer_handles[handle] = printer;
891     handle++;
892 end:
893     LeaveCriticalSection(&printer_handles_cs);
894     if (!handle && printer) {
895         /* Something Failed: Free the Buffers */
896         HeapFree(GetProcessHeap(), 0, printer->name);
897         if (!queue) HeapFree(GetProcessHeap(), 0, printer->queue);
898         HeapFree(GetProcessHeap(), 0, printer);
899     }
900
901     return (HANDLE)handle;
902 }
903
904 /******************************************************************
905  *  get_opened_printer
906  *  Get the pointer to the opened printer referred by the handle
907  */
908 static opened_printer_t *get_opened_printer(HANDLE hprn)
909 {
910     UINT_PTR idx = (UINT_PTR)hprn;
911     opened_printer_t *ret = NULL;
912
913     EnterCriticalSection(&printer_handles_cs);
914
915     if ((idx <= 0) || (idx > nb_printer_handles))
916         goto end;
917
918     ret = printer_handles[idx - 1];
919 end:
920     LeaveCriticalSection(&printer_handles_cs);
921     return ret;
922 }
923
924 /******************************************************************
925  *  get_opened_printer_name
926  *  Get the pointer to the opened printer name referred by the handle
927  */
928 static LPCWSTR get_opened_printer_name(HANDLE hprn)
929 {
930     opened_printer_t *printer = get_opened_printer(hprn);
931     if(!printer) return NULL;
932     return printer->name;
933 }
934
935 /******************************************************************
936  *  WINSPOOL_GetOpenedPrinterRegKey
937  *
938  */
939 static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey)
940 {
941     LPCWSTR name = get_opened_printer_name(hPrinter);
942     DWORD ret;
943     HKEY hkeyPrinters;
944
945     if(!name) return ERROR_INVALID_HANDLE;
946
947     if((ret = RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters)) !=
948        ERROR_SUCCESS)
949         return ret;
950
951     if(RegOpenKeyW(hkeyPrinters, name, phkey) != ERROR_SUCCESS)
952     {
953         ERR("Can't find opened printer %s in registry\n",
954             debugstr_w(name));
955         RegCloseKey(hkeyPrinters);
956         return ERROR_INVALID_PRINTER_NAME; /* ? */
957     }
958     RegCloseKey(hkeyPrinters);
959     return ERROR_SUCCESS;
960 }
961
962 /******************************************************************
963  *                  get_job
964  *
965  *  Get the pointer to the specified job.
966  *  Should hold the printer_handles_cs before calling.
967  */
968 static job_t *get_job(HANDLE hprn, DWORD JobId)
969 {
970     opened_printer_t *printer = get_opened_printer(hprn);
971     job_t *job;
972
973     if(!printer) return NULL;
974     LIST_FOR_EACH_ENTRY(job, &printer->queue->jobs, job_t, entry)
975     {
976         if(job->job_id == JobId)
977             return job;
978     }
979     return NULL;
980 }
981
982 /***********************************************************
983  *      DEVMODEcpyAtoW
984  */
985 static LPDEVMODEW DEVMODEcpyAtoW(DEVMODEW *dmW, const DEVMODEA *dmA)
986 {
987     BOOL Formname;
988     ptrdiff_t off_formname = (const char *)dmA->dmFormName - (const char *)dmA;
989     DWORD size;
990
991     Formname = (dmA->dmSize > off_formname);
992     size = dmA->dmSize + CCHDEVICENAME + (Formname ? CCHFORMNAME : 0);
993     MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmDeviceName, -1,
994                         dmW->dmDeviceName, CCHDEVICENAME);
995     if(!Formname) {
996       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
997              dmA->dmSize - CCHDEVICENAME);
998     } else {
999       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
1000              off_formname - CCHDEVICENAME);
1001       MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmFormName, -1,
1002                           dmW->dmFormName, CCHFORMNAME);
1003       memcpy(&dmW->dmLogPixels, &dmA->dmLogPixels, dmA->dmSize -
1004              (off_formname + CCHFORMNAME));
1005     }
1006     dmW->dmSize = size;
1007     memcpy((char *)dmW + dmW->dmSize, (const char *)dmA + dmA->dmSize,
1008            dmA->dmDriverExtra);
1009     return dmW;
1010 }
1011
1012 /***********************************************************
1013  *      DEVMODEdupWtoA
1014  * Creates an ascii copy of supplied devmode on heap
1015  */
1016 static LPDEVMODEA DEVMODEdupWtoA(HANDLE heap, const DEVMODEW *dmW)
1017 {
1018     LPDEVMODEA dmA;
1019     DWORD size;
1020     BOOL Formname;
1021     ptrdiff_t off_formname = (const char *)dmW->dmFormName - (const char *)dmW;
1022
1023     if(!dmW) return NULL;
1024     Formname = (dmW->dmSize > off_formname);
1025     size = dmW->dmSize - CCHDEVICENAME - (Formname ? CCHFORMNAME : 0);
1026     dmA = HeapAlloc(heap, HEAP_ZERO_MEMORY, size + dmW->dmDriverExtra);
1027     WideCharToMultiByte(CP_ACP, 0, dmW->dmDeviceName, -1,
1028                         (LPSTR)dmA->dmDeviceName, CCHDEVICENAME, NULL, NULL);
1029     if(!Formname) {
1030       memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
1031              dmW->dmSize - CCHDEVICENAME * sizeof(WCHAR));
1032     } else {
1033       memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
1034              off_formname - CCHDEVICENAME * sizeof(WCHAR));
1035       WideCharToMultiByte(CP_ACP, 0, dmW->dmFormName, -1,
1036                           (LPSTR)dmA->dmFormName, CCHFORMNAME, NULL, NULL);
1037       memcpy(&dmA->dmLogPixels, &dmW->dmLogPixels, dmW->dmSize -
1038              (off_formname + CCHFORMNAME * sizeof(WCHAR)));
1039     }
1040     dmA->dmSize = size;
1041     memcpy((char *)dmA + dmA->dmSize, (const char *)dmW + dmW->dmSize,
1042            dmW->dmDriverExtra);
1043     return dmA;
1044 }
1045
1046 /***********************************************************
1047  *             PRINTER_INFO_2AtoW
1048  * Creates a unicode copy of PRINTER_INFO_2A on heap
1049  */
1050 static LPPRINTER_INFO_2W PRINTER_INFO_2AtoW(HANDLE heap, LPPRINTER_INFO_2A piA)
1051 {
1052     LPPRINTER_INFO_2W piW;
1053     UNICODE_STRING usBuffer;
1054
1055     if(!piA) return NULL;
1056     piW = HeapAlloc(heap, 0, sizeof(*piW));
1057     memcpy(piW, piA, sizeof(*piW)); /* copy everything first */
1058     
1059     piW->pServerName = asciitounicode(&usBuffer,piA->pServerName);
1060     piW->pPrinterName = asciitounicode(&usBuffer,piA->pPrinterName);
1061     piW->pShareName = asciitounicode(&usBuffer,piA->pShareName);
1062     piW->pPortName = asciitounicode(&usBuffer,piA->pPortName);
1063     piW->pDriverName = asciitounicode(&usBuffer,piA->pDriverName);
1064     piW->pComment = asciitounicode(&usBuffer,piA->pComment);
1065     piW->pLocation = asciitounicode(&usBuffer,piA->pLocation);
1066     piW->pDevMode = piA->pDevMode ? GdiConvertToDevmodeW(piA->pDevMode) : NULL;
1067     piW->pSepFile = asciitounicode(&usBuffer,piA->pSepFile);
1068     piW->pPrintProcessor = asciitounicode(&usBuffer,piA->pPrintProcessor);
1069     piW->pDatatype = asciitounicode(&usBuffer,piA->pDatatype);
1070     piW->pParameters = asciitounicode(&usBuffer,piA->pParameters);
1071     return piW;
1072 }
1073
1074 /***********************************************************
1075  *       FREE_PRINTER_INFO_2W
1076  * Free PRINTER_INFO_2W and all strings
1077  */
1078 static void FREE_PRINTER_INFO_2W(HANDLE heap, LPPRINTER_INFO_2W piW)
1079 {
1080     if(!piW) return;
1081
1082     HeapFree(heap,0,piW->pServerName);
1083     HeapFree(heap,0,piW->pPrinterName);
1084     HeapFree(heap,0,piW->pShareName);
1085     HeapFree(heap,0,piW->pPortName);
1086     HeapFree(heap,0,piW->pDriverName);
1087     HeapFree(heap,0,piW->pComment);
1088     HeapFree(heap,0,piW->pLocation);
1089     HeapFree(heap,0,piW->pDevMode);
1090     HeapFree(heap,0,piW->pSepFile);
1091     HeapFree(heap,0,piW->pPrintProcessor);
1092     HeapFree(heap,0,piW->pDatatype);
1093     HeapFree(heap,0,piW->pParameters);
1094     HeapFree(heap,0,piW);
1095     return;
1096 }
1097
1098 /******************************************************************
1099  *              DeviceCapabilities     [WINSPOOL.@]
1100  *              DeviceCapabilitiesA    [WINSPOOL.@]
1101  *
1102  */
1103 INT WINAPI DeviceCapabilitiesA(LPCSTR pDevice,LPCSTR pPort, WORD cap,
1104                                LPSTR pOutput, LPDEVMODEA lpdm)
1105 {
1106     INT ret;
1107
1108     if (!GDI_CallDeviceCapabilities16)
1109     {
1110         GDI_CallDeviceCapabilities16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1111                                                               (LPCSTR)104 );
1112         if (!GDI_CallDeviceCapabilities16) return -1;
1113     }
1114     ret = GDI_CallDeviceCapabilities16(pDevice, pPort, cap, pOutput, lpdm);
1115
1116     /* If DC_PAPERSIZE map POINT16s to POINTs */
1117     if(ret != -1 && cap == DC_PAPERSIZE && pOutput) {
1118         POINT16 *tmp = HeapAlloc( GetProcessHeap(), 0, ret * sizeof(POINT16) );
1119         POINT *pt = (POINT *)pOutput;
1120         INT i;
1121         memcpy(tmp, pOutput, ret * sizeof(POINT16));
1122         for(i = 0; i < ret; i++, pt++)
1123         {
1124             pt->x = tmp[i].x;
1125             pt->y = tmp[i].y;
1126         }
1127         HeapFree( GetProcessHeap(), 0, tmp );
1128     }
1129     return ret;
1130 }
1131
1132
1133 /*****************************************************************************
1134  *          DeviceCapabilitiesW        [WINSPOOL.@]
1135  *
1136  * Call DeviceCapabilitiesA since we later call 16bit stuff anyway
1137  *
1138  */
1139 INT WINAPI DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort,
1140                                WORD fwCapability, LPWSTR pOutput,
1141                                const DEVMODEW *pDevMode)
1142 {
1143     LPDEVMODEA dmA = DEVMODEdupWtoA(GetProcessHeap(), pDevMode);
1144     LPSTR pDeviceA = HEAP_strdupWtoA(GetProcessHeap(),0,pDevice);
1145     LPSTR pPortA = HEAP_strdupWtoA(GetProcessHeap(),0,pPort);
1146     INT ret;
1147
1148     if(pOutput && (fwCapability == DC_BINNAMES ||
1149                    fwCapability == DC_FILEDEPENDENCIES ||
1150                    fwCapability == DC_PAPERNAMES)) {
1151       /* These need A -> W translation */
1152         INT size = 0, i;
1153         LPSTR pOutputA;
1154         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, NULL,
1155                                   dmA);
1156         if(ret == -1)
1157             return ret;
1158         switch(fwCapability) {
1159         case DC_BINNAMES:
1160             size = 24;
1161             break;
1162         case DC_PAPERNAMES:
1163         case DC_FILEDEPENDENCIES:
1164             size = 64;
1165             break;
1166         }
1167         pOutputA = HeapAlloc(GetProcessHeap(), 0, size * ret);
1168         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, pOutputA,
1169                                   dmA);
1170         for(i = 0; i < ret; i++)
1171             MultiByteToWideChar(CP_ACP, 0, pOutputA + (i * size), -1,
1172                                 pOutput + (i * size), size);
1173         HeapFree(GetProcessHeap(), 0, pOutputA);
1174     } else {
1175         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability,
1176                                   (LPSTR)pOutput, dmA);
1177     }
1178     HeapFree(GetProcessHeap(),0,pPortA);
1179     HeapFree(GetProcessHeap(),0,pDeviceA);
1180     HeapFree(GetProcessHeap(),0,dmA);
1181     return ret;
1182 }
1183
1184 /******************************************************************
1185  *              DocumentPropertiesA   [WINSPOOL.@]
1186  *
1187  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1188  */
1189 LONG WINAPI DocumentPropertiesA(HWND hWnd,HANDLE hPrinter,
1190                                 LPSTR pDeviceName, LPDEVMODEA pDevModeOutput,
1191                                 LPDEVMODEA pDevModeInput,DWORD fMode )
1192 {
1193     LPSTR lpName = pDeviceName;
1194     static CHAR port[] = "LPT1:";
1195     LONG ret;
1196
1197     TRACE("(%p,%p,%s,%p,%p,%ld)\n",
1198         hWnd,hPrinter,pDeviceName,pDevModeOutput,pDevModeInput,fMode
1199     );
1200
1201     if(!pDeviceName) {
1202         LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
1203         if(!lpNameW) {
1204                 ERR("no name from hPrinter?\n");
1205                 SetLastError(ERROR_INVALID_HANDLE);
1206                 return -1;
1207         }
1208         lpName = HEAP_strdupWtoA(GetProcessHeap(),0,lpNameW);
1209     }
1210
1211     if (!GDI_CallExtDeviceMode16)
1212     {
1213         GDI_CallExtDeviceMode16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1214                                                          (LPCSTR)102 );
1215         if (!GDI_CallExtDeviceMode16) {
1216                 ERR("No CallExtDeviceMode16?\n");
1217                 return -1;
1218         }
1219     }
1220     ret = GDI_CallExtDeviceMode16(hWnd, pDevModeOutput, lpName, port,
1221                                   pDevModeInput, NULL, fMode);
1222
1223     if(!pDeviceName)
1224         HeapFree(GetProcessHeap(),0,lpName);
1225     return ret;
1226 }
1227
1228
1229 /*****************************************************************************
1230  *          DocumentPropertiesW (WINSPOOL.@)
1231  *
1232  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1233  */
1234 LONG WINAPI DocumentPropertiesW(HWND hWnd, HANDLE hPrinter,
1235                                 LPWSTR pDeviceName,
1236                                 LPDEVMODEW pDevModeOutput,
1237                                 LPDEVMODEW pDevModeInput, DWORD fMode)
1238 {
1239
1240     LPSTR pDeviceNameA = HEAP_strdupWtoA(GetProcessHeap(),0,pDeviceName);
1241     LPDEVMODEA pDevModeInputA = DEVMODEdupWtoA(GetProcessHeap(),pDevModeInput);
1242     LPDEVMODEA pDevModeOutputA = NULL;
1243     LONG ret;
1244
1245     TRACE("(%p,%p,%s,%p,%p,%ld)\n",
1246           hWnd,hPrinter,debugstr_w(pDeviceName),pDevModeOutput,pDevModeInput,
1247           fMode);
1248     if(pDevModeOutput) {
1249         ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, NULL, NULL, 0);
1250         if(ret < 0) return ret;
1251         pDevModeOutputA = HeapAlloc(GetProcessHeap(), 0, ret);
1252     }
1253     ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, pDevModeOutputA,
1254                               pDevModeInputA, fMode);
1255     if(pDevModeOutput) {
1256         DEVMODEcpyAtoW(pDevModeOutput, pDevModeOutputA);
1257         HeapFree(GetProcessHeap(),0,pDevModeOutputA);
1258     }
1259     if(fMode == 0 && ret > 0)
1260         ret += (CCHDEVICENAME + CCHFORMNAME);
1261     HeapFree(GetProcessHeap(),0,pDevModeInputA);
1262     HeapFree(GetProcessHeap(),0,pDeviceNameA);
1263     return ret;
1264 }
1265
1266 /******************************************************************
1267  *              OpenPrinterA        [WINSPOOL.@]
1268  *
1269  * See OpenPrinterW.
1270  *
1271  */
1272 BOOL WINAPI OpenPrinterA(LPSTR lpPrinterName,HANDLE *phPrinter,
1273                          LPPRINTER_DEFAULTSA pDefault)
1274 {
1275     UNICODE_STRING lpPrinterNameW;
1276     UNICODE_STRING usBuffer;
1277     PRINTER_DEFAULTSW DefaultW, *pDefaultW = NULL;
1278     PWSTR pwstrPrinterNameW;
1279     BOOL ret;
1280
1281     pwstrPrinterNameW = asciitounicode(&lpPrinterNameW,lpPrinterName);
1282
1283     if(pDefault) {
1284         DefaultW.pDatatype = asciitounicode(&usBuffer,pDefault->pDatatype);
1285         DefaultW.pDevMode = pDefault->pDevMode ? GdiConvertToDevmodeW(pDefault->pDevMode) : NULL;
1286         DefaultW.DesiredAccess = pDefault->DesiredAccess;
1287         pDefaultW = &DefaultW;
1288     }
1289     ret = OpenPrinterW(pwstrPrinterNameW, phPrinter, pDefaultW);
1290     if(pDefault) {
1291         RtlFreeUnicodeString(&usBuffer);
1292         HeapFree(GetProcessHeap(), 0, DefaultW.pDevMode);
1293     }
1294     RtlFreeUnicodeString(&lpPrinterNameW);
1295     return ret;
1296 }
1297
1298 /******************************************************************
1299  *              OpenPrinterW        [WINSPOOL.@]
1300  *
1301  * Open a Printer / Printserver or a Printer-Object
1302  *
1303  * PARAMS
1304  *  lpPrinterName [I] Name of Printserver, Printer, or Printer-Object
1305  *  phPrinter     [O] The resulting Handle is stored here
1306  *  pDefault      [I] PTR to Default Printer Settings or NULL
1307  *
1308  * RETURNS
1309  *  Success: TRUE
1310  *  Failure: FALSE
1311  *
1312  * NOTES
1313  *  lpPrinterName is one of:
1314  *|  Printserver (NT only): "Servername" or NULL for the local Printserver
1315  *|  Printer: "PrinterName"
1316  *|  Printer-Object: "PrinterName,Job xxx"
1317  *|  XcvMonitor: "Servername,XcvMonitor MonitorName"
1318  *|  XcvPort: "Servername,XcvPort PortName"
1319  *
1320  * BUGS
1321  *|  Printer-Object not supported
1322  *|  XcvMonitor not supported
1323  *|  XcvPort not supported
1324  *|  pDefaults is ignored
1325  *
1326  */
1327 BOOL WINAPI OpenPrinterW(LPWSTR lpPrinterName,HANDLE *phPrinter, LPPRINTER_DEFAULTSW pDefault)
1328 {
1329     HKEY hkeyPrinters = NULL;
1330     HKEY hkeyPrinter = NULL;
1331
1332     TRACE("(%s, %p, %p)\n", debugstr_w(lpPrinterName), phPrinter, pDefault);
1333     if (pDefault) {
1334         FIXME("PRINTER_DEFAULTS ignored => %s,%p,0x%08lx\n",
1335         debugstr_w(pDefault->pDatatype), pDefault->pDevMode, pDefault->DesiredAccess);
1336     }
1337
1338     if(lpPrinterName != NULL)
1339     {
1340         /* Check any Printer exists */
1341         if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) != ERROR_SUCCESS) {
1342             ERR("Can't create Printers key\n");
1343             SetLastError(ERROR_FILE_NOT_FOUND);
1344             return FALSE;
1345         }
1346         if((lpPrinterName[0] == '\0') ||        /* explicitly exclude "" */
1347            (RegOpenKeyW(hkeyPrinters, lpPrinterName, &hkeyPrinter) != ERROR_SUCCESS)) {
1348
1349             WARN("Printer not found in Registry: '%s'\n", debugstr_w(lpPrinterName));
1350             RegCloseKey(hkeyPrinters);
1351             SetLastError(ERROR_INVALID_PRINTER_NAME);
1352             return FALSE;
1353         }
1354         RegCloseKey(hkeyPrinter);
1355         RegCloseKey(hkeyPrinters);
1356     }
1357     if(!phPrinter) {
1358         /* NT: FALSE with ERROR_INVALID_PARAMETER, 9x: TRUE */
1359         SetLastError(ERROR_INVALID_PARAMETER);
1360         return FALSE;
1361     }
1362
1363     /* Get the unique handle of the printer or Printserver */
1364     *phPrinter = get_opened_printer_entry(lpPrinterName, pDefault);
1365     return (*phPrinter != 0);
1366 }
1367
1368 /******************************************************************
1369  *              AddMonitorA        [WINSPOOL.@]
1370  *
1371  * See AddMonitorW.
1372  *
1373  */
1374 BOOL WINAPI AddMonitorA(LPSTR pName, DWORD Level, LPBYTE pMonitors)
1375 {
1376     LPWSTR  nameW = NULL;
1377     INT     len;
1378     BOOL    res;
1379     LPMONITOR_INFO_2A mi2a;
1380     MONITOR_INFO_2W mi2w;
1381
1382     mi2a = (LPMONITOR_INFO_2A) pMonitors;
1383     TRACE("(%s, %ld, %p) :  %s %s %s\n", debugstr_a(pName), Level, pMonitors, 
1384             mi2a ? debugstr_a(mi2a->pName) : NULL,
1385             mi2a ? debugstr_a(mi2a->pEnvironment) : NULL,
1386             mi2a ? debugstr_a(mi2a->pDLLName) : NULL);
1387
1388     if  (Level != 2) {
1389         SetLastError(ERROR_INVALID_LEVEL);
1390         return FALSE;
1391     }
1392
1393     /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
1394     if (mi2a == NULL) {
1395         return FALSE;
1396     }
1397
1398     if (pName) {
1399         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
1400         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1401         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
1402     }
1403
1404     memset(&mi2w, 0, sizeof(MONITOR_INFO_2W));
1405     if (mi2a->pName) {
1406         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, NULL, 0);
1407         mi2w.pName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1408         MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, mi2w.pName, len);
1409     }
1410     if (mi2a->pEnvironment) {
1411         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, NULL, 0);
1412         mi2w.pEnvironment = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1413         MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, mi2w.pEnvironment, len);
1414     }
1415     if (mi2a->pDLLName) {
1416         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, NULL, 0);
1417         mi2w.pDLLName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1418         MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, mi2w.pDLLName, len);
1419     }
1420
1421     res = AddMonitorW(nameW, Level, (LPBYTE) &mi2w);
1422
1423     HeapFree(GetProcessHeap(), 0, mi2w.pName); 
1424     HeapFree(GetProcessHeap(), 0, mi2w.pEnvironment); 
1425     HeapFree(GetProcessHeap(), 0, mi2w.pDLLName); 
1426
1427     HeapFree(GetProcessHeap(), 0, nameW); 
1428     return (res);
1429 }
1430
1431 /******************************************************************************
1432  *              AddMonitorW        [WINSPOOL.@]
1433  *
1434  * Install a Printmonitor
1435  *
1436  * PARAMS
1437  *  pName       [I] Servername or NULL (local Computer)
1438  *  Level       [I] Structure-Level (Must be 2)
1439  *  pMonitors   [I] PTR to MONITOR_INFO_2
1440  *
1441  * RETURNS
1442  *  Success: TRUE
1443  *  Failure: FALSE
1444  *
1445  * NOTES
1446  *  All Files for the Monitor must already be copied to %winsysdir% ("%SystemRoot%\system32")
1447  *
1448  */
1449 BOOL WINAPI AddMonitorW(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
1450 {
1451     LPMONITOR_INFO_2W mi2w;
1452     HKEY    hroot = NULL;
1453     HKEY    hentry = NULL;
1454     HMODULE hdll = NULL;
1455     DWORD   disposition;
1456     BOOL    res = FALSE;
1457
1458     mi2w = (LPMONITOR_INFO_2W) pMonitors;
1459     TRACE("(%s, %ld, %p) :  %s %s %s\n", debugstr_w(pName), Level, pMonitors, 
1460             mi2w ? debugstr_w(mi2w->pName) : NULL,
1461             mi2w ? debugstr_w(mi2w->pEnvironment) : NULL,
1462             mi2w ? debugstr_w(mi2w->pDLLName) : NULL);
1463
1464     if (Level != 2) {
1465         SetLastError(ERROR_INVALID_LEVEL);
1466         return FALSE;
1467     }
1468
1469     /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
1470     if (mi2w == NULL) {
1471         return FALSE;
1472     }
1473
1474     if (pName && (pName[0])) {
1475         FIXME("for server %s not implemented\n", debugstr_w(pName));
1476         SetLastError(ERROR_ACCESS_DENIED);
1477         return FALSE;
1478     }
1479
1480
1481     if (!mi2w->pName || (! mi2w->pName[0])) {
1482         WARN("pName not valid : %s\n", debugstr_w(mi2w->pName));
1483         SetLastError(ERROR_INVALID_PARAMETER);
1484         return FALSE;
1485     }
1486     if (!mi2w->pEnvironment || lstrcmpW(mi2w->pEnvironment, envname_x86W)) {
1487         WARN("Environment %s requested (we support only %s)\n", 
1488                 debugstr_w(mi2w->pEnvironment), debugstr_w(envname_x86W));
1489         SetLastError(ERROR_INVALID_ENVIRONMENT);
1490         return FALSE;
1491     }
1492
1493     if (!mi2w->pDLLName || (! mi2w->pDLLName[0])) {
1494         WARN("pDLLName not valid : %s\n", debugstr_w(mi2w->pDLLName));
1495         SetLastError(ERROR_INVALID_PARAMETER);
1496         return FALSE;
1497     }
1498
1499     if ((hdll = LoadLibraryW(mi2w->pDLLName)) == NULL) {
1500         return FALSE;
1501     }
1502     FreeLibrary(hdll);
1503
1504     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) != ERROR_SUCCESS) {
1505         ERR("unable to create key %s\n", debugstr_w(MonitorsW));
1506         return FALSE;
1507     }
1508
1509     if(RegCreateKeyExW( hroot, mi2w->pName, 0, NULL, REG_OPTION_NON_VOLATILE,
1510                         KEY_WRITE | KEY_QUERY_VALUE, NULL, &hentry,
1511                         &disposition) == ERROR_SUCCESS) {
1512
1513         /* Some installers set options for the port before calling AddMonitor.
1514            We query the "Driver" entry to verify that the monitor is installed,
1515            before we return an error.
1516            When a user installs two print monitors at the same time with the
1517            same name but with a different driver DLL and a task switch comes
1518            between RegQueryValueExW and RegSetValueExW, a race condition
1519            is possible but silently ignored. */
1520
1521         DWORD   namesize = 0;
1522
1523         if ((disposition == REG_OPENED_EXISTING_KEY) &&
1524             (RegQueryValueExW(hentry, DriverW, NULL, NULL, NULL,
1525                               &namesize) == ERROR_SUCCESS)) {
1526             TRACE("monitor %s already exists\n", debugstr_w(mi2w->pName));
1527             /* NT: ERROR_PRINT_MONITOR_ALREADY_INSTALLED (3006)
1528                9x: ERROR_ALREADY_EXISTS (183) */
1529             SetLastError(ERROR_PRINT_MONITOR_ALREADY_INSTALLED);
1530         }
1531         else
1532         {
1533                INT len;
1534                len = (lstrlenW(mi2w->pDLLName) +1) * sizeof(WCHAR);
1535                res = (RegSetValueExW(hentry, DriverW, 0,
1536                       REG_SZ, (LPBYTE) mi2w->pDLLName, len) == ERROR_SUCCESS);
1537         }
1538         RegCloseKey(hentry);
1539     }
1540
1541     RegCloseKey(hroot);
1542     return (res);
1543 }
1544
1545 /******************************************************************
1546  *              DeletePrinterDriverA        [WINSPOOL.@]
1547  *
1548  */
1549 BOOL WINAPI
1550 DeletePrinterDriverA (LPSTR pName, LPSTR pEnvironment, LPSTR pDriverName)
1551 {
1552     FIXME("(%s,%s,%s):stub\n",debugstr_a(pName),debugstr_a(pEnvironment),
1553           debugstr_a(pDriverName));
1554     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1555     return FALSE;
1556 }
1557
1558 /******************************************************************
1559  *              DeletePrinterDriverW        [WINSPOOL.@]
1560  *
1561  */
1562 BOOL WINAPI
1563 DeletePrinterDriverW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pDriverName)
1564 {
1565     FIXME("(%s,%s,%s):stub\n",debugstr_w(pName),debugstr_w(pEnvironment),
1566           debugstr_w(pDriverName));
1567     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1568     return FALSE;
1569 }
1570
1571 /******************************************************************
1572  *              DeleteMonitorA        [WINSPOOL.@]
1573  *
1574  * See DeleteMonitorW.
1575  *
1576  */
1577 BOOL WINAPI DeleteMonitorA (LPSTR pName, LPSTR pEnvironment, LPSTR pMonitorName)
1578 {
1579     LPWSTR  nameW = NULL;
1580     LPWSTR  EnvironmentW = NULL;
1581     LPWSTR  MonitorNameW = NULL;
1582     BOOL    res;
1583     INT     len;
1584
1585     if (pName) {
1586         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
1587         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1588         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
1589     }
1590
1591     if (pEnvironment) {
1592         len = MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, NULL, 0);
1593         EnvironmentW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1594         MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, EnvironmentW, len);
1595     }
1596     if (pMonitorName) {
1597         len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
1598         MonitorNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1599         MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, MonitorNameW, len);
1600     }
1601
1602     res = DeleteMonitorW(nameW, EnvironmentW, MonitorNameW);
1603
1604     HeapFree(GetProcessHeap(), 0, MonitorNameW); 
1605     HeapFree(GetProcessHeap(), 0, EnvironmentW);
1606     HeapFree(GetProcessHeap(), 0, nameW); 
1607     return (res);
1608 }
1609
1610 /******************************************************************
1611  *              DeleteMonitorW        [WINSPOOL.@]
1612  *
1613  * Delete a specific Printmonitor from a Printing-Environment
1614  *
1615  * PARAMS
1616  *  pName        [I] Servername or NULL (local Computer)
1617  *  pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
1618  *  pMonitorName [I] Name of the Monitor, that should be deleted
1619  *
1620  * RETURNS
1621  *  Success: TRUE
1622  *  Failure: FALSE
1623  *
1624  * NOTES
1625  *  pEnvironment is ignored in Windows for the local Computer.
1626  *
1627  */
1628
1629 BOOL WINAPI DeleteMonitorW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
1630 {
1631     HKEY    hroot = NULL;
1632
1633     TRACE("(%s, %s, %s)\n",debugstr_w(pName),debugstr_w(pEnvironment),
1634            debugstr_w(pMonitorName));
1635
1636     if (pName && (pName[0])) {
1637         FIXME("for server %s not implemented\n", debugstr_w(pName));
1638         SetLastError(ERROR_ACCESS_DENIED);
1639         return FALSE;
1640     }
1641
1642     /*  pEnvironment is ignored in Windows for the local Computer */
1643
1644     if (!pMonitorName || !pMonitorName[0]) {
1645         WARN("pMonitorName %s is invalid\n", debugstr_w(pMonitorName));
1646         SetLastError(ERROR_INVALID_PARAMETER);
1647         return FALSE;
1648     }
1649
1650     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) != ERROR_SUCCESS) {
1651         ERR("unable to create key %s\n", debugstr_w(MonitorsW));
1652         return FALSE;
1653     }
1654
1655     /* change this, when advapi32.dll/RegDeleteTree is implemented */
1656     if(WINSPOOL_SHDeleteKeyW(hroot, pMonitorName) == ERROR_SUCCESS) {
1657         TRACE("monitor %s deleted\n", debugstr_w(pMonitorName));
1658         RegCloseKey(hroot);
1659         return TRUE;
1660     }
1661
1662     WARN("monitor %s does not exists\n", debugstr_w(pMonitorName));
1663     RegCloseKey(hroot);
1664
1665     /* NT: ERROR_UNKNOWN_PRINT_MONITOR (3000), 9x: ERROR_INVALID_PARAMETER (87) */
1666     SetLastError(ERROR_UNKNOWN_PRINT_MONITOR);
1667     return (FALSE);
1668 }
1669
1670 /******************************************************************
1671  *              DeletePortA        [WINSPOOL.@]
1672  *
1673  * See DeletePortW.
1674  *
1675  */
1676 BOOL WINAPI
1677 DeletePortA (LPSTR pName, HWND hWnd, LPSTR pPortName)
1678 {
1679     FIXME("(%s,%p,%s):stub\n",debugstr_a(pName),hWnd,
1680           debugstr_a(pPortName));
1681     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1682     return FALSE;
1683 }
1684
1685 /******************************************************************
1686  *              DeletePortW        [WINSPOOL.@]
1687  *
1688  * Delete a specific Port
1689  *
1690  * PARAMS
1691  *  pName     [I] Servername or NULL (local Computer)
1692  *  hWnd      [I] Handle to parent Window for the Dialog-Box
1693  *  pPortName [I] Name of the Port, that should be deleted
1694  *
1695  * RETURNS
1696  *  Success: TRUE
1697  *  Failure: FALSE
1698  *
1699  * BUGS
1700  *  only a Stub
1701  *
1702  */
1703 BOOL WINAPI
1704 DeletePortW (LPWSTR pName, HWND hWnd, LPWSTR pPortName)
1705 {
1706     FIXME("(%s,%p,%s):stub\n",debugstr_w(pName),hWnd,
1707           debugstr_w(pPortName));
1708     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1709     return FALSE;
1710 }
1711
1712 /******************************************************************************
1713  *    SetPrinterW  [WINSPOOL.@]
1714  */
1715 BOOL WINAPI SetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD Command)
1716 {
1717     FIXME("(%p, %ld, %p, %ld): stub\n", hPrinter, Level, pPrinter, Command);
1718     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1719     return FALSE;
1720 }
1721
1722 /******************************************************************************
1723  *    WritePrinter  [WINSPOOL.@]
1724  */
1725 BOOL WINAPI WritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
1726 {
1727     opened_printer_t *printer;
1728     BOOL ret = FALSE;
1729
1730     TRACE("(%p, %p, %ld, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
1731
1732     EnterCriticalSection(&printer_handles_cs);
1733     printer = get_opened_printer(hPrinter);
1734     if(!printer)
1735     {
1736         SetLastError(ERROR_INVALID_HANDLE);
1737         goto end;
1738     }
1739
1740     if(!printer->doc)
1741     {
1742         SetLastError(ERROR_SPL_NO_STARTDOC);
1743         goto end;
1744     }
1745
1746     ret = WriteFile(printer->doc->hf, pBuf, cbBuf, pcWritten, NULL);
1747 end:
1748     LeaveCriticalSection(&printer_handles_cs);
1749     return ret;
1750 }
1751
1752 /*****************************************************************************
1753  *          AddFormA  [WINSPOOL.@]
1754  */
1755 BOOL WINAPI AddFormA(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
1756 {
1757     FIXME("(%p,%ld,%p): stub\n", hPrinter, Level, pForm);
1758     return 1;
1759 }
1760
1761 /*****************************************************************************
1762  *          AddFormW  [WINSPOOL.@]
1763  */
1764 BOOL WINAPI AddFormW(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
1765 {
1766     FIXME("(%p,%ld,%p): stub\n", hPrinter, Level, pForm);
1767     return 1;
1768 }
1769
1770 /*****************************************************************************
1771  *          AddJobA  [WINSPOOL.@]
1772  */
1773 BOOL WINAPI AddJobA(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
1774 {
1775     BOOL ret;
1776     BYTE buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
1777     DWORD needed;
1778
1779     if(Level != 1) {
1780         SetLastError(ERROR_INVALID_LEVEL);
1781         return FALSE;
1782     }
1783
1784     ret = AddJobW(hPrinter, Level, buf, sizeof(buf), &needed);
1785
1786     if(ret) {
1787         ADDJOB_INFO_1W *addjobW = (ADDJOB_INFO_1W*)buf;
1788         DWORD len = WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, NULL, 0, NULL, NULL);
1789         *pcbNeeded = len + sizeof(ADDJOB_INFO_1A);
1790         if(*pcbNeeded > cbBuf) {
1791             SetLastError(ERROR_INSUFFICIENT_BUFFER);
1792             ret = FALSE;
1793         } else {
1794             ADDJOB_INFO_1A *addjobA = (ADDJOB_INFO_1A*)pData;
1795             addjobA->JobId = addjobW->JobId;
1796             addjobA->Path = (char *)(addjobA + 1);
1797             WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, addjobA->Path, len, NULL, NULL);
1798         }
1799     }
1800     return ret;
1801 }
1802
1803 /*****************************************************************************
1804  *          AddJobW  [WINSPOOL.@]
1805  */
1806 BOOL WINAPI AddJobW(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
1807 {
1808     opened_printer_t *printer;
1809     job_t *job;
1810     BOOL ret = FALSE;
1811     static const WCHAR spool_path[] = {'s','p','o','o','l','\\','P','R','I','N','T','E','R','S','\\',0};
1812     static const WCHAR fmtW[] = {'%','s','%','0','5','d','.','S','P','L',0};
1813     WCHAR path[MAX_PATH], filename[MAX_PATH];
1814     DWORD len;
1815     ADDJOB_INFO_1W *addjob;
1816
1817     TRACE("(%p,%ld,%p,%ld,%p)\n", hPrinter, Level, pData, cbBuf, pcbNeeded);
1818     
1819     EnterCriticalSection(&printer_handles_cs);
1820
1821     printer = get_opened_printer(hPrinter);
1822
1823     if(!printer) {
1824         SetLastError(ERROR_INVALID_HANDLE);
1825         goto end;
1826     }
1827
1828     if(Level != 1) {
1829         SetLastError(ERROR_INVALID_LEVEL);
1830         goto end;
1831     }
1832
1833     job = HeapAlloc(GetProcessHeap(), 0, sizeof(*job));
1834     if(!job)
1835         goto end;
1836
1837     job->job_id = InterlockedIncrement(&next_job_id);
1838
1839     len = GetSystemDirectoryW(path, sizeof(path) / sizeof(WCHAR));
1840     if(path[len - 1] != '\\')
1841         path[len++] = '\\';
1842     memcpy(path + len, spool_path, sizeof(spool_path));    
1843     sprintfW(filename, fmtW, path, job->job_id);
1844
1845     len = strlenW(filename);
1846     job->filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1847     memcpy(job->filename, filename, (len + 1) * sizeof(WCHAR));
1848     job->document_title = strdupW(default_doc_title);
1849     list_add_tail(&printer->queue->jobs, &job->entry);
1850
1851     *pcbNeeded = (len + 1) * sizeof(WCHAR) + sizeof(*addjob);
1852     if(*pcbNeeded <= cbBuf) {
1853         addjob = (ADDJOB_INFO_1W*)pData;
1854         addjob->JobId = job->job_id;
1855         addjob->Path = (WCHAR *)(addjob + 1);
1856         memcpy(addjob->Path, filename, (len + 1) * sizeof(WCHAR));
1857         ret = TRUE;
1858     } else 
1859         SetLastError(ERROR_INSUFFICIENT_BUFFER);
1860
1861 end:
1862     LeaveCriticalSection(&printer_handles_cs);
1863     return ret;
1864 }
1865
1866 /*****************************************************************************
1867  *          GetPrintProcessorDirectoryA  [WINSPOOL.@]
1868  *
1869  * Return the PATH for the Print-Processors
1870  *
1871  * See GetPrintProcessorDirectoryW.
1872  *
1873  *
1874  */
1875 BOOL WINAPI GetPrintProcessorDirectoryA(LPSTR server, LPSTR env,
1876                                         DWORD level,  LPBYTE Info,
1877                                         DWORD cbBuf,  LPDWORD pcbNeeded)
1878 {
1879     LPWSTR  serverW = NULL;
1880     LPWSTR  envW = NULL;
1881     BOOL    ret;
1882     INT     len;
1883
1884     TRACE("(%s, %s, %ld, %p, %ld, %p)\n", debugstr_a(server), 
1885           debugstr_a(env), level, Info, cbBuf, pcbNeeded);
1886  
1887
1888     if (server) {
1889         len = MultiByteToWideChar(CP_ACP, 0, server, -1, NULL, 0);
1890         serverW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1891         MultiByteToWideChar(CP_ACP, 0, server, -1, serverW, len);
1892     }
1893
1894     if (env) {
1895         len = MultiByteToWideChar(CP_ACP, 0, env, -1, NULL, 0);
1896         envW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1897         MultiByteToWideChar(CP_ACP, 0, env, -1, envW, len);
1898     }
1899
1900     /* NT requires the buffersize from GetPrintProcessorDirectoryW also
1901        for GetPrintProcessorDirectoryA and WC2MB is done in-place.
1902      */
1903     ret = GetPrintProcessorDirectoryW(serverW, envW, level, Info, 
1904                                       cbBuf, pcbNeeded);
1905
1906     if (ret) ret = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)Info, -1, (LPSTR)Info,
1907                                        cbBuf, NULL, NULL) > 0;
1908
1909
1910     TRACE(" required: 0x%lx/%ld\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
1911     HeapFree(GetProcessHeap(), 0, envW);
1912     HeapFree(GetProcessHeap(), 0, serverW);
1913     return ret;
1914 }
1915
1916 /*****************************************************************************
1917  *          GetPrintProcessorDirectoryW  [WINSPOOL.@]
1918  *
1919  * Return the PATH for the Print-Processors
1920  *
1921  * PARAMS
1922  *   server     [I] Servername (NT only) or NULL (local Computer)
1923  *   env        [I] Printing-Environment (see below) or NULL (Default)
1924  *   level      [I] Structure-Level (must be 1)
1925  *   Info       [O] PTR to Buffer that receives the Result
1926  *   cbBuf      [I] Size of Buffer at "Info"
1927  *   pcbNeeded  [O] PTR to DWORD that receives the size in Bytes used / 
1928  *                  required for the Buffer at "Info"
1929  *
1930  * RETURNS
1931  *   Success: TRUE  and in pcbNeeded the Bytes used in Info
1932  *   Failure: FALSE and in pcbNeeded the Bytes required for Info,
1933  *   if cbBuf is too small
1934  * 
1935  *   Native Values returned in Info on Success:
1936  *|  NT(Windows NT x86):  "%winsysdir%\\spool\\PRTPROCS\\w32x86" 
1937  *|  NT(Windows 4.0):     "%winsysdir%\\spool\\PRTPROCS\\win40" 
1938  *|  win9x(Windows 4.0):  "%winsysdir%" 
1939  *
1940  *   "%winsysdir%" is the Value from GetSystemDirectoryW()
1941  *
1942  * BUGS
1943  *  Only NULL or "" is supported for server
1944  *
1945  */
1946 BOOL WINAPI GetPrintProcessorDirectoryW(LPWSTR server, LPWSTR env,
1947                                         DWORD level,  LPBYTE Info,
1948                                         DWORD cbBuf,  LPDWORD pcbNeeded)
1949 {
1950     DWORD needed;
1951     const printenv_t * env_t;
1952
1953     TRACE("(%s, %s, %ld, %p, %ld, %p)\n", debugstr_w(server),
1954             debugstr_w(env), level, Info, cbBuf, pcbNeeded);
1955
1956     if(server != NULL && server[0]) {
1957         FIXME("server not supported: %s\n", debugstr_w(server));
1958         SetLastError(ERROR_INVALID_PARAMETER);
1959         return FALSE;
1960     }
1961
1962     env_t = validate_envW(env);
1963     if(!env_t) return FALSE;  /* environment invalid or unsupported */
1964
1965     if(level != 1) {
1966         WARN("(Level: %ld) is ignored in win9x\n", level);
1967         SetLastError(ERROR_INVALID_LEVEL);
1968         return FALSE;
1969     }
1970
1971     /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
1972     needed = GetSystemDirectoryW(NULL, 0);
1973     /* add the Size for the Subdirectories */
1974     needed += lstrlenW(spoolprtprocsW);
1975     needed += lstrlenW(env_t->subdir);
1976     needed *= sizeof(WCHAR);  /* return-value is size in Bytes */
1977
1978     if(pcbNeeded) *pcbNeeded = needed;
1979     TRACE ("required: 0x%lx/%ld\n", needed, needed);
1980     if (needed > cbBuf) {
1981         SetLastError(ERROR_INSUFFICIENT_BUFFER);
1982         return FALSE;
1983     }
1984     if(pcbNeeded == NULL) {
1985         /* NT: RPC_X_NULL_REF_POINTER, 9x: ignored */
1986         WARN("(pcbNeeded == NULL) is ignored in win9x\n");
1987         SetLastError(RPC_X_NULL_REF_POINTER);
1988         return FALSE;
1989     }
1990     if(Info == NULL) {
1991         /* NT: RPC_X_NULL_REF_POINTER, 9x: ERROR_INVALID_PARAMETER */
1992         SetLastError(RPC_X_NULL_REF_POINTER);
1993         return FALSE;
1994     }
1995     
1996     GetSystemDirectoryW((LPWSTR) Info, cbBuf/sizeof(WCHAR));
1997     /* add the Subdirectories */
1998     lstrcatW((LPWSTR) Info, spoolprtprocsW);
1999     lstrcatW((LPWSTR) Info, env_t->subdir);
2000     TRACE(" => %s\n", debugstr_w((LPWSTR) Info));
2001     return TRUE;
2002 }
2003
2004 /*****************************************************************************
2005  *          WINSPOOL_OpenDriverReg [internal]
2006  *
2007  * opens the registry for the printer drivers depending on the given input
2008  * variable pEnvironment
2009  *
2010  * RETURNS:
2011  *    the opened hkey on success
2012  *    NULL on error
2013  */
2014 static HKEY WINSPOOL_OpenDriverReg( LPVOID pEnvironment, BOOL unicode)
2015 {   
2016     HKEY  retval = NULL;
2017     LPWSTR buffer;
2018     const printenv_t * env;
2019
2020     TRACE("(%s, %d)\n",
2021           (unicode) ? debugstr_w(pEnvironment) : debugstr_a(pEnvironment), unicode);
2022
2023     if (!pEnvironment || unicode) {
2024         /* pEnvironment was NULL or an Unicode-String: use it direct */
2025         env = validate_envW(pEnvironment);
2026     }
2027     else
2028     {
2029         /* pEnvironment was an ANSI-String: convert to unicode first */
2030         LPWSTR  buffer;
2031         INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, NULL, 0);
2032         buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2033         if (buffer) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, buffer, len);
2034         env = validate_envW(buffer);
2035         HeapFree(GetProcessHeap(), 0, buffer);
2036     }
2037     if (!env) return NULL;
2038
2039     buffer = HeapAlloc( GetProcessHeap(), 0,
2040                 (strlenW(DriversW) + strlenW(env->envname) + 
2041                  strlenW(env->versionregpath) + 1) * sizeof(WCHAR));
2042     if(buffer) {
2043         wsprintfW(buffer, DriversW, env->envname, env->versionregpath);
2044         RegCreateKeyW(HKEY_LOCAL_MACHINE, buffer, &retval);
2045         HeapFree(GetProcessHeap(), 0, buffer);
2046     }
2047     return retval;
2048 }
2049
2050 /*****************************************************************************
2051  *          AddPrinterW  [WINSPOOL.@]
2052  */
2053 HANDLE WINAPI AddPrinterW(LPWSTR pName, DWORD Level, LPBYTE pPrinter)
2054 {
2055     PRINTER_INFO_2W *pi = (PRINTER_INFO_2W *) pPrinter;
2056     LPDEVMODEA dmA;
2057     LPDEVMODEW dmW;
2058     HANDLE retval;
2059     HKEY hkeyPrinter, hkeyPrinters, hkeyDriver, hkeyDrivers;
2060     LONG size;
2061
2062     TRACE("(%s,%ld,%p)\n", debugstr_w(pName), Level, pPrinter);
2063
2064     if(pName != NULL) {
2065         ERR("pName = %s - unsupported\n", debugstr_w(pName));
2066         SetLastError(ERROR_INVALID_PARAMETER);
2067         return 0;
2068     }
2069     if(Level != 2) {
2070         ERR("Level = %ld, unsupported!\n", Level);
2071         SetLastError(ERROR_INVALID_LEVEL);
2072         return 0;
2073     }
2074     if(!pPrinter) {
2075         SetLastError(ERROR_INVALID_PARAMETER);
2076         return 0;
2077     }
2078     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
2079        ERROR_SUCCESS) {
2080         ERR("Can't create Printers key\n");
2081         return 0;
2082     }
2083     if(!RegOpenKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter)) {
2084         if (!RegQueryValueA(hkeyPrinter,"Attributes",NULL,NULL)) {
2085             SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
2086             RegCloseKey(hkeyPrinter);
2087             RegCloseKey(hkeyPrinters);
2088             return 0;
2089         }
2090         RegCloseKey(hkeyPrinter);
2091     }
2092     hkeyDrivers = WINSPOOL_OpenDriverReg( NULL, TRUE);
2093     if(!hkeyDrivers) {
2094         ERR("Can't create Drivers key\n");
2095         RegCloseKey(hkeyPrinters);
2096         return 0;
2097     }
2098     if(RegOpenKeyW(hkeyDrivers, pi->pDriverName, &hkeyDriver) !=
2099        ERROR_SUCCESS) {
2100         WARN("Can't find driver %s\n", debugstr_w(pi->pDriverName));
2101         RegCloseKey(hkeyPrinters);
2102         RegCloseKey(hkeyDrivers);
2103         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER);
2104         return 0;
2105     }
2106     RegCloseKey(hkeyDriver);
2107     RegCloseKey(hkeyDrivers);
2108
2109     if(lstrcmpiW(pi->pPrintProcessor, WinPrintW)) {  /* FIXME */
2110         FIXME("Can't find processor %s\n", debugstr_w(pi->pPrintProcessor));
2111         SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR);
2112         RegCloseKey(hkeyPrinters);
2113         return 0;
2114     }
2115
2116     if(RegCreateKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter) !=
2117        ERROR_SUCCESS) {
2118         FIXME("Can't create printer %s\n", debugstr_w(pi->pPrinterName));
2119         SetLastError(ERROR_INVALID_PRINTER_NAME);
2120         RegCloseKey(hkeyPrinters);
2121         return 0;
2122     }
2123     RegSetValueExA(hkeyPrinter, "Attributes", 0, REG_DWORD,
2124                    (LPBYTE)&pi->Attributes, sizeof(DWORD));
2125     set_reg_szW(hkeyPrinter, DatatypeW, pi->pDatatype);
2126
2127     /* See if we can load the driver.  We may need the devmode structure anyway
2128      *
2129      * FIXME:
2130      * Note that DocumentPropertiesW will briefly try to open the printer we
2131      * just create to find a DEVMODEA struct (it will use the WINEPS default
2132      * one in case it is not there, so we are ok).
2133      */
2134     size = DocumentPropertiesW(0, 0, pi->pPrinterName, NULL, NULL, 0);
2135
2136     if(size < 0) {
2137         FIXME("DocumentPropertiesW on printer '%s' fails\n", debugstr_w(pi->pPrinterName));
2138         size = sizeof(DEVMODEW);
2139     }
2140     if(pi->pDevMode)
2141         dmW = pi->pDevMode;
2142     else 
2143     {
2144         dmW = HeapAlloc(GetProcessHeap(), 0, size);
2145         ZeroMemory(dmW,size);
2146         dmW->dmSize = size;
2147         if (0>DocumentPropertiesW(0,0,pi->pPrinterName,dmW,NULL,DM_OUT_BUFFER))
2148         {
2149             WARN("DocumentPropertiesW on printer '%s' failed!\n", debugstr_w(pi->pPrinterName));
2150             HeapFree(GetProcessHeap(),0,dmW);
2151             dmW=NULL;
2152         }
2153         else
2154         {
2155             /* set devmode to printer name */
2156             lstrcpynW(dmW->dmDeviceName, pi->pPrinterName, CCHDEVICENAME);
2157         }
2158     }
2159
2160     /* Write DEVMODEA not DEVMODEW into reg.  This is what win9x does
2161        and we support these drivers.  NT writes DEVMODEW so somehow
2162        we'll need to distinguish between these when we support NT
2163        drivers */
2164     if (dmW)
2165     {
2166         dmA = DEVMODEdupWtoA(GetProcessHeap(), dmW);
2167         RegSetValueExA(hkeyPrinter, "Default DevMode", 0, REG_BINARY, 
2168                        (LPBYTE)dmA, dmA->dmSize + dmA->dmDriverExtra);
2169         HeapFree(GetProcessHeap(), 0, dmA);
2170         if(!pi->pDevMode)
2171             HeapFree(GetProcessHeap(), 0, dmW);
2172     }
2173     set_reg_szW(hkeyPrinter, DescriptionW, pi->pComment);
2174     set_reg_szW(hkeyPrinter, LocationW, pi->pLocation);
2175     set_reg_szW(hkeyPrinter, NameW, pi->pPrinterName);
2176     set_reg_szW(hkeyPrinter, ParametersW, pi->pParameters);
2177
2178     set_reg_szW(hkeyPrinter, PortW, pi->pPortName);
2179     set_reg_szW(hkeyPrinter, Print_ProcessorW, pi->pPrintProcessor);
2180     set_reg_szW(hkeyPrinter, Printer_DriverW, pi->pDriverName);
2181     RegSetValueExA(hkeyPrinter, "Priority", 0, REG_DWORD,
2182                    (LPBYTE)&pi->Priority, sizeof(DWORD));
2183     set_reg_szW(hkeyPrinter, Separator_FileW, pi->pSepFile);
2184     set_reg_szW(hkeyPrinter, Share_NameW, pi->pShareName);
2185     RegSetValueExA(hkeyPrinter, "StartTime", 0, REG_DWORD,
2186                    (LPBYTE)&pi->StartTime, sizeof(DWORD));
2187     RegSetValueExA(hkeyPrinter, "Status", 0, REG_DWORD,
2188                    (LPBYTE)&pi->Status, sizeof(DWORD));
2189     RegSetValueExA(hkeyPrinter, "UntilTime", 0, REG_DWORD,
2190                    (LPBYTE)&pi->UntilTime, sizeof(DWORD));
2191
2192     RegCloseKey(hkeyPrinter);
2193     RegCloseKey(hkeyPrinters);
2194     if(!OpenPrinterW(pi->pPrinterName, &retval, NULL)) {
2195         ERR("OpenPrinter failing\n");
2196         return 0;
2197     }
2198     return retval;
2199 }
2200
2201 /*****************************************************************************
2202  *          AddPrinterA  [WINSPOOL.@]
2203  */
2204 HANDLE WINAPI AddPrinterA(LPSTR pName, DWORD Level, LPBYTE pPrinter)
2205 {
2206     UNICODE_STRING pNameW;
2207     PWSTR pwstrNameW;
2208     PRINTER_INFO_2W *piW;
2209     PRINTER_INFO_2A *piA = (PRINTER_INFO_2A*)pPrinter;
2210     HANDLE ret;
2211
2212     TRACE("(%s,%ld,%p): stub\n", debugstr_a(pName), Level, pPrinter);
2213     if(Level != 2) {
2214         ERR("Level = %ld, unsupported!\n", Level);
2215         SetLastError(ERROR_INVALID_LEVEL);
2216         return 0;
2217     }
2218     pwstrNameW = asciitounicode(&pNameW,pName);
2219     piW = PRINTER_INFO_2AtoW(GetProcessHeap(), piA);
2220
2221     ret = AddPrinterW(pwstrNameW, Level, (LPBYTE)piW);
2222
2223     FREE_PRINTER_INFO_2W(GetProcessHeap(), piW);
2224     RtlFreeUnicodeString(&pNameW);
2225     return ret;
2226 }
2227
2228
2229 /*****************************************************************************
2230  *          ClosePrinter  [WINSPOOL.@]
2231  */
2232 BOOL WINAPI ClosePrinter(HANDLE hPrinter)
2233 {
2234     UINT_PTR i = (UINT_PTR)hPrinter;
2235     opened_printer_t *printer = NULL;
2236     BOOL ret = FALSE;
2237
2238     TRACE("Handle %p\n", hPrinter);
2239
2240     EnterCriticalSection(&printer_handles_cs);
2241
2242     if ((i > 0) && (i <= nb_printer_handles))
2243         printer = printer_handles[i - 1];
2244
2245     if(printer)
2246     {
2247         struct list *cursor, *cursor2;
2248
2249         if(printer->doc)
2250             EndDocPrinter(hPrinter);
2251
2252         if(InterlockedDecrement(&printer->queue->ref) == 0)
2253         {
2254             LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
2255             {
2256                 job_t *job = LIST_ENTRY(cursor, job_t, entry);
2257                 ScheduleJob(hPrinter, job->job_id);
2258             }
2259             HeapFree(GetProcessHeap(), 0, printer->queue);
2260         }
2261         HeapFree(GetProcessHeap(), 0, printer->name);
2262         HeapFree(GetProcessHeap(), 0, printer);
2263         printer_handles[i - 1] = NULL;
2264         ret = TRUE;
2265     }
2266     LeaveCriticalSection(&printer_handles_cs);
2267     return ret;
2268 }
2269
2270 /*****************************************************************************
2271  *          DeleteFormA  [WINSPOOL.@]
2272  */
2273 BOOL WINAPI DeleteFormA(HANDLE hPrinter, LPSTR pFormName)
2274 {
2275     FIXME("(%p,%s): stub\n", hPrinter, pFormName);
2276     return 1;
2277 }
2278
2279 /*****************************************************************************
2280  *          DeleteFormW  [WINSPOOL.@]
2281  */
2282 BOOL WINAPI DeleteFormW(HANDLE hPrinter, LPWSTR pFormName)
2283 {
2284     FIXME("(%p,%s): stub\n", hPrinter, debugstr_w(pFormName));
2285     return 1;
2286 }
2287
2288 /*****************************************************************************
2289  *   WINSPOOL_SHRegDeleteKey
2290  *
2291  *   Recursively delete subkeys.
2292  *   Cut & paste from shlwapi.
2293  * 
2294  */
2295 static DWORD WINSPOOL_SHDeleteKeyW(HKEY hKey, LPCWSTR lpszSubKey)
2296 {
2297   DWORD dwRet, dwKeyCount = 0, dwMaxSubkeyLen = 0, dwSize, i;
2298   WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
2299   HKEY hSubKey = 0;
2300
2301   dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
2302   if(!dwRet)
2303   {
2304     /* Find how many subkeys there are */
2305     dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwKeyCount,
2306                              &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
2307     if(!dwRet)
2308     {
2309       dwMaxSubkeyLen++;
2310       if (dwMaxSubkeyLen > sizeof(szNameBuf)/sizeof(WCHAR))
2311         /* Name too big: alloc a buffer for it */
2312         lpszName = HeapAlloc(GetProcessHeap(), 0, dwMaxSubkeyLen*sizeof(WCHAR));
2313
2314       if(!lpszName)
2315         dwRet = ERROR_NOT_ENOUGH_MEMORY;
2316       else
2317       {
2318         /* Recursively delete all the subkeys */
2319         for(i = 0; i < dwKeyCount && !dwRet; i++)
2320         {
2321           dwSize = dwMaxSubkeyLen;
2322           dwRet = RegEnumKeyExW(hSubKey, i, lpszName, &dwSize, NULL, NULL, NULL, NULL);
2323           if(!dwRet)
2324             dwRet = WINSPOOL_SHDeleteKeyW(hSubKey, lpszName);
2325         }
2326
2327         if (lpszName != szNameBuf)
2328           HeapFree(GetProcessHeap(), 0, lpszName); /* Free buffer if allocated */
2329       }
2330     }
2331
2332     RegCloseKey(hSubKey);
2333     if(!dwRet)
2334       dwRet = RegDeleteKeyW(hKey, lpszSubKey);
2335   }
2336   return dwRet;
2337 }
2338
2339 /*****************************************************************************
2340  *          DeletePrinter  [WINSPOOL.@]
2341  */
2342 BOOL WINAPI DeletePrinter(HANDLE hPrinter)
2343 {
2344     LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
2345     HKEY hkeyPrinters, hkey;
2346
2347     if(!lpNameW) {
2348         SetLastError(ERROR_INVALID_HANDLE);
2349         return FALSE;
2350     }
2351     if(RegOpenKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
2352         WINSPOOL_SHDeleteKeyW(hkeyPrinters, lpNameW);
2353         RegCloseKey(hkeyPrinters);
2354     }
2355     WriteProfileStringW(devicesW, lpNameW, NULL);
2356     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
2357         RegDeleteValueW(hkey, lpNameW);
2358         RegCloseKey(hkey);
2359     }
2360     return TRUE;
2361 }
2362
2363 /*****************************************************************************
2364  *          SetPrinterA  [WINSPOOL.@]
2365  */
2366 BOOL WINAPI SetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
2367                            DWORD Command)
2368 {
2369     FIXME("(%p,%ld,%p,%ld): stub\n",hPrinter,Level,pPrinter,Command);
2370     return FALSE;
2371 }
2372
2373 /*****************************************************************************
2374  *          SetJobA  [WINSPOOL.@]
2375  */
2376 BOOL WINAPI SetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level,
2377                     LPBYTE pJob, DWORD Command)
2378 {
2379     BOOL ret;
2380     LPBYTE JobW;
2381     UNICODE_STRING usBuffer;
2382
2383     TRACE("(%p, %ld, %ld, %p, %ld)\n",hPrinter, JobId, Level, pJob, Command);
2384
2385     /* JobId, pPrinterName, pMachineName, pDriverName, Size, Submitted, Time and TotalPages
2386        are all ignored by SetJob, so we don't bother copying them */
2387     switch(Level)
2388     {
2389     case 0:
2390         JobW = NULL;
2391         break;
2392     case 1:
2393       {
2394         JOB_INFO_1W *info1W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info1W));
2395         JOB_INFO_1A *info1A = (JOB_INFO_1A*)pJob;
2396
2397         JobW = (LPBYTE)info1W;
2398         info1W->pUserName = asciitounicode(&usBuffer, info1A->pUserName);
2399         info1W->pDocument = asciitounicode(&usBuffer, info1A->pDocument);
2400         info1W->pDatatype = asciitounicode(&usBuffer, info1A->pDatatype);
2401         info1W->pStatus = asciitounicode(&usBuffer, info1A->pStatus);
2402         info1W->Status = info1A->Status;
2403         info1W->Priority = info1A->Priority;
2404         info1W->Position = info1A->Position;
2405         info1W->PagesPrinted = info1A->PagesPrinted;
2406         break;
2407       }
2408     case 2:
2409       {
2410         JOB_INFO_2W *info2W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info2W));
2411         JOB_INFO_2A *info2A = (JOB_INFO_2A*)pJob;
2412
2413         JobW = (LPBYTE)info2W;
2414         info2W->pUserName = asciitounicode(&usBuffer, info2A->pUserName);
2415         info2W->pDocument = asciitounicode(&usBuffer, info2A->pDocument);
2416         info2W->pNotifyName = asciitounicode(&usBuffer, info2A->pNotifyName);
2417         info2W->pDatatype = asciitounicode(&usBuffer, info2A->pDatatype);
2418         info2W->pPrintProcessor = asciitounicode(&usBuffer, info2A->pPrintProcessor);
2419         info2W->pParameters = asciitounicode(&usBuffer, info2A->pParameters);
2420         info2W->pDevMode = info2A->pDevMode ? GdiConvertToDevmodeW(info2A->pDevMode) : NULL;
2421         info2W->pStatus = asciitounicode(&usBuffer, info2A->pStatus);
2422         info2W->pSecurityDescriptor = info2A->pSecurityDescriptor;
2423         info2W->Status = info2A->Status;
2424         info2W->Priority = info2A->Priority;
2425         info2W->Position = info2A->Position;
2426         info2W->StartTime = info2A->StartTime;
2427         info2W->UntilTime = info2A->UntilTime;
2428         info2W->PagesPrinted = info2A->PagesPrinted;
2429         break;
2430       }
2431     case 3:
2432         JobW = HeapAlloc(GetProcessHeap(), 0, sizeof(JOB_INFO_3));
2433         memcpy(JobW, pJob, sizeof(JOB_INFO_3));
2434         break;
2435     default:
2436         SetLastError(ERROR_INVALID_LEVEL);
2437         return FALSE;
2438     }
2439
2440     ret = SetJobW(hPrinter, JobId, Level, JobW, Command);
2441
2442     switch(Level)
2443     {
2444     case 1:
2445       {
2446         JOB_INFO_1W *info1W = (JOB_INFO_1W*)JobW;
2447         HeapFree(GetProcessHeap(), 0, info1W->pUserName);
2448         HeapFree(GetProcessHeap(), 0, info1W->pDocument); 
2449         HeapFree(GetProcessHeap(), 0, info1W->pDatatype);
2450         HeapFree(GetProcessHeap(), 0, info1W->pStatus);
2451         break;
2452       }
2453     case 2:
2454       {
2455         JOB_INFO_2W *info2W = (JOB_INFO_2W*)JobW;
2456         HeapFree(GetProcessHeap(), 0, info2W->pUserName);
2457         HeapFree(GetProcessHeap(), 0, info2W->pDocument); 
2458         HeapFree(GetProcessHeap(), 0, info2W->pNotifyName);
2459         HeapFree(GetProcessHeap(), 0, info2W->pDatatype);
2460         HeapFree(GetProcessHeap(), 0, info2W->pPrintProcessor);
2461         HeapFree(GetProcessHeap(), 0, info2W->pParameters);
2462         HeapFree(GetProcessHeap(), 0, info2W->pDevMode);
2463         HeapFree(GetProcessHeap(), 0, info2W->pStatus);
2464         break;
2465       }
2466     }
2467     HeapFree(GetProcessHeap(), 0, JobW);
2468
2469     return ret;
2470 }
2471
2472 /*****************************************************************************
2473  *          SetJobW  [WINSPOOL.@]
2474  */
2475 BOOL WINAPI SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level,
2476                     LPBYTE pJob, DWORD Command)
2477 {
2478     BOOL ret = FALSE;
2479     job_t *job;
2480
2481     TRACE("(%p, %ld, %ld, %p, %ld)\n", hPrinter, JobId, Level, pJob, Command);
2482     FIXME("Ignoring everything other than document title\n");
2483
2484     EnterCriticalSection(&printer_handles_cs);
2485     job = get_job(hPrinter, JobId);
2486     if(!job)
2487         goto end;
2488
2489     switch(Level)
2490     {
2491     case 0:
2492         break;
2493     case 1:
2494       {
2495         JOB_INFO_1W *info1 = (JOB_INFO_1W*)pJob;
2496         HeapFree(GetProcessHeap(), 0, job->document_title);
2497         job->document_title = strdupW(info1->pDocument);
2498         break;
2499       }
2500     case 2:
2501       {
2502         JOB_INFO_2W *info2 = (JOB_INFO_2W*)pJob;
2503         HeapFree(GetProcessHeap(), 0, job->document_title);
2504         job->document_title = strdupW(info2->pDocument);
2505         break;
2506       }
2507     case 3:
2508         break;
2509     default:
2510         SetLastError(ERROR_INVALID_LEVEL);
2511         goto end;
2512     }
2513     ret = TRUE;
2514 end:
2515     LeaveCriticalSection(&printer_handles_cs);
2516     return ret;
2517 }
2518
2519 /*****************************************************************************
2520  *          EndDocPrinter  [WINSPOOL.@]
2521  */
2522 BOOL WINAPI EndDocPrinter(HANDLE hPrinter)
2523 {
2524     opened_printer_t *printer;
2525     BOOL ret = FALSE;
2526     TRACE("(%p)\n", hPrinter);
2527
2528     EnterCriticalSection(&printer_handles_cs);
2529
2530     printer = get_opened_printer(hPrinter);
2531     if(!printer)
2532     {
2533         SetLastError(ERROR_INVALID_HANDLE);
2534         goto end;
2535     }
2536
2537     if(!printer->doc)    
2538     {
2539         SetLastError(ERROR_SPL_NO_STARTDOC);
2540         goto end;
2541     }
2542
2543     CloseHandle(printer->doc->hf);
2544     ScheduleJob(hPrinter, printer->doc->job_id);
2545     HeapFree(GetProcessHeap(), 0, printer->doc);
2546     printer->doc = NULL;
2547     ret = TRUE;
2548 end:
2549     LeaveCriticalSection(&printer_handles_cs);
2550     return ret;
2551 }
2552
2553 /*****************************************************************************
2554  *          EndPagePrinter  [WINSPOOL.@]
2555  */
2556 BOOL WINAPI EndPagePrinter(HANDLE hPrinter)
2557 {
2558     FIXME("(%p): stub\n", hPrinter);
2559     return TRUE;
2560 }
2561
2562 /*****************************************************************************
2563  *          StartDocPrinterA  [WINSPOOL.@]
2564  */
2565 DWORD WINAPI StartDocPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
2566 {
2567     UNICODE_STRING usBuffer;
2568     DOC_INFO_2W doc2W;
2569     DOC_INFO_2A *doc2 = (DOC_INFO_2A*)pDocInfo;
2570     DWORD ret;
2571
2572     /* DOC_INFO_1, 2 and 3 all have the strings in the same place with either two (DOC_INFO_2)
2573        or one (DOC_INFO_3) extra DWORDs */
2574
2575     switch(Level) {
2576     case 2:
2577         doc2W.JobId = doc2->JobId;
2578         /* fall through */
2579     case 3:
2580         doc2W.dwMode = doc2->dwMode;
2581         /* fall through */
2582     case 1:
2583         doc2W.pDocName = asciitounicode(&usBuffer, doc2->pDocName);
2584         doc2W.pOutputFile = asciitounicode(&usBuffer, doc2->pOutputFile);
2585         doc2W.pDatatype = asciitounicode(&usBuffer, doc2->pDatatype);
2586         break;
2587
2588     default:
2589         SetLastError(ERROR_INVALID_LEVEL);
2590         return FALSE;
2591     }
2592
2593     ret = StartDocPrinterW(hPrinter, Level, (LPBYTE)&doc2W);
2594
2595     HeapFree(GetProcessHeap(), 0, doc2W.pDatatype);
2596     HeapFree(GetProcessHeap(), 0, doc2W.pOutputFile);
2597     HeapFree(GetProcessHeap(), 0, doc2W.pDocName);
2598
2599     return ret;
2600 }
2601
2602 /*****************************************************************************
2603  *          StartDocPrinterW  [WINSPOOL.@]
2604  */
2605 DWORD WINAPI StartDocPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
2606 {
2607     DOC_INFO_2W *doc = (DOC_INFO_2W *)pDocInfo;
2608     opened_printer_t *printer;
2609     BYTE addjob_buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
2610     ADDJOB_INFO_1W *addjob = (ADDJOB_INFO_1W*) addjob_buf;
2611     JOB_INFO_1W job_info;
2612     DWORD needed, ret = 0;
2613     HANDLE hf;
2614     WCHAR *filename;
2615
2616     TRACE("(hPrinter = %p, Level = %ld, pDocInfo = %p {pDocName = %s, pOutputFile = %s, pDatatype = %s}):\n",
2617           hPrinter, Level, doc, debugstr_w(doc->pDocName), debugstr_w(doc->pOutputFile),
2618           debugstr_w(doc->pDatatype));
2619
2620     if(Level < 1 || Level > 3)
2621     {
2622         SetLastError(ERROR_INVALID_LEVEL);
2623         return 0;
2624     }
2625
2626     EnterCriticalSection(&printer_handles_cs);
2627     printer = get_opened_printer(hPrinter);
2628     if(!printer)
2629     {
2630         SetLastError(ERROR_INVALID_HANDLE);
2631         goto end;
2632     }
2633
2634     if(printer->doc)
2635     {
2636         SetLastError(ERROR_INVALID_PRINTER_STATE);
2637         goto end;
2638     }
2639
2640     /* Even if we're printing to a file we still add a print job, we'll
2641        just ignore the spool file name */
2642
2643     if(!AddJobW(hPrinter, 1, addjob_buf, sizeof(addjob_buf), &needed))
2644     {
2645         ERR("AddJob failed gle %08lx\n", GetLastError());
2646         goto end;
2647     }
2648
2649     if(doc->pOutputFile)
2650         filename = doc->pOutputFile;
2651     else
2652         filename = addjob->Path;
2653
2654     hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2655     if(hf == INVALID_HANDLE_VALUE)
2656         goto end;
2657
2658     memset(&job_info, 0, sizeof(job_info));
2659     job_info.pDocument = doc->pDocName;
2660     SetJobW(hPrinter, addjob->JobId, 1, (LPBYTE)&job_info, 0);
2661
2662     printer->doc = HeapAlloc(GetProcessHeap(), 0, sizeof(*printer->doc));
2663     printer->doc->hf = hf;
2664     ret = printer->doc->job_id = addjob->JobId;
2665 end:
2666     LeaveCriticalSection(&printer_handles_cs);
2667
2668     return ret;
2669 }
2670
2671 /*****************************************************************************
2672  *          StartPagePrinter  [WINSPOOL.@]
2673  */
2674 BOOL WINAPI StartPagePrinter(HANDLE hPrinter)
2675 {
2676     FIXME("(%p): stub\n", hPrinter);
2677     return TRUE;
2678 }
2679
2680 /*****************************************************************************
2681  *          GetFormA  [WINSPOOL.@]
2682  */
2683 BOOL WINAPI GetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
2684                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
2685 {
2686     FIXME("(%p,%s,%ld,%p,%ld,%p): stub\n",hPrinter,pFormName,
2687          Level,pForm,cbBuf,pcbNeeded);
2688     return FALSE;
2689 }
2690
2691 /*****************************************************************************
2692  *          GetFormW  [WINSPOOL.@]
2693  */
2694 BOOL WINAPI GetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
2695                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
2696 {
2697     FIXME("(%p,%s,%ld,%p,%ld,%p): stub\n",hPrinter,
2698           debugstr_w(pFormName),Level,pForm,cbBuf,pcbNeeded);
2699     return FALSE;
2700 }
2701
2702 /*****************************************************************************
2703  *          SetFormA  [WINSPOOL.@]
2704  */
2705 BOOL WINAPI SetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
2706                         LPBYTE pForm)
2707 {
2708     FIXME("(%p,%s,%ld,%p): stub\n",hPrinter,pFormName,Level,pForm);
2709     return FALSE;
2710 }
2711
2712 /*****************************************************************************
2713  *          SetFormW  [WINSPOOL.@]
2714  */
2715 BOOL WINAPI SetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
2716                         LPBYTE pForm)
2717 {
2718     FIXME("(%p,%p,%ld,%p): stub\n",hPrinter,pFormName,Level,pForm);
2719     return FALSE;
2720 }
2721
2722 /*****************************************************************************
2723  *          ReadPrinter  [WINSPOOL.@]
2724  */
2725 BOOL WINAPI ReadPrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf,
2726                            LPDWORD pNoBytesRead)
2727 {
2728     FIXME("(%p,%p,%ld,%p): stub\n",hPrinter,pBuf,cbBuf,pNoBytesRead);
2729     return FALSE;
2730 }
2731
2732 /*****************************************************************************
2733  *          ResetPrinterA  [WINSPOOL.@]
2734  */
2735 BOOL WINAPI ResetPrinterA(HANDLE hPrinter, LPPRINTER_DEFAULTSA pDefault)
2736 {
2737     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
2738     return FALSE;
2739 }
2740
2741 /*****************************************************************************
2742  *          ResetPrinterW  [WINSPOOL.@]
2743  */
2744 BOOL WINAPI ResetPrinterW(HANDLE hPrinter, LPPRINTER_DEFAULTSW pDefault)
2745 {
2746     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
2747     return FALSE;
2748 }
2749
2750 /*****************************************************************************
2751  *    WINSPOOL_GetDWORDFromReg
2752  *
2753  * Return DWORD associated with ValueName from hkey.
2754  */
2755 static DWORD WINSPOOL_GetDWORDFromReg(HKEY hkey, LPCSTR ValueName)
2756 {
2757     DWORD sz = sizeof(DWORD), type, value = 0;
2758     LONG ret;
2759
2760     ret = RegQueryValueExA(hkey, ValueName, 0, &type, (LPBYTE)&value, &sz);
2761
2762     if(ret != ERROR_SUCCESS) {
2763         WARN("Got ret = %ld on name %s\n", ret, ValueName);
2764         return 0;
2765     }
2766     if(type != REG_DWORD) {
2767         ERR("Got type %ld\n", type);
2768         return 0;
2769     }
2770     return value;
2771 }
2772
2773 /*****************************************************************************
2774  *    WINSPOOL_GetStringFromReg
2775  *
2776  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
2777  * String is stored either as unicode or ascii.
2778  * Bit of a hack here to get the ValueName if we want ascii.
2779  */
2780 static BOOL WINSPOOL_GetStringFromReg(HKEY hkey, LPCWSTR ValueName, LPBYTE ptr,
2781                                       DWORD buflen, DWORD *needed,
2782                                       BOOL unicode)
2783 {
2784     DWORD sz = buflen, type;
2785     LONG ret;
2786
2787     if(unicode)
2788         ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
2789     else {
2790         LPSTR ValueNameA = HEAP_strdupWtoA(GetProcessHeap(),0,ValueName);
2791         ret = RegQueryValueExA(hkey, ValueNameA, 0, &type, ptr, &sz);
2792         HeapFree(GetProcessHeap(),0,ValueNameA);
2793     }
2794     if(ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) {
2795         WARN("Got ret = %ld\n", ret);
2796         *needed = 0;
2797         return FALSE;
2798     }
2799     /* add space for terminating '\0' */
2800     sz += unicode ? sizeof(WCHAR) : 1;
2801     *needed = sz;
2802
2803     if (ptr)
2804         TRACE("%s: %s\n", debugstr_w(ValueName), unicode ? debugstr_w((LPCWSTR)ptr) : debugstr_a((LPCSTR)ptr));
2805
2806     return TRUE;
2807 }
2808
2809 /*****************************************************************************
2810  *    WINSPOOL_GetDefaultDevMode
2811  *
2812  * Get a default DevMode values for wineps.
2813  * FIXME - use ppd.
2814  */
2815
2816 static void WINSPOOL_GetDefaultDevMode(
2817         LPBYTE ptr,
2818         DWORD buflen, DWORD *needed,
2819         BOOL unicode)
2820 {
2821     DEVMODEA    dm;
2822     static const char szwps[] = "wineps.drv";
2823
2824         /* fill default DEVMODE - should be read from ppd... */
2825         ZeroMemory( &dm, sizeof(dm) );
2826         memcpy(dm.dmDeviceName,szwps,sizeof szwps);
2827         dm.dmSpecVersion = DM_SPECVERSION;
2828         dm.dmDriverVersion = 1;
2829         dm.dmSize = sizeof(DEVMODEA);
2830         dm.dmDriverExtra = 0;
2831         dm.dmFields =
2832                 DM_ORIENTATION | DM_PAPERSIZE |
2833                 DM_PAPERLENGTH | DM_PAPERWIDTH |
2834                 DM_SCALE |
2835                 DM_COPIES |
2836                 DM_DEFAULTSOURCE | DM_PRINTQUALITY |
2837                 DM_YRESOLUTION | DM_TTOPTION;
2838
2839         dm.u1.s1.dmOrientation = DMORIENT_PORTRAIT;
2840         dm.u1.s1.dmPaperSize = DMPAPER_A4;
2841         dm.u1.s1.dmPaperLength = 2970;
2842         dm.u1.s1.dmPaperWidth = 2100;
2843
2844         dm.dmScale = 100;
2845         dm.dmCopies = 1;
2846         dm.dmDefaultSource = DMBIN_AUTO;
2847         dm.dmPrintQuality = DMRES_MEDIUM;
2848         /* dm.dmColor */
2849         /* dm.dmDuplex */
2850         dm.dmYResolution = 300; /* 300dpi */
2851         dm.dmTTOption = DMTT_BITMAP;
2852         /* dm.dmCollate */
2853         /* dm.dmFormName */
2854         /* dm.dmLogPixels */
2855         /* dm.dmBitsPerPel */
2856         /* dm.dmPelsWidth */
2857         /* dm.dmPelsHeight */
2858         /* dm.dmDisplayFlags */
2859         /* dm.dmDisplayFrequency */
2860         /* dm.dmICMMethod */
2861         /* dm.dmICMIntent */
2862         /* dm.dmMediaType */
2863         /* dm.dmDitherType */
2864         /* dm.dmReserved1 */
2865         /* dm.dmReserved2 */
2866         /* dm.dmPanningWidth */
2867         /* dm.dmPanningHeight */
2868
2869     if(unicode) {
2870         if(buflen >= sizeof(DEVMODEW)) {
2871             DEVMODEW *pdmW = GdiConvertToDevmodeW(&dm);
2872             memcpy(ptr, pdmW, sizeof(DEVMODEW));
2873             HeapFree(GetProcessHeap(),0,pdmW);
2874         }
2875         *needed = sizeof(DEVMODEW);
2876     }
2877     else
2878     {
2879         if(buflen >= sizeof(DEVMODEA)) {
2880             memcpy(ptr, &dm, sizeof(DEVMODEA));
2881         }
2882         *needed = sizeof(DEVMODEA);
2883     }
2884 }
2885
2886 /*****************************************************************************
2887  *    WINSPOOL_GetDevModeFromReg
2888  *
2889  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
2890  * DevMode is stored either as unicode or ascii.
2891  */
2892 static BOOL WINSPOOL_GetDevModeFromReg(HKEY hkey, LPCWSTR ValueName,
2893                                        LPBYTE ptr,
2894                                        DWORD buflen, DWORD *needed,
2895                                        BOOL unicode)
2896 {
2897     DWORD sz = buflen, type;
2898     LONG ret;
2899
2900     if (ptr && buflen>=sizeof(DEVMODEA)) memset(ptr, 0, sizeof(DEVMODEA));
2901     ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
2902     if ((ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA)) sz = 0;
2903     if (sz < sizeof(DEVMODEA))
2904     {
2905         TRACE("corrupted registry for %s ( size %ld)\n",debugstr_w(ValueName),sz);
2906         return FALSE;
2907     }
2908     /* ensures that dmSize is not erratically bogus if registry is invalid */
2909     if (ptr && ((DEVMODEA*)ptr)->dmSize < sizeof(DEVMODEA))
2910         ((DEVMODEA*)ptr)->dmSize = sizeof(DEVMODEA);
2911     if(unicode) {
2912         sz += (CCHDEVICENAME + CCHFORMNAME);
2913         if(buflen >= sz) {
2914             DEVMODEW *dmW = GdiConvertToDevmodeW((DEVMODEA*)ptr);
2915             memcpy(ptr, dmW, sz);
2916             HeapFree(GetProcessHeap(),0,dmW);
2917         }
2918     }
2919     *needed = sz;
2920     return TRUE;
2921 }
2922
2923 /*********************************************************************
2924  *    WINSPOOL_GetPrinter_2
2925  *
2926  * Fills out a PRINTER_INFO_2A|W struct storing the strings in buf.
2927  * The strings are either stored as unicode or ascii.
2928  */
2929 static BOOL WINSPOOL_GetPrinter_2(HKEY hkeyPrinter, PRINTER_INFO_2W *pi2,
2930                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
2931                                   BOOL unicode)
2932 {
2933     DWORD size, left = cbBuf;
2934     BOOL space = (cbBuf > 0);
2935     LPBYTE ptr = buf;
2936
2937     *pcbNeeded = 0;
2938
2939     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
2940                                  unicode)) {
2941         if(space && size <= left) {
2942             pi2->pPrinterName = (LPWSTR)ptr;
2943             ptr += size;
2944             left -= size;
2945         } else
2946             space = FALSE;
2947         *pcbNeeded += size;
2948     }
2949     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Share_NameW, ptr, left, &size,
2950                                  unicode)) {
2951         if(space && size <= left) {
2952             pi2->pShareName = (LPWSTR)ptr;
2953             ptr += size;
2954             left -= size;
2955         } else
2956             space = FALSE;
2957         *pcbNeeded += size;
2958     }
2959     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
2960                                  unicode)) {
2961         if(space && size <= left) {
2962             pi2->pPortName = (LPWSTR)ptr;
2963             ptr += size;
2964             left -= size;
2965         } else
2966             space = FALSE;
2967         *pcbNeeded += size;
2968     }
2969     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Printer_DriverW, ptr, left,
2970                                  &size, unicode)) {
2971         if(space && size <= left) {
2972             pi2->pDriverName = (LPWSTR)ptr;
2973             ptr += size;
2974             left -= size;
2975         } else
2976             space = FALSE;
2977         *pcbNeeded += size;
2978     }
2979     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size,
2980                                  unicode)) {
2981         if(space && size <= left) {
2982             pi2->pComment = (LPWSTR)ptr;
2983             ptr += size;
2984             left -= size;
2985         } else
2986             space = FALSE;
2987         *pcbNeeded += size;
2988     }
2989     if(WINSPOOL_GetStringFromReg(hkeyPrinter, LocationW, ptr, left, &size,
2990                                  unicode)) {
2991         if(space && size <= left) {
2992             pi2->pLocation = (LPWSTR)ptr;
2993             ptr += size;
2994             left -= size;
2995         } else
2996             space = FALSE;
2997         *pcbNeeded += size;
2998     }
2999     if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, ptr, left,
3000                                   &size, unicode)) {
3001         if(space && size <= left) {
3002             pi2->pDevMode = (LPDEVMODEW)ptr;
3003             ptr += size;
3004             left -= size;
3005         } else
3006             space = FALSE;
3007         *pcbNeeded += size;
3008     }
3009     else
3010     {
3011         WINSPOOL_GetDefaultDevMode(ptr, left, &size, unicode);
3012         if(space && size <= left) {
3013             pi2->pDevMode = (LPDEVMODEW)ptr;
3014             ptr += size;
3015             left -= size;
3016         } else
3017             space = FALSE;
3018         *pcbNeeded += size;
3019     }
3020     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Separator_FileW, ptr, left,
3021                                  &size, unicode)) {
3022         if(space && size <= left) {
3023             pi2->pSepFile = (LPWSTR)ptr;
3024             ptr += size;
3025             left -= size;
3026         } else
3027             space = FALSE;
3028         *pcbNeeded += size;
3029     }
3030     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Print_ProcessorW, ptr, left,
3031                                  &size, unicode)) {
3032         if(space && size <= left) {
3033             pi2->pPrintProcessor = (LPWSTR)ptr;
3034             ptr += size;
3035             left -= size;
3036         } else
3037             space = FALSE;
3038         *pcbNeeded += size;
3039     }
3040     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DatatypeW, ptr, left,
3041                                  &size, unicode)) {
3042         if(space && size <= left) {
3043             pi2->pDatatype = (LPWSTR)ptr;
3044             ptr += size;
3045             left -= size;
3046         } else
3047             space = FALSE;
3048         *pcbNeeded += size;
3049     }
3050     if(WINSPOOL_GetStringFromReg(hkeyPrinter, ParametersW, ptr, left,
3051                                  &size, unicode)) {
3052         if(space && size <= left) {
3053             pi2->pParameters = (LPWSTR)ptr;
3054             ptr += size;
3055             left -= size;
3056         } else
3057             space = FALSE;
3058         *pcbNeeded += size;
3059     }
3060     if(pi2) {
3061         pi2->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3062         pi2->Priority = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Priority");
3063         pi2->DefaultPriority = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3064                                                         "Default Priority");
3065         pi2->StartTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "StartTime");
3066         pi2->UntilTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "UntilTime");
3067     }
3068
3069     if(!space && pi2) /* zero out pi2 if we can't completely fill buf */
3070         memset(pi2, 0, sizeof(*pi2));
3071
3072     return space;
3073 }
3074
3075 /*********************************************************************
3076  *    WINSPOOL_GetPrinter_4
3077  *
3078  * Fills out a PRINTER_INFO_4 struct storing the strings in buf.
3079  */
3080 static BOOL WINSPOOL_GetPrinter_4(HKEY hkeyPrinter, PRINTER_INFO_4W *pi4,
3081                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3082                                   BOOL unicode)
3083 {
3084     DWORD size, left = cbBuf;
3085     BOOL space = (cbBuf > 0);
3086     LPBYTE ptr = buf;
3087
3088     *pcbNeeded = 0;
3089
3090     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3091                                  unicode)) {
3092         if(space && size <= left) {
3093             pi4->pPrinterName = (LPWSTR)ptr;
3094             ptr += size;
3095             left -= size;
3096         } else
3097             space = FALSE;
3098         *pcbNeeded += size;
3099     }
3100     if(pi4) {
3101         pi4->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3102     }
3103
3104     if(!space && pi4) /* zero out pi4 if we can't completely fill buf */
3105         memset(pi4, 0, sizeof(*pi4));
3106
3107     return space;
3108 }
3109
3110 /*********************************************************************
3111  *    WINSPOOL_GetPrinter_5
3112  *
3113  * Fills out a PRINTER_INFO_5 struct storing the strings in buf.
3114  */
3115 static BOOL WINSPOOL_GetPrinter_5(HKEY hkeyPrinter, PRINTER_INFO_5W *pi5,
3116                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3117                                   BOOL unicode)
3118 {
3119     DWORD size, left = cbBuf;
3120     BOOL space = (cbBuf > 0);
3121     LPBYTE ptr = buf;
3122
3123     *pcbNeeded = 0;
3124
3125     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3126                                  unicode)) {
3127         if(space && size <= left) {
3128             pi5->pPrinterName = (LPWSTR)ptr;
3129             ptr += size;
3130             left -= size;
3131         } else
3132             space = FALSE;
3133         *pcbNeeded += size;
3134     }
3135     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
3136                                  unicode)) {
3137         if(space && size <= left) {
3138             pi5->pPortName = (LPWSTR)ptr;
3139             ptr += size;
3140             left -= size;
3141         } else
3142             space = FALSE;
3143         *pcbNeeded += size;
3144     }
3145     if(pi5) {
3146         pi5->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3147         pi5->DeviceNotSelectedTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3148                                                                 "dnsTimeout");
3149         pi5->TransmissionRetryTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3150                                                                  "txTimeout");
3151     }
3152
3153     if(!space && pi5) /* zero out pi5 if we can't completely fill buf */
3154         memset(pi5, 0, sizeof(*pi5));
3155
3156     return space;
3157 }
3158
3159 /*****************************************************************************
3160  *          WINSPOOL_GetPrinter
3161  *
3162  *    Implementation of GetPrinterA|W.  Relies on PRINTER_INFO_*W being
3163  *    essentially the same as PRINTER_INFO_*A. i.e. the structure itself is
3164  *    just a collection of pointers to strings.
3165  */
3166 static BOOL WINSPOOL_GetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
3167                                 DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
3168 {
3169     LPCWSTR name;
3170     DWORD size, needed = 0;
3171     LPBYTE ptr = NULL;
3172     HKEY hkeyPrinter, hkeyPrinters;
3173     BOOL ret;
3174
3175     TRACE("(%p,%ld,%p,%ld,%p)\n",hPrinter,Level,pPrinter,cbBuf, pcbNeeded);
3176
3177     if (!(name = get_opened_printer_name(hPrinter))) {
3178         SetLastError(ERROR_INVALID_HANDLE);
3179         return FALSE;
3180     }
3181
3182     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
3183        ERROR_SUCCESS) {
3184         ERR("Can't create Printers key\n");
3185         return FALSE;
3186     }
3187     if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter) != ERROR_SUCCESS)
3188     {
3189         ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
3190         RegCloseKey(hkeyPrinters);
3191         SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
3192         return FALSE;
3193     }
3194
3195     switch(Level) {
3196     case 2:
3197       {
3198         PRINTER_INFO_2W *pi2 = (PRINTER_INFO_2W *)pPrinter;
3199
3200         size = sizeof(PRINTER_INFO_2W);
3201         if(size <= cbBuf) {
3202             ptr = pPrinter + size;
3203             cbBuf -= size;
3204             memset(pPrinter, 0, size);
3205         } else {
3206             pi2 = NULL;
3207             cbBuf = 0;
3208         }
3209         ret = WINSPOOL_GetPrinter_2(hkeyPrinter, pi2, ptr, cbBuf, &needed,
3210                                     unicode);
3211         needed += size;
3212         break;
3213       }
3214
3215     case 4:
3216       {
3217         PRINTER_INFO_4W *pi4 = (PRINTER_INFO_4W *)pPrinter;
3218
3219         size = sizeof(PRINTER_INFO_4W);
3220         if(size <= cbBuf) {
3221             ptr = pPrinter + size;
3222             cbBuf -= size;
3223             memset(pPrinter, 0, size);
3224         } else {
3225             pi4 = NULL;
3226             cbBuf = 0;
3227         }
3228         ret = WINSPOOL_GetPrinter_4(hkeyPrinter, pi4, ptr, cbBuf, &needed,
3229                                     unicode);
3230         needed += size;
3231         break;
3232       }
3233
3234
3235     case 5:
3236       {
3237         PRINTER_INFO_5W *pi5 = (PRINTER_INFO_5W *)pPrinter;
3238
3239         size = sizeof(PRINTER_INFO_5W);
3240         if(size <= cbBuf) {
3241             ptr = pPrinter + size;
3242             cbBuf -= size;
3243             memset(pPrinter, 0, size);
3244         } else {
3245             pi5 = NULL;
3246             cbBuf = 0;
3247         }
3248
3249         ret = WINSPOOL_GetPrinter_5(hkeyPrinter, pi5, ptr, cbBuf, &needed,
3250                                     unicode);
3251         needed += size;
3252         break;
3253       }
3254
3255     default:
3256         FIXME("Unimplemented level %ld\n", Level);
3257         SetLastError(ERROR_INVALID_LEVEL);
3258         RegCloseKey(hkeyPrinters);
3259         RegCloseKey(hkeyPrinter);
3260         return FALSE;
3261     }
3262
3263     RegCloseKey(hkeyPrinter);
3264     RegCloseKey(hkeyPrinters);
3265
3266     TRACE("returning %d needed = %ld\n", ret, needed);
3267     if(pcbNeeded) *pcbNeeded = needed;
3268     if(!ret)
3269         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3270     return ret;
3271 }
3272
3273 /*****************************************************************************
3274  *          GetPrinterW  [WINSPOOL.@]
3275  */
3276 BOOL WINAPI GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
3277                         DWORD cbBuf, LPDWORD pcbNeeded)
3278 {
3279     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
3280                                TRUE);
3281 }
3282
3283 /*****************************************************************************
3284  *          GetPrinterA  [WINSPOOL.@]
3285  */
3286 BOOL WINAPI GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
3287                     DWORD cbBuf, LPDWORD pcbNeeded)
3288 {
3289     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
3290                                FALSE);
3291 }
3292
3293 /*****************************************************************************
3294  *          WINSPOOL_EnumPrinters
3295  *
3296  *    Implementation of EnumPrintersA|W
3297  */
3298 static BOOL WINSPOOL_EnumPrinters(DWORD dwType, LPWSTR lpszName,
3299                                   DWORD dwLevel, LPBYTE lpbPrinters,
3300                                   DWORD cbBuf, LPDWORD lpdwNeeded,
3301                                   LPDWORD lpdwReturned, BOOL unicode)
3302
3303 {
3304     HKEY hkeyPrinters, hkeyPrinter;
3305     WCHAR PrinterName[255];
3306     DWORD needed = 0, number = 0;
3307     DWORD used, i, left;
3308     PBYTE pi, buf;
3309
3310     if(lpbPrinters)
3311         memset(lpbPrinters, 0, cbBuf);
3312     if(lpdwReturned)
3313         *lpdwReturned = 0;
3314     if(lpdwNeeded)
3315         *lpdwNeeded = 0;
3316
3317     /* PRINTER_ENUM_DEFAULT is only supported under win9x, we behave like NT */
3318     if(dwType == PRINTER_ENUM_DEFAULT)
3319         return TRUE;
3320
3321     if (dwType & PRINTER_ENUM_CONNECTIONS) {
3322         FIXME("We don't handle PRINTER_ENUM_CONNECTIONS\n");
3323         dwType &= ~PRINTER_ENUM_CONNECTIONS; /* we don't handle that */
3324         if(!dwType) return TRUE;
3325     }
3326
3327     if (!((dwType & PRINTER_ENUM_LOCAL) || (dwType & PRINTER_ENUM_NAME))) {
3328         FIXME("dwType = %08lx\n", dwType);
3329         SetLastError(ERROR_INVALID_FLAGS);
3330         return FALSE;
3331     }
3332
3333     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
3334        ERROR_SUCCESS) {
3335         ERR("Can't create Printers key\n");
3336         return FALSE;
3337     }
3338
3339     if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &number, NULL, NULL,
3340                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
3341         RegCloseKey(hkeyPrinters);
3342         ERR("Can't query Printers key\n");
3343         return FALSE;
3344     }
3345     TRACE("Found %ld printers\n", number);
3346
3347     switch(dwLevel) {
3348     case 1:
3349         RegCloseKey(hkeyPrinters);
3350         if (lpdwReturned)
3351             *lpdwReturned = number;
3352         return TRUE;
3353
3354     case 2:
3355         used = number * sizeof(PRINTER_INFO_2W);
3356         break;
3357     case 4:
3358         used = number * sizeof(PRINTER_INFO_4W);
3359         break;
3360     case 5:
3361         used = number * sizeof(PRINTER_INFO_5W);
3362         break;
3363
3364     default:
3365         SetLastError(ERROR_INVALID_LEVEL);
3366         RegCloseKey(hkeyPrinters);
3367         return FALSE;
3368     }
3369     pi = (used <= cbBuf) ? lpbPrinters : NULL;
3370
3371     for(i = 0; i < number; i++) {
3372         if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)) !=
3373            ERROR_SUCCESS) {
3374             ERR("Can't enum key number %ld\n", i);
3375             RegCloseKey(hkeyPrinters);
3376             return FALSE;
3377         }
3378         TRACE("Printer %ld is %s\n", i, debugstr_w(PrinterName));
3379         if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkeyPrinter) !=
3380            ERROR_SUCCESS) {
3381             ERR("Can't open key %s\n", debugstr_w(PrinterName));
3382             RegCloseKey(hkeyPrinters);
3383             return FALSE;
3384         }
3385
3386         if(cbBuf > used) {
3387             buf = lpbPrinters + used;
3388             left = cbBuf - used;
3389         } else {
3390             buf = NULL;
3391             left = 0;
3392         }
3393
3394         switch(dwLevel) {
3395         case 2:
3396             WINSPOOL_GetPrinter_2(hkeyPrinter, (PRINTER_INFO_2W *)pi, buf,
3397                                   left, &needed, unicode);
3398             used += needed;
3399             if(pi) pi += sizeof(PRINTER_INFO_2W);
3400             break;
3401         case 4:
3402             WINSPOOL_GetPrinter_4(hkeyPrinter, (PRINTER_INFO_4W *)pi, buf,
3403                                   left, &needed, unicode);
3404             used += needed;
3405             if(pi) pi += sizeof(PRINTER_INFO_4W);
3406             break;
3407         case 5:
3408             WINSPOOL_GetPrinter_5(hkeyPrinter, (PRINTER_INFO_5W *)pi, buf,
3409                                   left, &needed, unicode);
3410             used += needed;
3411             if(pi) pi += sizeof(PRINTER_INFO_5W);
3412             break;
3413         default:
3414             ERR("Shouldn't be here!\n");
3415             RegCloseKey(hkeyPrinter);
3416             RegCloseKey(hkeyPrinters);
3417             return FALSE;
3418         }
3419         RegCloseKey(hkeyPrinter);
3420     }
3421     RegCloseKey(hkeyPrinters);
3422
3423     if(lpdwNeeded)
3424         *lpdwNeeded = used;
3425
3426     if(used > cbBuf) {
3427         if(lpbPrinters)
3428             memset(lpbPrinters, 0, cbBuf);
3429         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3430         return FALSE;
3431     }
3432     if(lpdwReturned)
3433         *lpdwReturned = number;
3434     SetLastError(ERROR_SUCCESS);
3435     return TRUE;
3436 }
3437
3438
3439 /******************************************************************
3440  *              EnumPrintersW        [WINSPOOL.@]
3441  *
3442  *    Enumerates the available printers, print servers and print
3443  *    providers, depending on the specified flags, name and level.
3444  *
3445  * RETURNS:
3446  *
3447  *    If level is set to 1:
3448  *      Not implemented yet!
3449  *      Returns TRUE with an empty list.
3450  *
3451  *    If level is set to 2:
3452  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
3453  *      Returns an array of PRINTER_INFO_2 data structures in the
3454  *      lpbPrinters buffer. Note that according to MSDN also an
3455  *      OpenPrinter should be performed on every remote printer.
3456  *
3457  *    If level is set to 4 (officially WinNT only):
3458  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
3459  *      Fast: Only the registry is queried to retrieve printer names,
3460  *      no connection to the driver is made.
3461  *      Returns an array of PRINTER_INFO_4 data structures in the
3462  *      lpbPrinters buffer.
3463  *
3464  *    If level is set to 5 (officially WinNT4/Win9x only):
3465  *      Fast: Only the registry is queried to retrieve printer names,
3466  *      no connection to the driver is made.
3467  *      Returns an array of PRINTER_INFO_5 data structures in the
3468  *      lpbPrinters buffer.
3469  *
3470  *    If level set to 3 or 6+:
3471  *          returns zero (failure!)
3472  *
3473  *    Returns nonzero (TRUE) on success, or zero on failure, use GetLastError
3474  *    for information.
3475  *
3476  * BUGS:
3477  *    - Only PRINTER_ENUM_LOCAL and PRINTER_ENUM_NAME are implemented.
3478  *    - Only levels 2, 4 and 5 are implemented at the moment.
3479  *    - 16-bit printer drivers are not enumerated.
3480  *    - Returned amount of bytes used/needed does not match the real Windoze
3481  *      implementation (as in this implementation, all strings are part
3482  *      of the buffer, whereas Win32 keeps them somewhere else)
3483  *    - At level 2, EnumPrinters should also call OpenPrinter for remote printers.
3484  *
3485  * NOTE:
3486  *    - In a regular Wine installation, no registry settings for printers
3487  *      exist, which makes this function return an empty list.
3488  */
3489 BOOL  WINAPI EnumPrintersW(
3490                 DWORD dwType,        /* [in] Types of print objects to enumerate */
3491                 LPWSTR lpszName,     /* [in] name of objects to enumerate */
3492                 DWORD dwLevel,       /* [in] type of printer info structure */
3493                 LPBYTE lpbPrinters,  /* [out] buffer which receives info */
3494                 DWORD cbBuf,         /* [in] max size of buffer in bytes */
3495                 LPDWORD lpdwNeeded,  /* [out] pointer to var: # bytes used/needed */
3496                 LPDWORD lpdwReturned /* [out] number of entries returned */
3497                 )
3498 {
3499     return WINSPOOL_EnumPrinters(dwType, lpszName, dwLevel, lpbPrinters, cbBuf,
3500                                  lpdwNeeded, lpdwReturned, TRUE);
3501 }
3502
3503 /******************************************************************
3504  *              EnumPrintersA        [WINSPOOL.@]
3505  *
3506  */
3507 BOOL WINAPI EnumPrintersA(DWORD dwType, LPSTR lpszName,
3508                           DWORD dwLevel, LPBYTE lpbPrinters,
3509                           DWORD cbBuf, LPDWORD lpdwNeeded,
3510                           LPDWORD lpdwReturned)
3511 {
3512     BOOL ret;
3513     UNICODE_STRING lpszNameW;
3514     PWSTR pwstrNameW;
3515     
3516     pwstrNameW = asciitounicode(&lpszNameW,lpszName);
3517     ret = WINSPOOL_EnumPrinters(dwType, pwstrNameW, dwLevel, lpbPrinters, cbBuf,
3518                                 lpdwNeeded, lpdwReturned, FALSE);
3519     RtlFreeUnicodeString(&lpszNameW);
3520     return ret;
3521 }
3522
3523 /*****************************************************************************
3524  *          WINSPOOL_GetDriverInfoFromReg [internal]
3525  *
3526  *    Enters the information from the registry into the DRIVER_INFO struct
3527  *
3528  * RETURNS
3529  *    zero if the printer driver does not exist in the registry
3530  *    (only if Level > 1) otherwise nonzero
3531  */
3532 static BOOL WINSPOOL_GetDriverInfoFromReg(
3533                             HKEY    hkeyDrivers,
3534                             LPWSTR  DriverName,
3535                             LPWSTR  pEnvironment,
3536                             DWORD   Level,
3537                             LPBYTE  ptr,            /* DRIVER_INFO */
3538                             LPBYTE  pDriverStrings, /* strings buffer */
3539                             DWORD   cbBuf,          /* size of string buffer */
3540                             LPDWORD pcbNeeded,      /* space needed for str. */
3541                             BOOL    unicode)        /* type of strings */
3542 {
3543     DWORD  size, tmp;
3544     HKEY   hkeyDriver;
3545     LPBYTE strPtr = pDriverStrings;
3546
3547     TRACE("%s,%s,%ld,%p,%p,%ld,%d\n",
3548           debugstr_w(DriverName), debugstr_w(pEnvironment),
3549           Level, ptr, pDriverStrings, cbBuf, unicode);
3550
3551     if(unicode) {
3552         *pcbNeeded = (lstrlenW(DriverName) + 1) * sizeof(WCHAR);
3553             if (*pcbNeeded <= cbBuf)
3554                strcpyW((LPWSTR)strPtr, DriverName);
3555     } else {
3556         *pcbNeeded = WideCharToMultiByte(CP_ACP, 0, DriverName, -1, NULL, 0,
3557                                           NULL, NULL);
3558         if(*pcbNeeded <= cbBuf)
3559             WideCharToMultiByte(CP_ACP, 0, DriverName, -1,
3560                                 (LPSTR)strPtr, *pcbNeeded, NULL, NULL);
3561     }
3562     if(Level == 1) {
3563        if(ptr)
3564           ((PDRIVER_INFO_1W) ptr)->pName = (LPWSTR) strPtr;
3565        return TRUE;
3566     } else {
3567        if(ptr)
3568           ((PDRIVER_INFO_2W) ptr)->pName = (LPWSTR) strPtr;
3569        strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
3570     }
3571
3572     if(!DriverName[0] || RegOpenKeyW(hkeyDrivers, DriverName, &hkeyDriver) != ERROR_SUCCESS) {
3573         ERR("Can't find driver '%s' in registry\n", debugstr_w(DriverName));
3574         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER); /* ? */
3575         return FALSE;
3576     }
3577
3578     if(ptr)
3579         ((PDRIVER_INFO_2A) ptr)->cVersion = (GetVersion() & 0x80000000) ? 0 : 3; /* FIXME: add 1, 2 */
3580
3581     if(!pEnvironment)
3582         pEnvironment = (LPWSTR)DefaultEnvironmentW;
3583     if(unicode)
3584         size = (lstrlenW(pEnvironment) + 1) * sizeof(WCHAR);
3585     else
3586         size = WideCharToMultiByte(CP_ACP, 0, pEnvironment, -1, NULL, 0,
3587                                    NULL, NULL);
3588     *pcbNeeded += size;
3589     if(*pcbNeeded <= cbBuf) {
3590         if(unicode)
3591             strcpyW((LPWSTR)strPtr, pEnvironment);
3592         else
3593             WideCharToMultiByte(CP_ACP, 0, pEnvironment, -1,
3594                                 (LPSTR)strPtr, size, NULL, NULL);
3595         if(ptr)
3596             ((PDRIVER_INFO_2W) ptr)->pEnvironment = (LPWSTR)strPtr;
3597         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
3598     }
3599
3600     if(WINSPOOL_GetStringFromReg(hkeyDriver, DriverW, strPtr, 0, &size,
3601                                  unicode)) {
3602         *pcbNeeded += size;
3603         if(*pcbNeeded <= cbBuf)
3604             WINSPOOL_GetStringFromReg(hkeyDriver, DriverW, strPtr, size, &tmp,
3605                                       unicode);
3606         if(ptr)
3607             ((PDRIVER_INFO_2W) ptr)->pDriverPath = (LPWSTR)strPtr;
3608         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
3609     }
3610
3611     if(WINSPOOL_GetStringFromReg(hkeyDriver, Data_FileW, strPtr, 0, &size,
3612                                  unicode)) {
3613         *pcbNeeded += size;
3614         if(*pcbNeeded <= cbBuf)
3615             WINSPOOL_GetStringFromReg(hkeyDriver, Data_FileW, strPtr, size,
3616                                       &tmp, unicode);
3617         if(ptr)
3618             ((PDRIVER_INFO_2W) ptr)->pDataFile = (LPWSTR)strPtr;
3619         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3620     }
3621
3622     if(WINSPOOL_GetStringFromReg(hkeyDriver, Configuration_FileW, strPtr,
3623                                  0, &size, unicode)) {
3624         *pcbNeeded += size;
3625         if(*pcbNeeded <= cbBuf)
3626             WINSPOOL_GetStringFromReg(hkeyDriver, Configuration_FileW, strPtr,
3627                                       size, &tmp, unicode);
3628         if(ptr)
3629             ((PDRIVER_INFO_2W) ptr)->pConfigFile = (LPWSTR)strPtr;
3630         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3631     }
3632
3633     if(Level == 2 ) {
3634         RegCloseKey(hkeyDriver);
3635         TRACE("buffer space %ld required %ld\n", cbBuf, *pcbNeeded);
3636         return TRUE;
3637     }
3638
3639     if (Level != 5 && WINSPOOL_GetStringFromReg(hkeyDriver, Help_FileW, strPtr, 0, &size,
3640                                  unicode)) {
3641         *pcbNeeded += size;
3642         if(*pcbNeeded <= cbBuf)
3643             WINSPOOL_GetStringFromReg(hkeyDriver, Help_FileW, strPtr,
3644                                       size, &tmp, unicode);
3645         if(ptr)
3646             ((PDRIVER_INFO_3W) ptr)->pHelpFile = (LPWSTR)strPtr;
3647         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3648     }
3649
3650     if (Level != 5 && WINSPOOL_GetStringFromReg(hkeyDriver, Dependent_FilesW, strPtr, 0,
3651                              &size, unicode)) {
3652         *pcbNeeded += size;
3653         if(*pcbNeeded <= cbBuf)
3654             WINSPOOL_GetStringFromReg(hkeyDriver, Dependent_FilesW, strPtr,
3655                                       size, &tmp, unicode);
3656         if(ptr)
3657             ((PDRIVER_INFO_3W) ptr)->pDependentFiles = (LPWSTR)strPtr;
3658         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3659     }
3660
3661     if (Level != 5 && WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, 0, &size,
3662                                  unicode)) {
3663         *pcbNeeded += size;
3664         if(*pcbNeeded <= cbBuf)
3665             WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr,
3666                                       size, &tmp, unicode);
3667         if(ptr)
3668             ((PDRIVER_INFO_3W) ptr)->pMonitorName = (LPWSTR)strPtr;
3669         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3670     }
3671
3672     if (Level != 5 && WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, 0, &size,
3673                                  unicode)) {
3674         *pcbNeeded += size;
3675         if(*pcbNeeded <= cbBuf)
3676             WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr,
3677                                       size, &tmp, unicode);
3678         if(ptr)
3679             ((PDRIVER_INFO_3W) ptr)->pDefaultDataType = (LPWSTR)strPtr;
3680         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3681     }
3682
3683     TRACE("buffer space %ld required %ld\n", cbBuf, *pcbNeeded);
3684     RegCloseKey(hkeyDriver);
3685     return TRUE;
3686 }
3687
3688 /*****************************************************************************
3689  *          WINSPOOL_GetPrinterDriver
3690  */
3691 static BOOL WINSPOOL_GetPrinterDriver(HANDLE hPrinter, LPWSTR pEnvironment,
3692                                       DWORD Level, LPBYTE pDriverInfo,
3693                                       DWORD cbBuf, LPDWORD pcbNeeded,
3694                                       BOOL unicode)
3695 {
3696     LPCWSTR name;
3697     WCHAR DriverName[100];
3698     DWORD ret, type, size, needed = 0;
3699     LPBYTE ptr = NULL;
3700     HKEY hkeyPrinter, hkeyPrinters, hkeyDrivers;
3701
3702     TRACE("(%p,%s,%ld,%p,%ld,%p)\n",hPrinter,debugstr_w(pEnvironment),
3703           Level,pDriverInfo,cbBuf, pcbNeeded);
3704
3705     ZeroMemory(pDriverInfo, cbBuf);
3706
3707     if (!(name = get_opened_printer_name(hPrinter))) {
3708         SetLastError(ERROR_INVALID_HANDLE);
3709         return FALSE;
3710     }
3711     if(Level < 1 || Level > 6) {
3712         SetLastError(ERROR_INVALID_LEVEL);
3713         return FALSE;
3714     }
3715     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
3716        ERROR_SUCCESS) {
3717         ERR("Can't create Printers key\n");
3718         return FALSE;
3719     }
3720     if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter)
3721        != ERROR_SUCCESS) {
3722         ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
3723         RegCloseKey(hkeyPrinters);
3724         SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
3725         return FALSE;
3726     }
3727     size = sizeof(DriverName);
3728     DriverName[0] = 0;
3729     ret = RegQueryValueExW(hkeyPrinter, Printer_DriverW, 0, &type,
3730                            (LPBYTE)DriverName, &size);
3731     RegCloseKey(hkeyPrinter);
3732     RegCloseKey(hkeyPrinters);
3733     if(ret != ERROR_SUCCESS) {
3734         ERR("Can't get DriverName for printer %s\n", debugstr_w(name));
3735         return FALSE;
3736     }
3737
3738     hkeyDrivers = WINSPOOL_OpenDriverReg( pEnvironment, TRUE);
3739     if(!hkeyDrivers) {
3740         ERR("Can't create Drivers key\n");
3741         return FALSE;
3742     }
3743
3744     switch(Level) {
3745     case 1:
3746         size = sizeof(DRIVER_INFO_1W);
3747         break;
3748     case 2:
3749         size = sizeof(DRIVER_INFO_2W);
3750         break;
3751     case 3:
3752         size = sizeof(DRIVER_INFO_3W);
3753         break;
3754     case 4:
3755         size = sizeof(DRIVER_INFO_4W);
3756         break;
3757     case 5:
3758         size = sizeof(DRIVER_INFO_5W);
3759         break;
3760     case 6:
3761         size = sizeof(DRIVER_INFO_6W);
3762         break;
3763     default:
3764         ERR("Invalid level\n");
3765         return FALSE;
3766     }
3767
3768     if(size <= cbBuf)
3769         ptr = pDriverInfo + size;
3770
3771     if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverName,
3772                          pEnvironment, Level, pDriverInfo,
3773                          (cbBuf < size) ? NULL : ptr,
3774                          (cbBuf < size) ? 0 : cbBuf - size,
3775                          &needed, unicode)) {
3776             RegCloseKey(hkeyDrivers);
3777             return FALSE;
3778     }
3779
3780     RegCloseKey(hkeyDrivers);
3781
3782     if(pcbNeeded) *pcbNeeded = size + needed;
3783     TRACE("buffer space %ld required %ld\n", cbBuf, size + needed);
3784     if(cbBuf >= needed) return TRUE;
3785     SetLastError(ERROR_INSUFFICIENT_BUFFER);
3786     return FALSE;
3787 }
3788
3789 /*****************************************************************************
3790  *          GetPrinterDriverA  [WINSPOOL.@]
3791  */
3792 BOOL WINAPI GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment,
3793                               DWORD Level, LPBYTE pDriverInfo,
3794                               DWORD cbBuf, LPDWORD pcbNeeded)
3795 {
3796     BOOL ret;
3797     UNICODE_STRING pEnvW;
3798     PWSTR pwstrEnvW;
3799     
3800     pwstrEnvW = asciitounicode(&pEnvW, pEnvironment);
3801     ret = WINSPOOL_GetPrinterDriver(hPrinter, pwstrEnvW, Level, pDriverInfo,
3802                                     cbBuf, pcbNeeded, FALSE);
3803     RtlFreeUnicodeString(&pEnvW);
3804     return ret;
3805 }
3806 /*****************************************************************************
3807  *          GetPrinterDriverW  [WINSPOOL.@]
3808  */
3809 BOOL WINAPI GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment,
3810                                   DWORD Level, LPBYTE pDriverInfo,
3811                                   DWORD cbBuf, LPDWORD pcbNeeded)
3812 {
3813     return WINSPOOL_GetPrinterDriver(hPrinter, pEnvironment, Level,
3814                                      pDriverInfo, cbBuf, pcbNeeded, TRUE);
3815 }
3816
3817 /*****************************************************************************
3818  *       GetPrinterDriverDirectoryW  [WINSPOOL.@]
3819  *
3820  * Return the PATH for the Printer-Drivers (UNICODE)
3821  *
3822  * PARAMS
3823  *   pName            [I] Servername (NT only) or NULL (local Computer)
3824  *   pEnvironment     [I] Printing-Environment (see below) or NULL (Default)
3825  *   Level            [I] Structure-Level (must be 1)
3826  *   pDriverDirectory [O] PTR to Buffer that receives the Result
3827  *   cbBuf            [I] Size of Buffer at pDriverDirectory
3828  *   pcbNeeded        [O] PTR to DWORD that receives the size in Bytes used / 
3829  *                        required for pDriverDirectory
3830  *
3831  * RETURNS
3832  *   Success: TRUE  and in pcbNeeded the Bytes used in pDriverDirectory
3833  *   Failure: FALSE and in pcbNeeded the Bytes required for pDriverDirectory,
3834  *   if cbBuf is too small
3835  * 
3836  *   Native Values returned in pDriverDirectory on Success:
3837  *|  NT(Windows NT x86):  "%winsysdir%\\spool\\DRIVERS\\w32x86" 
3838  *|  NT(Windows 4.0):     "%winsysdir%\\spool\\DRIVERS\\win40" 
3839  *|  win9x(Windows 4.0):  "%winsysdir%" 
3840  *
3841  *   "%winsysdir%" is the Value from GetSystemDirectoryW()
3842  *
3843  * FIXME
3844  *-  Only NULL or "" is supported for pName
3845  *
3846  */
3847 BOOL WINAPI GetPrinterDriverDirectoryW(LPWSTR pName, LPWSTR pEnvironment,
3848                                        DWORD Level, LPBYTE pDriverDirectory,
3849                                        DWORD cbBuf, LPDWORD pcbNeeded)
3850 {
3851     DWORD needed;
3852     const printenv_t * env;
3853
3854     TRACE("(%s, %s, %ld, %p, %ld, %p)\n", debugstr_w(pName), 
3855           debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
3856     if(pName != NULL && pName[0]) {
3857         FIXME("pName unsupported: %s\n", debugstr_w(pName));
3858         SetLastError(ERROR_INVALID_PARAMETER);
3859         return FALSE;
3860     }
3861
3862     env = validate_envW(pEnvironment);
3863     if(!env) return FALSE;  /* pEnvironment invalid or unsupported */
3864
3865     if(Level != 1) {
3866         WARN("(Level: %ld) is ignored in win9x\n", Level);
3867         SetLastError(ERROR_INVALID_LEVEL);
3868         return FALSE;
3869     }
3870
3871     /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
3872     needed = GetSystemDirectoryW(NULL, 0);
3873     /* add the Size for the Subdirectories */
3874     needed += lstrlenW(spooldriversW);
3875     needed += lstrlenW(env->subdir);
3876     needed *= sizeof(WCHAR);  /* return-value is size in Bytes */
3877
3878     if(pcbNeeded)
3879         *pcbNeeded = needed;
3880     TRACE("required: 0x%lx/%ld\n", needed, needed);
3881     if(needed > cbBuf) {
3882         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3883         return FALSE;
3884     }
3885     if(pcbNeeded == NULL) {
3886         WARN("(pcbNeeded == NULL) is ignored in win9x\n");
3887         SetLastError(RPC_X_NULL_REF_POINTER);
3888         return FALSE;
3889     }
3890     if(pDriverDirectory == NULL) {
3891         /* ERROR_INVALID_USER_BUFFER is NT, ERROR_INVALID_PARAMETER is win9x */
3892         SetLastError(ERROR_INVALID_USER_BUFFER);
3893         return FALSE;
3894     }
3895     
3896     GetSystemDirectoryW((LPWSTR) pDriverDirectory, cbBuf/sizeof(WCHAR));
3897     /* add the Subdirectories */
3898     lstrcatW((LPWSTR) pDriverDirectory, spooldriversW);
3899     lstrcatW((LPWSTR) pDriverDirectory, env->subdir);
3900     TRACE(" => %s\n", debugstr_w((LPWSTR) pDriverDirectory));
3901     return TRUE;
3902 }
3903
3904
3905 /*****************************************************************************
3906  *       GetPrinterDriverDirectoryA  [WINSPOOL.@]
3907  *
3908  * Return the PATH for the Printer-Drivers (ANSI)
3909  *
3910  * See GetPrinterDriverDirectoryW.
3911  *
3912  * NOTES
3913  * On NT, pDriverDirectory need the same Size as the Unicode-Version
3914  *
3915  */
3916 BOOL WINAPI GetPrinterDriverDirectoryA(LPSTR pName, LPSTR pEnvironment,
3917                                        DWORD Level, LPBYTE pDriverDirectory,
3918                                        DWORD cbBuf, LPDWORD pcbNeeded)
3919 {
3920     UNICODE_STRING nameW, environmentW;
3921     BOOL ret;
3922     DWORD pcbNeededW;
3923     INT len = cbBuf * sizeof(WCHAR)/sizeof(CHAR);
3924     WCHAR *driverDirectoryW = NULL;
3925
3926     TRACE("(%s, %s, %ld, %p, %ld, %p)\n", debugstr_a(pName), 
3927           debugstr_a(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
3928  
3929     if (len) driverDirectoryW = HeapAlloc( GetProcessHeap(), 0, len );
3930
3931     if(pName) RtlCreateUnicodeStringFromAsciiz(&nameW, pName);
3932     else nameW.Buffer = NULL;
3933     if(pEnvironment) RtlCreateUnicodeStringFromAsciiz(&environmentW, pEnvironment);
3934     else environmentW.Buffer = NULL;
3935
3936     ret = GetPrinterDriverDirectoryW( nameW.Buffer, environmentW.Buffer, Level,
3937                                       (LPBYTE)driverDirectoryW, len, &pcbNeededW );
3938     if (ret) {
3939         DWORD needed;
3940         needed =  WideCharToMultiByte( CP_ACP, 0, driverDirectoryW, -1, 
3941                                    (LPSTR)pDriverDirectory, cbBuf, NULL, NULL);
3942         if(pcbNeeded)
3943             *pcbNeeded = needed;
3944         ret = (needed <= cbBuf) ? TRUE : FALSE;
3945     } else 
3946         if(pcbNeeded) *pcbNeeded = pcbNeededW * sizeof(CHAR)/sizeof(WCHAR);
3947
3948     TRACE("required: 0x%lx/%ld\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
3949
3950     HeapFree( GetProcessHeap(), 0, driverDirectoryW );
3951     RtlFreeUnicodeString(&environmentW);
3952     RtlFreeUnicodeString(&nameW);
3953
3954     return ret;
3955 }
3956
3957 /*****************************************************************************
3958  *          AddPrinterDriverA  [WINSPOOL.@]
3959  */
3960 BOOL WINAPI AddPrinterDriverA(LPSTR pName, DWORD level, LPBYTE pDriverInfo)
3961 {
3962     DRIVER_INFO_3A di3;
3963     HKEY hkeyDrivers, hkeyName;
3964     static CHAR empty[]    = "",
3965                 nullnull[] = "\0";
3966
3967     TRACE("(%s,%ld,%p)\n",debugstr_a(pName),level,pDriverInfo);
3968
3969     if(level != 2 && level != 3) {
3970         SetLastError(ERROR_INVALID_LEVEL);
3971         return FALSE;
3972     }
3973     if ((pName) && (pName[0])) {
3974         FIXME("pName= %s - unsupported\n", debugstr_a(pName));
3975         SetLastError(ERROR_INVALID_PARAMETER);
3976         return FALSE;
3977     }
3978     if(!pDriverInfo) {
3979         WARN("pDriverInfo == NULL\n");
3980         SetLastError(ERROR_INVALID_PARAMETER);
3981         return FALSE;
3982     }
3983
3984     if(level == 3)
3985         di3 = *(DRIVER_INFO_3A *)pDriverInfo;
3986     else {
3987         memset(&di3, 0, sizeof(di3));
3988         memcpy(&di3, pDriverInfo, sizeof(DRIVER_INFO_2A));
3989     }
3990
3991     if(!di3.pName || !di3.pDriverPath || !di3.pConfigFile ||
3992        !di3.pDataFile) {
3993         SetLastError(ERROR_INVALID_PARAMETER);
3994         return FALSE;
3995     }
3996
3997     if(!di3.pDefaultDataType) di3.pDefaultDataType = empty;
3998     if(!di3.pDependentFiles) di3.pDependentFiles = nullnull;
3999     if(!di3.pHelpFile) di3.pHelpFile = empty;
4000     if(!di3.pMonitorName) di3.pMonitorName = empty;
4001
4002     hkeyDrivers = WINSPOOL_OpenDriverReg(di3.pEnvironment, FALSE);
4003
4004     if(!hkeyDrivers) {
4005         ERR("Can't create Drivers key\n");
4006         return FALSE;
4007     }
4008
4009     if(level == 2) { /* apparently can't overwrite with level2 */
4010         if(RegOpenKeyA(hkeyDrivers, di3.pName, &hkeyName) == ERROR_SUCCESS) {
4011             RegCloseKey(hkeyName);
4012             RegCloseKey(hkeyDrivers);
4013             WARN("Trying to create existing printer driver %s\n", debugstr_a(di3.pName));
4014             SetLastError(ERROR_PRINTER_DRIVER_ALREADY_INSTALLED);
4015             return FALSE;
4016         }
4017     }
4018     if(RegCreateKeyA(hkeyDrivers, di3.pName, &hkeyName) != ERROR_SUCCESS) {
4019         RegCloseKey(hkeyDrivers);
4020         ERR("Can't create Name key\n");
4021         return FALSE;
4022     }
4023     RegSetValueExA(hkeyName, "Configuration File", 0, REG_SZ, (LPBYTE) di3.pConfigFile,
4024                    0);
4025     RegSetValueExA(hkeyName, "Data File", 0, REG_SZ, (LPBYTE) di3.pDataFile, 0);
4026     RegSetValueExA(hkeyName, "Driver", 0, REG_SZ, (LPBYTE) di3.pDriverPath, 0);
4027     RegSetValueExA(hkeyName, "Version", 0, REG_DWORD, (LPBYTE) &di3.cVersion,
4028                    sizeof(DWORD));
4029     RegSetValueExA(hkeyName, "Datatype", 0, REG_SZ, (LPBYTE) di3.pDefaultDataType, 0);
4030     RegSetValueExA(hkeyName, "Dependent Files", 0, REG_MULTI_SZ,
4031                    (LPBYTE) di3.pDependentFiles, 0);
4032     RegSetValueExA(hkeyName, "Help File", 0, REG_SZ, (LPBYTE) di3.pHelpFile, 0);
4033     RegSetValueExA(hkeyName, "Monitor", 0, REG_SZ, (LPBYTE) di3.pMonitorName, 0);
4034     RegCloseKey(hkeyName);
4035     RegCloseKey(hkeyDrivers);
4036
4037     return TRUE;
4038 }
4039
4040 /*****************************************************************************
4041  *          AddPrinterDriverW  [WINSPOOL.@]
4042  */
4043 BOOL WINAPI AddPrinterDriverW(LPWSTR printerName,DWORD level,
4044                                    LPBYTE pDriverInfo)
4045 {
4046     FIXME("(%s,%ld,%p): stub\n",debugstr_w(printerName),
4047           level,pDriverInfo);
4048     return FALSE;
4049 }
4050
4051 /*****************************************************************************
4052  *          AddPrintProcessorA  [WINSPOOL.@]
4053  */
4054 BOOL WINAPI AddPrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPathName,
4055                                LPSTR pPrintProcessorName)
4056 {
4057     FIXME("(%s,%s,%s,%s): stub\n", debugstr_a(pName), debugstr_a(pEnvironment),
4058           debugstr_a(pPathName), debugstr_a(pPrintProcessorName));
4059     return FALSE;
4060 }
4061
4062 /*****************************************************************************
4063  *          AddPrintProcessorW  [WINSPOOL.@]
4064  */
4065 BOOL WINAPI AddPrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPathName,
4066                                LPWSTR pPrintProcessorName)
4067 {
4068     FIXME("(%s,%s,%s,%s): stub\n", debugstr_w(pName), debugstr_w(pEnvironment),
4069           debugstr_w(pPathName), debugstr_w(pPrintProcessorName));
4070     return FALSE;
4071 }
4072
4073 /*****************************************************************************
4074  *          AddPrintProvidorA  [WINSPOOL.@]
4075  */
4076 BOOL WINAPI AddPrintProvidorA(LPSTR pName, DWORD Level, LPBYTE pProviderInfo)
4077 {
4078     FIXME("(%s,0x%08lx,%p): stub\n", debugstr_a(pName), Level, pProviderInfo);
4079     return FALSE;
4080 }
4081
4082 /*****************************************************************************
4083  *          AddPrintProvidorW  [WINSPOOL.@]
4084  */
4085 BOOL WINAPI AddPrintProvidorW(LPWSTR pName, DWORD Level, LPBYTE pProviderInfo)
4086 {
4087     FIXME("(%s,0x%08lx,%p): stub\n", debugstr_w(pName), Level, pProviderInfo);
4088     return FALSE;
4089 }
4090
4091 /*****************************************************************************
4092  *          AdvancedDocumentPropertiesA  [WINSPOOL.@]
4093  */
4094 LONG WINAPI AdvancedDocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName,
4095                                         PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput)
4096 {
4097     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_a(pDeviceName),
4098           pDevModeOutput, pDevModeInput);
4099     return 0;
4100 }
4101
4102 /*****************************************************************************
4103  *          AdvancedDocumentPropertiesW  [WINSPOOL.@]
4104  */
4105 LONG WINAPI AdvancedDocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName,
4106                                         PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput)
4107 {
4108     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_w(pDeviceName),
4109           pDevModeOutput, pDevModeInput);
4110     return 0;
4111 }
4112
4113 /*****************************************************************************
4114  *          PrinterProperties  [WINSPOOL.@]
4115  *
4116  *     Displays a dialog to set the properties of the printer.
4117  *
4118  * RETURNS
4119  *     nonzero on success or zero on failure
4120  *
4121  * BUGS
4122  *         implemented as stub only
4123  */
4124 BOOL WINAPI PrinterProperties(HWND hWnd,      /* [in] handle to parent window */
4125                               HANDLE hPrinter /* [in] handle to printer object */
4126 ){
4127     FIXME("(%p,%p): stub\n", hWnd, hPrinter);
4128     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4129     return FALSE;
4130 }
4131
4132 /*****************************************************************************
4133  *          EnumJobsA [WINSPOOL.@]
4134  *
4135  */
4136 BOOL WINAPI EnumJobsA(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
4137                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
4138                       LPDWORD pcReturned)
4139 {
4140     FIXME("(%p,first=%ld,no=%ld,level=%ld,job=%p,cb=%ld,%p,%p), stub!\n",
4141         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
4142     );
4143     if(pcbNeeded) *pcbNeeded = 0;
4144     if(pcReturned) *pcReturned = 0;
4145     return FALSE;
4146 }
4147
4148
4149 /*****************************************************************************
4150  *          EnumJobsW [WINSPOOL.@]
4151  *
4152  */
4153 BOOL WINAPI EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
4154                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
4155                       LPDWORD pcReturned)
4156 {
4157     FIXME("(%p,first=%ld,no=%ld,level=%ld,job=%p,cb=%ld,%p,%p), stub!\n",
4158         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
4159     );
4160     if(pcbNeeded) *pcbNeeded = 0;
4161     if(pcReturned) *pcReturned = 0;
4162     return FALSE;
4163 }
4164
4165 /*****************************************************************************
4166  *          WINSPOOL_EnumPrinterDrivers [internal]
4167  *
4168  *    Delivers information about all printer drivers installed on the
4169  *    localhost or a given server
4170  *
4171  * RETURNS
4172  *    nonzero on success or zero on failure. If the buffer for the returned
4173  *    information is too small the function will return an error
4174  *
4175  * BUGS
4176  *    - only implemented for localhost, foreign hosts will return an error
4177  */
4178 static BOOL WINSPOOL_EnumPrinterDrivers(LPWSTR pName, LPWSTR pEnvironment,
4179                                         DWORD Level, LPBYTE pDriverInfo,
4180                                         DWORD cbBuf, LPDWORD pcbNeeded,
4181                                         LPDWORD pcReturned, BOOL unicode)
4182
4183 {   HKEY  hkeyDrivers;
4184     DWORD i, needed, number = 0, size = 0;
4185     WCHAR DriverNameW[255];
4186     PBYTE ptr;
4187
4188     TRACE("%s,%s,%ld,%p,%ld,%d\n",
4189           debugstr_w(pName), debugstr_w(pEnvironment),
4190           Level, pDriverInfo, cbBuf, unicode);
4191
4192     /* check for local drivers */
4193     if((pName) && (pName[0])) {
4194         FIXME("remote drivers (%s) not supported!\n", debugstr_w(pName));
4195         SetLastError(ERROR_ACCESS_DENIED);
4196         return FALSE;
4197     }
4198
4199     /* check input parameter */
4200     if((Level < 1) || (Level > 3)) {
4201         ERR("unsupported level %ld\n", Level);
4202         SetLastError(ERROR_INVALID_LEVEL);
4203         return FALSE;
4204     }
4205
4206     /* initialize return values */
4207     if(pDriverInfo)
4208         memset( pDriverInfo, 0, cbBuf);
4209     *pcbNeeded  = 0;
4210     *pcReturned = 0;
4211
4212     hkeyDrivers = WINSPOOL_OpenDriverReg(pEnvironment, TRUE);
4213     if(!hkeyDrivers) {
4214         ERR("Can't open Drivers key\n");
4215         return FALSE;
4216     }
4217
4218     if(RegQueryInfoKeyA(hkeyDrivers, NULL, NULL, NULL, &number, NULL, NULL,
4219                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
4220         RegCloseKey(hkeyDrivers);
4221         ERR("Can't query Drivers key\n");
4222         return FALSE;
4223     }
4224     TRACE("Found %ld Drivers\n", number);
4225
4226     /* get size of single struct
4227      * unicode and ascii structure have the same size
4228      */
4229     switch (Level) {
4230         case 1:
4231             size = sizeof(DRIVER_INFO_1A);
4232             break;
4233         case 2:
4234             size = sizeof(DRIVER_INFO_2A);
4235             break;
4236         case 3:
4237             size = sizeof(DRIVER_INFO_3A);
4238             break;
4239     }
4240
4241     /* calculate required buffer size */
4242     *pcbNeeded = size * number;
4243
4244     for( i = 0,  ptr = (pDriverInfo && (cbBuf >= size)) ? pDriverInfo : NULL ;
4245          i < number;
4246          i++, ptr = (ptr && (cbBuf >= size * i)) ? ptr + size : NULL) {
4247         if(RegEnumKeyW(hkeyDrivers, i, DriverNameW, sizeof(DriverNameW))
4248                        != ERROR_SUCCESS) {
4249             ERR("Can't enum key number %ld\n", i);
4250             RegCloseKey(hkeyDrivers);
4251             return FALSE;
4252         }
4253         if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverNameW,
4254                          pEnvironment, Level, ptr,
4255                          (cbBuf < *pcbNeeded) ? NULL : pDriverInfo + *pcbNeeded,
4256                          (cbBuf < *pcbNeeded) ? 0 : cbBuf - *pcbNeeded,
4257                          &needed, unicode)) {
4258             RegCloseKey(hkeyDrivers);
4259             return FALSE;
4260         }
4261         (*pcbNeeded) += needed;
4262     }
4263
4264     RegCloseKey(hkeyDrivers);
4265
4266     if(cbBuf < *pcbNeeded){
4267         SetLastError(ERROR_INSUFFICIENT_BUFFER);
4268         return FALSE;
4269     }
4270
4271     *pcReturned = number;
4272     return TRUE;
4273 }
4274
4275 /*****************************************************************************
4276  *          EnumPrinterDriversW  [WINSPOOL.@]
4277  *
4278  *    see function EnumPrinterDrivers for RETURNS, BUGS
4279  */
4280 BOOL WINAPI EnumPrinterDriversW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
4281                                 LPBYTE pDriverInfo, DWORD cbBuf,
4282                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
4283 {
4284     return WINSPOOL_EnumPrinterDrivers(pName, pEnvironment, Level, pDriverInfo,
4285                                        cbBuf, pcbNeeded, pcReturned, TRUE);
4286 }
4287
4288 /*****************************************************************************
4289  *          EnumPrinterDriversA  [WINSPOOL.@]
4290  *
4291  *    see function EnumPrinterDrivers for RETURNS, BUGS
4292  */
4293 BOOL WINAPI EnumPrinterDriversA(LPSTR pName, LPSTR pEnvironment, DWORD Level,
4294                                 LPBYTE pDriverInfo, DWORD cbBuf,
4295                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
4296 {   BOOL ret;
4297     UNICODE_STRING pNameW, pEnvironmentW;
4298     PWSTR pwstrNameW, pwstrEnvironmentW;
4299
4300     pwstrNameW = asciitounicode(&pNameW, pName);
4301     pwstrEnvironmentW = asciitounicode(&pEnvironmentW, pEnvironment);
4302
4303     ret = WINSPOOL_EnumPrinterDrivers(pwstrNameW, pwstrEnvironmentW,
4304                                       Level, pDriverInfo, cbBuf, pcbNeeded,
4305                                       pcReturned, FALSE);
4306     RtlFreeUnicodeString(&pNameW);
4307     RtlFreeUnicodeString(&pEnvironmentW);
4308
4309     return ret;
4310 }
4311
4312 static CHAR PortMonitor[] = "Wine Port Monitor";
4313 static CHAR PortDescription[] = "Wine Port";
4314
4315 static BOOL WINSPOOL_ComPortExists( LPCSTR name )
4316 {
4317     HANDLE handle;
4318
4319     handle = CreateFileA( name, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
4320                          NULL, OPEN_EXISTING, 0, NULL );
4321     if (handle == INVALID_HANDLE_VALUE)
4322         return FALSE;
4323     TRACE("Checking %s exists\n", name );
4324     CloseHandle( handle );
4325     return TRUE;
4326 }
4327
4328 static DWORD WINSPOOL_CountSerialPorts(void)
4329 {
4330     CHAR name[6];
4331     DWORD n = 0, i;
4332
4333     for (i=0; i<4; i++)
4334     {
4335         strcpy( name, "COMx:" );
4336         name[3] = '1' + i;
4337         if (WINSPOOL_ComPortExists( name ))
4338             n++;
4339     }
4340
4341     return n;
4342 }
4343
4344 /******************************************************************************
4345  *              EnumPortsA   (WINSPOOL.@)
4346  *
4347  * See EnumPortsW.
4348  *
4349  * BUGS
4350  *  ANSI-Version did not call the UNICODE-Version
4351  *
4352  */
4353 BOOL WINAPI EnumPortsA(LPSTR name,DWORD level,LPBYTE buffer,DWORD bufsize,
4354                        LPDWORD bufneeded,LPDWORD bufreturned)
4355 {
4356     CHAR portname[10];
4357     DWORD info_size, ofs, i, printer_count, serial_count, count, n, r;
4358     const LPCSTR szPrinterPortKey = "Software\\Wine\\Wine\\Config\\spooler";
4359     HKEY hkey_printer;
4360     BOOL retval = TRUE;
4361
4362     TRACE("(%s,%ld,%p,%ld,%p,%p)\n",
4363           debugstr_a(name),level,buffer,bufsize,bufneeded,bufreturned);
4364
4365     switch( level )
4366     {
4367     case 1:
4368         info_size = sizeof (PORT_INFO_1A);
4369         break;
4370     case 2:
4371         info_size = sizeof (PORT_INFO_2A);
4372         break;
4373     default:
4374         SetLastError(ERROR_INVALID_LEVEL);
4375         return FALSE;
4376     }
4377     
4378     /* see how many exist */
4379
4380     hkey_printer = 0;
4381     serial_count = WINSPOOL_CountSerialPorts();
4382     printer_count = 0;
4383
4384     r = RegOpenKeyA( HKEY_LOCAL_MACHINE, szPrinterPortKey, &hkey_printer);
4385     if ( r == ERROR_SUCCESS )
4386     {
4387         RegQueryInfoKeyA( hkey_printer, NULL, NULL, NULL, NULL, NULL, NULL,
4388             &printer_count, NULL, NULL, NULL, NULL);
4389     }
4390     count = serial_count + printer_count;
4391
4392     /* then fill in the structure info structure once
4393        we know the offset to the first string */
4394
4395     memset( buffer, 0, bufsize );
4396     n = 0;
4397     ofs = info_size*count; 
4398     for ( i=0; i<count; i++)
4399     {
4400         DWORD vallen = sizeof(portname) - 1;
4401
4402         /* get the serial port values, then the printer values */
4403         if ( i < serial_count )
4404         {
4405             strcpy( portname, "COMx:" );
4406             portname[3] = '1' + i;
4407             if (!WINSPOOL_ComPortExists( portname ))
4408                 continue;
4409
4410             TRACE("Found %s\n", portname );
4411             vallen = strlen( portname );
4412         }
4413         else
4414         {
4415             r = RegEnumValueA( hkey_printer, i-serial_count, 
4416                      portname, &vallen, NULL, NULL, NULL, 0 );
4417             if ( r )
4418                 continue;
4419         }
4420
4421         /* add a colon if necessary, and make it upper case */
4422         CharUpperBuffA(portname,vallen);
4423         if (strcasecmp(portname,"nul")!=0)
4424             if (vallen && (portname[vallen-1] != ':') )
4425                 lstrcatA(portname,":");
4426
4427         /* add the port info structure if we can fit it */
4428         if ( info_size*(n+1) < bufsize )
4429         {
4430             if ( level == 1)
4431             {
4432                 PORT_INFO_1A *info = (PORT_INFO_1A*) &buffer[info_size*n];
4433                 info->pName = (LPSTR) &buffer[ofs];
4434             }
4435             else if ( level == 2)
4436             {
4437                 PORT_INFO_2A *info = (PORT_INFO_2A*) &buffer[info_size*n];
4438                 info->pPortName = (LPSTR) &buffer[ofs];
4439                 /* FIXME: fill in more stuff here */
4440                 info->pMonitorName = PortMonitor;
4441                 info->pDescription = PortDescription;
4442                 info->fPortType = PORT_TYPE_WRITE|PORT_TYPE_READ;
4443             }
4444
4445             /* add the name of the port if we can fit it */
4446             if ( ofs < bufsize )
4447                 lstrcpynA((LPSTR)&buffer[ofs],portname,bufsize - ofs);
4448
4449             n++;
4450         }
4451         else
4452             retval = FALSE;
4453         ofs += lstrlenA(portname)+1;
4454     }
4455
4456     RegCloseKey(hkey_printer);
4457
4458     if(bufneeded)
4459         *bufneeded = ofs;
4460
4461     if(bufreturned)
4462         *bufreturned = n;
4463
4464     return retval;
4465 }
4466
4467 /******************************************************************************
4468  *      EnumPortsW   (WINSPOOL.@)
4469  *
4470  * Enumerate available Ports
4471  *
4472  * PARAMS
4473  *  name        [I] Servername or NULL (local Computer)
4474  *  level       [I] Structure-Level (1 or 2)
4475  *  buffer      [O] PTR to Buffer that receives the Result
4476  *  bufsize     [I] Size of Buffer at buffer
4477  *  bufneeded   [O] PTR to DWORD that receives the size in Bytes used / required for buffer
4478  *  bufreturned [O] PTR to DWORD that receives the number of Ports in buffer
4479  *
4480  * RETURNS
4481  *  Success: TRUE
4482  *  Failure: FALSE and in bufneeded the Bytes required for buffer, if bufsize is too small
4483  *
4484  * BUGS
4485  *  UNICODE-Version is a stub
4486  *
4487  */
4488 BOOL WINAPI EnumPortsW(LPWSTR name,DWORD level,LPBYTE buffer,DWORD bufsize,
4489                        LPDWORD bufneeded,LPDWORD bufreturned)
4490 {
4491     FIXME("(%s,%ld,%p,%ld,%p,%p) - stub\n",
4492           debugstr_w(name),level,buffer,bufsize,bufneeded,bufreturned);
4493     return FALSE;
4494 }
4495
4496 /******************************************************************************
4497  *              GetDefaultPrinterW   (WINSPOOL.@)
4498  *
4499  * FIXME
4500  *      This function must read the value from data 'device' of key
4501  *      HCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows
4502  */
4503 BOOL WINAPI GetDefaultPrinterW(LPWSTR name, LPDWORD namesize)
4504 {
4505     BOOL  retval = TRUE;
4506     DWORD insize, len;
4507     WCHAR *buffer, *ptr;
4508
4509     if (!namesize)
4510     {
4511         SetLastError(ERROR_INVALID_PARAMETER);
4512         return FALSE;
4513     }
4514
4515     /* make the buffer big enough for the stuff from the profile/registry,
4516      * the content must fit into the local buffer to compute the correct
4517      * size even if the extern buffer is too small or not given.
4518      * (20 for ,driver,port) */
4519     insize = *namesize;
4520     len = max(100, (insize + 20));
4521     buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
4522
4523     if (!GetProfileStringW(windowsW, deviceW, emptyStringW, buffer, len))
4524     {
4525         SetLastError (ERROR_FILE_NOT_FOUND);
4526         retval = FALSE;
4527         goto end;
4528     }
4529     TRACE("%s\n", debugstr_w(buffer));
4530
4531     if ((ptr = strchrW(buffer, ',')) == NULL)
4532     {
4533         SetLastError(ERROR_INVALID_NAME);
4534         retval = FALSE;
4535         goto end;
4536     }
4537
4538     *ptr = 0;
4539     *namesize = strlenW(buffer) + 1;
4540     if(!name || (*namesize > insize))
4541     {
4542         SetLastError(ERROR_INSUFFICIENT_BUFFER);
4543         retval = FALSE;
4544         goto end;
4545     }
4546     strcpyW(name, buffer);
4547
4548 end:
4549     HeapFree( GetProcessHeap(), 0, buffer);
4550     return retval;
4551 }
4552
4553
4554 /******************************************************************************
4555  *              GetDefaultPrinterA   (WINSPOOL.@)
4556  */
4557 BOOL WINAPI GetDefaultPrinterA(LPSTR name, LPDWORD namesize)
4558 {
4559     BOOL  retval = TRUE;
4560     DWORD insize = 0;
4561     WCHAR *bufferW = NULL;
4562
4563     if (!namesize)
4564     {
4565         SetLastError(ERROR_INVALID_PARAMETER);
4566         return FALSE;
4567     }
4568
4569     if(name && *namesize) {
4570         insize = *namesize;
4571         bufferW = HeapAlloc( GetProcessHeap(), 0, insize * sizeof(WCHAR));
4572     }
4573
4574     if(!GetDefaultPrinterW( bufferW, namesize)) {
4575         retval = FALSE;
4576         goto end;
4577     }
4578
4579     *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, name, insize,
4580                                     NULL, NULL);
4581     if (!*namesize)
4582     {
4583         *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
4584         retval = FALSE;
4585     }
4586     TRACE("0x%08lx/0x%08lx:%s\n", *namesize, insize, debugstr_w(bufferW));
4587
4588 end:
4589     HeapFree( GetProcessHeap(), 0, bufferW);
4590     return retval;
4591 }
4592
4593
4594 /******************************************************************************
4595  *              SetDefaultPrinterW   (WINSPOOL.204)
4596  *
4597  * Set the Name of the Default Printer
4598  *
4599  * PARAMS
4600  *  pszPrinter [I] Name of the Printer or NULL
4601  *
4602  * RETURNS
4603  *  Success:    True
4604  *  Failure:    FALSE
4605  *
4606  * NOTES
4607  *  When the Parameter is NULL or points to an Empty String and
4608  *  a Default Printer was already present, then this Function changes nothing.
4609  *  Without a Default Printer and NULL (or an Empty String) as Parameter,
4610  *  the First enumerated local Printer is used.
4611  *
4612  */
4613 BOOL WINAPI SetDefaultPrinterW(LPCWSTR pszPrinter)
4614 {
4615
4616     TRACE("(%s)\n", debugstr_w(pszPrinter));
4617
4618     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4619     return FALSE;
4620 }
4621
4622 /******************************************************************************
4623  *              SetDefaultPrinterA   (WINSPOOL.202)
4624  *
4625  * See SetDefaultPrinterW.
4626  *
4627  */
4628 BOOL WINAPI SetDefaultPrinterA(LPCSTR pszPrinter)
4629 {
4630
4631     TRACE("(%s)\n", debugstr_a(pszPrinter));
4632
4633     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4634     return FALSE;
4635 }
4636
4637
4638 /******************************************************************************
4639  *              SetPrinterDataExA   (WINSPOOL.@)
4640  */
4641 DWORD WINAPI SetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
4642                                LPCSTR pValueName, DWORD Type,
4643                                LPBYTE pData, DWORD cbData)
4644 {
4645     HKEY hkeyPrinter, hkeySubkey;
4646     DWORD ret;
4647
4648     TRACE("(%p, %s, %s %08lx, %p, %08lx)\n", hPrinter, debugstr_a(pKeyName),
4649           debugstr_a(pValueName), Type, pData, cbData);
4650
4651     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
4652        != ERROR_SUCCESS)
4653         return ret;
4654
4655     if((ret = RegCreateKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
4656        != ERROR_SUCCESS) {
4657         ERR("Can't create subkey %s\n", debugstr_a(pKeyName));
4658         RegCloseKey(hkeyPrinter);
4659         return ret;
4660     }
4661     ret = RegSetValueExA(hkeySubkey, pValueName, 0, Type, pData, cbData);
4662     RegCloseKey(hkeySubkey);
4663     RegCloseKey(hkeyPrinter);
4664     return ret;
4665 }
4666
4667 /******************************************************************************
4668  *              SetPrinterDataExW   (WINSPOOL.@)
4669  */
4670 DWORD WINAPI SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
4671                                LPCWSTR pValueName, DWORD Type,
4672                                LPBYTE pData, DWORD cbData)
4673 {
4674     HKEY hkeyPrinter, hkeySubkey;
4675     DWORD ret;
4676
4677     TRACE("(%p, %s, %s %08lx, %p, %08lx)\n", hPrinter, debugstr_w(pKeyName),
4678           debugstr_w(pValueName), Type, pData, cbData);
4679
4680     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
4681        != ERROR_SUCCESS)
4682         return ret;
4683
4684     if((ret = RegCreateKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
4685        != ERROR_SUCCESS) {
4686         ERR("Can't create subkey %s\n", debugstr_w(pKeyName));
4687         RegCloseKey(hkeyPrinter);
4688         return ret;
4689     }
4690     ret = RegSetValueExW(hkeySubkey, pValueName, 0, Type, pData, cbData);
4691     RegCloseKey(hkeySubkey);
4692     RegCloseKey(hkeyPrinter);
4693     return ret;
4694 }
4695
4696 /******************************************************************************
4697  *              SetPrinterDataA   (WINSPOOL.@)
4698  */
4699 DWORD WINAPI SetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, DWORD Type,
4700                                LPBYTE pData, DWORD cbData)
4701 {
4702     return SetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, Type,
4703                              pData, cbData);
4704 }
4705
4706 /******************************************************************************
4707  *              SetPrinterDataW   (WINSPOOL.@)
4708  */
4709 DWORD WINAPI SetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, DWORD Type,
4710                              LPBYTE pData, DWORD cbData)
4711 {
4712     return SetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, Type,
4713                              pData, cbData);
4714 }
4715
4716 /******************************************************************************
4717  *              GetPrinterDataExA   (WINSPOOL.@)
4718  */
4719 DWORD WINAPI GetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
4720                                LPCSTR pValueName, LPDWORD pType,
4721                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
4722 {
4723     HKEY hkeyPrinter, hkeySubkey;
4724     DWORD ret;
4725
4726     TRACE("(%p, %s, %s %p, %p, %08lx, %p)\n", hPrinter,
4727           debugstr_a(pKeyName), debugstr_a(pValueName), pType, pData, nSize,
4728           pcbNeeded);
4729
4730     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
4731        != ERROR_SUCCESS)
4732         return ret;
4733
4734     if((ret = RegOpenKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
4735        != ERROR_SUCCESS) {
4736         WARN("Can't open subkey %s\n", debugstr_a(pKeyName));
4737         RegCloseKey(hkeyPrinter);
4738         return ret;
4739     }
4740     *pcbNeeded = nSize;
4741     ret = RegQueryValueExA(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
4742     RegCloseKey(hkeySubkey);
4743     RegCloseKey(hkeyPrinter);
4744     return ret;
4745 }
4746
4747 /******************************************************************************
4748  *              GetPrinterDataExW   (WINSPOOL.@)
4749  */
4750 DWORD WINAPI GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
4751                                LPCWSTR pValueName, LPDWORD pType,
4752                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
4753 {
4754     HKEY hkeyPrinter, hkeySubkey;
4755     DWORD ret;
4756
4757     TRACE("(%p, %s, %s %p, %p, %08lx, %p)\n", hPrinter,
4758           debugstr_w(pKeyName), debugstr_w(pValueName), pType, pData, nSize,
4759           pcbNeeded);
4760
4761     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
4762        != ERROR_SUCCESS)
4763         return ret;
4764
4765     if((ret = RegOpenKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
4766        != ERROR_SUCCESS) {
4767         WARN("Can't open subkey %s\n", debugstr_w(pKeyName));
4768         RegCloseKey(hkeyPrinter);
4769         return ret;
4770     }
4771     *pcbNeeded = nSize;
4772     ret = RegQueryValueExW(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
4773     RegCloseKey(hkeySubkey);
4774     RegCloseKey(hkeyPrinter);
4775     return ret;
4776 }
4777
4778 /******************************************************************************
4779  *              GetPrinterDataA   (WINSPOOL.@)
4780  */
4781 DWORD WINAPI GetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, LPDWORD pType,
4782                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
4783 {
4784     return GetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, pType,
4785                              pData, nSize, pcbNeeded);
4786 }
4787
4788 /******************************************************************************
4789  *              GetPrinterDataW   (WINSPOOL.@)
4790  */
4791 DWORD WINAPI GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType,
4792                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
4793 {
4794     return GetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, pType,
4795                              pData, nSize, pcbNeeded);
4796 }
4797
4798 /*******************************************************************************
4799  *              EnumPrinterDataExW      [WINSPOOL.@]
4800  */
4801 DWORD WINAPI EnumPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
4802                                 LPBYTE pEnumValues, DWORD cbEnumValues,
4803                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
4804 {
4805     HKEY                    hkPrinter, hkSubKey;
4806     DWORD                   r, ret, dwIndex, cValues, cbMaxValueNameLen,
4807                             cbValueNameLen, cbMaxValueLen, cbValueLen,
4808                             cbBufSize, dwType;
4809     LPWSTR                  lpValueName;
4810     HANDLE                  hHeap;
4811     PBYTE                   lpValue;
4812     PPRINTER_ENUM_VALUESW   ppev;
4813
4814     TRACE ("%p %s\n", hPrinter, debugstr_w (pKeyName));
4815
4816     if (pKeyName == NULL || *pKeyName == 0)
4817         return ERROR_INVALID_PARAMETER;
4818
4819     ret = WINSPOOL_GetOpenedPrinterRegKey (hPrinter, &hkPrinter);
4820     if (ret != ERROR_SUCCESS)
4821     {
4822         TRACE ("WINSPOOL_GetOpenedPrinterRegKey (%p) returned %li\n",
4823                 hPrinter, ret);
4824         return ret;
4825     }
4826
4827     ret = RegOpenKeyExW (hkPrinter, pKeyName, 0, KEY_READ, &hkSubKey);
4828     if (ret != ERROR_SUCCESS)
4829     {
4830         r = RegCloseKey (hkPrinter);
4831         if (r != ERROR_SUCCESS)
4832             WARN ("RegCloseKey returned %li\n", r);
4833         TRACE ("RegOpenKeyExW (%p, %s) returned %li\n", hPrinter,
4834                 debugstr_w (pKeyName), ret);
4835         return ret;
4836     }
4837
4838     ret = RegCloseKey (hkPrinter);
4839     if (ret != ERROR_SUCCESS)
4840     {
4841         ERR ("RegCloseKey returned %li\n", ret);
4842         r = RegCloseKey (hkSubKey);
4843         if (r != ERROR_SUCCESS)
4844             WARN ("RegCloseKey returned %li\n", r);
4845         return ret;
4846     }
4847
4848     ret = RegQueryInfoKeyW (hkSubKey, NULL, NULL, NULL, NULL, NULL, NULL,
4849             &cValues, &cbMaxValueNameLen, &cbMaxValueLen, NULL, NULL);
4850     if (ret != ERROR_SUCCESS)
4851     {
4852         r = RegCloseKey (hkSubKey);
4853         if (r != ERROR_SUCCESS)
4854             WARN ("RegCloseKey returned %li\n", r);
4855         TRACE ("RegQueryInfoKeyW (%p) returned %li\n", hkSubKey, ret);
4856         return ret;
4857     }
4858
4859     TRACE ("RegQueryInfoKeyW returned cValues = %li, cbMaxValueNameLen = %li, "
4860             "cbMaxValueLen = %li\n", cValues, cbMaxValueNameLen, cbMaxValueLen);
4861
4862     if (cValues == 0)                   /* empty key */
4863     {
4864         r = RegCloseKey (hkSubKey);
4865         if (r != ERROR_SUCCESS)
4866             WARN ("RegCloseKey returned %li\n", r);
4867         *pcbEnumValues = *pnEnumValues = 0;
4868         return ERROR_SUCCESS;
4869     }
4870
4871     ++cbMaxValueNameLen;                        /* allow for trailing '\0' */
4872
4873     hHeap = GetProcessHeap ();
4874     if (hHeap == NULL)
4875     {
4876         ERR ("GetProcessHeap failed\n");
4877         r = RegCloseKey (hkSubKey);
4878         if (r != ERROR_SUCCESS)
4879             WARN ("RegCloseKey returned %li\n", r);
4880         return ERROR_OUTOFMEMORY;
4881     }
4882
4883     lpValueName = HeapAlloc (hHeap, 0, cbMaxValueNameLen * sizeof (WCHAR));
4884     if (lpValueName == NULL)
4885     {
4886         ERR ("Failed to allocate %li bytes from process heap\n",
4887                 cbMaxValueNameLen * sizeof (WCHAR));
4888         r = RegCloseKey (hkSubKey);
4889         if (r != ERROR_SUCCESS)
4890             WARN ("RegCloseKey returned %li\n", r);
4891         return ERROR_OUTOFMEMORY;
4892     }
4893
4894     lpValue = HeapAlloc (hHeap, 0, cbMaxValueLen);
4895     if (lpValue == NULL)
4896     {
4897         ERR ("Failed to allocate %li bytes from process heap\n", cbMaxValueLen);
4898         if (HeapFree (hHeap, 0, lpValueName) == 0)
4899             WARN ("HeapFree failed with code %li\n", GetLastError ());
4900         r = RegCloseKey (hkSubKey);
4901         if (r != ERROR_SUCCESS)
4902             WARN ("RegCloseKey returned %li\n", r);
4903         return ERROR_OUTOFMEMORY;
4904     }
4905
4906     TRACE ("pass 1: calculating buffer required for all names and values\n");
4907
4908     cbBufSize = cValues * sizeof (PRINTER_ENUM_VALUESW);
4909
4910     TRACE ("%li bytes required for %li headers\n", cbBufSize, cValues);
4911
4912     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
4913     {
4914         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
4915         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
4916                 NULL, NULL, lpValue, &cbValueLen);
4917         if (ret != ERROR_SUCCESS)
4918         {
4919             if (HeapFree (hHeap, 0, lpValue) == 0)
4920                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4921             if (HeapFree (hHeap, 0, lpValueName) == 0)
4922                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4923             r = RegCloseKey (hkSubKey);
4924             if (r != ERROR_SUCCESS)
4925                 WARN ("RegCloseKey returned %li\n", r);
4926             TRACE ("RegEnumValueW (%li) returned %li\n", dwIndex, ret);
4927             return ret;
4928         }
4929
4930         TRACE ("%s [%li]: name needs %li bytes, data needs %li bytes\n",
4931                 debugstr_w (lpValueName), dwIndex,
4932                 (cbValueNameLen + 1) * sizeof (WCHAR), cbValueLen);
4933
4934         cbBufSize += (cbValueNameLen + 1) * sizeof (WCHAR);
4935         cbBufSize += cbValueLen;
4936     }
4937
4938     TRACE ("%li bytes required for all %li values\n", cbBufSize, cValues);
4939
4940     *pcbEnumValues = cbBufSize;
4941     *pnEnumValues = cValues;
4942
4943     if (cbEnumValues < cbBufSize)       /* buffer too small */
4944     {
4945         if (HeapFree (hHeap, 0, lpValue) == 0)
4946             WARN ("HeapFree failed with code %li\n", GetLastError ());
4947         if (HeapFree (hHeap, 0, lpValueName) == 0)
4948             WARN ("HeapFree failed with code %li\n", GetLastError ());
4949         r = RegCloseKey (hkSubKey);
4950         if (r != ERROR_SUCCESS)
4951             WARN ("RegCloseKey returned %li\n", r);
4952         TRACE ("%li byte buffer is not large enough\n", cbEnumValues);
4953         return ERROR_MORE_DATA;
4954     }
4955
4956     TRACE ("pass 2: copying all names and values to buffer\n");
4957
4958     ppev = (PPRINTER_ENUM_VALUESW) pEnumValues;         /* array of structs */
4959     pEnumValues += cValues * sizeof (PRINTER_ENUM_VALUESW);
4960
4961     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
4962     {
4963         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
4964         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
4965                 NULL, &dwType, lpValue, &cbValueLen);
4966         if (ret != ERROR_SUCCESS)
4967         {
4968             if (HeapFree (hHeap, 0, lpValue) == 0)
4969                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4970             if (HeapFree (hHeap, 0, lpValueName) == 0)
4971                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4972             r = RegCloseKey (hkSubKey);
4973             if (r != ERROR_SUCCESS)
4974                 WARN ("RegCloseKey returned %li\n", r);
4975             TRACE ("RegEnumValueW (%li) returned %li\n", dwIndex, ret);
4976             return ret;
4977         }
4978
4979         cbValueNameLen = (cbValueNameLen + 1) * sizeof (WCHAR);
4980         memcpy (pEnumValues, lpValueName, cbValueNameLen);
4981         ppev[dwIndex].pValueName = (LPWSTR) pEnumValues;
4982         pEnumValues += cbValueNameLen;
4983
4984         /* return # of *bytes* (including trailing \0), not # of chars */
4985         ppev[dwIndex].cbValueName = cbValueNameLen;
4986
4987         ppev[dwIndex].dwType = dwType;
4988
4989         memcpy (pEnumValues, lpValue, cbValueLen);
4990         ppev[dwIndex].pData = pEnumValues;
4991         pEnumValues += cbValueLen;
4992
4993         ppev[dwIndex].cbData = cbValueLen;
4994
4995         TRACE ("%s [%li]: copied name (%li bytes) and data (%li bytes)\n",
4996                 debugstr_w (lpValueName), dwIndex, cbValueNameLen, cbValueLen);
4997     }
4998
4999     if (HeapFree (hHeap, 0, lpValue) == 0)
5000     {
5001         ret = GetLastError ();
5002         ERR ("HeapFree failed with code %li\n", ret);
5003         if (HeapFree (hHeap, 0, lpValueName) == 0)
5004             WARN ("HeapFree failed with code %li\n", GetLastError ());
5005         r = RegCloseKey (hkSubKey);
5006         if (r != ERROR_SUCCESS)
5007             WARN ("RegCloseKey returned %li\n", r);
5008         return ret;
5009     }
5010
5011     if (HeapFree (hHeap, 0, lpValueName) == 0)
5012     {
5013         ret = GetLastError ();
5014         ERR ("HeapFree failed with code %li\n", ret);
5015         r = RegCloseKey (hkSubKey);
5016         if (r != ERROR_SUCCESS)
5017             WARN ("RegCloseKey returned %li\n", r);
5018         return ret;
5019     }
5020
5021     ret = RegCloseKey (hkSubKey);
5022     if (ret != ERROR_SUCCESS)
5023     {
5024         ERR ("RegCloseKey returned %li\n", ret);
5025         return ret;
5026     }
5027
5028     return ERROR_SUCCESS;
5029 }
5030
5031 /*******************************************************************************
5032  *              EnumPrinterDataExA      [WINSPOOL.@]
5033  *
5034  * This functions returns value names and REG_SZ, REG_EXPAND_SZ, and
5035  * REG_MULTI_SZ values as ASCII strings in Unicode-sized buffers.  This is
5036  * what Windows 2000 SP1 does.
5037  *
5038  */
5039 DWORD WINAPI EnumPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5040                                 LPBYTE pEnumValues, DWORD cbEnumValues,
5041                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
5042 {
5043     INT     len;
5044     LPWSTR  pKeyNameW;
5045     DWORD   ret, dwIndex, dwBufSize;
5046     HANDLE  hHeap;
5047     LPSTR   pBuffer;
5048
5049     TRACE ("%p %s\n", hPrinter, pKeyName);
5050
5051     if (pKeyName == NULL || *pKeyName == 0)
5052         return ERROR_INVALID_PARAMETER;
5053
5054     len = MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, NULL, 0);
5055     if (len == 0)
5056     {
5057         ret = GetLastError ();
5058         ERR ("MultiByteToWideChar failed with code %li\n", ret);
5059         return ret;
5060     }
5061
5062     hHeap = GetProcessHeap ();
5063     if (hHeap == NULL)
5064     {
5065         ERR ("GetProcessHeap failed\n");
5066         return ERROR_OUTOFMEMORY;
5067     }
5068
5069     pKeyNameW = HeapAlloc (hHeap, 0, len * sizeof (WCHAR));
5070     if (pKeyNameW == NULL)
5071     {
5072         ERR ("Failed to allocate %li bytes from process heap\n",
5073                 (LONG) len * sizeof (WCHAR));
5074         return ERROR_OUTOFMEMORY;
5075     }
5076
5077     if (MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, pKeyNameW, len) == 0)
5078     {
5079         ret = GetLastError ();
5080         ERR ("MultiByteToWideChar failed with code %li\n", ret);
5081         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
5082             WARN ("HeapFree failed with code %li\n", GetLastError ());
5083         return ret;
5084     }
5085
5086     ret = EnumPrinterDataExW (hPrinter, pKeyNameW, pEnumValues, cbEnumValues,
5087             pcbEnumValues, pnEnumValues);
5088     if (ret != ERROR_SUCCESS)
5089     {
5090         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
5091             WARN ("HeapFree failed with code %li\n", GetLastError ());
5092         TRACE ("EnumPrinterDataExW returned %li\n", ret);
5093         return ret;
5094     }
5095
5096     if (HeapFree (hHeap, 0, pKeyNameW) == 0)
5097     {
5098         ret = GetLastError ();
5099         ERR ("HeapFree failed with code %li\n", ret);
5100         return ret;
5101     }
5102
5103     if (*pnEnumValues == 0)     /* empty key */
5104         return ERROR_SUCCESS;
5105
5106     dwBufSize = 0;
5107     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
5108     {
5109         PPRINTER_ENUM_VALUESW ppev =
5110                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
5111
5112         if (dwBufSize < ppev->cbValueName)
5113             dwBufSize = ppev->cbValueName;
5114
5115         if (dwBufSize < ppev->cbData && (ppev->dwType == REG_SZ ||
5116                 ppev->dwType == REG_EXPAND_SZ || ppev->dwType == REG_MULTI_SZ))
5117             dwBufSize = ppev->cbData;
5118     }
5119
5120     TRACE ("Largest Unicode name or value is %li bytes\n", dwBufSize);
5121
5122     pBuffer = HeapAlloc (hHeap, 0, dwBufSize);
5123     if (pBuffer == NULL)
5124     {
5125         ERR ("Failed to allocate %li bytes from process heap\n", dwBufSize);
5126         return ERROR_OUTOFMEMORY;
5127     }
5128
5129     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
5130     {
5131         PPRINTER_ENUM_VALUESW ppev =
5132                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
5133
5134         len = WideCharToMultiByte (CP_ACP, 0, ppev->pValueName,
5135                 ppev->cbValueName / sizeof (WCHAR), pBuffer, dwBufSize, NULL,
5136                 NULL);
5137         if (len == 0)
5138         {
5139             ret = GetLastError ();
5140             ERR ("WideCharToMultiByte failed with code %li\n", ret);
5141             if (HeapFree (hHeap, 0, pBuffer) == 0)
5142                 WARN ("HeapFree failed with code %li\n", GetLastError ());
5143             return ret;
5144         }
5145
5146         memcpy (ppev->pValueName, pBuffer, len);
5147
5148         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
5149
5150         if (ppev->dwType != REG_SZ && ppev->dwType != REG_EXPAND_SZ &&
5151                 ppev->dwType != REG_MULTI_SZ)
5152             continue;
5153
5154         len = WideCharToMultiByte (CP_ACP, 0, (LPWSTR) ppev->pData,
5155                 ppev->cbData / sizeof (WCHAR), pBuffer, dwBufSize, NULL, NULL);
5156         if (len == 0)
5157         {
5158             ret = GetLastError ();
5159             ERR ("WideCharToMultiByte failed with code %li\n", ret);
5160             if (HeapFree (hHeap, 0, pBuffer) == 0)
5161                 WARN ("HeapFree failed with code %li\n", GetLastError ());
5162             return ret;
5163         }
5164
5165         memcpy (ppev->pData, pBuffer, len);
5166
5167         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
5168         TRACE ("  (only first string of REG_MULTI_SZ printed)\n");
5169     }
5170
5171     if (HeapFree (hHeap, 0, pBuffer) == 0)
5172     {
5173         ret = GetLastError ();
5174         ERR ("HeapFree failed with code %li\n", ret);
5175         return ret;
5176     }
5177
5178     return ERROR_SUCCESS;
5179 }
5180
5181 /******************************************************************************
5182  *      AbortPrinter (WINSPOOL.@)
5183  */
5184 BOOL WINAPI AbortPrinter( HANDLE hPrinter )
5185 {
5186     FIXME("(%p), stub!\n", hPrinter);
5187     return TRUE;
5188 }
5189
5190 /******************************************************************************
5191  *              AddPortA (WINSPOOL.@)
5192  *
5193  * See AddPortW.
5194  *
5195  */
5196 BOOL WINAPI AddPortA(LPSTR pName, HWND hWnd, LPSTR pMonitorName)
5197 {
5198     FIXME("(%s, %p, %s), stub!\n",debugstr_a(pName),hWnd,debugstr_a(pMonitorName));
5199     return FALSE;
5200 }
5201
5202 /******************************************************************************
5203  *      AddPortW (WINSPOOL.@)
5204  *
5205  * Add a Port for a specific Monitor
5206  *
5207  * PARAMS
5208  *  pName        [I] Servername or NULL (local Computer)
5209  *  hWnd         [I] Handle to parent Window for the Dialog-Box
5210  *  pMonitorName [I] Name of the Monitor that manage the Port
5211  *
5212  * RETURNS
5213  *  Success: TRUE
5214  *  Failure: FALSE
5215  *
5216  * BUGS
5217  *  only a Stub
5218  *
5219  */
5220 BOOL WINAPI AddPortW(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
5221 {
5222     FIXME("(%s, %p, %s), stub!\n",debugstr_w(pName),hWnd,debugstr_w(pMonitorName));
5223     return FALSE;
5224 }
5225
5226 /******************************************************************************
5227  *             AddPortExA (WINSPOOL.@)
5228  *
5229  * See AddPortExW.
5230  *
5231  */
5232 BOOL WINAPI AddPortExA(HANDLE hMonitor, LPSTR pName, DWORD Level, LPBYTE lpBuffer, LPSTR lpMonitorName)
5233 {
5234     FIXME("(%p, %s, %ld, %p, %s), stub!\n",hMonitor, debugstr_a(pName), Level,
5235           lpBuffer, debugstr_a(lpMonitorName));
5236     return FALSE;
5237 }
5238
5239 /******************************************************************************
5240  *             AddPortExW (WINSPOOL.@)
5241  *
5242  * Add a Port for a specific Monitor, without presenting a user interface
5243  *
5244  * PARAMS
5245  *  hMonitor      [I] Handle from InitializePrintMonitor2()
5246  *  pName         [I] Servername or NULL (local Computer)
5247  *  Level         [I] Structure-Level (1 or 2) for lpBuffer
5248  *  lpBuffer      [I] PTR to: PORT_INFO_1 or PORT_INFO_2
5249  *  lpMonitorName [I] Name of the Monitor that manage the Port or NULL
5250  *
5251  * RETURNS
5252  *  Success: TRUE
5253  *  Failure: FALSE
5254  *
5255  * BUGS
5256  *  only a Stub
5257  *
5258  */
5259 BOOL WINAPI AddPortExW(HANDLE hMonitor, LPWSTR pName, DWORD Level, LPBYTE lpBuffer, LPWSTR lpMonitorName)
5260 {
5261     FIXME("(%p, %s, %ld, %p, %s), stub!\n", hMonitor, debugstr_w(pName), Level,
5262           lpBuffer, debugstr_w(lpMonitorName));
5263     return FALSE;
5264 }
5265
5266 /******************************************************************************
5267  *      AddPrinterConnectionA (WINSPOOL.@)
5268  */
5269 BOOL WINAPI AddPrinterConnectionA( LPSTR pName )
5270 {
5271     FIXME("%s\n", debugstr_a(pName));
5272     return FALSE;
5273 }
5274
5275 /******************************************************************************
5276  *      AddPrinterConnectionW (WINSPOOL.@)
5277  */
5278 BOOL WINAPI AddPrinterConnectionW( LPWSTR pName )
5279 {
5280     FIXME("%s\n", debugstr_w(pName));
5281     return FALSE;
5282 }
5283
5284 /******************************************************************************
5285  *              AddPrinterDriverExW (WINSPOOL.@)
5286  */
5287 BOOL WINAPI AddPrinterDriverExW( LPWSTR pName, DWORD Level,
5288     LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
5289 {
5290     FIXME("%s %ld %p %ld\n", debugstr_w(pName),
5291            Level, pDriverInfo, dwFileCopyFlags);
5292     SetLastError(ERROR_PRINTER_DRIVER_BLOCKED);
5293     return FALSE;
5294 }
5295
5296 /******************************************************************************
5297  *              AddPrinterDriverExA (WINSPOOL.@)
5298  */
5299 BOOL WINAPI AddPrinterDriverExA( LPSTR pName, DWORD Level,
5300     LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
5301 {
5302     FIXME("%s %ld %p %ld\n", debugstr_a(pName),
5303            Level, pDriverInfo, dwFileCopyFlags);
5304     SetLastError(ERROR_PRINTER_DRIVER_BLOCKED);
5305     return FALSE;
5306 }
5307
5308 /******************************************************************************
5309  *      ConfigurePortA (WINSPOOL.@)
5310  *
5311  * See ConfigurePortW.
5312  *
5313  */
5314 BOOL WINAPI ConfigurePortA(LPSTR pName, HWND hWnd, LPSTR pPortName)
5315 {
5316     FIXME("%s %p %s\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
5317     return FALSE;
5318 }
5319
5320 /******************************************************************************
5321  *      ConfigurePortW (WINSPOOL.@)
5322  *
5323  * Display the Configuration-Dialog for a specific Port
5324  *
5325  * PARAMS
5326  *  pName     [I] Servername or NULL (local Computer)
5327  *  hWnd      [I] Handle to parent Window for the Dialog-Box
5328  *  pPortName [I] Name of the Port, that should be configured
5329  *
5330  * RETURNS
5331  *  Success: TRUE
5332  *  Failure: FALSE
5333  *
5334  * BUGS
5335  *  only a Stub
5336  *
5337  */
5338 BOOL WINAPI ConfigurePortW(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
5339 {
5340     FIXME("%s %p %s\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
5341     return FALSE;
5342 }
5343
5344 /******************************************************************************
5345  *      ConnectToPrinterDlg (WINSPOOL.@)
5346  */
5347 HANDLE WINAPI ConnectToPrinterDlg( HWND hWnd, DWORD Flags )
5348 {
5349     FIXME("%p %lx\n", hWnd, Flags);
5350     return NULL;
5351 }
5352
5353 /******************************************************************************
5354  *      DeletePrinterConnectionA (WINSPOOL.@)
5355  */
5356 BOOL WINAPI DeletePrinterConnectionA( LPSTR pName )
5357 {
5358     FIXME("%s\n", debugstr_a(pName));
5359     return TRUE;
5360 }
5361
5362 /******************************************************************************
5363  *      DeletePrinterConnectionW (WINSPOOL.@)
5364  */
5365 BOOL WINAPI DeletePrinterConnectionW( LPWSTR pName )
5366 {
5367     FIXME("%s\n", debugstr_w(pName));
5368     return TRUE;
5369 }
5370
5371 /******************************************************************************
5372  *              DeletePrinterDriverExW (WINSPOOL.@)
5373  */
5374 BOOL WINAPI DeletePrinterDriverExW( LPWSTR pName, LPWSTR pEnvironment,
5375     LPWSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
5376 {
5377     FIXME("%s %s %s %lx %lx\n", debugstr_w(pName), debugstr_w(pEnvironment),
5378           debugstr_w(pDriverName), dwDeleteFlag, dwVersionFlag);
5379     return TRUE;
5380 }
5381
5382 /******************************************************************************
5383  *              DeletePrinterDriverExA (WINSPOOL.@)
5384  */
5385 BOOL WINAPI DeletePrinterDriverExA( LPSTR pName, LPSTR pEnvironment,
5386     LPSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
5387 {
5388     FIXME("%s %s %s %lx %lx\n", debugstr_a(pName), debugstr_a(pEnvironment),
5389           debugstr_a(pDriverName), dwDeleteFlag, dwVersionFlag);
5390     return TRUE;
5391 }
5392
5393 /******************************************************************************
5394  *              DeletePrinterDataExW (WINSPOOL.@)
5395  */
5396 DWORD WINAPI DeletePrinterDataExW( HANDLE hPrinter, LPCWSTR pKeyName,
5397                                   LPCWSTR pValueName)
5398 {
5399     FIXME("%p %s %s\n", hPrinter, 
5400           debugstr_w(pKeyName), debugstr_w(pValueName));
5401     return ERROR_INVALID_PARAMETER;
5402 }
5403
5404 /******************************************************************************
5405  *              DeletePrinterDataExA (WINSPOOL.@)
5406  */
5407 DWORD WINAPI DeletePrinterDataExA( HANDLE hPrinter, LPCSTR pKeyName,
5408                                   LPCSTR pValueName)
5409 {
5410     FIXME("%p %s %s\n", hPrinter, 
5411           debugstr_a(pKeyName), debugstr_a(pValueName));
5412     return ERROR_INVALID_PARAMETER;
5413 }
5414
5415 /******************************************************************************
5416  *      DeletePrintProcessorA (WINSPOOL.@)
5417  */
5418 BOOL WINAPI DeletePrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProcessorName)
5419 {
5420     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
5421           debugstr_a(pPrintProcessorName));
5422     return TRUE;
5423 }
5424
5425 /******************************************************************************
5426  *      DeletePrintProcessorW (WINSPOOL.@)
5427  */
5428 BOOL WINAPI DeletePrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProcessorName)
5429 {
5430     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
5431           debugstr_w(pPrintProcessorName));
5432     return TRUE;
5433 }
5434
5435 /******************************************************************************
5436  *      DeletePrintProvidorA (WINSPOOL.@)
5437  */
5438 BOOL WINAPI DeletePrintProvidorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProviderName)
5439 {
5440     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
5441           debugstr_a(pPrintProviderName));
5442     return TRUE;
5443 }
5444
5445 /******************************************************************************
5446  *      DeletePrintProvidorW (WINSPOOL.@)
5447  */
5448 BOOL WINAPI DeletePrintProvidorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProviderName)
5449 {
5450     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
5451           debugstr_w(pPrintProviderName));
5452     return TRUE;
5453 }
5454
5455 /******************************************************************************
5456  *      EnumFormsA (WINSPOOL.@)
5457  */
5458 BOOL WINAPI EnumFormsA( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
5459     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
5460 {
5461     FIXME("%p %lx %p %lx %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
5462     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5463     return FALSE;
5464 }
5465
5466 /******************************************************************************
5467  *      EnumFormsW (WINSPOOL.@)
5468  */
5469 BOOL WINAPI EnumFormsW( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
5470     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
5471 {
5472     FIXME("%p %lx %p %lx %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
5473     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5474     return FALSE;
5475 }
5476
5477 /*****************************************************************************
5478  *          EnumMonitorsA [WINSPOOL.@]
5479  *
5480  * See EnumMonitorsW.
5481  *
5482  */
5483 BOOL WINAPI EnumMonitorsA(LPSTR pName, DWORD Level, LPBYTE pMonitors,
5484                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
5485 {
5486     BOOL    res;
5487     LPBYTE  bufferW = NULL;
5488     LPWSTR  nameW = NULL;
5489     DWORD   needed = 0;
5490     DWORD   numentries = 0;
5491     INT     len;
5492
5493     TRACE("(%s, %ld, %p, %ld, %p, %p)\n", debugstr_a(pName), Level, pMonitors,
5494           cbBuf, pcbNeeded, pcReturned);
5495
5496     /* convert servername to unicode */
5497     if (pName) {
5498         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
5499         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5500         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
5501     }
5502     /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the monitors */
5503     needed = cbBuf * sizeof(WCHAR);    
5504     if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
5505     res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5506
5507     if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
5508         if (pcbNeeded) needed = *pcbNeeded;
5509         /* HeapReAlloc return NULL, when bufferW was NULL */
5510         bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
5511                               HeapAlloc(GetProcessHeap(), 0, needed);
5512
5513         /* Try again with the large Buffer */
5514         res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5515     }
5516     numentries = pcReturned ? *pcReturned : 0;
5517     needed = 0;
5518     /*
5519        W2k require the buffersize from EnumMonitorsW also for EnumMonitorsA.
5520        We use the smaller Ansi-Size to avoid conflicts with fixed Buffers of old Apps.
5521      */
5522     if (res) {
5523         /* EnumMonitorsW collected all Data. Parse them to caclulate ANSI-Size */
5524         DWORD   entrysize = 0;
5525         DWORD   index;
5526         LPSTR   ptr;
5527         LPMONITOR_INFO_2W mi2w;
5528         LPMONITOR_INFO_2A mi2a;
5529
5530         /* MONITOR_INFO_*W and MONITOR_INFO_*A have the same size */
5531         entrysize = (Level == 1) ? sizeof(MONITOR_INFO_1A) : sizeof(MONITOR_INFO_2A);
5532
5533         /* First pass: calculate the size for all Entries */
5534         mi2w = (LPMONITOR_INFO_2W) bufferW;
5535         mi2a = (LPMONITOR_INFO_2A) pMonitors;
5536         index = 0;
5537         while (index < numentries) {
5538             index++;
5539             needed += entrysize;    /* MONITOR_INFO_?A */
5540             TRACE("%p: parsing #%ld (%s)\n", mi2w, index, debugstr_w(mi2w->pName));
5541
5542             needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
5543                                             NULL, 0, NULL, NULL);
5544             if (Level > 1) {
5545                 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
5546                                                 NULL, 0, NULL, NULL);
5547                 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
5548                                                 NULL, 0, NULL, NULL);
5549             }
5550             /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
5551             mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
5552             mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
5553         }
5554
5555         /* check for errors and quit on failure */
5556         if (cbBuf < needed) {
5557             SetLastError(ERROR_INSUFFICIENT_BUFFER);
5558             res = FALSE;
5559             goto emA_cleanup;
5560         }
5561         len = entrysize * numentries;       /* room for all MONITOR_INFO_?A */
5562         ptr = (LPSTR) &pMonitors[len];      /* room for strings */
5563         cbBuf -= len ;                      /* free Bytes in the user-Buffer */
5564         mi2w = (LPMONITOR_INFO_2W) bufferW;
5565         mi2a = (LPMONITOR_INFO_2A) pMonitors;
5566         index = 0;
5567         /* Second Pass: Fill the User Buffer (if we have one) */
5568         while ((index < numentries) && pMonitors) {
5569             index++;
5570             TRACE("%p: writing MONITOR_INFO_%ldA #%ld\n", mi2a, Level, index);
5571             mi2a->pName = ptr;
5572             len = WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
5573                                             ptr, cbBuf , NULL, NULL);
5574             ptr += len;
5575             cbBuf -= len;
5576             if (Level > 1) {
5577                 mi2a->pEnvironment = ptr;
5578                 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
5579                                             ptr, cbBuf, NULL, NULL);
5580                 ptr += len;
5581                 cbBuf -= len;
5582
5583                 mi2a->pDLLName = ptr;
5584                 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
5585                                             ptr, cbBuf, NULL, NULL);
5586                 ptr += len;
5587                 cbBuf -= len;
5588             }
5589             /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
5590             mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
5591             mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
5592         }
5593     }
5594 emA_cleanup:
5595     if (pcbNeeded)  *pcbNeeded = needed;
5596     if (pcReturned) *pcReturned = (res) ? numentries : 0;
5597
5598     HeapFree(GetProcessHeap(), 0, nameW);
5599     HeapFree(GetProcessHeap(), 0, bufferW);
5600
5601     TRACE("returning %d with %ld (%ld byte for %ld entries)\n", 
5602             (res), GetLastError(), needed, numentries);
5603
5604     return (res);
5605
5606 }
5607
5608 /*****************************************************************************
5609  *          EnumMonitorsW [WINSPOOL.@]
5610  *
5611  * Enumerate available Port-Monitors
5612  *
5613  * PARAMS
5614  *  pName       [I] Servername or NULL (local Computer)
5615  *  Level       [I] Structure-Level (1:Win9x+NT or 2:NT only)
5616  *  pMonitors   [O] PTR to Buffer that receives the Result
5617  *  cbBuf       [I] Size of Buffer at pMonitors
5618  *  pcbNeeded   [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
5619  *  pcReturned  [O] PTR to DWORD that receives the number of Monitors in pMonitors
5620  *
5621  * RETURNS
5622  *  Success: TRUE
5623  *  Failure: FALSE and in pcbNeeded the Bytes required for buffer, if cbBuf is too small
5624  *
5625  * NOTES
5626  *  Windows reads the Registry once and cache the Results.
5627  *
5628  *|  Language-Monitors are also installed in the same Registry-Location but 
5629  *|  they are filtered in Windows (not returned by EnumMonitors).
5630  *|  We do no filtering to simplify our Code.
5631  *
5632  */
5633 BOOL WINAPI EnumMonitorsW(LPWSTR pName, DWORD Level, LPBYTE pMonitors,
5634                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
5635 {
5636     DWORD   needed = 0;
5637     DWORD   numentries = 0;
5638     BOOL    res = FALSE;
5639
5640     TRACE("(%s, %ld, %p, %ld, %p, %p)\n", debugstr_w(pName), Level, pMonitors,
5641           cbBuf, pcbNeeded, pcReturned);
5642
5643     if (pName && (lstrlenW(pName))) {
5644         FIXME("for Server %s not implemented\n", debugstr_w(pName));
5645         SetLastError(ERROR_ACCESS_DENIED);
5646         goto emW_cleanup;
5647     }
5648
5649     /* Level is not checked in win9x */
5650     if (!Level || (Level > 2)) {
5651         WARN("level (%ld) is ignored in win9x\n", Level);
5652         SetLastError(ERROR_INVALID_LEVEL);
5653         goto emW_cleanup;
5654     }
5655     if (!pcbNeeded) {
5656         SetLastError(RPC_X_NULL_REF_POINTER);
5657         goto emW_cleanup;
5658     }
5659
5660     /* Scan all Monitor-Keys */
5661     numentries = 0;
5662     needed = get_local_monitors(Level, NULL, 0, &numentries);
5663
5664     /* we calculated the needed buffersize. now do the error-checks */
5665     if (cbBuf < needed) {
5666         SetLastError(ERROR_INSUFFICIENT_BUFFER);
5667         goto emW_cleanup;
5668     }
5669     else if (!pMonitors || !pcReturned) {
5670         SetLastError(RPC_X_NULL_REF_POINTER);
5671         goto emW_cleanup;
5672     }
5673
5674     /* fill the Buffer with the Monitor-Keys */
5675     needed = get_local_monitors(Level, pMonitors, cbBuf, &numentries);
5676     res = TRUE;
5677
5678 emW_cleanup:
5679     if (pcbNeeded)  *pcbNeeded = needed;
5680     if (pcReturned) *pcReturned = numentries;
5681
5682     TRACE("returning %d with %ld (%ld byte for %ld entries)\n", 
5683             res, GetLastError(), needed, numentries);
5684
5685     return (res);
5686 }
5687
5688 /******************************************************************************
5689  *              XcvDataW (WINSPOOL.@)
5690  *
5691  * Notes:
5692  *  There doesn't seem to be an A version...
5693  */
5694 BOOL WINAPI XcvDataW( HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData,
5695     DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData,
5696     PDWORD pcbOutputNeeded, PDWORD pdwStatus)
5697 {
5698     FIXME("%p %s %p %ld %p %ld %p %p\n", hXcv, debugstr_w(pszDataName), 
5699           pInputData, cbInputData, pOutputData,
5700           cbOutputData, pcbOutputNeeded, pdwStatus);
5701     return FALSE;
5702 }
5703
5704 /*****************************************************************************
5705  *          EnumPrinterDataA [WINSPOOL.@]
5706  *
5707  */
5708 DWORD WINAPI EnumPrinterDataA( HANDLE hPrinter, DWORD dwIndex, LPSTR pValueName,
5709     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
5710     DWORD cbData, LPDWORD pcbData )
5711 {
5712     FIXME("%p %lx %p %lx %p %p %p %lx %p\n", hPrinter, dwIndex, pValueName,
5713           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
5714     return ERROR_NO_MORE_ITEMS;
5715 }
5716
5717 /*****************************************************************************
5718  *          EnumPrinterDataW [WINSPOOL.@]
5719  *
5720  */
5721 DWORD WINAPI EnumPrinterDataW( HANDLE hPrinter, DWORD dwIndex, LPWSTR pValueName,
5722     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
5723     DWORD cbData, LPDWORD pcbData )
5724 {
5725     FIXME("%p %lx %p %lx %p %p %p %lx %p\n", hPrinter, dwIndex, pValueName,
5726           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
5727     return ERROR_NO_MORE_ITEMS;
5728 }
5729
5730 /*****************************************************************************
5731  *          EnumPrintProcessorDatatypesA [WINSPOOL.@]
5732  *
5733  */
5734 BOOL WINAPI EnumPrintProcessorDatatypesA(LPSTR pName, LPSTR pPrintProcessorName,
5735                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
5736                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
5737 {
5738     FIXME("Stub: %s %s %ld %p %ld %p %p\n", debugstr_a(pName),
5739           debugstr_a(pPrintProcessorName), Level, pDatatypes, cbBuf,
5740           pcbNeeded, pcReturned);
5741     return FALSE;
5742 }
5743
5744 /*****************************************************************************
5745  *          EnumPrintProcessorDatatypesW [WINSPOOL.@]
5746  *
5747  */
5748 BOOL WINAPI EnumPrintProcessorDatatypesW(LPWSTR pName, LPWSTR pPrintProcessorName,
5749                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
5750                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
5751 {
5752     FIXME("Stub: %s %s %ld %p %ld %p %p\n", debugstr_w(pName),
5753           debugstr_w(pPrintProcessorName), Level, pDatatypes, cbBuf,
5754           pcbNeeded, pcReturned);
5755     return FALSE;
5756 }
5757
5758 /*****************************************************************************
5759  *          EnumPrintProcessorsA [WINSPOOL.@]
5760  *
5761  */
5762 BOOL WINAPI EnumPrintProcessorsA(LPSTR pName, LPSTR pEnvironment, DWORD Level, 
5763     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
5764 {
5765     FIXME("Stub: %s %s %ld %p %ld %p %p\n", pName, pEnvironment, Level,
5766         pPrintProcessorInfo, cbBuf, pcbNeeded, pcbReturned);
5767     return FALSE;
5768 }
5769
5770 /*****************************************************************************
5771  *          EnumPrintProcessorsW [WINSPOOL.@]
5772  *
5773  */
5774 BOOL WINAPI EnumPrintProcessorsW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
5775     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
5776 {
5777     FIXME("Stub: %s %s %ld %p %ld %p %p\n", debugstr_w(pName),
5778         debugstr_w(pEnvironment), Level, pPrintProcessorInfo,
5779         cbBuf, pcbNeeded, pcbReturned);
5780     return FALSE;
5781 }
5782
5783 /*****************************************************************************
5784  *          ExtDeviceMode [WINSPOOL.@]
5785  *
5786  */
5787 LONG WINAPI ExtDeviceMode( HWND hWnd, HANDLE hInst, LPDEVMODEA pDevModeOutput,
5788     LPSTR pDeviceName, LPSTR pPort, LPDEVMODEA pDevModeInput, LPSTR pProfile,
5789     DWORD fMode)
5790 {
5791     FIXME("Stub: %p %p %p %s %s %p %s %lx\n", hWnd, hInst, pDevModeOutput,
5792           debugstr_a(pDeviceName), debugstr_a(pPort), pDevModeInput,
5793           debugstr_a(pProfile), fMode);
5794     return -1;
5795 }
5796
5797 /*****************************************************************************
5798  *          FindClosePrinterChangeNotification [WINSPOOL.@]
5799  *
5800  */
5801 BOOL WINAPI FindClosePrinterChangeNotification( HANDLE hChange )
5802 {
5803     FIXME("Stub: %p\n", hChange);
5804     return TRUE;
5805 }
5806
5807 /*****************************************************************************
5808  *          FindFirstPrinterChangeNotification [WINSPOOL.@]
5809  *
5810  */
5811 HANDLE WINAPI FindFirstPrinterChangeNotification( HANDLE hPrinter,
5812     DWORD fdwFlags, DWORD fdwOptions, LPVOID pPrinterNotifyOptions )
5813 {
5814     FIXME("Stub: %p %lx %lx %p\n",
5815           hPrinter, fdwFlags, fdwOptions, pPrinterNotifyOptions);
5816     return INVALID_HANDLE_VALUE;
5817 }
5818
5819 /*****************************************************************************
5820  *          FindNextPrinterChangeNotification [WINSPOOL.@]
5821  *
5822  */
5823 BOOL WINAPI FindNextPrinterChangeNotification( HANDLE hChange, PDWORD pdwChange,
5824     LPVOID pPrinterNotifyOptions, LPVOID *ppPrinterNotifyInfo )
5825 {
5826     FIXME("Stub: %p %p %p %p\n",
5827           hChange, pdwChange, pPrinterNotifyOptions, ppPrinterNotifyInfo);
5828     return FALSE;
5829 }
5830
5831 /*****************************************************************************
5832  *          FreePrinterNotifyInfo [WINSPOOL.@]
5833  *
5834  */
5835 BOOL WINAPI FreePrinterNotifyInfo( PPRINTER_NOTIFY_INFO pPrinterNotifyInfo )
5836 {
5837     FIXME("Stub: %p\n", pPrinterNotifyInfo);
5838     return TRUE;
5839 }
5840
5841 /*****************************************************************************
5842  *          string_to_buf
5843  *
5844  * Copies a unicode string into a buffer.  The buffer will either contain unicode or
5845  * ansi depending on the unicode parameter.
5846  */
5847 static BOOL string_to_buf(LPCWSTR str, LPBYTE ptr, DWORD cb, DWORD *size, BOOL unicode)
5848 {
5849     if(!str)
5850     {
5851         *size = 0;
5852         return TRUE;
5853     }
5854
5855     if(unicode)
5856     {
5857         *size = (strlenW(str) + 1) * sizeof(WCHAR);
5858         if(*size <= cb)
5859         {
5860             memcpy(ptr, str, *size);
5861             return TRUE;
5862         }
5863         return FALSE;
5864     }
5865     else
5866     {
5867         *size = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
5868         if(*size <= cb)
5869         {
5870             WideCharToMultiByte(CP_ACP, 0, str, -1, (LPSTR)ptr, *size, NULL, NULL);
5871             return TRUE;
5872         }
5873         return FALSE;
5874     }
5875 }
5876
5877 /*****************************************************************************
5878  *          get_job_info_1
5879  */
5880 static BOOL get_job_info_1(job_t *job, JOB_INFO_1W *ji1, LPBYTE buf, DWORD cbBuf,
5881                            LPDWORD pcbNeeded, BOOL unicode)
5882 {
5883     DWORD size, left = cbBuf;
5884     BOOL space = (cbBuf > 0);
5885     LPBYTE ptr = buf;
5886
5887     *pcbNeeded = 0;
5888
5889     if(space)
5890     {
5891         ji1->JobId = job->job_id;
5892     }
5893
5894     string_to_buf(job->document_title, ptr, left, &size, unicode);
5895     if(space && size <= left)
5896     {
5897         ji1->pDocument = (LPWSTR)ptr;
5898         ptr += size;
5899         left -= size;
5900     }
5901     else
5902         space = FALSE;
5903     *pcbNeeded += size;
5904
5905     return space;
5906 }
5907
5908 /*****************************************************************************
5909  *          get_job_info_2
5910  */
5911 static BOOL get_job_info_2(job_t *job, JOB_INFO_2W *ji2, LPBYTE buf, DWORD cbBuf,
5912                            LPDWORD pcbNeeded, BOOL unicode)
5913 {
5914     DWORD size, left = cbBuf;
5915     BOOL space = (cbBuf > 0);
5916     LPBYTE ptr = buf;
5917
5918     *pcbNeeded = 0;
5919
5920     if(space)
5921     {
5922         ji2->JobId = job->job_id;
5923     }
5924
5925     string_to_buf(job->document_title, ptr, left, &size, unicode);
5926     if(space && size <= left)
5927     {
5928         ji2->pDocument = (LPWSTR)ptr;
5929         ptr += size;
5930         left -= size;
5931     }
5932     else
5933         space = FALSE;
5934     *pcbNeeded += size;
5935
5936     return space;
5937 }
5938
5939 /*****************************************************************************
5940  *          get_job_info
5941  */
5942 static BOOL get_job_info(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
5943                          DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
5944 {
5945     BOOL ret = FALSE;
5946     DWORD needed = 0, size;
5947     job_t *job;
5948     LPBYTE ptr = pJob;
5949
5950     TRACE("%p %ld %ld %p %ld %p\n", hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
5951
5952     EnterCriticalSection(&printer_handles_cs);
5953     job = get_job(hPrinter, JobId);
5954     if(!job)
5955         goto end;
5956
5957     switch(Level)
5958     {
5959     case 1:
5960         size = sizeof(JOB_INFO_1W);
5961         if(cbBuf >= size)
5962         {
5963             cbBuf -= size;
5964             ptr += size;
5965             memset(pJob, 0, size);
5966         }
5967         else
5968             cbBuf = 0;
5969         ret = get_job_info_1(job, (JOB_INFO_1W *)pJob, ptr, cbBuf, &needed, unicode);
5970         needed += size;
5971         break;
5972
5973     case 2:
5974         size = sizeof(JOB_INFO_2W);
5975         if(cbBuf >= size)
5976         {
5977             cbBuf -= size;
5978             ptr += size;
5979             memset(pJob, 0, size);
5980         }
5981         else
5982             cbBuf = 0;
5983         ret = get_job_info_2(job, (JOB_INFO_2W *)pJob, ptr, cbBuf, &needed, unicode);
5984         needed += size;
5985         break;
5986
5987     case 3:
5988         size = sizeof(JOB_INFO_3);
5989         if(cbBuf >= size)
5990         {
5991             cbBuf -= size;
5992             memset(pJob, 0, size);
5993             ret = TRUE;
5994         }
5995         else
5996             cbBuf = 0;
5997         needed = size;
5998         break;
5999
6000     default:
6001         SetLastError(ERROR_INVALID_LEVEL);
6002         goto end;
6003     }
6004     if(pcbNeeded)
6005         *pcbNeeded = needed;
6006 end:
6007     LeaveCriticalSection(&printer_handles_cs);
6008     return ret;
6009 }
6010
6011 /*****************************************************************************
6012  *          GetJobA [WINSPOOL.@]
6013  *
6014  */
6015 BOOL WINAPI GetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
6016                     DWORD cbBuf, LPDWORD pcbNeeded)
6017 {
6018     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, FALSE);
6019 }
6020
6021 /*****************************************************************************
6022  *          GetJobW [WINSPOOL.@]
6023  *
6024  */
6025 BOOL WINAPI GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
6026                     DWORD cbBuf, LPDWORD pcbNeeded)
6027 {
6028     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, TRUE);
6029 }
6030
6031 /*****************************************************************************
6032  *          schedule_lpr
6033  */
6034 static BOOL schedule_lpr(LPCWSTR printer_name, LPCWSTR filename)
6035 {
6036     char *unixname, *queue, *cmd;
6037     char fmt[] = "lpr -P%s %s";
6038     DWORD len;
6039
6040     if(!(unixname = wine_get_unix_file_name(filename)))
6041         return FALSE;
6042
6043     len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
6044     queue = HeapAlloc(GetProcessHeap(), 0, len);
6045     WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
6046
6047     cmd = HeapAlloc(GetProcessHeap(), 0, strlen(unixname) + len + sizeof(fmt) - 5);
6048     sprintf(cmd, fmt, queue, unixname);
6049
6050     TRACE("printing with: %s\n", cmd);
6051     system(cmd);
6052
6053     HeapFree(GetProcessHeap(), 0, cmd);
6054     HeapFree(GetProcessHeap(), 0, queue);
6055     HeapFree(GetProcessHeap(), 0, unixname);
6056     return TRUE;
6057 }
6058
6059 /*****************************************************************************
6060  *          schedule_cups
6061  */
6062 static BOOL schedule_cups(LPCWSTR printer_name, LPCWSTR filename, LPCWSTR document_title)
6063 {
6064 #if HAVE_CUPS_CUPS_H
6065     if(pcupsPrintFile)
6066     {
6067         char *unixname, *queue, *doc_titleA;
6068         DWORD len;
6069         BOOL ret;
6070
6071         if(!(unixname = wine_get_unix_file_name(filename)))
6072             return FALSE;
6073
6074         len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
6075         queue = HeapAlloc(GetProcessHeap(), 0, len);
6076         WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
6077
6078         len = WideCharToMultiByte(CP_ACP, 0, document_title, -1, NULL, 0, NULL, NULL);
6079         doc_titleA = HeapAlloc(GetProcessHeap(), 0, len);
6080         WideCharToMultiByte(CP_ACP, 0, document_title, -1, doc_titleA, len, NULL, NULL);
6081
6082         TRACE("printing via cups\n");
6083         ret = pcupsPrintFile(queue, unixname, doc_titleA, 0, NULL);
6084         HeapFree(GetProcessHeap(), 0, doc_titleA);
6085         HeapFree(GetProcessHeap(), 0, queue);
6086         HeapFree(GetProcessHeap(), 0, unixname);
6087         return ret;
6088     }
6089     else
6090 #endif
6091     {
6092         return schedule_lpr(printer_name, filename);
6093     }
6094 }
6095
6096 INT_PTR CALLBACK file_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
6097 {
6098     LPWSTR filename;
6099
6100     switch(msg)
6101     {
6102     case WM_INITDIALOG:
6103         SetWindowLongPtrW(hwnd, DWLP_USER, lparam);
6104         return TRUE;
6105
6106     case WM_COMMAND:
6107         if(HIWORD(wparam) == BN_CLICKED)
6108         {
6109             if(LOWORD(wparam) == IDOK)
6110             {
6111                 HANDLE hf;
6112                 DWORD len = SendDlgItemMessageW(hwnd, EDITBOX, WM_GETTEXTLENGTH, 0, 0);
6113                 LPWSTR *output;
6114
6115                 filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
6116                 GetDlgItemTextW(hwnd, EDITBOX, filename, len + 1);
6117
6118                 if(GetFileAttributesW(filename) != INVALID_FILE_ATTRIBUTES)
6119                 {
6120                     WCHAR caption[200], message[200];
6121                     int mb_ret;
6122
6123                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
6124                     LoadStringW(WINSPOOL_hInstance, IDS_FILE_EXISTS, message, sizeof(message) / sizeof(WCHAR));
6125                     mb_ret = MessageBoxW(hwnd, message, caption, MB_OKCANCEL | MB_ICONEXCLAMATION);
6126                     if(mb_ret == IDCANCEL)
6127                     {
6128                         HeapFree(GetProcessHeap(), 0, filename);
6129                         return TRUE;
6130                     }
6131                 }
6132                 hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
6133                 if(hf == INVALID_HANDLE_VALUE)
6134                 {
6135                     WCHAR caption[200], message[200];
6136
6137                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
6138                     LoadStringW(WINSPOOL_hInstance, IDS_CANNOT_OPEN, message, sizeof(message) / sizeof(WCHAR));
6139                     MessageBoxW(hwnd, message, caption, MB_OK | MB_ICONEXCLAMATION);
6140                     HeapFree(GetProcessHeap(), 0, filename);
6141                     return TRUE;
6142                 }
6143                 CloseHandle(hf);
6144                 DeleteFileW(filename);
6145                 output = (LPWSTR *)GetWindowLongPtrW(hwnd, DWLP_USER);
6146                 *output = filename;
6147                 EndDialog(hwnd, IDOK);
6148                 return TRUE;
6149             }
6150             if(LOWORD(wparam) == IDCANCEL)
6151             {
6152                 EndDialog(hwnd, IDCANCEL);
6153                 return TRUE;
6154             }
6155         }
6156         return FALSE;
6157     }
6158     return FALSE;
6159 }
6160
6161 /*****************************************************************************
6162  *          get_filename
6163  */
6164 static BOOL get_filename(LPWSTR *filename)
6165 {
6166     return DialogBoxParamW(WINSPOOL_hInstance, MAKEINTRESOURCEW(FILENAME_DIALOG), GetForegroundWindow(),
6167                            file_dlg_proc, (LPARAM)filename) == IDOK;
6168 }
6169
6170 /*****************************************************************************
6171  *          schedule_file
6172  */
6173 static BOOL schedule_file(LPCWSTR filename)
6174 {
6175     LPWSTR output = NULL;
6176
6177     if(get_filename(&output))
6178     {
6179         TRACE("copy to %s\n", debugstr_w(output));
6180         CopyFileW(filename, output, FALSE);
6181         HeapFree(GetProcessHeap(), 0, output);
6182         return TRUE;
6183     }
6184     return FALSE;
6185 }
6186
6187 /*****************************************************************************
6188  *          schedule_pipe
6189  */
6190 static BOOL schedule_pipe(LPCWSTR cmd, LPCWSTR filename)
6191 {
6192 #ifdef HAVE_FORK
6193     char *unixname, *cmdA;
6194     DWORD len;
6195     int fds[2] = {-1, -1}, file_fd = -1, no_read;
6196     BOOL ret = FALSE;
6197     char buf[1024];
6198
6199     if(!(unixname = wine_get_unix_file_name(filename)))
6200         return FALSE;
6201
6202     len = WideCharToMultiByte(CP_ACP, 0, cmd, -1, NULL, 0, NULL, NULL);
6203     cmdA = HeapAlloc(GetProcessHeap(), 0, len);
6204     WideCharToMultiByte(CP_ACP, 0, cmd, -1, cmdA, len, NULL, NULL);
6205
6206     TRACE("printing with: %s\n", cmdA);
6207
6208     if((file_fd = open(unixname, O_RDONLY)) == -1)
6209         goto end;
6210
6211     if (pipe(fds))
6212     {
6213         ERR("pipe() failed!\n"); 
6214         goto end;
6215     }
6216
6217     if (fork() == 0)
6218     {
6219         close(0);
6220         dup2(fds[0], 0);
6221         close(fds[1]);
6222
6223         /* reset signals that we previously set to SIG_IGN */
6224         signal(SIGPIPE, SIG_DFL);
6225         signal(SIGCHLD, SIG_DFL);
6226
6227         system(cmdA);
6228         exit(0);
6229     }
6230
6231     while((no_read = read(file_fd, buf, sizeof(buf))) > 0)
6232         write(fds[1], buf, no_read);
6233
6234     ret = TRUE;
6235
6236 end:
6237     if(file_fd != -1) close(file_fd);
6238     if(fds[0] != -1) close(fds[0]);
6239     if(fds[1] != -1) close(fds[1]);
6240
6241     HeapFree(GetProcessHeap(), 0, cmdA);
6242     HeapFree(GetProcessHeap(), 0, unixname);
6243     return ret;
6244 #else
6245     return FALSE;
6246 #endif
6247 }
6248
6249 /*****************************************************************************
6250  *          schedule_unixfile
6251  */
6252 static BOOL schedule_unixfile(LPCWSTR output, LPCWSTR filename)
6253 {
6254     int in_fd, out_fd, no_read;
6255     char buf[1024];
6256     BOOL ret = FALSE;
6257     char *unixname, *outputA;
6258     DWORD len;
6259
6260     if(!(unixname = wine_get_unix_file_name(filename)))
6261         return FALSE;
6262
6263     len = WideCharToMultiByte(CP_ACP, 0, output, -1, NULL, 0, NULL, NULL);
6264     outputA = HeapAlloc(GetProcessHeap(), 0, len);
6265     WideCharToMultiByte(CP_ACP, 0, output, -1, outputA, len, NULL, NULL);
6266     
6267     out_fd = open(outputA, O_CREAT | O_TRUNC | O_WRONLY, 0666);
6268     in_fd = open(unixname, O_RDONLY);
6269     if(out_fd == -1 || in_fd == -1)
6270         goto end;
6271
6272     while((no_read = read(in_fd, buf, sizeof(buf))) > 0)
6273         write(out_fd, buf, no_read);
6274
6275     ret = TRUE;
6276 end:
6277     if(in_fd != -1) close(in_fd);
6278     if(out_fd != -1) close(out_fd);
6279     HeapFree(GetProcessHeap(), 0, outputA);
6280     HeapFree(GetProcessHeap(), 0, unixname);
6281     return ret;
6282 }
6283
6284 /*****************************************************************************
6285  *          ScheduleJob [WINSPOOL.@]
6286  *
6287  */
6288 BOOL WINAPI ScheduleJob( HANDLE hPrinter, DWORD dwJobID )
6289 {
6290     opened_printer_t *printer;
6291     BOOL ret = FALSE;
6292     struct list *cursor, *cursor2;
6293
6294     TRACE("(%p, %lx)\n", hPrinter, dwJobID);
6295     EnterCriticalSection(&printer_handles_cs);
6296     printer = get_opened_printer(hPrinter);
6297     if(!printer)
6298         goto end;
6299
6300     LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
6301     {
6302         job_t *job = LIST_ENTRY(cursor, job_t, entry);
6303         HANDLE hf;
6304
6305         if(job->job_id != dwJobID) continue;
6306
6307         hf = CreateFileW(job->filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
6308         if(hf != INVALID_HANDLE_VALUE)
6309         {
6310             PRINTER_INFO_5W *pi5;
6311             DWORD needed;
6312             HKEY hkey;
6313             WCHAR output[1024];
6314             static const WCHAR spooler_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
6315                                                 'P','r','i','n','t','i','n','g','\\','S','p','o','o','l','e','r',0};
6316
6317             GetPrinterW(hPrinter, 5, NULL, 0, &needed);
6318             pi5 = HeapAlloc(GetProcessHeap(), 0, needed);
6319             GetPrinterW(hPrinter, 5, (LPBYTE)pi5, needed, &needed);
6320             TRACE("need to schedule job %ld filename %s to port %s\n", job->job_id, debugstr_w(job->filename),
6321                   debugstr_w(pi5->pPortName));
6322             
6323             output[0] = 0;
6324
6325             /* @@ Wine registry key: HKCU\Software\Wine\Printing\Spooler */
6326             if(RegOpenKeyW(HKEY_CURRENT_USER, spooler_key, &hkey) == ERROR_SUCCESS)
6327             {
6328                 DWORD type, count = sizeof(output);
6329                 RegQueryValueExW(hkey, pi5->pPortName, NULL, &type, (LPBYTE)output, &count);
6330                 RegCloseKey(hkey);
6331             }
6332             if(output[0] == '|')
6333             {
6334                 schedule_pipe(output + 1, job->filename);
6335             }
6336             else if(output[0])
6337             {
6338                 schedule_unixfile(output, job->filename);
6339             }
6340             else if(!strncmpW(pi5->pPortName, LPR_Port, strlenW(LPR_Port)))
6341             {
6342                 schedule_lpr(pi5->pPortName + strlenW(LPR_Port), job->filename);
6343             }
6344             else if(!strncmpW(pi5->pPortName, CUPS_Port, strlenW(CUPS_Port)))
6345             {
6346                 schedule_cups(pi5->pPortName + strlenW(CUPS_Port), job->filename, job->document_title);
6347             }
6348             else if(!strncmpW(pi5->pPortName, FILE_Port, strlenW(FILE_Port)))
6349             {
6350                 schedule_file(job->filename);
6351             }
6352             else
6353             {
6354                 FIXME("can't schedule to port %s\n", debugstr_w(pi5->pPortName));
6355             }
6356             HeapFree(GetProcessHeap(), 0, pi5);
6357             CloseHandle(hf);
6358             DeleteFileW(job->filename);
6359         }
6360         list_remove(cursor);
6361         HeapFree(GetProcessHeap(), 0, job->document_title);
6362         HeapFree(GetProcessHeap(), 0, job->filename);
6363         HeapFree(GetProcessHeap(), 0, job);
6364         ret = TRUE;
6365         break;
6366     }
6367 end:
6368     LeaveCriticalSection(&printer_handles_cs);
6369     return ret;
6370 }
6371
6372 /*****************************************************************************
6373  *          StartDocDlgA [WINSPOOL.@]
6374  */
6375  LPSTR WINAPI StartDocDlgA( HANDLE hPrinter, DOCINFOA *doc )
6376 {
6377     UNICODE_STRING usBuffer;
6378     DOCINFOW docW;
6379     LPWSTR retW;
6380     LPSTR ret = NULL;
6381
6382     docW.cbSize = sizeof(docW);
6383     docW.lpszDocName = asciitounicode(&usBuffer, doc->lpszDocName);
6384     docW.lpszOutput = asciitounicode(&usBuffer, doc->lpszOutput);
6385     docW.lpszDatatype = asciitounicode(&usBuffer, doc->lpszDatatype);
6386     docW.fwType = doc->fwType;
6387
6388     retW = StartDocDlgW(hPrinter, &docW);
6389
6390     if(retW)
6391     {
6392         DWORD len = WideCharToMultiByte(CP_ACP, 0, retW, -1, NULL, 0, NULL, NULL);
6393         ret = HeapAlloc(GetProcessHeap(), 0, len);
6394         WideCharToMultiByte(CP_ACP, 0, retW, -1, ret, len, NULL, NULL);
6395         HeapFree(GetProcessHeap(), 0, retW);
6396     }
6397
6398     HeapFree(GetProcessHeap(), 0, (LPWSTR)docW.lpszDatatype);
6399     HeapFree(GetProcessHeap(), 0, (LPWSTR)docW.lpszOutput);
6400     HeapFree(GetProcessHeap(), 0, (LPWSTR)docW.lpszDocName);
6401
6402     return ret;
6403 }
6404
6405 /*****************************************************************************
6406  *          StartDocDlgW [WINSPOOL.@]
6407  *
6408  * Undocumented: Apparently used by gdi32:StartDocW() to popup the file dialog
6409  * when lpszOutput is "FILE:" or if lpszOutput is NULL and the default printer
6410  * port is "FILE:". Also returns the full path if passed a relative path.
6411  *
6412  * The caller should free the returned string from the process heap.
6413  */
6414 LPWSTR WINAPI StartDocDlgW( HANDLE hPrinter, DOCINFOW *doc )
6415 {
6416     LPWSTR ret = NULL;
6417     DWORD len, attr;
6418
6419     if(doc->lpszOutput == NULL) /* Check whether default port is FILE: */
6420     {
6421         PRINTER_INFO_5W *pi5;
6422         GetPrinterW(hPrinter, 5, NULL, 0, &len);
6423         if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
6424             return NULL;
6425         pi5 = HeapAlloc(GetProcessHeap(), 0, len);
6426         GetPrinterW(hPrinter, 5, (LPBYTE)pi5, len, &len);
6427         if(!pi5->pPortName || strcmpW(pi5->pPortName, FILE_Port))
6428         {
6429             HeapFree(GetProcessHeap(), 0, pi5);
6430             return NULL;
6431         }
6432         HeapFree(GetProcessHeap(), 0, pi5);
6433     }
6434
6435     if(doc->lpszOutput == NULL || !strcmpW(doc->lpszOutput, FILE_Port))
6436     {
6437         LPWSTR name;
6438         get_filename(&name);
6439         if(name)
6440         {
6441             if(!(len = GetFullPathNameW(name, 0, NULL, NULL)))
6442             {
6443                 HeapFree(GetProcessHeap(), 0, name);
6444                 return NULL;
6445             }
6446             ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6447             GetFullPathNameW(name, len, ret, NULL);
6448             HeapFree(GetProcessHeap(), 0, name);
6449         }
6450         return ret;
6451     }
6452
6453     if(!(len = GetFullPathNameW(doc->lpszOutput, 0, NULL, NULL)))
6454         return NULL;
6455
6456     ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6457     GetFullPathNameW(doc->lpszOutput, len, ret, NULL);
6458         
6459     attr = GetFileAttributesW(ret);
6460     if(attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY))
6461     {
6462         HeapFree(GetProcessHeap(), 0, ret);
6463         ret = NULL;
6464     }
6465     return ret;
6466 }