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