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