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