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