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