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