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