winsock: Implement getnameinfo.
[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  *              SetDefaultPrinterW   (WINSPOOL.204)
4152  *
4153  * Set the Name of the Default Printer
4154  *
4155  * PARAMS
4156  *  pszPrinter [I] Name of the Printer or NULL
4157  *
4158  * RETURNS
4159  *  Success:    True
4160  *  Failure:    FALSE
4161  *
4162  * NOTES
4163  *  When the Parameter is NULL or points to an Empty String and
4164  *  a Default Printer was already present, then this Function changes nothing.
4165  *  Without a Default Printer and NULL (or an Empty String) as Parameter,
4166  *  the First enumerated local Printer is used.
4167  *
4168  */
4169 BOOL WINAPI SetDefaultPrinterW(LPCWSTR pszPrinter)
4170 {
4171
4172     TRACE("(%s)\n", debugstr_w(pszPrinter));
4173
4174     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4175     return FALSE;
4176 }
4177
4178 /******************************************************************************
4179  *              SetDefaultPrinterA   (WINSPOOL.202)
4180  *
4181  * See SetDefaultPrinterW.
4182  *
4183  */
4184 BOOL WINAPI SetDefaultPrinterA(LPCSTR pszPrinter)
4185 {
4186
4187     TRACE("(%s)\n", debugstr_a(pszPrinter));
4188
4189     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4190     return FALSE;
4191 }
4192
4193
4194 /******************************************************************************
4195  *              SetPrinterDataExA   (WINSPOOL.@)
4196  */
4197 DWORD WINAPI SetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
4198                                LPCSTR pValueName, DWORD Type,
4199                                LPBYTE pData, DWORD cbData)
4200 {
4201     HKEY hkeyPrinter, hkeySubkey;
4202     DWORD ret;
4203
4204     TRACE("(%p, %s, %s %08lx, %p, %08lx)\n", hPrinter, debugstr_a(pKeyName),
4205           debugstr_a(pValueName), Type, pData, cbData);
4206
4207     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
4208        != ERROR_SUCCESS)
4209         return ret;
4210
4211     if((ret = RegCreateKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
4212        != ERROR_SUCCESS) {
4213         ERR("Can't create subkey %s\n", debugstr_a(pKeyName));
4214         RegCloseKey(hkeyPrinter);
4215         return ret;
4216     }
4217     ret = RegSetValueExA(hkeySubkey, pValueName, 0, Type, pData, cbData);
4218     RegCloseKey(hkeySubkey);
4219     RegCloseKey(hkeyPrinter);
4220     return ret;
4221 }
4222
4223 /******************************************************************************
4224  *              SetPrinterDataExW   (WINSPOOL.@)
4225  */
4226 DWORD WINAPI SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
4227                                LPCWSTR pValueName, DWORD Type,
4228                                LPBYTE pData, DWORD cbData)
4229 {
4230     HKEY hkeyPrinter, hkeySubkey;
4231     DWORD ret;
4232
4233     TRACE("(%p, %s, %s %08lx, %p, %08lx)\n", hPrinter, debugstr_w(pKeyName),
4234           debugstr_w(pValueName), Type, pData, cbData);
4235
4236     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
4237        != ERROR_SUCCESS)
4238         return ret;
4239
4240     if((ret = RegCreateKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
4241        != ERROR_SUCCESS) {
4242         ERR("Can't create subkey %s\n", debugstr_w(pKeyName));
4243         RegCloseKey(hkeyPrinter);
4244         return ret;
4245     }
4246     ret = RegSetValueExW(hkeySubkey, pValueName, 0, Type, pData, cbData);
4247     RegCloseKey(hkeySubkey);
4248     RegCloseKey(hkeyPrinter);
4249     return ret;
4250 }
4251
4252 /******************************************************************************
4253  *              SetPrinterDataA   (WINSPOOL.@)
4254  */
4255 DWORD WINAPI SetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, DWORD Type,
4256                                LPBYTE pData, DWORD cbData)
4257 {
4258     return SetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, Type,
4259                              pData, cbData);
4260 }
4261
4262 /******************************************************************************
4263  *              SetPrinterDataW   (WINSPOOL.@)
4264  */
4265 DWORD WINAPI SetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, DWORD Type,
4266                              LPBYTE pData, DWORD cbData)
4267 {
4268     return SetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, Type,
4269                              pData, cbData);
4270 }
4271
4272 /******************************************************************************
4273  *              GetPrinterDataExA   (WINSPOOL.@)
4274  */
4275 DWORD WINAPI GetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
4276                                LPCSTR pValueName, LPDWORD pType,
4277                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
4278 {
4279     HKEY hkeyPrinter, hkeySubkey;
4280     DWORD ret;
4281
4282     TRACE("(%p, %s, %s %p, %p, %08lx, %p)\n", hPrinter,
4283           debugstr_a(pKeyName), debugstr_a(pValueName), pType, pData, nSize,
4284           pcbNeeded);
4285
4286     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
4287        != ERROR_SUCCESS)
4288         return ret;
4289
4290     if((ret = RegOpenKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
4291        != ERROR_SUCCESS) {
4292         WARN("Can't open subkey %s\n", debugstr_a(pKeyName));
4293         RegCloseKey(hkeyPrinter);
4294         return ret;
4295     }
4296     *pcbNeeded = nSize;
4297     ret = RegQueryValueExA(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
4298     RegCloseKey(hkeySubkey);
4299     RegCloseKey(hkeyPrinter);
4300     return ret;
4301 }
4302
4303 /******************************************************************************
4304  *              GetPrinterDataExW   (WINSPOOL.@)
4305  */
4306 DWORD WINAPI GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
4307                                LPCWSTR pValueName, LPDWORD pType,
4308                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
4309 {
4310     HKEY hkeyPrinter, hkeySubkey;
4311     DWORD ret;
4312
4313     TRACE("(%p, %s, %s %p, %p, %08lx, %p)\n", hPrinter,
4314           debugstr_w(pKeyName), debugstr_w(pValueName), pType, pData, nSize,
4315           pcbNeeded);
4316
4317     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
4318        != ERROR_SUCCESS)
4319         return ret;
4320
4321     if((ret = RegOpenKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
4322        != ERROR_SUCCESS) {
4323         WARN("Can't open subkey %s\n", debugstr_w(pKeyName));
4324         RegCloseKey(hkeyPrinter);
4325         return ret;
4326     }
4327     *pcbNeeded = nSize;
4328     ret = RegQueryValueExW(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
4329     RegCloseKey(hkeySubkey);
4330     RegCloseKey(hkeyPrinter);
4331     return ret;
4332 }
4333
4334 /******************************************************************************
4335  *              GetPrinterDataA   (WINSPOOL.@)
4336  */
4337 DWORD WINAPI GetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, LPDWORD pType,
4338                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
4339 {
4340     return GetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, pType,
4341                              pData, nSize, pcbNeeded);
4342 }
4343
4344 /******************************************************************************
4345  *              GetPrinterDataW   (WINSPOOL.@)
4346  */
4347 DWORD WINAPI GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType,
4348                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
4349 {
4350     return GetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, pType,
4351                              pData, nSize, pcbNeeded);
4352 }
4353
4354 /*******************************************************************************
4355  *              EnumPrinterDataExW      [WINSPOOL.@]
4356  */
4357 DWORD WINAPI EnumPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
4358                                 LPBYTE pEnumValues, DWORD cbEnumValues,
4359                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
4360 {
4361     HKEY                    hkPrinter, hkSubKey;
4362     DWORD                   r, ret, dwIndex, cValues, cbMaxValueNameLen,
4363                             cbValueNameLen, cbMaxValueLen, cbValueLen,
4364                             cbBufSize, dwType;
4365     LPWSTR                  lpValueName;
4366     HANDLE                  hHeap;
4367     PBYTE                   lpValue;
4368     PPRINTER_ENUM_VALUESW   ppev;
4369
4370     TRACE ("%p %s\n", hPrinter, debugstr_w (pKeyName));
4371
4372     if (pKeyName == NULL || *pKeyName == 0)
4373         return ERROR_INVALID_PARAMETER;
4374
4375     ret = WINSPOOL_GetOpenedPrinterRegKey (hPrinter, &hkPrinter);
4376     if (ret != ERROR_SUCCESS)
4377     {
4378         TRACE ("WINSPOOL_GetOpenedPrinterRegKey (%p) returned %li\n",
4379                 hPrinter, ret);
4380         return ret;
4381     }
4382
4383     ret = RegOpenKeyExW (hkPrinter, pKeyName, 0, KEY_READ, &hkSubKey);
4384     if (ret != ERROR_SUCCESS)
4385     {
4386         r = RegCloseKey (hkPrinter);
4387         if (r != ERROR_SUCCESS)
4388             WARN ("RegCloseKey returned %li\n", r);
4389         TRACE ("RegOpenKeyExW (%p, %s) returned %li\n", hPrinter,
4390                 debugstr_w (pKeyName), ret);
4391         return ret;
4392     }
4393
4394     ret = RegCloseKey (hkPrinter);
4395     if (ret != ERROR_SUCCESS)
4396     {
4397         ERR ("RegCloseKey returned %li\n", ret);
4398         r = RegCloseKey (hkSubKey);
4399         if (r != ERROR_SUCCESS)
4400             WARN ("RegCloseKey returned %li\n", r);
4401         return ret;
4402     }
4403
4404     ret = RegQueryInfoKeyW (hkSubKey, NULL, NULL, NULL, NULL, NULL, NULL,
4405             &cValues, &cbMaxValueNameLen, &cbMaxValueLen, NULL, NULL);
4406     if (ret != ERROR_SUCCESS)
4407     {
4408         r = RegCloseKey (hkSubKey);
4409         if (r != ERROR_SUCCESS)
4410             WARN ("RegCloseKey returned %li\n", r);
4411         TRACE ("RegQueryInfoKeyW (%p) returned %li\n", hkSubKey, ret);
4412         return ret;
4413     }
4414
4415     TRACE ("RegQueryInfoKeyW returned cValues = %li, cbMaxValueNameLen = %li, "
4416             "cbMaxValueLen = %li\n", cValues, cbMaxValueNameLen, cbMaxValueLen);
4417
4418     if (cValues == 0)                   /* empty key */
4419     {
4420         r = RegCloseKey (hkSubKey);
4421         if (r != ERROR_SUCCESS)
4422             WARN ("RegCloseKey returned %li\n", r);
4423         *pcbEnumValues = *pnEnumValues = 0;
4424         return ERROR_SUCCESS;
4425     }
4426
4427     ++cbMaxValueNameLen;                        /* allow for trailing '\0' */
4428
4429     hHeap = GetProcessHeap ();
4430     if (hHeap == NULL)
4431     {
4432         ERR ("GetProcessHeap failed\n");
4433         r = RegCloseKey (hkSubKey);
4434         if (r != ERROR_SUCCESS)
4435             WARN ("RegCloseKey returned %li\n", r);
4436         return ERROR_OUTOFMEMORY;
4437     }
4438
4439     lpValueName = HeapAlloc (hHeap, 0, cbMaxValueNameLen * sizeof (WCHAR));
4440     if (lpValueName == NULL)
4441     {
4442         ERR ("Failed to allocate %li bytes from process heap\n",
4443                 cbMaxValueNameLen * sizeof (WCHAR));
4444         r = RegCloseKey (hkSubKey);
4445         if (r != ERROR_SUCCESS)
4446             WARN ("RegCloseKey returned %li\n", r);
4447         return ERROR_OUTOFMEMORY;
4448     }
4449
4450     lpValue = HeapAlloc (hHeap, 0, cbMaxValueLen);
4451     if (lpValue == NULL)
4452     {
4453         ERR ("Failed to allocate %li bytes from process heap\n", cbMaxValueLen);
4454         if (HeapFree (hHeap, 0, lpValueName) == 0)
4455             WARN ("HeapFree failed with code %li\n", GetLastError ());
4456         r = RegCloseKey (hkSubKey);
4457         if (r != ERROR_SUCCESS)
4458             WARN ("RegCloseKey returned %li\n", r);
4459         return ERROR_OUTOFMEMORY;
4460     }
4461
4462     TRACE ("pass 1: calculating buffer required for all names and values\n");
4463
4464     cbBufSize = cValues * sizeof (PRINTER_ENUM_VALUESW);
4465
4466     TRACE ("%li bytes required for %li headers\n", cbBufSize, cValues);
4467
4468     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
4469     {
4470         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
4471         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
4472                 NULL, NULL, lpValue, &cbValueLen);
4473         if (ret != ERROR_SUCCESS)
4474         {
4475             if (HeapFree (hHeap, 0, lpValue) == 0)
4476                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4477             if (HeapFree (hHeap, 0, lpValueName) == 0)
4478                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4479             r = RegCloseKey (hkSubKey);
4480             if (r != ERROR_SUCCESS)
4481                 WARN ("RegCloseKey returned %li\n", r);
4482             TRACE ("RegEnumValueW (%li) returned %li\n", dwIndex, ret);
4483             return ret;
4484         }
4485
4486         TRACE ("%s [%li]: name needs %li bytes, data needs %li bytes\n",
4487                 debugstr_w (lpValueName), dwIndex,
4488                 (cbValueNameLen + 1) * sizeof (WCHAR), cbValueLen);
4489
4490         cbBufSize += (cbValueNameLen + 1) * sizeof (WCHAR);
4491         cbBufSize += cbValueLen;
4492     }
4493
4494     TRACE ("%li bytes required for all %li values\n", cbBufSize, cValues);
4495
4496     *pcbEnumValues = cbBufSize;
4497     *pnEnumValues = cValues;
4498
4499     if (cbEnumValues < cbBufSize)       /* buffer too small */
4500     {
4501         if (HeapFree (hHeap, 0, lpValue) == 0)
4502             WARN ("HeapFree failed with code %li\n", GetLastError ());
4503         if (HeapFree (hHeap, 0, lpValueName) == 0)
4504             WARN ("HeapFree failed with code %li\n", GetLastError ());
4505         r = RegCloseKey (hkSubKey);
4506         if (r != ERROR_SUCCESS)
4507             WARN ("RegCloseKey returned %li\n", r);
4508         TRACE ("%li byte buffer is not large enough\n", cbEnumValues);
4509         return ERROR_MORE_DATA;
4510     }
4511
4512     TRACE ("pass 2: copying all names and values to buffer\n");
4513
4514     ppev = (PPRINTER_ENUM_VALUESW) pEnumValues;         /* array of structs */
4515     pEnumValues += cValues * sizeof (PRINTER_ENUM_VALUESW);
4516
4517     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
4518     {
4519         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
4520         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
4521                 NULL, &dwType, lpValue, &cbValueLen);
4522         if (ret != ERROR_SUCCESS)
4523         {
4524             if (HeapFree (hHeap, 0, lpValue) == 0)
4525                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4526             if (HeapFree (hHeap, 0, lpValueName) == 0)
4527                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4528             r = RegCloseKey (hkSubKey);
4529             if (r != ERROR_SUCCESS)
4530                 WARN ("RegCloseKey returned %li\n", r);
4531             TRACE ("RegEnumValueW (%li) returned %li\n", dwIndex, ret);
4532             return ret;
4533         }
4534
4535         cbValueNameLen = (cbValueNameLen + 1) * sizeof (WCHAR);
4536         memcpy (pEnumValues, lpValueName, cbValueNameLen);
4537         ppev[dwIndex].pValueName = (LPWSTR) pEnumValues;
4538         pEnumValues += cbValueNameLen;
4539
4540         /* return # of *bytes* (including trailing \0), not # of chars */
4541         ppev[dwIndex].cbValueName = cbValueNameLen;
4542
4543         ppev[dwIndex].dwType = dwType;
4544
4545         memcpy (pEnumValues, lpValue, cbValueLen);
4546         ppev[dwIndex].pData = pEnumValues;
4547         pEnumValues += cbValueLen;
4548
4549         ppev[dwIndex].cbData = cbValueLen;
4550
4551         TRACE ("%s [%li]: copied name (%li bytes) and data (%li bytes)\n",
4552                 debugstr_w (lpValueName), dwIndex, cbValueNameLen, cbValueLen);
4553     }
4554
4555     if (HeapFree (hHeap, 0, lpValue) == 0)
4556     {
4557         ret = GetLastError ();
4558         ERR ("HeapFree failed with code %li\n", ret);
4559         if (HeapFree (hHeap, 0, lpValueName) == 0)
4560             WARN ("HeapFree failed with code %li\n", GetLastError ());
4561         r = RegCloseKey (hkSubKey);
4562         if (r != ERROR_SUCCESS)
4563             WARN ("RegCloseKey returned %li\n", r);
4564         return ret;
4565     }
4566
4567     if (HeapFree (hHeap, 0, lpValueName) == 0)
4568     {
4569         ret = GetLastError ();
4570         ERR ("HeapFree failed with code %li\n", ret);
4571         r = RegCloseKey (hkSubKey);
4572         if (r != ERROR_SUCCESS)
4573             WARN ("RegCloseKey returned %li\n", r);
4574         return ret;
4575     }
4576
4577     ret = RegCloseKey (hkSubKey);
4578     if (ret != ERROR_SUCCESS)
4579     {
4580         ERR ("RegCloseKey returned %li\n", ret);
4581         return ret;
4582     }
4583
4584     return ERROR_SUCCESS;
4585 }
4586
4587 /*******************************************************************************
4588  *              EnumPrinterDataExA      [WINSPOOL.@]
4589  *
4590  * This functions returns value names and REG_SZ, REG_EXPAND_SZ, and
4591  * REG_MULTI_SZ values as ASCII strings in Unicode-sized buffers.  This is
4592  * what Windows 2000 SP1 does.
4593  *
4594  */
4595 DWORD WINAPI EnumPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
4596                                 LPBYTE pEnumValues, DWORD cbEnumValues,
4597                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
4598 {
4599     INT     len;
4600     LPWSTR  pKeyNameW;
4601     DWORD   ret, dwIndex, dwBufSize;
4602     HANDLE  hHeap;
4603     LPSTR   pBuffer;
4604
4605     TRACE ("%p %s\n", hPrinter, pKeyName);
4606
4607     if (pKeyName == NULL || *pKeyName == 0)
4608         return ERROR_INVALID_PARAMETER;
4609
4610     len = MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, NULL, 0);
4611     if (len == 0)
4612     {
4613         ret = GetLastError ();
4614         ERR ("MultiByteToWideChar failed with code %li\n", ret);
4615         return ret;
4616     }
4617
4618     hHeap = GetProcessHeap ();
4619     if (hHeap == NULL)
4620     {
4621         ERR ("GetProcessHeap failed\n");
4622         return ERROR_OUTOFMEMORY;
4623     }
4624
4625     pKeyNameW = HeapAlloc (hHeap, 0, len * sizeof (WCHAR));
4626     if (pKeyNameW == NULL)
4627     {
4628         ERR ("Failed to allocate %li bytes from process heap\n",
4629                 (LONG) len * sizeof (WCHAR));
4630         return ERROR_OUTOFMEMORY;
4631     }
4632
4633     if (MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, pKeyNameW, len) == 0)
4634     {
4635         ret = GetLastError ();
4636         ERR ("MultiByteToWideChar failed with code %li\n", ret);
4637         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
4638             WARN ("HeapFree failed with code %li\n", GetLastError ());
4639         return ret;
4640     }
4641
4642     ret = EnumPrinterDataExW (hPrinter, pKeyNameW, pEnumValues, cbEnumValues,
4643             pcbEnumValues, pnEnumValues);
4644     if (ret != ERROR_SUCCESS)
4645     {
4646         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
4647             WARN ("HeapFree failed with code %li\n", GetLastError ());
4648         TRACE ("EnumPrinterDataExW returned %li\n", ret);
4649         return ret;
4650     }
4651
4652     if (HeapFree (hHeap, 0, pKeyNameW) == 0)
4653     {
4654         ret = GetLastError ();
4655         ERR ("HeapFree failed with code %li\n", ret);
4656         return ret;
4657     }
4658
4659     if (*pnEnumValues == 0)     /* empty key */
4660         return ERROR_SUCCESS;
4661
4662     dwBufSize = 0;
4663     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
4664     {
4665         PPRINTER_ENUM_VALUESW ppev =
4666                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
4667
4668         if (dwBufSize < ppev->cbValueName)
4669             dwBufSize = ppev->cbValueName;
4670
4671         if (dwBufSize < ppev->cbData && (ppev->dwType == REG_SZ ||
4672                 ppev->dwType == REG_EXPAND_SZ || ppev->dwType == REG_MULTI_SZ))
4673             dwBufSize = ppev->cbData;
4674     }
4675
4676     TRACE ("Largest Unicode name or value is %li bytes\n", dwBufSize);
4677
4678     pBuffer = HeapAlloc (hHeap, 0, dwBufSize);
4679     if (pBuffer == NULL)
4680     {
4681         ERR ("Failed to allocate %li bytes from process heap\n", dwBufSize);
4682         return ERROR_OUTOFMEMORY;
4683     }
4684
4685     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
4686     {
4687         PPRINTER_ENUM_VALUESW ppev =
4688                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
4689
4690         len = WideCharToMultiByte (CP_ACP, 0, ppev->pValueName,
4691                 ppev->cbValueName / sizeof (WCHAR), pBuffer, dwBufSize, NULL,
4692                 NULL);
4693         if (len == 0)
4694         {
4695             ret = GetLastError ();
4696             ERR ("WideCharToMultiByte failed with code %li\n", ret);
4697             if (HeapFree (hHeap, 0, pBuffer) == 0)
4698                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4699             return ret;
4700         }
4701
4702         memcpy (ppev->pValueName, pBuffer, len);
4703
4704         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
4705
4706         if (ppev->dwType != REG_SZ && ppev->dwType != REG_EXPAND_SZ &&
4707                 ppev->dwType != REG_MULTI_SZ)
4708             continue;
4709
4710         len = WideCharToMultiByte (CP_ACP, 0, (LPWSTR) ppev->pData,
4711                 ppev->cbData / sizeof (WCHAR), pBuffer, dwBufSize, NULL, NULL);
4712         if (len == 0)
4713         {
4714             ret = GetLastError ();
4715             ERR ("WideCharToMultiByte failed with code %li\n", ret);
4716             if (HeapFree (hHeap, 0, pBuffer) == 0)
4717                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4718             return ret;
4719         }
4720
4721         memcpy (ppev->pData, pBuffer, len);
4722
4723         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
4724         TRACE ("  (only first string of REG_MULTI_SZ printed)\n");
4725     }
4726
4727     if (HeapFree (hHeap, 0, pBuffer) == 0)
4728     {
4729         ret = GetLastError ();
4730         ERR ("HeapFree failed with code %li\n", ret);
4731         return ret;
4732     }
4733
4734     return ERROR_SUCCESS;
4735 }
4736
4737 /******************************************************************************
4738  *      AbortPrinter (WINSPOOL.@)
4739  */
4740 BOOL WINAPI AbortPrinter( HANDLE hPrinter )
4741 {
4742     FIXME("(%p), stub!\n", hPrinter);
4743     return TRUE;
4744 }
4745
4746 /******************************************************************************
4747  *              AddPortA (WINSPOOL.@)
4748  *
4749  * See AddPortW.
4750  *
4751  */
4752 BOOL WINAPI AddPortA(LPSTR pName, HWND hWnd, LPSTR pMonitorName)
4753 {
4754     FIXME("(%s, %p, %s), stub!\n",debugstr_a(pName),hWnd,debugstr_a(pMonitorName));
4755     return FALSE;
4756 }
4757
4758 /******************************************************************************
4759  *      AddPortW (WINSPOOL.@)
4760  *
4761  * Add a Port for a specific Monitor
4762  *
4763  * PARAMS
4764  *  pName        [I] Servername or NULL (local Computer)
4765  *  hWnd         [I] Handle to parent Window for the Dialog-Box
4766  *  pMonitorName [I] Name of the Monitor that manage the Port
4767  *
4768  * RETURNS
4769  *  Success: TRUE
4770  *  Failure: FALSE
4771  *
4772  * BUGS
4773  *  only a Stub
4774  *
4775  */
4776 BOOL WINAPI AddPortW(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
4777 {
4778     FIXME("(%s, %p, %s), stub!\n",debugstr_w(pName),hWnd,debugstr_w(pMonitorName));
4779     return FALSE;
4780 }
4781
4782 /******************************************************************************
4783  *             AddPortExA (WINSPOOL.@)
4784  *
4785  * See AddPortExW.
4786  *
4787  */
4788 BOOL WINAPI AddPortExA(HANDLE hMonitor, LPSTR pName, DWORD Level, LPBYTE lpBuffer, LPSTR lpMonitorName)
4789 {
4790     FIXME("(%p, %s, %ld, %p, %s), stub!\n",hMonitor, debugstr_a(pName), Level,
4791           lpBuffer, debugstr_a(lpMonitorName));
4792     return FALSE;
4793 }
4794
4795 /******************************************************************************
4796  *             AddPortExW (WINSPOOL.@)
4797  *
4798  * Add a Port for a specific Monitor, without presenting a user interface
4799  *
4800  * PARAMS
4801  *  hMonitor      [I] Handle from InitializePrintMonitor2()
4802  *  pName         [I] Servername or NULL (local Computer)
4803  *  Level         [I] Structure-Level (1 or 2) for lpBuffer
4804  *  lpBuffer      [I] PTR to: PORT_INFO_1 or PORT_INFO_2
4805  *  lpMonitorName [I] Name of the Monitor that manage the Port or NULL
4806  *
4807  * RETURNS
4808  *  Success: TRUE
4809  *  Failure: FALSE
4810  *
4811  * BUGS
4812  *  only a Stub
4813  *
4814  */
4815 BOOL WINAPI AddPortExW(HANDLE hMonitor, LPWSTR pName, DWORD Level, LPBYTE lpBuffer, LPWSTR lpMonitorName)
4816 {
4817     FIXME("(%p, %s, %ld, %p, %s), stub!\n", hMonitor, debugstr_w(pName), Level,
4818           lpBuffer, debugstr_w(lpMonitorName));
4819     return FALSE;
4820 }
4821
4822 /******************************************************************************
4823  *      AddPrinterConnectionA (WINSPOOL.@)
4824  */
4825 BOOL WINAPI AddPrinterConnectionA( LPSTR pName )
4826 {
4827     FIXME("%s\n", debugstr_a(pName));
4828     return FALSE;
4829 }
4830
4831 /******************************************************************************
4832  *      AddPrinterConnectionW (WINSPOOL.@)
4833  */
4834 BOOL WINAPI AddPrinterConnectionW( LPWSTR pName )
4835 {
4836     FIXME("%s\n", debugstr_w(pName));
4837     return FALSE;
4838 }
4839
4840 /******************************************************************************
4841  *              AddPrinterDriverExW (WINSPOOL.@)
4842  */
4843 BOOL WINAPI AddPrinterDriverExW( LPWSTR pName, DWORD Level,
4844     LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
4845 {
4846     FIXME("%s %ld %p %ld\n", debugstr_w(pName),
4847            Level, pDriverInfo, dwFileCopyFlags);
4848     SetLastError(ERROR_PRINTER_DRIVER_BLOCKED);
4849     return FALSE;
4850 }
4851
4852 /******************************************************************************
4853  *              AddPrinterDriverExA (WINSPOOL.@)
4854  */
4855 BOOL WINAPI AddPrinterDriverExA( LPSTR pName, DWORD Level,
4856     LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
4857 {
4858     FIXME("%s %ld %p %ld\n", debugstr_a(pName),
4859            Level, pDriverInfo, dwFileCopyFlags);
4860     SetLastError(ERROR_PRINTER_DRIVER_BLOCKED);
4861     return FALSE;
4862 }
4863
4864 /******************************************************************************
4865  *      ConfigurePortA (WINSPOOL.@)
4866  *
4867  * See ConfigurePortW.
4868  *
4869  */
4870 BOOL WINAPI ConfigurePortA(LPSTR pName, HWND hWnd, LPSTR pPortName)
4871 {
4872     FIXME("%s %p %s\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
4873     return FALSE;
4874 }
4875
4876 /******************************************************************************
4877  *      ConfigurePortW (WINSPOOL.@)
4878  *
4879  * Display the Configuration-Dialog for a specific Port
4880  *
4881  * PARAMS
4882  *  pName     [I] Servername or NULL (local Computer)
4883  *  hWnd      [I] Handle to parent Window for the Dialog-Box
4884  *  pPortName [I] Name of the Port, that should be configured
4885  *
4886  * RETURNS
4887  *  Success: TRUE
4888  *  Failure: FALSE
4889  *
4890  * BUGS
4891  *  only a Stub
4892  *
4893  */
4894 BOOL WINAPI ConfigurePortW(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
4895 {
4896     FIXME("%s %p %s\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
4897     return FALSE;
4898 }
4899
4900 /******************************************************************************
4901  *      ConnectToPrinterDlg (WINSPOOL.@)
4902  */
4903 HANDLE WINAPI ConnectToPrinterDlg( HWND hWnd, DWORD Flags )
4904 {
4905     FIXME("%p %lx\n", hWnd, Flags);
4906     return NULL;
4907 }
4908
4909 /******************************************************************************
4910  *      DeletePrinterConnectionA (WINSPOOL.@)
4911  */
4912 BOOL WINAPI DeletePrinterConnectionA( LPSTR pName )
4913 {
4914     FIXME("%s\n", debugstr_a(pName));
4915     return TRUE;
4916 }
4917
4918 /******************************************************************************
4919  *      DeletePrinterConnectionW (WINSPOOL.@)
4920  */
4921 BOOL WINAPI DeletePrinterConnectionW( LPWSTR pName )
4922 {
4923     FIXME("%s\n", debugstr_w(pName));
4924     return TRUE;
4925 }
4926
4927 /******************************************************************************
4928  *              DeletePrinterDriverExW (WINSPOOL.@)
4929  */
4930 BOOL WINAPI DeletePrinterDriverExW( LPWSTR pName, LPWSTR pEnvironment,
4931     LPWSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
4932 {
4933     FIXME("%s %s %s %lx %lx\n", debugstr_w(pName), debugstr_w(pEnvironment),
4934           debugstr_w(pDriverName), dwDeleteFlag, dwVersionFlag);
4935     return TRUE;
4936 }
4937
4938 /******************************************************************************
4939  *              DeletePrinterDriverExA (WINSPOOL.@)
4940  */
4941 BOOL WINAPI DeletePrinterDriverExA( LPSTR pName, LPSTR pEnvironment,
4942     LPSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
4943 {
4944     FIXME("%s %s %s %lx %lx\n", debugstr_a(pName), debugstr_a(pEnvironment),
4945           debugstr_a(pDriverName), dwDeleteFlag, dwVersionFlag);
4946     return TRUE;
4947 }
4948
4949 /******************************************************************************
4950  *              DeletePrinterDataExW (WINSPOOL.@)
4951  */
4952 DWORD WINAPI DeletePrinterDataExW( HANDLE hPrinter, LPCWSTR pKeyName,
4953                                   LPCWSTR pValueName)
4954 {
4955     FIXME("%p %s %s\n", hPrinter, 
4956           debugstr_w(pKeyName), debugstr_w(pValueName));
4957     return ERROR_INVALID_PARAMETER;
4958 }
4959
4960 /******************************************************************************
4961  *              DeletePrinterDataExA (WINSPOOL.@)
4962  */
4963 DWORD WINAPI DeletePrinterDataExA( HANDLE hPrinter, LPCSTR pKeyName,
4964                                   LPCSTR pValueName)
4965 {
4966     FIXME("%p %s %s\n", hPrinter, 
4967           debugstr_a(pKeyName), debugstr_a(pValueName));
4968     return ERROR_INVALID_PARAMETER;
4969 }
4970
4971 /******************************************************************************
4972  *      DeletePrintProcessorA (WINSPOOL.@)
4973  */
4974 BOOL WINAPI DeletePrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProcessorName)
4975 {
4976     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
4977           debugstr_a(pPrintProcessorName));
4978     return TRUE;
4979 }
4980
4981 /******************************************************************************
4982  *      DeletePrintProcessorW (WINSPOOL.@)
4983  */
4984 BOOL WINAPI DeletePrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProcessorName)
4985 {
4986     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
4987           debugstr_w(pPrintProcessorName));
4988     return TRUE;
4989 }
4990
4991 /******************************************************************************
4992  *      DeletePrintProvidorA (WINSPOOL.@)
4993  */
4994 BOOL WINAPI DeletePrintProvidorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProviderName)
4995 {
4996     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
4997           debugstr_a(pPrintProviderName));
4998     return TRUE;
4999 }
5000
5001 /******************************************************************************
5002  *      DeletePrintProvidorW (WINSPOOL.@)
5003  */
5004 BOOL WINAPI DeletePrintProvidorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProviderName)
5005 {
5006     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
5007           debugstr_w(pPrintProviderName));
5008     return TRUE;
5009 }
5010
5011 /******************************************************************************
5012  *      EnumFormsA (WINSPOOL.@)
5013  */
5014 BOOL WINAPI EnumFormsA( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
5015     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
5016 {
5017     FIXME("%p %lx %p %lx %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
5018     return FALSE;
5019 }
5020
5021 /******************************************************************************
5022  *      EnumFormsW (WINSPOOL.@)
5023  */
5024 BOOL WINAPI EnumFormsW( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
5025     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
5026 {
5027     FIXME("%p %lx %p %lx %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
5028     return FALSE;
5029 }
5030
5031 /*****************************************************************************
5032  *          EnumMonitorsA [WINSPOOL.@]
5033  *
5034  * See EnumMonitorsW.
5035  *
5036  */
5037 BOOL WINAPI EnumMonitorsA(LPSTR pName, DWORD Level, LPBYTE pMonitors,
5038                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
5039 {
5040     FIXME("%s,%ld,%p,%ld,%p,%p\n", debugstr_a(pName), Level, pMonitors,
5041           cbBuf, pcbNeeded, pcReturned);
5042     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5043     return 0;
5044 }
5045
5046 /*****************************************************************************
5047  *          EnumMonitorsW [WINSPOOL.@]
5048  *
5049  * Enumerate available Monitors
5050  *
5051  * PARAMS
5052  *  pName       [I] Servername or NULL (local Computer)
5053  *  Level       [I] Structure-Level (1:Win9x+NT or 2:NT only)
5054  *  pMonitors   [O] PTR to Buffer that receives the Result
5055  *  cbBuf       [I] Size of Buffer at pMonitors
5056  *  pcbNeeded   [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
5057  *  pcReturned  [O] PTR to DWORD that receives the number of Monitors in pMonitors
5058  *
5059  * RETURNS
5060  *  Success: TRUE
5061  *  Failure: FALSE and in bufneeded the Bytes required for buffer, if bufsize is too small
5062  *
5063  * BUGS
5064  *  only a Stub
5065  *
5066  */
5067 BOOL WINAPI EnumMonitorsW(LPWSTR pName, DWORD Level, LPBYTE pMonitors,
5068                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
5069 {
5070     FIXME("%s,%ld,%p,%ld,%p,%p\n", debugstr_w(pName), Level, pMonitors,
5071           cbBuf, pcbNeeded, pcReturned);
5072     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5073     return 0;
5074 }
5075
5076 /******************************************************************************
5077  *              XcvDataW (WINSPOOL.@)
5078  *
5079  * Notes:
5080  *  There doesn't seem to be an A version...
5081  */
5082 BOOL WINAPI XcvDataW( HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData,
5083     DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData,
5084     PDWORD pcbOutputNeeded, PDWORD pdwStatus)
5085 {
5086     FIXME("%p %s %p %ld %p %ld %p %p\n", hXcv, debugstr_w(pszDataName), 
5087           pInputData, cbInputData, pOutputData,
5088           cbOutputData, pcbOutputNeeded, pdwStatus);
5089     return FALSE;
5090 }
5091
5092 /*****************************************************************************
5093  *          EnumPrinterDataA [WINSPOOL.@]
5094  *
5095  */
5096 DWORD WINAPI EnumPrinterDataA( HANDLE hPrinter, DWORD dwIndex, LPSTR pValueName,
5097     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
5098     DWORD cbData, LPDWORD pcbData )
5099 {
5100     FIXME("%p %lx %p %lx %p %p %p %lx %p\n", hPrinter, dwIndex, pValueName,
5101           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
5102     return ERROR_NO_MORE_ITEMS;
5103 }
5104
5105 /*****************************************************************************
5106  *          EnumPrinterDataW [WINSPOOL.@]
5107  *
5108  */
5109 DWORD WINAPI EnumPrinterDataW( HANDLE hPrinter, DWORD dwIndex, LPWSTR pValueName,
5110     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
5111     DWORD cbData, LPDWORD pcbData )
5112 {
5113     FIXME("%p %lx %p %lx %p %p %p %lx %p\n", hPrinter, dwIndex, pValueName,
5114           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
5115     return ERROR_NO_MORE_ITEMS;
5116 }
5117
5118 /*****************************************************************************
5119  *          EnumPrintProcessorDatatypesA [WINSPOOL.@]
5120  *
5121  */
5122 BOOL WINAPI EnumPrintProcessorDatatypesA(LPSTR pName, LPSTR pPrintProcessorName,
5123                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
5124                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
5125 {
5126     FIXME("Stub: %s %s %ld %p %ld %p %p\n", debugstr_a(pName),
5127           debugstr_a(pPrintProcessorName), Level, pDatatypes, cbBuf,
5128           pcbNeeded, pcReturned);
5129     return FALSE;
5130 }
5131
5132 /*****************************************************************************
5133  *          EnumPrintProcessorDatatypesW [WINSPOOL.@]
5134  *
5135  */
5136 BOOL WINAPI EnumPrintProcessorDatatypesW(LPWSTR pName, LPWSTR pPrintProcessorName,
5137                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
5138                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
5139 {
5140     FIXME("Stub: %s %s %ld %p %ld %p %p\n", debugstr_w(pName),
5141           debugstr_w(pPrintProcessorName), Level, pDatatypes, cbBuf,
5142           pcbNeeded, pcReturned);
5143     return FALSE;
5144 }
5145
5146 /*****************************************************************************
5147  *          EnumPrintProcessorsA [WINSPOOL.@]
5148  *
5149  */
5150 BOOL WINAPI EnumPrintProcessorsA(LPSTR pName, LPSTR pEnvironment, DWORD Level, 
5151     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
5152 {
5153     FIXME("Stub: %s %s %ld %p %ld %p %p\n", pName, pEnvironment, Level,
5154         pPrintProcessorInfo, cbBuf, pcbNeeded, pcbReturned);
5155     return FALSE;
5156 }
5157
5158 /*****************************************************************************
5159  *          EnumPrintProcessorsW [WINSPOOL.@]
5160  *
5161  */
5162 BOOL WINAPI EnumPrintProcessorsW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
5163     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
5164 {
5165     FIXME("Stub: %s %s %ld %p %ld %p %p\n", debugstr_w(pName),
5166         debugstr_w(pEnvironment), Level, pPrintProcessorInfo,
5167         cbBuf, pcbNeeded, pcbReturned);
5168     return FALSE;
5169 }
5170
5171 /*****************************************************************************
5172  *          ExtDeviceMode [WINSPOOL.@]
5173  *
5174  */
5175 LONG WINAPI ExtDeviceMode( HWND hWnd, HANDLE hInst, LPDEVMODEA pDevModeOutput,
5176     LPSTR pDeviceName, LPSTR pPort, LPDEVMODEA pDevModeInput, LPSTR pProfile,
5177     DWORD fMode)
5178 {
5179     FIXME("Stub: %p %p %p %s %s %p %s %lx\n", hWnd, hInst, pDevModeOutput,
5180           debugstr_a(pDeviceName), debugstr_a(pPort), pDevModeInput,
5181           debugstr_a(pProfile), fMode);
5182     return -1;
5183 }
5184
5185 /*****************************************************************************
5186  *          FindClosePrinterChangeNotification [WINSPOOL.@]
5187  *
5188  */
5189 BOOL WINAPI FindClosePrinterChangeNotification( HANDLE hChange )
5190 {
5191     FIXME("Stub: %p\n", hChange);
5192     return TRUE;
5193 }
5194
5195 /*****************************************************************************
5196  *          FindFirstPrinterChangeNotification [WINSPOOL.@]
5197  *
5198  */
5199 HANDLE WINAPI FindFirstPrinterChangeNotification( HANDLE hPrinter,
5200     DWORD fdwFlags, DWORD fdwOptions, LPVOID pPrinterNotifyOptions )
5201 {
5202     FIXME("Stub: %p %lx %lx %p\n",
5203           hPrinter, fdwFlags, fdwOptions, pPrinterNotifyOptions);
5204     return INVALID_HANDLE_VALUE;
5205 }
5206
5207 /*****************************************************************************
5208  *          FindNextPrinterChangeNotification [WINSPOOL.@]
5209  *
5210  */
5211 BOOL WINAPI FindNextPrinterChangeNotification( HANDLE hChange, PDWORD pdwChange,
5212     LPVOID pPrinterNotifyOptions, LPVOID *ppPrinterNotifyInfo )
5213 {
5214     FIXME("Stub: %p %p %p %p\n",
5215           hChange, pdwChange, pPrinterNotifyOptions, ppPrinterNotifyInfo);
5216     return FALSE;
5217 }
5218
5219 /*****************************************************************************
5220  *          FreePrinterNotifyInfo [WINSPOOL.@]
5221  *
5222  */
5223 BOOL WINAPI FreePrinterNotifyInfo( PPRINTER_NOTIFY_INFO pPrinterNotifyInfo )
5224 {
5225     FIXME("Stub: %p\n", pPrinterNotifyInfo);
5226     return TRUE;
5227 }
5228
5229 /*****************************************************************************
5230  *          string_to_buf
5231  *
5232  * Copies a unicode string into a buffer.  The buffer will either contain unicode or
5233  * ansi depending on the unicode parameter.
5234  */
5235 static BOOL string_to_buf(LPCWSTR str, LPBYTE ptr, DWORD cb, DWORD *size, BOOL unicode)
5236 {
5237     if(!str)
5238     {
5239         *size = 0;
5240         return TRUE;
5241     }
5242
5243     if(unicode)
5244     {
5245         *size = (strlenW(str) + 1) * sizeof(WCHAR);
5246         if(*size <= cb)
5247         {
5248             memcpy(ptr, str, *size);
5249             return TRUE;
5250         }
5251         return FALSE;
5252     }
5253     else
5254     {
5255         *size = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
5256         if(*size <= cb)
5257         {
5258             WideCharToMultiByte(CP_ACP, 0, str, -1, (LPSTR)ptr, *size, NULL, NULL);
5259             return TRUE;
5260         }
5261         return FALSE;
5262     }
5263 }
5264
5265 /*****************************************************************************
5266  *          get_job_info_1
5267  */
5268 static BOOL get_job_info_1(job_t *job, JOB_INFO_1W *ji1, LPBYTE buf, DWORD cbBuf,
5269                            LPDWORD pcbNeeded, BOOL unicode)
5270 {
5271     DWORD size, left = cbBuf;
5272     BOOL space = (cbBuf > 0);
5273     LPBYTE ptr = buf;
5274
5275     *pcbNeeded = 0;
5276
5277     if(space)
5278     {
5279         ji1->JobId = job->job_id;
5280     }
5281
5282     string_to_buf(job->document_title, ptr, left, &size, unicode);
5283     if(space && size <= left)
5284     {
5285         ji1->pDocument = (LPWSTR)ptr;
5286         ptr += size;
5287         left -= size;
5288     }
5289     else
5290         space = FALSE;
5291     *pcbNeeded += size;
5292
5293     return space;
5294 }
5295
5296 /*****************************************************************************
5297  *          get_job_info_2
5298  */
5299 static BOOL get_job_info_2(job_t *job, JOB_INFO_2W *ji2, LPBYTE buf, DWORD cbBuf,
5300                            LPDWORD pcbNeeded, BOOL unicode)
5301 {
5302     DWORD size, left = cbBuf;
5303     BOOL space = (cbBuf > 0);
5304     LPBYTE ptr = buf;
5305
5306     *pcbNeeded = 0;
5307
5308     if(space)
5309     {
5310         ji2->JobId = job->job_id;
5311     }
5312
5313     string_to_buf(job->document_title, ptr, left, &size, unicode);
5314     if(space && size <= left)
5315     {
5316         ji2->pDocument = (LPWSTR)ptr;
5317         ptr += size;
5318         left -= size;
5319     }
5320     else
5321         space = FALSE;
5322     *pcbNeeded += size;
5323
5324     return space;
5325 }
5326
5327 /*****************************************************************************
5328  *          get_job_info
5329  */
5330 static BOOL get_job_info(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
5331                          DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
5332 {
5333     BOOL ret = FALSE;
5334     DWORD needed = 0, size;
5335     job_t *job;
5336     LPBYTE ptr = pJob;
5337
5338     TRACE("%p %ld %ld %p %ld %p\n", hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
5339
5340     EnterCriticalSection(&printer_handles_cs);
5341     job = get_job(hPrinter, JobId);
5342     if(!job)
5343         goto end;
5344
5345     switch(Level)
5346     {
5347     case 1:
5348         size = sizeof(JOB_INFO_1W);
5349         if(cbBuf >= size)
5350         {
5351             cbBuf -= size;
5352             ptr += size;
5353             memset(pJob, 0, size);
5354         }
5355         else
5356             cbBuf = 0;
5357         ret = get_job_info_1(job, (JOB_INFO_1W *)pJob, ptr, cbBuf, &needed, unicode);
5358         needed += size;
5359         break;
5360
5361     case 2:
5362         size = sizeof(JOB_INFO_2W);
5363         if(cbBuf >= size)
5364         {
5365             cbBuf -= size;
5366             ptr += size;
5367             memset(pJob, 0, size);
5368         }
5369         else
5370             cbBuf = 0;
5371         ret = get_job_info_2(job, (JOB_INFO_2W *)pJob, ptr, cbBuf, &needed, unicode);
5372         needed += size;
5373         break;
5374
5375     case 3:
5376         size = sizeof(JOB_INFO_3);
5377         if(cbBuf >= size)
5378         {
5379             cbBuf -= size;
5380             memset(pJob, 0, size);
5381             ret = TRUE;
5382         }
5383         else
5384             cbBuf = 0;
5385         needed = size;
5386         break;
5387
5388     default:
5389         SetLastError(ERROR_INVALID_LEVEL);
5390         goto end;
5391     }
5392     if(pcbNeeded)
5393         *pcbNeeded = needed;
5394 end:
5395     LeaveCriticalSection(&printer_handles_cs);
5396     return ret;
5397 }
5398
5399 /*****************************************************************************
5400  *          GetJobA [WINSPOOL.@]
5401  *
5402  */
5403 BOOL WINAPI GetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
5404                     DWORD cbBuf, LPDWORD pcbNeeded)
5405 {
5406     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, FALSE);
5407 }
5408
5409 /*****************************************************************************
5410  *          GetJobW [WINSPOOL.@]
5411  *
5412  */
5413 BOOL WINAPI GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
5414                     DWORD cbBuf, LPDWORD pcbNeeded)
5415 {
5416     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, TRUE);
5417 }
5418
5419 /*****************************************************************************
5420  *          schedule_lpr
5421  */
5422 static BOOL schedule_lpr(LPCWSTR printer_name, LPCWSTR filename)
5423 {
5424     char *unixname, *queue, *cmd;
5425     char fmt[] = "lpr -P%s %s";
5426     DWORD len;
5427
5428     if(!(unixname = wine_get_unix_file_name(filename)))
5429         return FALSE;
5430
5431     len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
5432     queue = HeapAlloc(GetProcessHeap(), 0, len);
5433     WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
5434
5435     cmd = HeapAlloc(GetProcessHeap(), 0, strlen(unixname) + len + sizeof(fmt) - 5);
5436     sprintf(cmd, fmt, queue, unixname);
5437
5438     TRACE("printing with: %s\n", cmd);
5439     system(cmd);
5440
5441     HeapFree(GetProcessHeap(), 0, cmd);
5442     HeapFree(GetProcessHeap(), 0, queue);
5443     HeapFree(GetProcessHeap(), 0, unixname);
5444     return TRUE;
5445 }
5446
5447 /*****************************************************************************
5448  *          schedule_cups
5449  */
5450 static BOOL schedule_cups(LPCWSTR printer_name, LPCWSTR filename, LPCWSTR document_title)
5451 {
5452 #if HAVE_CUPS_CUPS_H
5453     if(pcupsPrintFile)
5454     {
5455         char *unixname, *queue, *doc_titleA;
5456         DWORD len;
5457         BOOL ret;
5458
5459         if(!(unixname = wine_get_unix_file_name(filename)))
5460             return FALSE;
5461
5462         len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
5463         queue = HeapAlloc(GetProcessHeap(), 0, len);
5464         WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
5465
5466         len = WideCharToMultiByte(CP_ACP, 0, document_title, -1, NULL, 0, NULL, NULL);
5467         doc_titleA = HeapAlloc(GetProcessHeap(), 0, len);
5468         WideCharToMultiByte(CP_ACP, 0, document_title, -1, doc_titleA, len, NULL, NULL);
5469
5470         TRACE("printing via cups\n");
5471         ret = pcupsPrintFile(queue, unixname, doc_titleA, 0, NULL);
5472         HeapFree(GetProcessHeap(), 0, doc_titleA);
5473         HeapFree(GetProcessHeap(), 0, queue);
5474         HeapFree(GetProcessHeap(), 0, unixname);
5475         return ret;
5476     }
5477     else
5478 #endif
5479     {
5480         return schedule_lpr(printer_name, filename);
5481     }
5482 }
5483
5484 INT_PTR CALLBACK file_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
5485 {
5486     LPWSTR filename;
5487
5488     switch(msg)
5489     {
5490     case WM_INITDIALOG:
5491         SetWindowLongPtrW(hwnd, DWLP_USER, lparam);
5492         return TRUE;
5493
5494     case WM_COMMAND:
5495         if(HIWORD(wparam) == BN_CLICKED)
5496         {
5497             if(LOWORD(wparam) == IDOK)
5498             {
5499                 HANDLE hf;
5500                 DWORD len = SendDlgItemMessageW(hwnd, EDITBOX, WM_GETTEXTLENGTH, 0, 0);
5501                 LPWSTR *output;
5502
5503                 filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
5504                 GetDlgItemTextW(hwnd, EDITBOX, filename, len + 1);
5505
5506                 if(GetFileAttributesW(filename) != INVALID_FILE_ATTRIBUTES)
5507                 {
5508                     WCHAR caption[200], message[200];
5509                     int mb_ret;
5510
5511                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
5512                     LoadStringW(WINSPOOL_hInstance, IDS_FILE_EXISTS, message, sizeof(message) / sizeof(WCHAR));
5513                     mb_ret = MessageBoxW(hwnd, message, caption, MB_OKCANCEL | MB_ICONEXCLAMATION);
5514                     if(mb_ret == IDCANCEL)
5515                     {
5516                         HeapFree(GetProcessHeap(), 0, filename);
5517                         return TRUE;
5518                     }
5519                 }
5520                 hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
5521                 if(hf == INVALID_HANDLE_VALUE)
5522                 {
5523                     WCHAR caption[200], message[200];
5524
5525                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
5526                     LoadStringW(WINSPOOL_hInstance, IDS_CANNOT_OPEN, message, sizeof(message) / sizeof(WCHAR));
5527                     MessageBoxW(hwnd, message, caption, MB_OK | MB_ICONEXCLAMATION);
5528                     HeapFree(GetProcessHeap(), 0, filename);
5529                     return TRUE;
5530                 }
5531                 CloseHandle(hf);
5532                 DeleteFileW(filename);
5533                 output = (LPWSTR *)GetWindowLongPtrW(hwnd, DWLP_USER);
5534                 *output = filename;
5535                 EndDialog(hwnd, IDOK);
5536                 return TRUE;
5537             }
5538             if(LOWORD(wparam) == IDCANCEL)
5539             {
5540                 EndDialog(hwnd, IDCANCEL);
5541                 return TRUE;
5542             }
5543         }
5544         return FALSE;
5545     }
5546     return FALSE;
5547 }
5548
5549 /*****************************************************************************
5550  *          get_filename
5551  */
5552 static BOOL get_filename(LPWSTR *filename)
5553 {
5554     return DialogBoxParamW(WINSPOOL_hInstance, MAKEINTRESOURCEW(FILENAME_DIALOG), GetForegroundWindow(),
5555                            file_dlg_proc, (LPARAM)filename) == IDOK;
5556 }
5557
5558 /*****************************************************************************
5559  *          schedule_file
5560  */
5561 static BOOL schedule_file(LPCWSTR filename)
5562 {
5563     LPWSTR output = NULL;
5564
5565     if(get_filename(&output))
5566     {
5567         TRACE("copy to %s\n", debugstr_w(output));
5568         CopyFileW(filename, output, FALSE);
5569         HeapFree(GetProcessHeap(), 0, output);
5570         return TRUE;
5571     }
5572     return FALSE;
5573 }
5574
5575 /*****************************************************************************
5576  *          schedule_pipe
5577  */
5578 static BOOL schedule_pipe(LPCWSTR cmd, LPCWSTR filename)
5579 {
5580 #ifdef HAVE_FORK
5581     char *unixname, *cmdA;
5582     DWORD len;
5583     int fds[2] = {-1, -1}, file_fd = -1, no_read;
5584     BOOL ret = FALSE;
5585     char buf[1024];
5586
5587     if(!(unixname = wine_get_unix_file_name(filename)))
5588         return FALSE;
5589
5590     len = WideCharToMultiByte(CP_ACP, 0, cmd, -1, NULL, 0, NULL, NULL);
5591     cmdA = HeapAlloc(GetProcessHeap(), 0, len);
5592     WideCharToMultiByte(CP_ACP, 0, cmd, -1, cmdA, len, NULL, NULL);
5593
5594     TRACE("printing with: %s\n", cmdA);
5595
5596     if((file_fd = open(unixname, O_RDONLY)) == -1)
5597         goto end;
5598
5599     if (pipe(fds))
5600     {
5601         ERR("pipe() failed!\n"); 
5602         goto end;
5603     }
5604
5605     if (fork() == 0)
5606     {
5607         close(0);
5608         dup2(fds[0], 0);
5609         close(fds[1]);
5610
5611         /* reset signals that we previously set to SIG_IGN */
5612         signal(SIGPIPE, SIG_DFL);
5613         signal(SIGCHLD, SIG_DFL);
5614
5615         system(cmdA);
5616         exit(0);
5617     }
5618
5619     while((no_read = read(file_fd, buf, sizeof(buf))))
5620         write(fds[1], buf, no_read);
5621
5622     ret = TRUE;
5623
5624 end:
5625     if(file_fd != -1) close(file_fd);
5626     if(fds[0] != -1) close(fds[0]);
5627     if(fds[1] != -1) close(fds[1]);
5628
5629     HeapFree(GetProcessHeap(), 0, cmdA);
5630     HeapFree(GetProcessHeap(), 0, unixname);
5631     return ret;
5632 #else
5633     return FALSE;
5634 #endif
5635 }
5636
5637 /*****************************************************************************
5638  *          schedule_unixfile
5639  */
5640 static BOOL schedule_unixfile(LPCWSTR output, LPCWSTR filename)
5641 {
5642     int in_fd, out_fd, no_read;
5643     char buf[1024];
5644     BOOL ret = FALSE;
5645     char *unixname, *outputA;
5646     DWORD len;
5647
5648     if(!(unixname = wine_get_unix_file_name(filename)))
5649         return FALSE;
5650
5651     len = WideCharToMultiByte(CP_ACP, 0, output, -1, NULL, 0, NULL, NULL);
5652     outputA = HeapAlloc(GetProcessHeap(), 0, len);
5653     WideCharToMultiByte(CP_ACP, 0, output, -1, outputA, len, NULL, NULL);
5654     
5655     out_fd = open(outputA, O_CREAT | O_TRUNC | O_WRONLY, 0666);
5656     in_fd = open(unixname, O_RDONLY);
5657     if(out_fd == -1 || in_fd == -1)
5658         goto end;
5659
5660     while((no_read = read(in_fd, buf, sizeof(buf))))
5661         write(out_fd, buf, no_read);
5662
5663     ret = TRUE;
5664 end:
5665     if(in_fd != -1) close(in_fd);
5666     if(out_fd != -1) close(out_fd);
5667     HeapFree(GetProcessHeap(), 0, outputA);
5668     HeapFree(GetProcessHeap(), 0, unixname);
5669     return ret;
5670 }
5671
5672 /*****************************************************************************
5673  *          ScheduleJob [WINSPOOL.@]
5674  *
5675  */
5676 BOOL WINAPI ScheduleJob( HANDLE hPrinter, DWORD dwJobID )
5677 {
5678     opened_printer_t *printer;
5679     BOOL ret = FALSE;
5680     struct list *cursor, *cursor2;
5681
5682     TRACE("(%p, %lx)\n", hPrinter, dwJobID);
5683     EnterCriticalSection(&printer_handles_cs);
5684     printer = get_opened_printer(hPrinter);
5685     if(!printer)
5686         goto end;
5687
5688     LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
5689     {
5690         job_t *job = LIST_ENTRY(cursor, job_t, entry);
5691         HANDLE hf;
5692
5693         if(job->job_id != dwJobID) continue;
5694
5695         hf = CreateFileW(job->filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
5696         if(hf != INVALID_HANDLE_VALUE)
5697         {
5698             PRINTER_INFO_5W *pi5;
5699             DWORD needed;
5700             HKEY hkey;
5701             WCHAR output[1024];
5702             static const WCHAR spooler_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
5703                                                 'P','r','i','n','t','i','n','g','\\','S','p','o','o','l','e','r',0};
5704
5705             GetPrinterW(hPrinter, 5, NULL, 0, &needed);
5706             pi5 = HeapAlloc(GetProcessHeap(), 0, needed);
5707             GetPrinterW(hPrinter, 5, (LPBYTE)pi5, needed, &needed);
5708             TRACE("need to schedule job %ld filename %s to port %s\n", job->job_id, debugstr_w(job->filename),
5709                   debugstr_w(pi5->pPortName));
5710             
5711             output[0] = 0;
5712
5713             /* @@ Wine registry key: HKCU\Software\Wine\Printing\Spooler */
5714             if(RegOpenKeyW(HKEY_CURRENT_USER, spooler_key, &hkey) == ERROR_SUCCESS)
5715             {
5716                 DWORD type, count = sizeof(output);
5717                 RegQueryValueExW(hkey, pi5->pPortName, NULL, &type, (LPBYTE)output, &count);
5718                 RegCloseKey(hkey);
5719             }
5720             if(output[0] == '|')
5721             {
5722                 schedule_pipe(output + 1, job->filename);
5723             }
5724             else if(output[0])
5725             {
5726                 schedule_unixfile(output, job->filename);
5727             }
5728             else if(!strncmpW(pi5->pPortName, LPR_Port, strlenW(LPR_Port)))
5729             {
5730                 schedule_lpr(pi5->pPortName + strlenW(LPR_Port), job->filename);
5731             }
5732             else if(!strncmpW(pi5->pPortName, CUPS_Port, strlenW(CUPS_Port)))
5733             {
5734                 schedule_cups(pi5->pPortName + strlenW(CUPS_Port), job->filename, job->document_title);
5735             }
5736             else if(!strncmpW(pi5->pPortName, FILE_Port, strlenW(FILE_Port)))
5737             {
5738                 schedule_file(job->filename);
5739             }
5740             else
5741             {
5742                 FIXME("can't schedule to port %s\n", debugstr_w(pi5->pPortName));
5743             }
5744             HeapFree(GetProcessHeap(), 0, pi5);
5745             CloseHandle(hf);
5746             DeleteFileW(job->filename);
5747         }
5748         list_remove(cursor);
5749         HeapFree(GetProcessHeap(), 0, job->document_title);
5750         HeapFree(GetProcessHeap(), 0, job->filename);
5751         HeapFree(GetProcessHeap(), 0, job);
5752         ret = TRUE;
5753         break;
5754     }
5755 end:
5756     LeaveCriticalSection(&printer_handles_cs);
5757     return ret;
5758 }
5759
5760 /*****************************************************************************
5761  *          StartDocDlgA [WINSPOOL.@]
5762  */
5763  LPSTR WINAPI StartDocDlgA( HANDLE hPrinter, DOCINFOA *doc )
5764 {
5765     UNICODE_STRING usBuffer;
5766     DOCINFOW docW;
5767     LPWSTR retW;
5768     LPSTR ret = NULL;
5769
5770     docW.cbSize = sizeof(docW);
5771     docW.lpszDocName = asciitounicode(&usBuffer, doc->lpszDocName);
5772     docW.lpszOutput = asciitounicode(&usBuffer, doc->lpszOutput);
5773     docW.lpszDatatype = asciitounicode(&usBuffer, doc->lpszDatatype);
5774     docW.fwType = doc->fwType;
5775
5776     retW = StartDocDlgW(hPrinter, &docW);
5777
5778     if(retW)
5779     {
5780         DWORD len = WideCharToMultiByte(CP_ACP, 0, retW, -1, NULL, 0, NULL, NULL);
5781         ret = HeapAlloc(GetProcessHeap(), 0, len);
5782         WideCharToMultiByte(CP_ACP, 0, retW, -1, ret, len, NULL, NULL);
5783         HeapFree(GetProcessHeap(), 0, retW);
5784     }
5785
5786     HeapFree(GetProcessHeap(), 0, (LPWSTR)docW.lpszDatatype);
5787     HeapFree(GetProcessHeap(), 0, (LPWSTR)docW.lpszOutput);
5788     HeapFree(GetProcessHeap(), 0, (LPWSTR)docW.lpszDocName);
5789
5790     return ret;
5791 }
5792
5793 /*****************************************************************************
5794  *          StartDocDlgW [WINSPOOL.@]
5795  *
5796  * Undocumented: Apparently used by gdi32:StartDocW() to popup the file dialog
5797  * when lpszOutput is "FILE:" or if lpszOutput is NULL and the default printer
5798  * port is "FILE:". Also returns the full path if passed a relative path.
5799  *
5800  * The caller should free the returned string from the process heap.
5801  */
5802 LPWSTR WINAPI StartDocDlgW( HANDLE hPrinter, DOCINFOW *doc )
5803 {
5804     LPWSTR ret = NULL;
5805     DWORD len, attr;
5806
5807     if(doc->lpszOutput == NULL) /* Check whether default port is FILE: */
5808     {
5809         PRINTER_INFO_5W *pi5;
5810         GetPrinterW(hPrinter, 5, NULL, 0, &len);
5811         if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
5812             return NULL;
5813         pi5 = HeapAlloc(GetProcessHeap(), 0, len);
5814         GetPrinterW(hPrinter, 5, (LPBYTE)pi5, len, &len);
5815         if(!pi5->pPortName || strcmpW(pi5->pPortName, FILE_Port))
5816         {
5817             HeapFree(GetProcessHeap(), 0, pi5);
5818             return NULL;
5819         }
5820         HeapFree(GetProcessHeap(), 0, pi5);
5821     }
5822
5823     if(doc->lpszOutput == NULL || !strcmpW(doc->lpszOutput, FILE_Port))
5824     {
5825         LPWSTR name;
5826         get_filename(&name);
5827         if(name)
5828         {
5829             if(!(len = GetFullPathNameW(name, 0, NULL, NULL)))
5830             {
5831                 HeapFree(GetProcessHeap(), 0, name);
5832                 return NULL;
5833             }
5834             ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5835             GetFullPathNameW(name, len, ret, NULL);
5836             HeapFree(GetProcessHeap(), 0, name);
5837         }
5838         return ret;
5839     }
5840
5841     if(!(len = GetFullPathNameW(doc->lpszOutput, 0, NULL, NULL)))
5842         return NULL;
5843
5844     ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5845     GetFullPathNameW(doc->lpszOutput, len, ret, NULL);
5846         
5847     attr = GetFileAttributesW(ret);
5848     if(attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY))
5849     {
5850         HeapFree(GetProcessHeap(), 0, ret);
5851         ret = NULL;
5852     }
5853     return ret;
5854 }