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