iphlpapi: Support for non-linux platforms, including Mac OS X.
[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(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 )) {
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         /* Check for [ppd] section in config file before parsing /etc/printcap */
1656         /* @@ Wine registry key: HKCU\Software\Wine\Printing\PPD Files */
1657         if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Printing\\PPD Files",
1658                         &hkey) == ERROR_SUCCESS) {
1659             RegCloseKey(hkey);
1660             PRINTCAP_LoadPrinters();
1661         }
1662     }
1663
1664     /* Now enumerate the list again and delete any printers that a still tagged */
1665     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
1666     if(needed) {
1667         PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
1668         if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
1669             for(i = 0; i < num; i++) {
1670                 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
1671                     if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
1672                         BOOL delete_driver = FALSE;
1673                         if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
1674                             DWORD dw, type, size = sizeof(dw);
1675                             if(RegQueryValueExW(hkey, May_Delete_Value, NULL, &type, (LPBYTE)&dw, &size) == ERROR_SUCCESS) {
1676                                 TRACE("Deleting old printer %s\n", pi[i].pPrinterName);
1677                                 DeletePrinter(hprn);
1678                                 delete_driver = TRUE;
1679                             }
1680                             RegCloseKey(hkey);
1681                         }
1682                         ClosePrinter(hprn);
1683                         if(delete_driver)
1684                             DeletePrinterDriverExA(NULL, NULL, pi[i].pPrinterName, 0, 0);
1685                     }
1686                 }
1687             }
1688         }
1689         HeapFree(GetProcessHeap(), 0, pi);
1690     }
1691
1692     return;
1693
1694 }
1695
1696 /******************************************************************
1697  *                  get_job
1698  *
1699  *  Get the pointer to the specified job.
1700  *  Should hold the printer_handles_cs before calling.
1701  */
1702 static job_t *get_job(HANDLE hprn, DWORD JobId)
1703 {
1704     opened_printer_t *printer = get_opened_printer(hprn);
1705     job_t *job;
1706
1707     if(!printer) return NULL;
1708     LIST_FOR_EACH_ENTRY(job, &printer->queue->jobs, job_t, entry)
1709     {
1710         if(job->job_id == JobId)
1711             return job;
1712     }
1713     return NULL;
1714 }
1715
1716 /***********************************************************
1717  *      DEVMODEcpyAtoW
1718  */
1719 static LPDEVMODEW DEVMODEcpyAtoW(DEVMODEW *dmW, const DEVMODEA *dmA)
1720 {
1721     BOOL Formname;
1722     ptrdiff_t off_formname = (const char *)dmA->dmFormName - (const char *)dmA;
1723     DWORD size;
1724
1725     Formname = (dmA->dmSize > off_formname);
1726     size = dmA->dmSize + CCHDEVICENAME + (Formname ? CCHFORMNAME : 0);
1727     MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmDeviceName, -1,
1728                         dmW->dmDeviceName, CCHDEVICENAME);
1729     if(!Formname) {
1730       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
1731              dmA->dmSize - CCHDEVICENAME);
1732     } else {
1733       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
1734              off_formname - CCHDEVICENAME);
1735       MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmFormName, -1,
1736                           dmW->dmFormName, CCHFORMNAME);
1737       memcpy(&dmW->dmLogPixels, &dmA->dmLogPixels, dmA->dmSize -
1738              (off_formname + CCHFORMNAME));
1739     }
1740     dmW->dmSize = size;
1741     memcpy((char *)dmW + dmW->dmSize, (const char *)dmA + dmA->dmSize,
1742            dmA->dmDriverExtra);
1743     return dmW;
1744 }
1745
1746 /***********************************************************
1747  *      DEVMODEdupWtoA
1748  * Creates an ascii copy of supplied devmode on heap
1749  */
1750 static LPDEVMODEA DEVMODEdupWtoA(HANDLE heap, const DEVMODEW *dmW)
1751 {
1752     LPDEVMODEA dmA;
1753     DWORD size;
1754     BOOL Formname;
1755     ptrdiff_t off_formname = (const char *)dmW->dmFormName - (const char *)dmW;
1756
1757     if(!dmW) return NULL;
1758     Formname = (dmW->dmSize > off_formname);
1759     size = dmW->dmSize - CCHDEVICENAME - (Formname ? CCHFORMNAME : 0);
1760     dmA = HeapAlloc(heap, HEAP_ZERO_MEMORY, size + dmW->dmDriverExtra);
1761     WideCharToMultiByte(CP_ACP, 0, dmW->dmDeviceName, -1,
1762                         (LPSTR)dmA->dmDeviceName, CCHDEVICENAME, NULL, NULL);
1763     if(!Formname) {
1764       memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
1765              dmW->dmSize - CCHDEVICENAME * sizeof(WCHAR));
1766     } else {
1767       memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
1768              off_formname - CCHDEVICENAME * sizeof(WCHAR));
1769       WideCharToMultiByte(CP_ACP, 0, dmW->dmFormName, -1,
1770                           (LPSTR)dmA->dmFormName, CCHFORMNAME, NULL, NULL);
1771       memcpy(&dmA->dmLogPixels, &dmW->dmLogPixels, dmW->dmSize -
1772              (off_formname + CCHFORMNAME * sizeof(WCHAR)));
1773     }
1774     dmA->dmSize = size;
1775     memcpy((char *)dmA + dmA->dmSize, (const char *)dmW + dmW->dmSize,
1776            dmW->dmDriverExtra);
1777     return dmA;
1778 }
1779
1780 /***********************************************************
1781  *             PRINTER_INFO_2AtoW
1782  * Creates a unicode copy of PRINTER_INFO_2A on heap
1783  */
1784 static LPPRINTER_INFO_2W PRINTER_INFO_2AtoW(HANDLE heap, LPPRINTER_INFO_2A piA)
1785 {
1786     LPPRINTER_INFO_2W piW;
1787     UNICODE_STRING usBuffer;
1788
1789     if(!piA) return NULL;
1790     piW = HeapAlloc(heap, 0, sizeof(*piW));
1791     memcpy(piW, piA, sizeof(*piW)); /* copy everything first */
1792     
1793     piW->pServerName = asciitounicode(&usBuffer,piA->pServerName);
1794     piW->pPrinterName = asciitounicode(&usBuffer,piA->pPrinterName);
1795     piW->pShareName = asciitounicode(&usBuffer,piA->pShareName);
1796     piW->pPortName = asciitounicode(&usBuffer,piA->pPortName);
1797     piW->pDriverName = asciitounicode(&usBuffer,piA->pDriverName);
1798     piW->pComment = asciitounicode(&usBuffer,piA->pComment);
1799     piW->pLocation = asciitounicode(&usBuffer,piA->pLocation);
1800     piW->pDevMode = piA->pDevMode ? GdiConvertToDevmodeW(piA->pDevMode) : NULL;
1801     piW->pSepFile = asciitounicode(&usBuffer,piA->pSepFile);
1802     piW->pPrintProcessor = asciitounicode(&usBuffer,piA->pPrintProcessor);
1803     piW->pDatatype = asciitounicode(&usBuffer,piA->pDatatype);
1804     piW->pParameters = asciitounicode(&usBuffer,piA->pParameters);
1805     return piW;
1806 }
1807
1808 /***********************************************************
1809  *       FREE_PRINTER_INFO_2W
1810  * Free PRINTER_INFO_2W and all strings
1811  */
1812 static void FREE_PRINTER_INFO_2W(HANDLE heap, LPPRINTER_INFO_2W piW)
1813 {
1814     if(!piW) return;
1815
1816     HeapFree(heap,0,piW->pServerName);
1817     HeapFree(heap,0,piW->pPrinterName);
1818     HeapFree(heap,0,piW->pShareName);
1819     HeapFree(heap,0,piW->pPortName);
1820     HeapFree(heap,0,piW->pDriverName);
1821     HeapFree(heap,0,piW->pComment);
1822     HeapFree(heap,0,piW->pLocation);
1823     HeapFree(heap,0,piW->pDevMode);
1824     HeapFree(heap,0,piW->pSepFile);
1825     HeapFree(heap,0,piW->pPrintProcessor);
1826     HeapFree(heap,0,piW->pDatatype);
1827     HeapFree(heap,0,piW->pParameters);
1828     HeapFree(heap,0,piW);
1829     return;
1830 }
1831
1832 /******************************************************************
1833  *              DeviceCapabilities     [WINSPOOL.@]
1834  *              DeviceCapabilitiesA    [WINSPOOL.@]
1835  *
1836  */
1837 INT WINAPI DeviceCapabilitiesA(LPCSTR pDevice,LPCSTR pPort, WORD cap,
1838                                LPSTR pOutput, LPDEVMODEA lpdm)
1839 {
1840     INT ret;
1841
1842     if (!GDI_CallDeviceCapabilities16)
1843     {
1844         GDI_CallDeviceCapabilities16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1845                                                               (LPCSTR)104 );
1846         if (!GDI_CallDeviceCapabilities16) return -1;
1847     }
1848     ret = GDI_CallDeviceCapabilities16(pDevice, pPort, cap, pOutput, lpdm);
1849
1850     /* If DC_PAPERSIZE map POINT16s to POINTs */
1851     if(ret != -1 && cap == DC_PAPERSIZE && pOutput) {
1852         POINT16 *tmp = HeapAlloc( GetProcessHeap(), 0, ret * sizeof(POINT16) );
1853         POINT *pt = (POINT *)pOutput;
1854         INT i;
1855         memcpy(tmp, pOutput, ret * sizeof(POINT16));
1856         for(i = 0; i < ret; i++, pt++)
1857         {
1858             pt->x = tmp[i].x;
1859             pt->y = tmp[i].y;
1860         }
1861         HeapFree( GetProcessHeap(), 0, tmp );
1862     }
1863     return ret;
1864 }
1865
1866
1867 /*****************************************************************************
1868  *          DeviceCapabilitiesW        [WINSPOOL.@]
1869  *
1870  * Call DeviceCapabilitiesA since we later call 16bit stuff anyway
1871  *
1872  */
1873 INT WINAPI DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort,
1874                                WORD fwCapability, LPWSTR pOutput,
1875                                const DEVMODEW *pDevMode)
1876 {
1877     LPDEVMODEA dmA = DEVMODEdupWtoA(GetProcessHeap(), pDevMode);
1878     LPSTR pDeviceA = strdupWtoA(pDevice);
1879     LPSTR pPortA = strdupWtoA(pPort);
1880     INT ret;
1881
1882     if(pOutput && (fwCapability == DC_BINNAMES ||
1883                    fwCapability == DC_FILEDEPENDENCIES ||
1884                    fwCapability == DC_PAPERNAMES)) {
1885       /* These need A -> W translation */
1886         INT size = 0, i;
1887         LPSTR pOutputA;
1888         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, NULL,
1889                                   dmA);
1890         if(ret == -1)
1891             return ret;
1892         switch(fwCapability) {
1893         case DC_BINNAMES:
1894             size = 24;
1895             break;
1896         case DC_PAPERNAMES:
1897         case DC_FILEDEPENDENCIES:
1898             size = 64;
1899             break;
1900         }
1901         pOutputA = HeapAlloc(GetProcessHeap(), 0, size * ret);
1902         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, pOutputA,
1903                                   dmA);
1904         for(i = 0; i < ret; i++)
1905             MultiByteToWideChar(CP_ACP, 0, pOutputA + (i * size), -1,
1906                                 pOutput + (i * size), size);
1907         HeapFree(GetProcessHeap(), 0, pOutputA);
1908     } else {
1909         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability,
1910                                   (LPSTR)pOutput, dmA);
1911     }
1912     HeapFree(GetProcessHeap(),0,pPortA);
1913     HeapFree(GetProcessHeap(),0,pDeviceA);
1914     HeapFree(GetProcessHeap(),0,dmA);
1915     return ret;
1916 }
1917
1918 /******************************************************************
1919  *              DocumentPropertiesA   [WINSPOOL.@]
1920  *
1921  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1922  */
1923 LONG WINAPI DocumentPropertiesA(HWND hWnd,HANDLE hPrinter,
1924                                 LPSTR pDeviceName, LPDEVMODEA pDevModeOutput,
1925                                 LPDEVMODEA pDevModeInput,DWORD fMode )
1926 {
1927     LPSTR lpName = pDeviceName;
1928     static CHAR port[] = "LPT1:";
1929     LONG ret;
1930
1931     TRACE("(%p,%p,%s,%p,%p,%d)\n",
1932         hWnd,hPrinter,pDeviceName,pDevModeOutput,pDevModeInput,fMode
1933     );
1934
1935     if(!pDeviceName) {
1936         LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
1937         if(!lpNameW) {
1938                 ERR("no name from hPrinter?\n");
1939                 SetLastError(ERROR_INVALID_HANDLE);
1940                 return -1;
1941         }
1942         lpName = strdupWtoA(lpNameW);
1943     }
1944
1945     if (!GDI_CallExtDeviceMode16)
1946     {
1947         GDI_CallExtDeviceMode16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1948                                                          (LPCSTR)102 );
1949         if (!GDI_CallExtDeviceMode16) {
1950                 ERR("No CallExtDeviceMode16?\n");
1951                 return -1;
1952         }
1953     }
1954     ret = GDI_CallExtDeviceMode16(hWnd, pDevModeOutput, lpName, port,
1955                                   pDevModeInput, NULL, fMode);
1956
1957     if(!pDeviceName)
1958         HeapFree(GetProcessHeap(),0,lpName);
1959     return ret;
1960 }
1961
1962
1963 /*****************************************************************************
1964  *          DocumentPropertiesW (WINSPOOL.@)
1965  *
1966  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1967  */
1968 LONG WINAPI DocumentPropertiesW(HWND hWnd, HANDLE hPrinter,
1969                                 LPWSTR pDeviceName,
1970                                 LPDEVMODEW pDevModeOutput,
1971                                 LPDEVMODEW pDevModeInput, DWORD fMode)
1972 {
1973
1974     LPSTR pDeviceNameA = strdupWtoA(pDeviceName);
1975     LPDEVMODEA pDevModeInputA = DEVMODEdupWtoA(GetProcessHeap(),pDevModeInput);
1976     LPDEVMODEA pDevModeOutputA = NULL;
1977     LONG ret;
1978
1979     TRACE("(%p,%p,%s,%p,%p,%d)\n",
1980           hWnd,hPrinter,debugstr_w(pDeviceName),pDevModeOutput,pDevModeInput,
1981           fMode);
1982     if(pDevModeOutput) {
1983         ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, NULL, NULL, 0);
1984         if(ret < 0) return ret;
1985         pDevModeOutputA = HeapAlloc(GetProcessHeap(), 0, ret);
1986     }
1987     ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, pDevModeOutputA,
1988                               pDevModeInputA, fMode);
1989     if(pDevModeOutput) {
1990         DEVMODEcpyAtoW(pDevModeOutput, pDevModeOutputA);
1991         HeapFree(GetProcessHeap(),0,pDevModeOutputA);
1992     }
1993     if(fMode == 0 && ret > 0)
1994         ret += (CCHDEVICENAME + CCHFORMNAME);
1995     HeapFree(GetProcessHeap(),0,pDevModeInputA);
1996     HeapFree(GetProcessHeap(),0,pDeviceNameA);
1997     return ret;
1998 }
1999
2000 /******************************************************************
2001  *              OpenPrinterA        [WINSPOOL.@]
2002  *
2003  * See OpenPrinterW.
2004  *
2005  */
2006 BOOL WINAPI OpenPrinterA(LPSTR lpPrinterName,HANDLE *phPrinter,
2007                          LPPRINTER_DEFAULTSA pDefault)
2008 {
2009     UNICODE_STRING lpPrinterNameW;
2010     UNICODE_STRING usBuffer;
2011     PRINTER_DEFAULTSW DefaultW, *pDefaultW = NULL;
2012     PWSTR pwstrPrinterNameW;
2013     BOOL ret;
2014
2015     pwstrPrinterNameW = asciitounicode(&lpPrinterNameW,lpPrinterName);
2016
2017     if(pDefault) {
2018         DefaultW.pDatatype = asciitounicode(&usBuffer,pDefault->pDatatype);
2019         DefaultW.pDevMode = pDefault->pDevMode ? GdiConvertToDevmodeW(pDefault->pDevMode) : NULL;
2020         DefaultW.DesiredAccess = pDefault->DesiredAccess;
2021         pDefaultW = &DefaultW;
2022     }
2023     ret = OpenPrinterW(pwstrPrinterNameW, phPrinter, pDefaultW);
2024     if(pDefault) {
2025         RtlFreeUnicodeString(&usBuffer);
2026         HeapFree(GetProcessHeap(), 0, DefaultW.pDevMode);
2027     }
2028     RtlFreeUnicodeString(&lpPrinterNameW);
2029     return ret;
2030 }
2031
2032 /******************************************************************
2033  *              OpenPrinterW        [WINSPOOL.@]
2034  *
2035  * Open a Printer / Printserver or a Printer-Object
2036  *
2037  * PARAMS
2038  *  lpPrinterName [I] Name of Printserver, Printer, or Printer-Object
2039  *  phPrinter     [O] The resulting Handle is stored here
2040  *  pDefault      [I] PTR to Default Printer Settings or NULL
2041  *
2042  * RETURNS
2043  *  Success: TRUE
2044  *  Failure: FALSE
2045  *
2046  * NOTES
2047  *  lpPrinterName is one of:
2048  *|  Printserver (NT only): "Servername" or NULL for the local Printserver
2049  *|  Printer: "PrinterName"
2050  *|  Printer-Object: "PrinterName,Job xxx"
2051  *|  XcvMonitor: "Servername,XcvMonitor MonitorName"
2052  *|  XcvPort: "Servername,XcvPort PortName"
2053  *
2054  * BUGS
2055  *|  Printer-Object not supported
2056  *|  pDefaults is ignored
2057  *
2058  */
2059 BOOL WINAPI OpenPrinterW(LPWSTR lpPrinterName,HANDLE *phPrinter, LPPRINTER_DEFAULTSW pDefault)
2060 {
2061
2062     TRACE("(%s, %p, %p)\n", debugstr_w(lpPrinterName), phPrinter, pDefault);
2063     if (pDefault) {
2064         FIXME("PRINTER_DEFAULTS ignored => %s,%p,0x%08x\n",
2065         debugstr_w(pDefault->pDatatype), pDefault->pDevMode, pDefault->DesiredAccess);
2066     }
2067
2068     if(!phPrinter) {
2069         /* NT: FALSE with ERROR_INVALID_PARAMETER, 9x: TRUE */
2070         SetLastError(ERROR_INVALID_PARAMETER);
2071         return FALSE;
2072     }
2073
2074     /* Get the unique handle of the printer or Printserver */
2075     *phPrinter = get_opened_printer_entry(lpPrinterName, pDefault);
2076     TRACE("returning %d with %u and %p\n", *phPrinter != NULL, GetLastError(), *phPrinter);
2077     return (*phPrinter != 0);
2078 }
2079
2080 /******************************************************************
2081  *              AddMonitorA        [WINSPOOL.@]
2082  *
2083  * See AddMonitorW.
2084  *
2085  */
2086 BOOL WINAPI AddMonitorA(LPSTR pName, DWORD Level, LPBYTE pMonitors)
2087 {
2088     LPWSTR  nameW = NULL;
2089     INT     len;
2090     BOOL    res;
2091     LPMONITOR_INFO_2A mi2a;
2092     MONITOR_INFO_2W mi2w;
2093
2094     mi2a = (LPMONITOR_INFO_2A) pMonitors;
2095     TRACE("(%s, %d, %p) :  %s %s %s\n", debugstr_a(pName), Level, pMonitors, 
2096             mi2a ? debugstr_a(mi2a->pName) : NULL,
2097             mi2a ? debugstr_a(mi2a->pEnvironment) : NULL,
2098             mi2a ? debugstr_a(mi2a->pDLLName) : NULL);
2099
2100     if  (Level != 2) {
2101         SetLastError(ERROR_INVALID_LEVEL);
2102         return FALSE;
2103     }
2104
2105     /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
2106     if (mi2a == NULL) {
2107         return FALSE;
2108     }
2109
2110     if (pName) {
2111         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2112         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2113         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2114     }
2115
2116     memset(&mi2w, 0, sizeof(MONITOR_INFO_2W));
2117     if (mi2a->pName) {
2118         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, NULL, 0);
2119         mi2w.pName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2120         MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, mi2w.pName, len);
2121     }
2122     if (mi2a->pEnvironment) {
2123         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, NULL, 0);
2124         mi2w.pEnvironment = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2125         MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, mi2w.pEnvironment, len);
2126     }
2127     if (mi2a->pDLLName) {
2128         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, NULL, 0);
2129         mi2w.pDLLName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2130         MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, mi2w.pDLLName, len);
2131     }
2132
2133     res = AddMonitorW(nameW, Level, (LPBYTE) &mi2w);
2134
2135     HeapFree(GetProcessHeap(), 0, mi2w.pName); 
2136     HeapFree(GetProcessHeap(), 0, mi2w.pEnvironment); 
2137     HeapFree(GetProcessHeap(), 0, mi2w.pDLLName); 
2138
2139     HeapFree(GetProcessHeap(), 0, nameW); 
2140     return (res);
2141 }
2142
2143 /******************************************************************************
2144  *              AddMonitorW        [WINSPOOL.@]
2145  *
2146  * Install a Printmonitor
2147  *
2148  * PARAMS
2149  *  pName       [I] Servername or NULL (local Computer)
2150  *  Level       [I] Structure-Level (Must be 2)
2151  *  pMonitors   [I] PTR to MONITOR_INFO_2
2152  *
2153  * RETURNS
2154  *  Success: TRUE
2155  *  Failure: FALSE
2156  *
2157  * NOTES
2158  *  All Files for the Monitor must already be copied to %winsysdir% ("%SystemRoot%\system32")
2159  *
2160  */
2161 BOOL WINAPI AddMonitorW(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
2162 {
2163     monitor_t * pm = NULL;
2164     LPMONITOR_INFO_2W mi2w;
2165     HKEY    hroot = NULL;
2166     HKEY    hentry = NULL;
2167     DWORD   disposition;
2168     BOOL    res = FALSE;
2169
2170     mi2w = (LPMONITOR_INFO_2W) pMonitors;
2171     TRACE("(%s, %d, %p) :  %s %s %s\n", debugstr_w(pName), Level, pMonitors, 
2172             mi2w ? debugstr_w(mi2w->pName) : NULL,
2173             mi2w ? debugstr_w(mi2w->pEnvironment) : NULL,
2174             mi2w ? debugstr_w(mi2w->pDLLName) : NULL);
2175
2176     if (Level != 2) {
2177         SetLastError(ERROR_INVALID_LEVEL);
2178         return FALSE;
2179     }
2180
2181     /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
2182     if (mi2w == NULL) {
2183         return FALSE;
2184     }
2185
2186     if (pName && (pName[0])) {
2187         FIXME("for server %s not implemented\n", debugstr_w(pName));
2188         SetLastError(ERROR_ACCESS_DENIED);
2189         return FALSE;
2190     }
2191
2192
2193     if (!mi2w->pName || (! mi2w->pName[0])) {
2194         WARN("pName not valid : %s\n", debugstr_w(mi2w->pName));
2195         SetLastError(ERROR_INVALID_PARAMETER);
2196         return FALSE;
2197     }
2198     if (!mi2w->pEnvironment || lstrcmpW(mi2w->pEnvironment, envname_x86W)) {
2199         WARN("Environment %s requested (we support only %s)\n", 
2200                 debugstr_w(mi2w->pEnvironment), debugstr_w(envname_x86W));
2201         SetLastError(ERROR_INVALID_ENVIRONMENT);
2202         return FALSE;
2203     }
2204
2205     if (!mi2w->pDLLName || (! mi2w->pDLLName[0])) {
2206         WARN("pDLLName not valid : %s\n", debugstr_w(mi2w->pDLLName));
2207         SetLastError(ERROR_INVALID_PARAMETER);
2208         return FALSE;
2209     }
2210
2211     /* Load and initialize the monitor. SetLastError() is called on failure */
2212     if ((pm = monitor_load(mi2w->pName, mi2w->pDLLName)) == NULL) {
2213         return FALSE;
2214     }
2215     monitor_unload(pm);
2216
2217     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) != ERROR_SUCCESS) {
2218         ERR("unable to create key %s\n", debugstr_w(MonitorsW));
2219         return FALSE;
2220     }
2221
2222     if(RegCreateKeyExW( hroot, mi2w->pName, 0, NULL, REG_OPTION_NON_VOLATILE,
2223                         KEY_WRITE | KEY_QUERY_VALUE, NULL, &hentry,
2224                         &disposition) == ERROR_SUCCESS) {
2225
2226         /* Some installers set options for the port before calling AddMonitor.
2227            We query the "Driver" entry to verify that the monitor is installed,
2228            before we return an error.
2229            When a user installs two print monitors at the same time with the
2230            same name but with a different driver DLL and a task switch comes
2231            between RegQueryValueExW and RegSetValueExW, a race condition
2232            is possible but silently ignored. */
2233
2234         DWORD   namesize = 0;
2235
2236         if ((disposition == REG_OPENED_EXISTING_KEY) &&
2237             (RegQueryValueExW(hentry, DriverW, NULL, NULL, NULL,
2238                               &namesize) == ERROR_SUCCESS)) {
2239             TRACE("monitor %s already exists\n", debugstr_w(mi2w->pName));
2240             /* NT: ERROR_PRINT_MONITOR_ALREADY_INSTALLED (3006)
2241                9x: ERROR_ALREADY_EXISTS (183) */
2242             SetLastError(ERROR_PRINT_MONITOR_ALREADY_INSTALLED);
2243         }
2244         else
2245         {
2246                INT len;
2247                len = (lstrlenW(mi2w->pDLLName) +1) * sizeof(WCHAR);
2248                res = (RegSetValueExW(hentry, DriverW, 0,
2249                       REG_SZ, (LPBYTE) mi2w->pDLLName, len) == ERROR_SUCCESS);
2250         }
2251         RegCloseKey(hentry);
2252     }
2253
2254     RegCloseKey(hroot);
2255     return (res);
2256 }
2257
2258 /******************************************************************
2259  *              DeletePrinterDriverA        [WINSPOOL.@]
2260  *
2261  */
2262 BOOL WINAPI DeletePrinterDriverA (LPSTR pName, LPSTR pEnvironment, LPSTR pDriverName)
2263 {
2264     return DeletePrinterDriverExA(pName, pEnvironment, pDriverName, 0, 0);
2265 }
2266
2267 /******************************************************************
2268  *              DeletePrinterDriverW        [WINSPOOL.@]
2269  *
2270  */
2271 BOOL WINAPI DeletePrinterDriverW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pDriverName)
2272 {
2273     return DeletePrinterDriverExW(pName, pEnvironment, pDriverName, 0, 0);
2274 }
2275
2276 /******************************************************************
2277  *              DeleteMonitorA        [WINSPOOL.@]
2278  *
2279  * See DeleteMonitorW.
2280  *
2281  */
2282 BOOL WINAPI DeleteMonitorA (LPSTR pName, LPSTR pEnvironment, LPSTR pMonitorName)
2283 {
2284     LPWSTR  nameW = NULL;
2285     LPWSTR  EnvironmentW = NULL;
2286     LPWSTR  MonitorNameW = NULL;
2287     BOOL    res;
2288     INT     len;
2289
2290     if (pName) {
2291         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2292         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2293         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2294     }
2295
2296     if (pEnvironment) {
2297         len = MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, NULL, 0);
2298         EnvironmentW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2299         MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, EnvironmentW, len);
2300     }
2301     if (pMonitorName) {
2302         len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
2303         MonitorNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2304         MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, MonitorNameW, len);
2305     }
2306
2307     res = DeleteMonitorW(nameW, EnvironmentW, MonitorNameW);
2308
2309     HeapFree(GetProcessHeap(), 0, MonitorNameW); 
2310     HeapFree(GetProcessHeap(), 0, EnvironmentW);
2311     HeapFree(GetProcessHeap(), 0, nameW); 
2312     return (res);
2313 }
2314
2315 /******************************************************************
2316  *              DeleteMonitorW        [WINSPOOL.@]
2317  *
2318  * Delete a specific Printmonitor from a Printing-Environment
2319  *
2320  * PARAMS
2321  *  pName        [I] Servername or NULL (local Computer)
2322  *  pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
2323  *  pMonitorName [I] Name of the Monitor, that should be deleted
2324  *
2325  * RETURNS
2326  *  Success: TRUE
2327  *  Failure: FALSE
2328  *
2329  * NOTES
2330  *  pEnvironment is ignored in Windows for the local Computer.
2331  *
2332  */
2333
2334 BOOL WINAPI DeleteMonitorW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
2335 {
2336     HKEY    hroot = NULL;
2337
2338     TRACE("(%s, %s, %s)\n",debugstr_w(pName),debugstr_w(pEnvironment),
2339            debugstr_w(pMonitorName));
2340
2341     if (pName && (pName[0])) {
2342         FIXME("for server %s not implemented\n", debugstr_w(pName));
2343         SetLastError(ERROR_ACCESS_DENIED);
2344         return FALSE;
2345     }
2346
2347     /*  pEnvironment is ignored in Windows for the local Computer */
2348
2349     if (!pMonitorName || !pMonitorName[0]) {
2350         WARN("pMonitorName %s is invalid\n", debugstr_w(pMonitorName));
2351         SetLastError(ERROR_INVALID_PARAMETER);
2352         return FALSE;
2353     }
2354
2355     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) != ERROR_SUCCESS) {
2356         ERR("unable to create key %s\n", debugstr_w(MonitorsW));
2357         return FALSE;
2358     }
2359
2360     /* change this, when advapi32.dll/RegDeleteTree is implemented */
2361     if(WINSPOOL_SHDeleteKeyW(hroot, pMonitorName) == ERROR_SUCCESS) {
2362         TRACE("monitor %s deleted\n", debugstr_w(pMonitorName));
2363         RegCloseKey(hroot);
2364         return TRUE;
2365     }
2366
2367     WARN("monitor %s does not exist\n", debugstr_w(pMonitorName));
2368     RegCloseKey(hroot);
2369
2370     /* NT: ERROR_UNKNOWN_PRINT_MONITOR (3000), 9x: ERROR_INVALID_PARAMETER (87) */
2371     SetLastError(ERROR_UNKNOWN_PRINT_MONITOR);
2372     return (FALSE);
2373 }
2374
2375 /******************************************************************
2376  *              DeletePortA        [WINSPOOL.@]
2377  *
2378  * See DeletePortW.
2379  *
2380  */
2381 BOOL WINAPI DeletePortA (LPSTR pName, HWND hWnd, LPSTR pPortName)
2382 {
2383     LPWSTR  nameW = NULL;
2384     LPWSTR  portW = NULL;
2385     INT     len;
2386     DWORD   res;
2387
2388     TRACE("(%s, %p, %s)\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
2389
2390     /* convert servername to unicode */
2391     if (pName) {
2392         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2393         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2394         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2395     }
2396
2397     /* convert portname to unicode */
2398     if (pPortName) {
2399         len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
2400         portW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2401         MultiByteToWideChar(CP_ACP, 0, pPortName, -1, portW, len);
2402     }
2403
2404     res = DeletePortW(nameW, hWnd, portW);
2405     HeapFree(GetProcessHeap(), 0, nameW);
2406     HeapFree(GetProcessHeap(), 0, portW);
2407     return res;
2408 }
2409
2410 /******************************************************************
2411  *              DeletePortW        [WINSPOOL.@]
2412  *
2413  * Delete a specific Port
2414  *
2415  * PARAMS
2416  *  pName     [I] Servername or NULL (local Computer)
2417  *  hWnd      [I] Handle to parent Window for the Dialog-Box
2418  *  pPortName [I] Name of the Port, that should be deleted
2419  *
2420  * RETURNS
2421  *  Success: TRUE
2422  *  Failure: FALSE
2423  *
2424  */
2425 BOOL WINAPI DeletePortW (LPWSTR pName, HWND hWnd, LPWSTR pPortName)
2426 {
2427     monitor_t * pm;
2428     monitor_t * pui;
2429     DWORD       res;
2430
2431     TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
2432
2433     if (pName && pName[0]) {
2434         SetLastError(ERROR_INVALID_PARAMETER);
2435         return FALSE;
2436     }
2437
2438     if (!pPortName) {
2439         SetLastError(RPC_X_NULL_REF_POINTER);
2440         return FALSE;
2441     }
2442
2443     /* an empty Portname is Invalid */
2444     if (!pPortName[0]) {
2445         SetLastError(ERROR_NOT_SUPPORTED);
2446         return FALSE;
2447     }
2448
2449     pm = monitor_load_by_port(pPortName);
2450     if (pm && pm->monitor && pm->monitor->pfnDeletePort) {
2451         TRACE("Using %s for %s (%p: %s)\n", debugstr_w(pm->name), debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
2452         res = pm->monitor->pfnDeletePort(pName, hWnd, pPortName);
2453         TRACE("got %d with %u\n", res, GetLastError());
2454     }
2455     else
2456     {
2457         pui = monitor_loadui(pm);
2458         if (pui && pui->monitorUI && pui->monitorUI->pfnDeletePortUI) {
2459             TRACE("use %s for %s (%p: %s)\n", debugstr_w(pui->name), debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
2460             res = pui->monitorUI->pfnDeletePortUI(pName, hWnd, pPortName);
2461             TRACE("got %d with %u\n", res, GetLastError());
2462         }
2463         else
2464         {
2465             FIXME("not implemented for %s (%p: %s => %p: %s)\n", debugstr_w(pPortName),
2466                 pm, pm ? debugstr_w(pm->dllname) : NULL, pui, pui ? debugstr_w(pui->dllname) : NULL);
2467
2468             /* XP: ERROR_NOT_SUPPORTED, NT351,9x: ERROR_INVALID_PARAMETER */
2469             SetLastError(ERROR_NOT_SUPPORTED);
2470             res = FALSE;
2471         }
2472         monitor_unload(pui);
2473     }
2474     /* always invalidate cached PORT_INFO_2W */
2475     monitor_flush(pm);
2476
2477     monitor_unload(pm);
2478
2479     TRACE("returning %d with %u\n", res, GetLastError());
2480     return res;
2481 }
2482
2483 /******************************************************************************
2484  *    SetPrinterW  [WINSPOOL.@]
2485  */
2486 BOOL WINAPI SetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD Command)
2487 {
2488     FIXME("(%p, %d, %p, %d): stub\n", hPrinter, Level, pPrinter, Command);
2489     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2490     return FALSE;
2491 }
2492
2493 /******************************************************************************
2494  *    WritePrinter  [WINSPOOL.@]
2495  */
2496 BOOL WINAPI WritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
2497 {
2498     opened_printer_t *printer;
2499     BOOL ret = FALSE;
2500
2501     TRACE("(%p, %p, %d, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
2502
2503     EnterCriticalSection(&printer_handles_cs);
2504     printer = get_opened_printer(hPrinter);
2505     if(!printer)
2506     {
2507         SetLastError(ERROR_INVALID_HANDLE);
2508         goto end;
2509     }
2510
2511     if(!printer->doc)
2512     {
2513         SetLastError(ERROR_SPL_NO_STARTDOC);
2514         goto end;
2515     }
2516
2517     ret = WriteFile(printer->doc->hf, pBuf, cbBuf, pcWritten, NULL);
2518 end:
2519     LeaveCriticalSection(&printer_handles_cs);
2520     return ret;
2521 }
2522
2523 /*****************************************************************************
2524  *          AddFormA  [WINSPOOL.@]
2525  */
2526 BOOL WINAPI AddFormA(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
2527 {
2528     FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
2529     return 1;
2530 }
2531
2532 /*****************************************************************************
2533  *          AddFormW  [WINSPOOL.@]
2534  */
2535 BOOL WINAPI AddFormW(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
2536 {
2537     FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
2538     return 1;
2539 }
2540
2541 /*****************************************************************************
2542  *          AddJobA  [WINSPOOL.@]
2543  */
2544 BOOL WINAPI AddJobA(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
2545 {
2546     BOOL ret;
2547     BYTE buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
2548     DWORD needed;
2549
2550     if(Level != 1) {
2551         SetLastError(ERROR_INVALID_LEVEL);
2552         return FALSE;
2553     }
2554
2555     ret = AddJobW(hPrinter, Level, buf, sizeof(buf), &needed);
2556
2557     if(ret) {
2558         ADDJOB_INFO_1W *addjobW = (ADDJOB_INFO_1W*)buf;
2559         DWORD len = WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, NULL, 0, NULL, NULL);
2560         *pcbNeeded = len + sizeof(ADDJOB_INFO_1A);
2561         if(*pcbNeeded > cbBuf) {
2562             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2563             ret = FALSE;
2564         } else {
2565             ADDJOB_INFO_1A *addjobA = (ADDJOB_INFO_1A*)pData;
2566             addjobA->JobId = addjobW->JobId;
2567             addjobA->Path = (char *)(addjobA + 1);
2568             WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, addjobA->Path, len, NULL, NULL);
2569         }
2570     }
2571     return ret;
2572 }
2573
2574 /*****************************************************************************
2575  *          AddJobW  [WINSPOOL.@]
2576  */
2577 BOOL WINAPI AddJobW(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
2578 {
2579     opened_printer_t *printer;
2580     job_t *job;
2581     BOOL ret = FALSE;
2582     static const WCHAR spool_path[] = {'s','p','o','o','l','\\','P','R','I','N','T','E','R','S','\\',0};
2583     static const WCHAR fmtW[] = {'%','s','%','0','5','d','.','S','P','L',0};
2584     WCHAR path[MAX_PATH], filename[MAX_PATH];
2585     DWORD len;
2586     ADDJOB_INFO_1W *addjob;
2587
2588     TRACE("(%p,%d,%p,%d,%p)\n", hPrinter, Level, pData, cbBuf, pcbNeeded);
2589     
2590     EnterCriticalSection(&printer_handles_cs);
2591
2592     printer = get_opened_printer(hPrinter);
2593
2594     if(!printer) {
2595         SetLastError(ERROR_INVALID_HANDLE);
2596         goto end;
2597     }
2598
2599     if(Level != 1) {
2600         SetLastError(ERROR_INVALID_LEVEL);
2601         goto end;
2602     }
2603
2604     job = HeapAlloc(GetProcessHeap(), 0, sizeof(*job));
2605     if(!job)
2606         goto end;
2607
2608     job->job_id = InterlockedIncrement(&next_job_id);
2609
2610     len = GetSystemDirectoryW(path, sizeof(path) / sizeof(WCHAR));
2611     if(path[len - 1] != '\\')
2612         path[len++] = '\\';
2613     memcpy(path + len, spool_path, sizeof(spool_path));    
2614     sprintfW(filename, fmtW, path, job->job_id);
2615
2616     len = strlenW(filename);
2617     job->filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
2618     memcpy(job->filename, filename, (len + 1) * sizeof(WCHAR));
2619     job->document_title = strdupW(default_doc_title);
2620     list_add_tail(&printer->queue->jobs, &job->entry);
2621
2622     *pcbNeeded = (len + 1) * sizeof(WCHAR) + sizeof(*addjob);
2623     if(*pcbNeeded <= cbBuf) {
2624         addjob = (ADDJOB_INFO_1W*)pData;
2625         addjob->JobId = job->job_id;
2626         addjob->Path = (WCHAR *)(addjob + 1);
2627         memcpy(addjob->Path, filename, (len + 1) * sizeof(WCHAR));
2628         ret = TRUE;
2629     } else 
2630         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2631
2632 end:
2633     LeaveCriticalSection(&printer_handles_cs);
2634     return ret;
2635 }
2636
2637 /*****************************************************************************
2638  *          GetPrintProcessorDirectoryA  [WINSPOOL.@]
2639  *
2640  * Return the PATH for the Print-Processors
2641  *
2642  * See GetPrintProcessorDirectoryW.
2643  *
2644  *
2645  */
2646 BOOL WINAPI GetPrintProcessorDirectoryA(LPSTR server, LPSTR env,
2647                                         DWORD level,  LPBYTE Info,
2648                                         DWORD cbBuf,  LPDWORD pcbNeeded)
2649 {
2650     LPWSTR  serverW = NULL;
2651     LPWSTR  envW = NULL;
2652     BOOL    ret;
2653     INT     len;
2654
2655     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_a(server), 
2656           debugstr_a(env), level, Info, cbBuf, pcbNeeded);
2657  
2658
2659     if (server) {
2660         len = MultiByteToWideChar(CP_ACP, 0, server, -1, NULL, 0);
2661         serverW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2662         MultiByteToWideChar(CP_ACP, 0, server, -1, serverW, len);
2663     }
2664
2665     if (env) {
2666         len = MultiByteToWideChar(CP_ACP, 0, env, -1, NULL, 0);
2667         envW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2668         MultiByteToWideChar(CP_ACP, 0, env, -1, envW, len);
2669     }
2670
2671     /* NT requires the buffersize from GetPrintProcessorDirectoryW also
2672        for GetPrintProcessorDirectoryA and WC2MB is done in-place.
2673      */
2674     ret = GetPrintProcessorDirectoryW(serverW, envW, level, Info, 
2675                                       cbBuf, pcbNeeded);
2676
2677     if (ret) ret = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)Info, -1, (LPSTR)Info,
2678                                        cbBuf, NULL, NULL) > 0;
2679
2680
2681     TRACE(" required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
2682     HeapFree(GetProcessHeap(), 0, envW);
2683     HeapFree(GetProcessHeap(), 0, serverW);
2684     return ret;
2685 }
2686
2687 /*****************************************************************************
2688  *          GetPrintProcessorDirectoryW  [WINSPOOL.@]
2689  *
2690  * Return the PATH for the Print-Processors
2691  *
2692  * PARAMS
2693  *   server     [I] Servername (NT only) or NULL (local Computer)
2694  *   env        [I] Printing-Environment (see below) or NULL (Default)
2695  *   level      [I] Structure-Level (must be 1)
2696  *   Info       [O] PTR to Buffer that receives the Result
2697  *   cbBuf      [I] Size of Buffer at "Info"
2698  *   pcbNeeded  [O] PTR to DWORD that receives the size in Bytes used / 
2699  *                  required for the Buffer at "Info"
2700  *
2701  * RETURNS
2702  *   Success: TRUE  and in pcbNeeded the Bytes used in Info
2703  *   Failure: FALSE and in pcbNeeded the Bytes required for Info,
2704  *   if cbBuf is too small
2705  * 
2706  *   Native Values returned in Info on Success:
2707  *|  NT(Windows NT x86):  "%winsysdir%\\spool\\PRTPROCS\\w32x86" 
2708  *|  NT(Windows 4.0):     "%winsysdir%\\spool\\PRTPROCS\\win40" 
2709  *|  win9x(Windows 4.0):  "%winsysdir%" 
2710  *
2711  *   "%winsysdir%" is the Value from GetSystemDirectoryW()
2712  *
2713  * BUGS
2714  *  Only NULL or "" is supported for server
2715  *
2716  */
2717 BOOL WINAPI GetPrintProcessorDirectoryW(LPWSTR server, LPWSTR env,
2718                                         DWORD level,  LPBYTE Info,
2719                                         DWORD cbBuf,  LPDWORD pcbNeeded)
2720 {
2721     DWORD needed;
2722     const printenv_t * env_t;
2723
2724     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(server),
2725             debugstr_w(env), level, Info, cbBuf, pcbNeeded);
2726
2727     if(server != NULL && server[0]) {
2728         FIXME("server not supported: %s\n", debugstr_w(server));
2729         SetLastError(ERROR_INVALID_PARAMETER);
2730         return FALSE;
2731     }
2732
2733     env_t = validate_envW(env);
2734     if(!env_t) return FALSE;  /* environment invalid or unsupported */
2735
2736     if(level != 1) {
2737         WARN("(Level: %d) is ignored in win9x\n", level);
2738         SetLastError(ERROR_INVALID_LEVEL);
2739         return FALSE;
2740     }
2741
2742     /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
2743     needed = GetSystemDirectoryW(NULL, 0);
2744     /* add the Size for the Subdirectories */
2745     needed += lstrlenW(spoolprtprocsW);
2746     needed += lstrlenW(env_t->subdir);
2747     needed *= sizeof(WCHAR);  /* return-value is size in Bytes */
2748
2749     if(pcbNeeded) *pcbNeeded = needed;
2750     TRACE ("required: 0x%x/%d\n", needed, needed);
2751     if (needed > cbBuf) {
2752         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2753         return FALSE;
2754     }
2755     if(pcbNeeded == NULL) {
2756         /* NT: RPC_X_NULL_REF_POINTER, 9x: ignored */
2757         WARN("(pcbNeeded == NULL) is ignored in win9x\n");
2758         SetLastError(RPC_X_NULL_REF_POINTER);
2759         return FALSE;
2760     }
2761     if(Info == NULL) {
2762         /* NT: RPC_X_NULL_REF_POINTER, 9x: ERROR_INVALID_PARAMETER */
2763         SetLastError(RPC_X_NULL_REF_POINTER);
2764         return FALSE;
2765     }
2766     
2767     GetSystemDirectoryW((LPWSTR) Info, cbBuf/sizeof(WCHAR));
2768     /* add the Subdirectories */
2769     lstrcatW((LPWSTR) Info, spoolprtprocsW);
2770     lstrcatW((LPWSTR) Info, env_t->subdir);
2771     TRACE(" => %s\n", debugstr_w((LPWSTR) Info));
2772     return TRUE;
2773 }
2774
2775 /*****************************************************************************
2776  *          WINSPOOL_OpenDriverReg [internal]
2777  *
2778  * opens the registry for the printer drivers depending on the given input
2779  * variable pEnvironment
2780  *
2781  * RETURNS:
2782  *    the opened hkey on success
2783  *    NULL on error
2784  */
2785 static HKEY WINSPOOL_OpenDriverReg( LPVOID pEnvironment, BOOL unicode)
2786 {   
2787     HKEY  retval = NULL;
2788     LPWSTR buffer;
2789     const printenv_t * env;
2790
2791     TRACE("(%s, %d)\n",
2792           (unicode) ? debugstr_w(pEnvironment) : debugstr_a(pEnvironment), unicode);
2793
2794     if (!pEnvironment || unicode) {
2795         /* pEnvironment was NULL or an Unicode-String: use it direct */
2796         env = validate_envW(pEnvironment);
2797     }
2798     else
2799     {
2800         /* pEnvironment was an ANSI-String: convert to unicode first */
2801         LPWSTR  buffer;
2802         INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, NULL, 0);
2803         buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2804         if (buffer) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, buffer, len);
2805         env = validate_envW(buffer);
2806         HeapFree(GetProcessHeap(), 0, buffer);
2807     }
2808     if (!env) return NULL;
2809
2810     buffer = HeapAlloc( GetProcessHeap(), 0,
2811                 (strlenW(DriversW) + strlenW(env->envname) + 
2812                  strlenW(env->versionregpath) + 1) * sizeof(WCHAR));
2813     if(buffer) {
2814         wsprintfW(buffer, DriversW, env->envname, env->versionregpath);
2815         RegCreateKeyW(HKEY_LOCAL_MACHINE, buffer, &retval);
2816         HeapFree(GetProcessHeap(), 0, buffer);
2817     }
2818     return retval;
2819 }
2820
2821 /*****************************************************************************
2822  *          AddPrinterW  [WINSPOOL.@]
2823  */
2824 HANDLE WINAPI AddPrinterW(LPWSTR pName, DWORD Level, LPBYTE pPrinter)
2825 {
2826     PRINTER_INFO_2W *pi = (PRINTER_INFO_2W *) pPrinter;
2827     LPDEVMODEA dmA;
2828     LPDEVMODEW dmW;
2829     HANDLE retval;
2830     HKEY hkeyPrinter, hkeyPrinters, hkeyDriver, hkeyDrivers;
2831     LONG size;
2832
2833     TRACE("(%s,%d,%p)\n", debugstr_w(pName), Level, pPrinter);
2834
2835     if(pName != NULL) {
2836         ERR("pName = %s - unsupported\n", debugstr_w(pName));
2837         SetLastError(ERROR_INVALID_PARAMETER);
2838         return 0;
2839     }
2840     if(Level != 2) {
2841         ERR("Level = %d, unsupported!\n", Level);
2842         SetLastError(ERROR_INVALID_LEVEL);
2843         return 0;
2844     }
2845     if(!pPrinter) {
2846         SetLastError(ERROR_INVALID_PARAMETER);
2847         return 0;
2848     }
2849     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
2850        ERROR_SUCCESS) {
2851         ERR("Can't create Printers key\n");
2852         return 0;
2853     }
2854     if(!RegOpenKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter)) {
2855         if (!RegQueryValueA(hkeyPrinter,"Attributes",NULL,NULL)) {
2856             SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
2857             RegCloseKey(hkeyPrinter);
2858             RegCloseKey(hkeyPrinters);
2859             return 0;
2860         }
2861         RegCloseKey(hkeyPrinter);
2862     }
2863     hkeyDrivers = WINSPOOL_OpenDriverReg( NULL, TRUE);
2864     if(!hkeyDrivers) {
2865         ERR("Can't create Drivers key\n");
2866         RegCloseKey(hkeyPrinters);
2867         return 0;
2868     }
2869     if(RegOpenKeyW(hkeyDrivers, pi->pDriverName, &hkeyDriver) !=
2870        ERROR_SUCCESS) {
2871         WARN("Can't find driver %s\n", debugstr_w(pi->pDriverName));
2872         RegCloseKey(hkeyPrinters);
2873         RegCloseKey(hkeyDrivers);
2874         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER);
2875         return 0;
2876     }
2877     RegCloseKey(hkeyDriver);
2878     RegCloseKey(hkeyDrivers);
2879
2880     if(lstrcmpiW(pi->pPrintProcessor, WinPrintW)) {  /* FIXME */
2881         FIXME("Can't find processor %s\n", debugstr_w(pi->pPrintProcessor));
2882         SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR);
2883         RegCloseKey(hkeyPrinters);
2884         return 0;
2885     }
2886
2887     if(RegCreateKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter) !=
2888        ERROR_SUCCESS) {
2889         FIXME("Can't create printer %s\n", debugstr_w(pi->pPrinterName));
2890         SetLastError(ERROR_INVALID_PRINTER_NAME);
2891         RegCloseKey(hkeyPrinters);
2892         return 0;
2893     }
2894     RegSetValueExA(hkeyPrinter, "Attributes", 0, REG_DWORD,
2895                    (LPBYTE)&pi->Attributes, sizeof(DWORD));
2896     set_reg_szW(hkeyPrinter, DatatypeW, pi->pDatatype);
2897
2898     /* See if we can load the driver.  We may need the devmode structure anyway
2899      *
2900      * FIXME:
2901      * Note that DocumentPropertiesW will briefly try to open the printer we
2902      * just create to find a DEVMODEA struct (it will use the WINEPS default
2903      * one in case it is not there, so we are ok).
2904      */
2905     size = DocumentPropertiesW(0, 0, pi->pPrinterName, NULL, NULL, 0);
2906
2907     if(size < 0) {
2908         FIXME("DocumentPropertiesW on printer %s fails\n", debugstr_w(pi->pPrinterName));
2909         size = sizeof(DEVMODEW);
2910     }
2911     if(pi->pDevMode)
2912         dmW = pi->pDevMode;
2913     else 
2914     {
2915         dmW = HeapAlloc(GetProcessHeap(), 0, size);
2916         ZeroMemory(dmW,size);
2917         dmW->dmSize = size;
2918         if (0>DocumentPropertiesW(0,0,pi->pPrinterName,dmW,NULL,DM_OUT_BUFFER))
2919         {
2920             WARN("DocumentPropertiesW on printer %s failed!\n", debugstr_w(pi->pPrinterName));
2921             HeapFree(GetProcessHeap(),0,dmW);
2922             dmW=NULL;
2923         }
2924         else
2925         {
2926             /* set devmode to printer name */
2927             lstrcpynW(dmW->dmDeviceName, pi->pPrinterName, CCHDEVICENAME);
2928         }
2929     }
2930
2931     /* Write DEVMODEA not DEVMODEW into reg.  This is what win9x does
2932        and we support these drivers.  NT writes DEVMODEW so somehow
2933        we'll need to distinguish between these when we support NT
2934        drivers */
2935     if (dmW)
2936     {
2937         dmA = DEVMODEdupWtoA(GetProcessHeap(), dmW);
2938         RegSetValueExA(hkeyPrinter, "Default DevMode", 0, REG_BINARY, 
2939                        (LPBYTE)dmA, dmA->dmSize + dmA->dmDriverExtra);
2940         HeapFree(GetProcessHeap(), 0, dmA);
2941         if(!pi->pDevMode)
2942             HeapFree(GetProcessHeap(), 0, dmW);
2943     }
2944     set_reg_szW(hkeyPrinter, DescriptionW, pi->pComment);
2945     set_reg_szW(hkeyPrinter, LocationW, pi->pLocation);
2946     set_reg_szW(hkeyPrinter, NameW, pi->pPrinterName);
2947     set_reg_szW(hkeyPrinter, ParametersW, pi->pParameters);
2948
2949     set_reg_szW(hkeyPrinter, PortW, pi->pPortName);
2950     set_reg_szW(hkeyPrinter, Print_ProcessorW, pi->pPrintProcessor);
2951     set_reg_szW(hkeyPrinter, Printer_DriverW, pi->pDriverName);
2952     RegSetValueExA(hkeyPrinter, "Priority", 0, REG_DWORD,
2953                    (LPBYTE)&pi->Priority, sizeof(DWORD));
2954     set_reg_szW(hkeyPrinter, Separator_FileW, pi->pSepFile);
2955     set_reg_szW(hkeyPrinter, Share_NameW, pi->pShareName);
2956     RegSetValueExA(hkeyPrinter, "StartTime", 0, REG_DWORD,
2957                    (LPBYTE)&pi->StartTime, sizeof(DWORD));
2958     RegSetValueExA(hkeyPrinter, "Status", 0, REG_DWORD,
2959                    (LPBYTE)&pi->Status, sizeof(DWORD));
2960     RegSetValueExA(hkeyPrinter, "UntilTime", 0, REG_DWORD,
2961                    (LPBYTE)&pi->UntilTime, sizeof(DWORD));
2962
2963     RegCloseKey(hkeyPrinter);
2964     RegCloseKey(hkeyPrinters);
2965     if(!OpenPrinterW(pi->pPrinterName, &retval, NULL)) {
2966         ERR("OpenPrinter failing\n");
2967         return 0;
2968     }
2969     return retval;
2970 }
2971
2972 /*****************************************************************************
2973  *          AddPrinterA  [WINSPOOL.@]
2974  */
2975 HANDLE WINAPI AddPrinterA(LPSTR pName, DWORD Level, LPBYTE pPrinter)
2976 {
2977     UNICODE_STRING pNameW;
2978     PWSTR pwstrNameW;
2979     PRINTER_INFO_2W *piW;
2980     PRINTER_INFO_2A *piA = (PRINTER_INFO_2A*)pPrinter;
2981     HANDLE ret;
2982
2983     TRACE("(%s,%d,%p): stub\n", debugstr_a(pName), Level, pPrinter);
2984     if(Level != 2) {
2985         ERR("Level = %d, unsupported!\n", Level);
2986         SetLastError(ERROR_INVALID_LEVEL);
2987         return 0;
2988     }
2989     pwstrNameW = asciitounicode(&pNameW,pName);
2990     piW = PRINTER_INFO_2AtoW(GetProcessHeap(), piA);
2991
2992     ret = AddPrinterW(pwstrNameW, Level, (LPBYTE)piW);
2993
2994     FREE_PRINTER_INFO_2W(GetProcessHeap(), piW);
2995     RtlFreeUnicodeString(&pNameW);
2996     return ret;
2997 }
2998
2999
3000 /*****************************************************************************
3001  *          ClosePrinter  [WINSPOOL.@]
3002  */
3003 BOOL WINAPI ClosePrinter(HANDLE hPrinter)
3004 {
3005     UINT_PTR i = (UINT_PTR)hPrinter;
3006     opened_printer_t *printer = NULL;
3007     BOOL ret = FALSE;
3008
3009     TRACE("(%p)\n", hPrinter);
3010
3011     EnterCriticalSection(&printer_handles_cs);
3012
3013     if ((i > 0) && (i <= nb_printer_handles))
3014         printer = printer_handles[i - 1];
3015
3016
3017     if(printer)
3018     {
3019         struct list *cursor, *cursor2;
3020
3021         TRACE("%p: %s (hXcv: %p) for %s (doc: %p)\n", printer->pm,
3022                 debugstr_w(printer->pm ? printer->pm->dllname : NULL),
3023                 printer->hXcv, debugstr_w(printer->name), printer->doc );
3024
3025         if(printer->doc)
3026             EndDocPrinter(hPrinter);
3027
3028         if(InterlockedDecrement(&printer->queue->ref) == 0)
3029         {
3030             LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
3031             {
3032                 job_t *job = LIST_ENTRY(cursor, job_t, entry);
3033                 ScheduleJob(hPrinter, job->job_id);
3034             }
3035             HeapFree(GetProcessHeap(), 0, printer->queue);
3036         }
3037         if (printer->hXcv) printer->pm->monitor->pfnXcvClosePort(printer->hXcv);
3038         monitor_unload(printer->pm);
3039         HeapFree(GetProcessHeap(), 0, printer->printername);
3040         HeapFree(GetProcessHeap(), 0, printer->name);
3041         HeapFree(GetProcessHeap(), 0, printer);
3042         printer_handles[i - 1] = NULL;
3043         ret = TRUE;
3044     }
3045     LeaveCriticalSection(&printer_handles_cs);
3046     return ret;
3047 }
3048
3049 /*****************************************************************************
3050  *          DeleteFormA  [WINSPOOL.@]
3051  */
3052 BOOL WINAPI DeleteFormA(HANDLE hPrinter, LPSTR pFormName)
3053 {
3054     FIXME("(%p,%s): stub\n", hPrinter, pFormName);
3055     return 1;
3056 }
3057
3058 /*****************************************************************************
3059  *          DeleteFormW  [WINSPOOL.@]
3060  */
3061 BOOL WINAPI DeleteFormW(HANDLE hPrinter, LPWSTR pFormName)
3062 {
3063     FIXME("(%p,%s): stub\n", hPrinter, debugstr_w(pFormName));
3064     return 1;
3065 }
3066
3067 /*****************************************************************************
3068  *          DeletePrinter  [WINSPOOL.@]
3069  */
3070 BOOL WINAPI DeletePrinter(HANDLE hPrinter)
3071 {
3072     LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
3073     HKEY hkeyPrinters, hkey;
3074
3075     if(!lpNameW) {
3076         SetLastError(ERROR_INVALID_HANDLE);
3077         return FALSE;
3078     }
3079     if(RegOpenKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
3080         WINSPOOL_SHDeleteKeyW(hkeyPrinters, lpNameW);
3081         RegCloseKey(hkeyPrinters);
3082     }
3083     WriteProfileStringW(devicesW, lpNameW, NULL);
3084     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
3085         RegDeleteValueW(hkey, lpNameW);
3086         RegCloseKey(hkey);
3087     }
3088     return TRUE;
3089 }
3090
3091 /*****************************************************************************
3092  *          SetPrinterA  [WINSPOOL.@]
3093  */
3094 BOOL WINAPI SetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
3095                            DWORD Command)
3096 {
3097     FIXME("(%p,%d,%p,%d): stub\n",hPrinter,Level,pPrinter,Command);
3098     return FALSE;
3099 }
3100
3101 /*****************************************************************************
3102  *          SetJobA  [WINSPOOL.@]
3103  */
3104 BOOL WINAPI SetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level,
3105                     LPBYTE pJob, DWORD Command)
3106 {
3107     BOOL ret;
3108     LPBYTE JobW;
3109     UNICODE_STRING usBuffer;
3110
3111     TRACE("(%p, %d, %d, %p, %d)\n",hPrinter, JobId, Level, pJob, Command);
3112
3113     /* JobId, pPrinterName, pMachineName, pDriverName, Size, Submitted, Time and TotalPages
3114        are all ignored by SetJob, so we don't bother copying them */
3115     switch(Level)
3116     {
3117     case 0:
3118         JobW = NULL;
3119         break;
3120     case 1:
3121       {
3122         JOB_INFO_1W *info1W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info1W));
3123         JOB_INFO_1A *info1A = (JOB_INFO_1A*)pJob;
3124
3125         JobW = (LPBYTE)info1W;
3126         info1W->pUserName = asciitounicode(&usBuffer, info1A->pUserName);
3127         info1W->pDocument = asciitounicode(&usBuffer, info1A->pDocument);
3128         info1W->pDatatype = asciitounicode(&usBuffer, info1A->pDatatype);
3129         info1W->pStatus = asciitounicode(&usBuffer, info1A->pStatus);
3130         info1W->Status = info1A->Status;
3131         info1W->Priority = info1A->Priority;
3132         info1W->Position = info1A->Position;
3133         info1W->PagesPrinted = info1A->PagesPrinted;
3134         break;
3135       }
3136     case 2:
3137       {
3138         JOB_INFO_2W *info2W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info2W));
3139         JOB_INFO_2A *info2A = (JOB_INFO_2A*)pJob;
3140
3141         JobW = (LPBYTE)info2W;
3142         info2W->pUserName = asciitounicode(&usBuffer, info2A->pUserName);
3143         info2W->pDocument = asciitounicode(&usBuffer, info2A->pDocument);
3144         info2W->pNotifyName = asciitounicode(&usBuffer, info2A->pNotifyName);
3145         info2W->pDatatype = asciitounicode(&usBuffer, info2A->pDatatype);
3146         info2W->pPrintProcessor = asciitounicode(&usBuffer, info2A->pPrintProcessor);
3147         info2W->pParameters = asciitounicode(&usBuffer, info2A->pParameters);
3148         info2W->pDevMode = info2A->pDevMode ? GdiConvertToDevmodeW(info2A->pDevMode) : NULL;
3149         info2W->pStatus = asciitounicode(&usBuffer, info2A->pStatus);
3150         info2W->pSecurityDescriptor = info2A->pSecurityDescriptor;
3151         info2W->Status = info2A->Status;
3152         info2W->Priority = info2A->Priority;
3153         info2W->Position = info2A->Position;
3154         info2W->StartTime = info2A->StartTime;
3155         info2W->UntilTime = info2A->UntilTime;
3156         info2W->PagesPrinted = info2A->PagesPrinted;
3157         break;
3158       }
3159     case 3:
3160         JobW = HeapAlloc(GetProcessHeap(), 0, sizeof(JOB_INFO_3));
3161         memcpy(JobW, pJob, sizeof(JOB_INFO_3));
3162         break;
3163     default:
3164         SetLastError(ERROR_INVALID_LEVEL);
3165         return FALSE;
3166     }
3167
3168     ret = SetJobW(hPrinter, JobId, Level, JobW, Command);
3169
3170     switch(Level)
3171     {
3172     case 1:
3173       {
3174         JOB_INFO_1W *info1W = (JOB_INFO_1W*)JobW;
3175         HeapFree(GetProcessHeap(), 0, info1W->pUserName);
3176         HeapFree(GetProcessHeap(), 0, info1W->pDocument); 
3177         HeapFree(GetProcessHeap(), 0, info1W->pDatatype);
3178         HeapFree(GetProcessHeap(), 0, info1W->pStatus);
3179         break;
3180       }
3181     case 2:
3182       {
3183         JOB_INFO_2W *info2W = (JOB_INFO_2W*)JobW;
3184         HeapFree(GetProcessHeap(), 0, info2W->pUserName);
3185         HeapFree(GetProcessHeap(), 0, info2W->pDocument); 
3186         HeapFree(GetProcessHeap(), 0, info2W->pNotifyName);
3187         HeapFree(GetProcessHeap(), 0, info2W->pDatatype);
3188         HeapFree(GetProcessHeap(), 0, info2W->pPrintProcessor);
3189         HeapFree(GetProcessHeap(), 0, info2W->pParameters);
3190         HeapFree(GetProcessHeap(), 0, info2W->pDevMode);
3191         HeapFree(GetProcessHeap(), 0, info2W->pStatus);
3192         break;
3193       }
3194     }
3195     HeapFree(GetProcessHeap(), 0, JobW);
3196
3197     return ret;
3198 }
3199
3200 /*****************************************************************************
3201  *          SetJobW  [WINSPOOL.@]
3202  */
3203 BOOL WINAPI SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level,
3204                     LPBYTE pJob, DWORD Command)
3205 {
3206     BOOL ret = FALSE;
3207     job_t *job;
3208
3209     TRACE("(%p, %d, %d, %p, %d)\n", hPrinter, JobId, Level, pJob, Command);
3210     FIXME("Ignoring everything other than document title\n");
3211
3212     EnterCriticalSection(&printer_handles_cs);
3213     job = get_job(hPrinter, JobId);
3214     if(!job)
3215         goto end;
3216
3217     switch(Level)
3218     {
3219     case 0:
3220         break;
3221     case 1:
3222       {
3223         JOB_INFO_1W *info1 = (JOB_INFO_1W*)pJob;
3224         HeapFree(GetProcessHeap(), 0, job->document_title);
3225         job->document_title = strdupW(info1->pDocument);
3226         break;
3227       }
3228     case 2:
3229       {
3230         JOB_INFO_2W *info2 = (JOB_INFO_2W*)pJob;
3231         HeapFree(GetProcessHeap(), 0, job->document_title);
3232         job->document_title = strdupW(info2->pDocument);
3233         break;
3234       }
3235     case 3:
3236         break;
3237     default:
3238         SetLastError(ERROR_INVALID_LEVEL);
3239         goto end;
3240     }
3241     ret = TRUE;
3242 end:
3243     LeaveCriticalSection(&printer_handles_cs);
3244     return ret;
3245 }
3246
3247 /*****************************************************************************
3248  *          EndDocPrinter  [WINSPOOL.@]
3249  */
3250 BOOL WINAPI EndDocPrinter(HANDLE hPrinter)
3251 {
3252     opened_printer_t *printer;
3253     BOOL ret = FALSE;
3254     TRACE("(%p)\n", hPrinter);
3255
3256     EnterCriticalSection(&printer_handles_cs);
3257
3258     printer = get_opened_printer(hPrinter);
3259     if(!printer)
3260     {
3261         SetLastError(ERROR_INVALID_HANDLE);
3262         goto end;
3263     }
3264
3265     if(!printer->doc)    
3266     {
3267         SetLastError(ERROR_SPL_NO_STARTDOC);
3268         goto end;
3269     }
3270
3271     CloseHandle(printer->doc->hf);
3272     ScheduleJob(hPrinter, printer->doc->job_id);
3273     HeapFree(GetProcessHeap(), 0, printer->doc);
3274     printer->doc = NULL;
3275     ret = TRUE;
3276 end:
3277     LeaveCriticalSection(&printer_handles_cs);
3278     return ret;
3279 }
3280
3281 /*****************************************************************************
3282  *          EndPagePrinter  [WINSPOOL.@]
3283  */
3284 BOOL WINAPI EndPagePrinter(HANDLE hPrinter)
3285 {
3286     FIXME("(%p): stub\n", hPrinter);
3287     return TRUE;
3288 }
3289
3290 /*****************************************************************************
3291  *          StartDocPrinterA  [WINSPOOL.@]
3292  */
3293 DWORD WINAPI StartDocPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
3294 {
3295     UNICODE_STRING usBuffer;
3296     DOC_INFO_2W doc2W;
3297     DOC_INFO_2A *doc2 = (DOC_INFO_2A*)pDocInfo;
3298     DWORD ret;
3299
3300     /* DOC_INFO_1, 2 and 3 all have the strings in the same place with either two (DOC_INFO_2)
3301        or one (DOC_INFO_3) extra DWORDs */
3302
3303     switch(Level) {
3304     case 2:
3305         doc2W.JobId = doc2->JobId;
3306         /* fall through */
3307     case 3:
3308         doc2W.dwMode = doc2->dwMode;
3309         /* fall through */
3310     case 1:
3311         doc2W.pDocName = asciitounicode(&usBuffer, doc2->pDocName);
3312         doc2W.pOutputFile = asciitounicode(&usBuffer, doc2->pOutputFile);
3313         doc2W.pDatatype = asciitounicode(&usBuffer, doc2->pDatatype);
3314         break;
3315
3316     default:
3317         SetLastError(ERROR_INVALID_LEVEL);
3318         return FALSE;
3319     }
3320
3321     ret = StartDocPrinterW(hPrinter, Level, (LPBYTE)&doc2W);
3322
3323     HeapFree(GetProcessHeap(), 0, doc2W.pDatatype);
3324     HeapFree(GetProcessHeap(), 0, doc2W.pOutputFile);
3325     HeapFree(GetProcessHeap(), 0, doc2W.pDocName);
3326
3327     return ret;
3328 }
3329
3330 /*****************************************************************************
3331  *          StartDocPrinterW  [WINSPOOL.@]
3332  */
3333 DWORD WINAPI StartDocPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
3334 {
3335     DOC_INFO_2W *doc = (DOC_INFO_2W *)pDocInfo;
3336     opened_printer_t *printer;
3337     BYTE addjob_buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
3338     ADDJOB_INFO_1W *addjob = (ADDJOB_INFO_1W*) addjob_buf;
3339     JOB_INFO_1W job_info;
3340     DWORD needed, ret = 0;
3341     HANDLE hf;
3342     WCHAR *filename;
3343
3344     TRACE("(hPrinter = %p, Level = %d, pDocInfo = %p {pDocName = %s, pOutputFile = %s, pDatatype = %s}):\n",
3345           hPrinter, Level, doc, debugstr_w(doc->pDocName), debugstr_w(doc->pOutputFile),
3346           debugstr_w(doc->pDatatype));
3347
3348     if(Level < 1 || Level > 3)
3349     {
3350         SetLastError(ERROR_INVALID_LEVEL);
3351         return 0;
3352     }
3353
3354     EnterCriticalSection(&printer_handles_cs);
3355     printer = get_opened_printer(hPrinter);
3356     if(!printer)
3357     {
3358         SetLastError(ERROR_INVALID_HANDLE);
3359         goto end;
3360     }
3361
3362     if(printer->doc)
3363     {
3364         SetLastError(ERROR_INVALID_PRINTER_STATE);
3365         goto end;
3366     }
3367
3368     /* Even if we're printing to a file we still add a print job, we'll
3369        just ignore the spool file name */
3370
3371     if(!AddJobW(hPrinter, 1, addjob_buf, sizeof(addjob_buf), &needed))
3372     {
3373         ERR("AddJob failed gle %u\n", GetLastError());
3374         goto end;
3375     }
3376
3377     if(doc->pOutputFile)
3378         filename = doc->pOutputFile;
3379     else
3380         filename = addjob->Path;
3381
3382     hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3383     if(hf == INVALID_HANDLE_VALUE)
3384         goto end;
3385
3386     memset(&job_info, 0, sizeof(job_info));
3387     job_info.pDocument = doc->pDocName;
3388     SetJobW(hPrinter, addjob->JobId, 1, (LPBYTE)&job_info, 0);
3389
3390     printer->doc = HeapAlloc(GetProcessHeap(), 0, sizeof(*printer->doc));
3391     printer->doc->hf = hf;
3392     ret = printer->doc->job_id = addjob->JobId;
3393 end:
3394     LeaveCriticalSection(&printer_handles_cs);
3395
3396     return ret;
3397 }
3398
3399 /*****************************************************************************
3400  *          StartPagePrinter  [WINSPOOL.@]
3401  */
3402 BOOL WINAPI StartPagePrinter(HANDLE hPrinter)
3403 {
3404     FIXME("(%p): stub\n", hPrinter);
3405     return TRUE;
3406 }
3407
3408 /*****************************************************************************
3409  *          GetFormA  [WINSPOOL.@]
3410  */
3411 BOOL WINAPI GetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
3412                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
3413 {
3414     FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,pFormName,
3415          Level,pForm,cbBuf,pcbNeeded);
3416     return FALSE;
3417 }
3418
3419 /*****************************************************************************
3420  *          GetFormW  [WINSPOOL.@]
3421  */
3422 BOOL WINAPI GetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
3423                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
3424 {
3425     FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,
3426           debugstr_w(pFormName),Level,pForm,cbBuf,pcbNeeded);
3427     return FALSE;
3428 }
3429
3430 /*****************************************************************************
3431  *          SetFormA  [WINSPOOL.@]
3432  */
3433 BOOL WINAPI SetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
3434                         LPBYTE pForm)
3435 {
3436     FIXME("(%p,%s,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
3437     return FALSE;
3438 }
3439
3440 /*****************************************************************************
3441  *          SetFormW  [WINSPOOL.@]
3442  */
3443 BOOL WINAPI SetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
3444                         LPBYTE pForm)
3445 {
3446     FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
3447     return FALSE;
3448 }
3449
3450 /*****************************************************************************
3451  *          ReadPrinter  [WINSPOOL.@]
3452  */
3453 BOOL WINAPI ReadPrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf,
3454                            LPDWORD pNoBytesRead)
3455 {
3456     FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pBuf,cbBuf,pNoBytesRead);
3457     return FALSE;
3458 }
3459
3460 /*****************************************************************************
3461  *          ResetPrinterA  [WINSPOOL.@]
3462  */
3463 BOOL WINAPI ResetPrinterA(HANDLE hPrinter, LPPRINTER_DEFAULTSA pDefault)
3464 {
3465     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
3466     return FALSE;
3467 }
3468
3469 /*****************************************************************************
3470  *          ResetPrinterW  [WINSPOOL.@]
3471  */
3472 BOOL WINAPI ResetPrinterW(HANDLE hPrinter, LPPRINTER_DEFAULTSW pDefault)
3473 {
3474     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
3475     return FALSE;
3476 }
3477
3478 /*****************************************************************************
3479  *    WINSPOOL_GetDWORDFromReg
3480  *
3481  * Return DWORD associated with ValueName from hkey.
3482  */
3483 static DWORD WINSPOOL_GetDWORDFromReg(HKEY hkey, LPCSTR ValueName)
3484 {
3485     DWORD sz = sizeof(DWORD), type, value = 0;
3486     LONG ret;
3487
3488     ret = RegQueryValueExA(hkey, ValueName, 0, &type, (LPBYTE)&value, &sz);
3489
3490     if(ret != ERROR_SUCCESS) {
3491         WARN("Got ret = %d on name %s\n", ret, ValueName);
3492         return 0;
3493     }
3494     if(type != REG_DWORD) {
3495         ERR("Got type %d\n", type);
3496         return 0;
3497     }
3498     return value;
3499 }
3500
3501 /*****************************************************************************
3502  *    WINSPOOL_GetStringFromReg
3503  *
3504  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
3505  * String is stored either as unicode or ascii.
3506  * Bit of a hack here to get the ValueName if we want ascii.
3507  */
3508 static BOOL WINSPOOL_GetStringFromReg(HKEY hkey, LPCWSTR ValueName, LPBYTE ptr,
3509                                       DWORD buflen, DWORD *needed,
3510                                       BOOL unicode)
3511 {
3512     DWORD sz = buflen, type;
3513     LONG ret;
3514
3515     if(unicode)
3516         ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3517     else {
3518         LPSTR ValueNameA = strdupWtoA(ValueName);
3519         ret = RegQueryValueExA(hkey, ValueNameA, 0, &type, ptr, &sz);
3520         HeapFree(GetProcessHeap(),0,ValueNameA);
3521     }
3522     if(ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) {
3523         WARN("Got ret = %d\n", ret);
3524         *needed = 0;
3525         return FALSE;
3526     }
3527     /* add space for terminating '\0' */
3528     sz += unicode ? sizeof(WCHAR) : 1;
3529     *needed = sz;
3530
3531     if (ptr)
3532         TRACE("%s: %s\n", debugstr_w(ValueName), unicode ? debugstr_w((LPCWSTR)ptr) : debugstr_a((LPCSTR)ptr));
3533
3534     return TRUE;
3535 }
3536
3537 /*****************************************************************************
3538  *    WINSPOOL_GetDefaultDevMode
3539  *
3540  * Get a default DevMode values for wineps.
3541  * FIXME - use ppd.
3542  */
3543
3544 static void WINSPOOL_GetDefaultDevMode(
3545         LPBYTE ptr,
3546         DWORD buflen, DWORD *needed,
3547         BOOL unicode)
3548 {
3549     DEVMODEA    dm;
3550     static const char szwps[] = "wineps.drv";
3551
3552         /* fill default DEVMODE - should be read from ppd... */
3553         ZeroMemory( &dm, sizeof(dm) );
3554         memcpy(dm.dmDeviceName,szwps,sizeof szwps);
3555         dm.dmSpecVersion = DM_SPECVERSION;
3556         dm.dmDriverVersion = 1;
3557         dm.dmSize = sizeof(DEVMODEA);
3558         dm.dmDriverExtra = 0;
3559         dm.dmFields =
3560                 DM_ORIENTATION | DM_PAPERSIZE |
3561                 DM_PAPERLENGTH | DM_PAPERWIDTH |
3562                 DM_SCALE |
3563                 DM_COPIES |
3564                 DM_DEFAULTSOURCE | DM_PRINTQUALITY |
3565                 DM_YRESOLUTION | DM_TTOPTION;
3566
3567         dm.u1.s1.dmOrientation = DMORIENT_PORTRAIT;
3568         dm.u1.s1.dmPaperSize = DMPAPER_A4;
3569         dm.u1.s1.dmPaperLength = 2970;
3570         dm.u1.s1.dmPaperWidth = 2100;
3571
3572         dm.dmScale = 100;
3573         dm.dmCopies = 1;
3574         dm.dmDefaultSource = DMBIN_AUTO;
3575         dm.dmPrintQuality = DMRES_MEDIUM;
3576         /* dm.dmColor */
3577         /* dm.dmDuplex */
3578         dm.dmYResolution = 300; /* 300dpi */
3579         dm.dmTTOption = DMTT_BITMAP;
3580         /* dm.dmCollate */
3581         /* dm.dmFormName */
3582         /* dm.dmLogPixels */
3583         /* dm.dmBitsPerPel */
3584         /* dm.dmPelsWidth */
3585         /* dm.dmPelsHeight */
3586         /* dm.dmDisplayFlags */
3587         /* dm.dmDisplayFrequency */
3588         /* dm.dmICMMethod */
3589         /* dm.dmICMIntent */
3590         /* dm.dmMediaType */
3591         /* dm.dmDitherType */
3592         /* dm.dmReserved1 */
3593         /* dm.dmReserved2 */
3594         /* dm.dmPanningWidth */
3595         /* dm.dmPanningHeight */
3596
3597     if(unicode) {
3598         if(buflen >= sizeof(DEVMODEW)) {
3599             DEVMODEW *pdmW = GdiConvertToDevmodeW(&dm);
3600             memcpy(ptr, pdmW, sizeof(DEVMODEW));
3601             HeapFree(GetProcessHeap(),0,pdmW);
3602         }
3603         *needed = sizeof(DEVMODEW);
3604     }
3605     else
3606     {
3607         if(buflen >= sizeof(DEVMODEA)) {
3608             memcpy(ptr, &dm, sizeof(DEVMODEA));
3609         }
3610         *needed = sizeof(DEVMODEA);
3611     }
3612 }
3613
3614 /*****************************************************************************
3615  *    WINSPOOL_GetDevModeFromReg
3616  *
3617  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
3618  * DevMode is stored either as unicode or ascii.
3619  */
3620 static BOOL WINSPOOL_GetDevModeFromReg(HKEY hkey, LPCWSTR ValueName,
3621                                        LPBYTE ptr,
3622                                        DWORD buflen, DWORD *needed,
3623                                        BOOL unicode)
3624 {
3625     DWORD sz = buflen, type;
3626     LONG ret;
3627
3628     if (ptr && buflen>=sizeof(DEVMODEA)) memset(ptr, 0, sizeof(DEVMODEA));
3629     ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3630     if ((ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA)) sz = 0;
3631     if (sz < sizeof(DEVMODEA))
3632     {
3633         TRACE("corrupted registry for %s ( size %d)\n",debugstr_w(ValueName),sz);
3634         return FALSE;
3635     }
3636     /* ensures that dmSize is not erratically bogus if registry is invalid */
3637     if (ptr && ((DEVMODEA*)ptr)->dmSize < sizeof(DEVMODEA))
3638         ((DEVMODEA*)ptr)->dmSize = sizeof(DEVMODEA);
3639     if(unicode) {
3640         sz += (CCHDEVICENAME + CCHFORMNAME);
3641         if(buflen >= sz) {
3642             DEVMODEW *dmW = GdiConvertToDevmodeW((DEVMODEA*)ptr);
3643             memcpy(ptr, dmW, sz);
3644             HeapFree(GetProcessHeap(),0,dmW);
3645         }
3646     }
3647     *needed = sz;
3648     return TRUE;
3649 }
3650
3651 /*********************************************************************
3652  *    WINSPOOL_GetPrinter_2
3653  *
3654  * Fills out a PRINTER_INFO_2A|W struct storing the strings in buf.
3655  * The strings are either stored as unicode or ascii.
3656  */
3657 static BOOL WINSPOOL_GetPrinter_2(HKEY hkeyPrinter, PRINTER_INFO_2W *pi2,
3658                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3659                                   BOOL unicode)
3660 {
3661     DWORD size, left = cbBuf;
3662     BOOL space = (cbBuf > 0);
3663     LPBYTE ptr = buf;
3664
3665     *pcbNeeded = 0;
3666
3667     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3668                                  unicode)) {
3669         if(space && size <= left) {
3670             pi2->pPrinterName = (LPWSTR)ptr;
3671             ptr += size;
3672             left -= size;
3673         } else
3674             space = FALSE;
3675         *pcbNeeded += size;
3676     }
3677     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Share_NameW, ptr, left, &size,
3678                                  unicode)) {
3679         if(space && size <= left) {
3680             pi2->pShareName = (LPWSTR)ptr;
3681             ptr += size;
3682             left -= size;
3683         } else
3684             space = FALSE;
3685         *pcbNeeded += size;
3686     }
3687     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
3688                                  unicode)) {
3689         if(space && size <= left) {
3690             pi2->pPortName = (LPWSTR)ptr;
3691             ptr += size;
3692             left -= size;
3693         } else
3694             space = FALSE;
3695         *pcbNeeded += size;
3696     }
3697     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Printer_DriverW, ptr, left,
3698                                  &size, unicode)) {
3699         if(space && size <= left) {
3700             pi2->pDriverName = (LPWSTR)ptr;
3701             ptr += size;
3702             left -= size;
3703         } else
3704             space = FALSE;
3705         *pcbNeeded += size;
3706     }
3707     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size,
3708                                  unicode)) {
3709         if(space && size <= left) {
3710             pi2->pComment = (LPWSTR)ptr;
3711             ptr += size;
3712             left -= size;
3713         } else
3714             space = FALSE;
3715         *pcbNeeded += size;
3716     }
3717     if(WINSPOOL_GetStringFromReg(hkeyPrinter, LocationW, ptr, left, &size,
3718                                  unicode)) {
3719         if(space && size <= left) {
3720             pi2->pLocation = (LPWSTR)ptr;
3721             ptr += size;
3722             left -= size;
3723         } else
3724             space = FALSE;
3725         *pcbNeeded += size;
3726     }
3727     if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, ptr, left,
3728                                   &size, unicode)) {
3729         if(space && size <= left) {
3730             pi2->pDevMode = (LPDEVMODEW)ptr;
3731             ptr += size;
3732             left -= size;
3733         } else
3734             space = FALSE;
3735         *pcbNeeded += size;
3736     }
3737     else
3738     {
3739         WINSPOOL_GetDefaultDevMode(ptr, left, &size, unicode);
3740         if(space && size <= left) {
3741             pi2->pDevMode = (LPDEVMODEW)ptr;
3742             ptr += size;
3743             left -= size;
3744         } else
3745             space = FALSE;
3746         *pcbNeeded += size;
3747     }
3748     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Separator_FileW, ptr, left,
3749                                  &size, unicode)) {
3750         if(space && size <= left) {
3751             pi2->pSepFile = (LPWSTR)ptr;
3752             ptr += size;
3753             left -= size;
3754         } else
3755             space = FALSE;
3756         *pcbNeeded += size;
3757     }
3758     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Print_ProcessorW, ptr, left,
3759                                  &size, unicode)) {
3760         if(space && size <= left) {
3761             pi2->pPrintProcessor = (LPWSTR)ptr;
3762             ptr += size;
3763             left -= size;
3764         } else
3765             space = FALSE;
3766         *pcbNeeded += size;
3767     }
3768     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DatatypeW, ptr, left,
3769                                  &size, unicode)) {
3770         if(space && size <= left) {
3771             pi2->pDatatype = (LPWSTR)ptr;
3772             ptr += size;
3773             left -= size;
3774         } else
3775             space = FALSE;
3776         *pcbNeeded += size;
3777     }
3778     if(WINSPOOL_GetStringFromReg(hkeyPrinter, ParametersW, ptr, left,
3779                                  &size, unicode)) {
3780         if(space && size <= left) {
3781             pi2->pParameters = (LPWSTR)ptr;
3782             ptr += size;
3783             left -= size;
3784         } else
3785             space = FALSE;
3786         *pcbNeeded += size;
3787     }
3788     if(pi2) {
3789         pi2->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3790         pi2->Priority = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Priority");
3791         pi2->DefaultPriority = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3792                                                         "Default Priority");
3793         pi2->StartTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "StartTime");
3794         pi2->UntilTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "UntilTime");
3795     }
3796
3797     if(!space && pi2) /* zero out pi2 if we can't completely fill buf */
3798         memset(pi2, 0, sizeof(*pi2));
3799
3800     return space;
3801 }
3802
3803 /*********************************************************************
3804  *    WINSPOOL_GetPrinter_4
3805  *
3806  * Fills out a PRINTER_INFO_4 struct storing the strings in buf.
3807  */
3808 static BOOL WINSPOOL_GetPrinter_4(HKEY hkeyPrinter, PRINTER_INFO_4W *pi4,
3809                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3810                                   BOOL unicode)
3811 {
3812     DWORD size, left = cbBuf;
3813     BOOL space = (cbBuf > 0);
3814     LPBYTE ptr = buf;
3815
3816     *pcbNeeded = 0;
3817
3818     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3819                                  unicode)) {
3820         if(space && size <= left) {
3821             pi4->pPrinterName = (LPWSTR)ptr;
3822             ptr += size;
3823             left -= size;
3824         } else
3825             space = FALSE;
3826         *pcbNeeded += size;
3827     }
3828     if(pi4) {
3829         pi4->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3830     }
3831
3832     if(!space && pi4) /* zero out pi4 if we can't completely fill buf */
3833         memset(pi4, 0, sizeof(*pi4));
3834
3835     return space;
3836 }
3837
3838 /*********************************************************************
3839  *    WINSPOOL_GetPrinter_5
3840  *
3841  * Fills out a PRINTER_INFO_5 struct storing the strings in buf.
3842  */
3843 static BOOL WINSPOOL_GetPrinter_5(HKEY hkeyPrinter, PRINTER_INFO_5W *pi5,
3844                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3845                                   BOOL unicode)
3846 {
3847     DWORD size, left = cbBuf;
3848     BOOL space = (cbBuf > 0);
3849     LPBYTE ptr = buf;
3850
3851     *pcbNeeded = 0;
3852
3853     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3854                                  unicode)) {
3855         if(space && size <= left) {
3856             pi5->pPrinterName = (LPWSTR)ptr;
3857             ptr += size;
3858             left -= size;
3859         } else
3860             space = FALSE;
3861         *pcbNeeded += size;
3862     }
3863     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
3864                                  unicode)) {
3865         if(space && size <= left) {
3866             pi5->pPortName = (LPWSTR)ptr;
3867             ptr += size;
3868             left -= size;
3869         } else
3870             space = FALSE;
3871         *pcbNeeded += size;
3872     }
3873     if(pi5) {
3874         pi5->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3875         pi5->DeviceNotSelectedTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3876                                                                 "dnsTimeout");
3877         pi5->TransmissionRetryTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3878                                                                  "txTimeout");
3879     }
3880
3881     if(!space && pi5) /* zero out pi5 if we can't completely fill buf */
3882         memset(pi5, 0, sizeof(*pi5));
3883
3884     return space;
3885 }
3886
3887 /*****************************************************************************
3888  *          WINSPOOL_GetPrinter
3889  *
3890  *    Implementation of GetPrinterA|W.  Relies on PRINTER_INFO_*W being
3891  *    essentially the same as PRINTER_INFO_*A. i.e. the structure itself is
3892  *    just a collection of pointers to strings.
3893  */
3894 static BOOL WINSPOOL_GetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
3895                                 DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
3896 {
3897     LPCWSTR name;
3898     DWORD size, needed = 0;
3899     LPBYTE ptr = NULL;
3900     HKEY hkeyPrinter, hkeyPrinters;
3901     BOOL ret;
3902
3903     TRACE("(%p,%d,%p,%d,%p)\n",hPrinter,Level,pPrinter,cbBuf, pcbNeeded);
3904
3905     if (!(name = get_opened_printer_name(hPrinter))) {
3906         SetLastError(ERROR_INVALID_HANDLE);
3907         return FALSE;
3908     }
3909
3910     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
3911        ERROR_SUCCESS) {
3912         ERR("Can't create Printers key\n");
3913         return FALSE;
3914     }
3915     if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter) != ERROR_SUCCESS)
3916     {
3917         ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
3918         RegCloseKey(hkeyPrinters);
3919         SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
3920         return FALSE;
3921     }
3922
3923     switch(Level) {
3924     case 2:
3925       {
3926         PRINTER_INFO_2W *pi2 = (PRINTER_INFO_2W *)pPrinter;
3927
3928         size = sizeof(PRINTER_INFO_2W);
3929         if(size <= cbBuf) {
3930             ptr = pPrinter + size;
3931             cbBuf -= size;
3932             memset(pPrinter, 0, size);
3933         } else {
3934             pi2 = NULL;
3935             cbBuf = 0;
3936         }
3937         ret = WINSPOOL_GetPrinter_2(hkeyPrinter, pi2, ptr, cbBuf, &needed,
3938                                     unicode);
3939         needed += size;
3940         break;
3941       }
3942
3943     case 4:
3944       {
3945         PRINTER_INFO_4W *pi4 = (PRINTER_INFO_4W *)pPrinter;
3946
3947         size = sizeof(PRINTER_INFO_4W);
3948         if(size <= cbBuf) {
3949             ptr = pPrinter + size;
3950             cbBuf -= size;
3951             memset(pPrinter, 0, size);
3952         } else {
3953             pi4 = NULL;
3954             cbBuf = 0;
3955         }
3956         ret = WINSPOOL_GetPrinter_4(hkeyPrinter, pi4, ptr, cbBuf, &needed,
3957                                     unicode);
3958         needed += size;
3959         break;
3960       }
3961
3962
3963     case 5:
3964       {
3965         PRINTER_INFO_5W *pi5 = (PRINTER_INFO_5W *)pPrinter;
3966
3967         size = sizeof(PRINTER_INFO_5W);
3968         if(size <= cbBuf) {
3969             ptr = pPrinter + size;
3970             cbBuf -= size;
3971             memset(pPrinter, 0, size);
3972         } else {
3973             pi5 = NULL;
3974             cbBuf = 0;
3975         }
3976
3977         ret = WINSPOOL_GetPrinter_5(hkeyPrinter, pi5, ptr, cbBuf, &needed,
3978                                     unicode);
3979         needed += size;
3980         break;
3981       }
3982
3983     default:
3984         FIXME("Unimplemented level %d\n", Level);
3985         SetLastError(ERROR_INVALID_LEVEL);
3986         RegCloseKey(hkeyPrinters);
3987         RegCloseKey(hkeyPrinter);
3988         return FALSE;
3989     }
3990
3991     RegCloseKey(hkeyPrinter);
3992     RegCloseKey(hkeyPrinters);
3993
3994     TRACE("returning %d needed = %d\n", ret, needed);
3995     if(pcbNeeded) *pcbNeeded = needed;
3996     if(!ret)
3997         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3998     return ret;
3999 }
4000
4001 /*****************************************************************************
4002  *          GetPrinterW  [WINSPOOL.@]
4003  */
4004 BOOL WINAPI GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
4005                         DWORD cbBuf, LPDWORD pcbNeeded)
4006 {
4007     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
4008                                TRUE);
4009 }
4010
4011 /*****************************************************************************
4012  *          GetPrinterA  [WINSPOOL.@]
4013  */
4014 BOOL WINAPI GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
4015                     DWORD cbBuf, LPDWORD pcbNeeded)
4016 {
4017     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
4018                                FALSE);
4019 }
4020
4021 /*****************************************************************************
4022  *          WINSPOOL_EnumPrinters
4023  *
4024  *    Implementation of EnumPrintersA|W
4025  */
4026 static BOOL WINSPOOL_EnumPrinters(DWORD dwType, LPWSTR lpszName,
4027                                   DWORD dwLevel, LPBYTE lpbPrinters,
4028                                   DWORD cbBuf, LPDWORD lpdwNeeded,
4029                                   LPDWORD lpdwReturned, BOOL unicode)
4030
4031 {
4032     HKEY hkeyPrinters, hkeyPrinter;
4033     WCHAR PrinterName[255];
4034     DWORD needed = 0, number = 0;
4035     DWORD used, i, left;
4036     PBYTE pi, buf;
4037
4038     if(lpbPrinters)
4039         memset(lpbPrinters, 0, cbBuf);
4040     if(lpdwReturned)
4041         *lpdwReturned = 0;
4042     if(lpdwNeeded)
4043         *lpdwNeeded = 0;
4044
4045     /* PRINTER_ENUM_DEFAULT is only supported under win9x, we behave like NT */
4046     if(dwType == PRINTER_ENUM_DEFAULT)
4047         return TRUE;
4048
4049     if (dwType & PRINTER_ENUM_CONNECTIONS) {
4050         TRACE("ignoring PRINTER_ENUM_CONNECTIONS\n");
4051         dwType &= ~PRINTER_ENUM_CONNECTIONS; /* we don't handle that */
4052         if (!dwType) {
4053             FIXME("We don't handle PRINTER_ENUM_CONNECTIONS\n");
4054             *lpdwNeeded = 0;
4055             *lpdwReturned = 0;
4056             return TRUE;
4057         }
4058
4059     }
4060
4061     if (!((dwType & PRINTER_ENUM_LOCAL) || (dwType & PRINTER_ENUM_NAME))) {
4062         FIXME("dwType = %08x\n", dwType);
4063         SetLastError(ERROR_INVALID_FLAGS);
4064         return FALSE;
4065     }
4066
4067     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
4068        ERROR_SUCCESS) {
4069         ERR("Can't create Printers key\n");
4070         return FALSE;
4071     }
4072
4073     if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &number, NULL, NULL,
4074                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
4075         RegCloseKey(hkeyPrinters);
4076         ERR("Can't query Printers key\n");
4077         return FALSE;
4078     }
4079     TRACE("Found %d printers\n", number);
4080
4081     switch(dwLevel) {
4082     case 1:
4083         RegCloseKey(hkeyPrinters);
4084         if (lpdwReturned)
4085             *lpdwReturned = number;
4086         return TRUE;
4087
4088     case 2:
4089         used = number * sizeof(PRINTER_INFO_2W);
4090         break;
4091     case 4:
4092         used = number * sizeof(PRINTER_INFO_4W);
4093         break;
4094     case 5:
4095         used = number * sizeof(PRINTER_INFO_5W);
4096         break;
4097
4098     default:
4099         SetLastError(ERROR_INVALID_LEVEL);
4100         RegCloseKey(hkeyPrinters);
4101         return FALSE;
4102     }
4103     pi = (used <= cbBuf) ? lpbPrinters : NULL;
4104
4105     for(i = 0; i < number; i++) {
4106         if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)) !=
4107            ERROR_SUCCESS) {
4108             ERR("Can't enum key number %d\n", i);
4109             RegCloseKey(hkeyPrinters);
4110             return FALSE;
4111         }
4112         TRACE("Printer %d is %s\n", i, debugstr_w(PrinterName));
4113         if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkeyPrinter) !=
4114            ERROR_SUCCESS) {
4115             ERR("Can't open key %s\n", debugstr_w(PrinterName));
4116             RegCloseKey(hkeyPrinters);
4117             return FALSE;
4118         }
4119
4120         if(cbBuf > used) {
4121             buf = lpbPrinters + used;
4122             left = cbBuf - used;
4123         } else {
4124             buf = NULL;
4125             left = 0;
4126         }
4127
4128         switch(dwLevel) {
4129         case 2:
4130             WINSPOOL_GetPrinter_2(hkeyPrinter, (PRINTER_INFO_2W *)pi, buf,
4131                                   left, &needed, unicode);
4132             used += needed;
4133             if(pi) pi += sizeof(PRINTER_INFO_2W);
4134             break;
4135         case 4:
4136             WINSPOOL_GetPrinter_4(hkeyPrinter, (PRINTER_INFO_4W *)pi, buf,
4137                                   left, &needed, unicode);
4138             used += needed;
4139             if(pi) pi += sizeof(PRINTER_INFO_4W);
4140             break;
4141         case 5:
4142             WINSPOOL_GetPrinter_5(hkeyPrinter, (PRINTER_INFO_5W *)pi, buf,
4143                                   left, &needed, unicode);
4144             used += needed;
4145             if(pi) pi += sizeof(PRINTER_INFO_5W);
4146             break;
4147         default:
4148             ERR("Shouldn't be here!\n");
4149             RegCloseKey(hkeyPrinter);
4150             RegCloseKey(hkeyPrinters);
4151             return FALSE;
4152         }
4153         RegCloseKey(hkeyPrinter);
4154     }
4155     RegCloseKey(hkeyPrinters);
4156
4157     if(lpdwNeeded)
4158         *lpdwNeeded = used;
4159
4160     if(used > cbBuf) {
4161         if(lpbPrinters)
4162             memset(lpbPrinters, 0, cbBuf);
4163         SetLastError(ERROR_INSUFFICIENT_BUFFER);
4164         return FALSE;
4165     }
4166     if(lpdwReturned)
4167         *lpdwReturned = number;
4168     SetLastError(ERROR_SUCCESS);
4169     return TRUE;
4170 }
4171
4172
4173 /******************************************************************
4174  *              EnumPrintersW        [WINSPOOL.@]
4175  *
4176  *    Enumerates the available printers, print servers and print
4177  *    providers, depending on the specified flags, name and level.
4178  *
4179  * RETURNS:
4180  *
4181  *    If level is set to 1:
4182  *      Not implemented yet!
4183  *      Returns TRUE with an empty list.
4184  *
4185  *    If level is set to 2:
4186  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
4187  *      Returns an array of PRINTER_INFO_2 data structures in the
4188  *      lpbPrinters buffer. Note that according to MSDN also an
4189  *      OpenPrinter should be performed on every remote printer.
4190  *
4191  *    If level is set to 4 (officially WinNT only):
4192  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
4193  *      Fast: Only the registry is queried to retrieve printer names,
4194  *      no connection to the driver is made.
4195  *      Returns an array of PRINTER_INFO_4 data structures in the
4196  *      lpbPrinters buffer.
4197  *
4198  *    If level is set to 5 (officially WinNT4/Win9x only):
4199  *      Fast: Only the registry is queried to retrieve printer names,
4200  *      no connection to the driver is made.
4201  *      Returns an array of PRINTER_INFO_5 data structures in the
4202  *      lpbPrinters buffer.
4203  *
4204  *    If level set to 3 or 6+:
4205  *          returns zero (failure!)
4206  *
4207  *    Returns nonzero (TRUE) on success, or zero on failure, use GetLastError
4208  *    for information.
4209  *
4210  * BUGS:
4211  *    - Only PRINTER_ENUM_LOCAL and PRINTER_ENUM_NAME are implemented.
4212  *    - Only levels 2, 4 and 5 are implemented at the moment.
4213  *    - 16-bit printer drivers are not enumerated.
4214  *    - Returned amount of bytes used/needed does not match the real Windoze
4215  *      implementation (as in this implementation, all strings are part
4216  *      of the buffer, whereas Win32 keeps them somewhere else)
4217  *    - At level 2, EnumPrinters should also call OpenPrinter for remote printers.
4218  *
4219  * NOTE:
4220  *    - In a regular Wine installation, no registry settings for printers
4221  *      exist, which makes this function return an empty list.
4222  */
4223 BOOL  WINAPI EnumPrintersW(
4224                 DWORD dwType,        /* [in] Types of print objects to enumerate */
4225                 LPWSTR lpszName,     /* [in] name of objects to enumerate */
4226                 DWORD dwLevel,       /* [in] type of printer info structure */
4227                 LPBYTE lpbPrinters,  /* [out] buffer which receives info */
4228                 DWORD cbBuf,         /* [in] max size of buffer in bytes */
4229                 LPDWORD lpdwNeeded,  /* [out] pointer to var: # bytes used/needed */
4230                 LPDWORD lpdwReturned /* [out] number of entries returned */
4231                 )
4232 {
4233     return WINSPOOL_EnumPrinters(dwType, lpszName, dwLevel, lpbPrinters, cbBuf,
4234                                  lpdwNeeded, lpdwReturned, TRUE);
4235 }
4236
4237 /******************************************************************
4238  *              EnumPrintersA        [WINSPOOL.@]
4239  *
4240  */
4241 BOOL WINAPI EnumPrintersA(DWORD dwType, LPSTR lpszName,
4242                           DWORD dwLevel, LPBYTE lpbPrinters,
4243                           DWORD cbBuf, LPDWORD lpdwNeeded,
4244                           LPDWORD lpdwReturned)
4245 {
4246     BOOL ret, unicode = FALSE;
4247     UNICODE_STRING lpszNameW;
4248     PWSTR pwstrNameW;
4249
4250     pwstrNameW = asciitounicode(&lpszNameW,lpszName);
4251     if(!cbBuf) unicode = TRUE; /* return a buffer that's big enough for the unicode version */
4252     ret = WINSPOOL_EnumPrinters(dwType, pwstrNameW, dwLevel, lpbPrinters, cbBuf,
4253                                 lpdwNeeded, lpdwReturned, unicode);
4254     RtlFreeUnicodeString(&lpszNameW);
4255     return ret;
4256 }
4257
4258 /*****************************************************************************
4259  *          WINSPOOL_GetDriverInfoFromReg [internal]
4260  *
4261  *    Enters the information from the registry into the DRIVER_INFO struct
4262  *
4263  * RETURNS
4264  *    zero if the printer driver does not exist in the registry
4265  *    (only if Level > 1) otherwise nonzero
4266  */
4267 static BOOL WINSPOOL_GetDriverInfoFromReg(
4268                             HKEY    hkeyDrivers,
4269                             LPWSTR  DriverName,
4270                             LPCWSTR pEnvironment,
4271                             DWORD   Level,
4272                             LPBYTE  ptr,            /* DRIVER_INFO */
4273                             LPBYTE  pDriverStrings, /* strings buffer */
4274                             DWORD   cbBuf,          /* size of string buffer */
4275                             LPDWORD pcbNeeded,      /* space needed for str. */
4276                             BOOL    unicode)        /* type of strings */
4277 {
4278     DWORD  size, tmp;
4279     HKEY   hkeyDriver;
4280     LPBYTE strPtr = pDriverStrings;
4281
4282     TRACE("%s,%s,%d,%p,%p,%d,%d\n",
4283           debugstr_w(DriverName), debugstr_w(pEnvironment),
4284           Level, ptr, pDriverStrings, cbBuf, unicode);
4285
4286     if(unicode) {
4287         *pcbNeeded = (lstrlenW(DriverName) + 1) * sizeof(WCHAR);
4288             if (*pcbNeeded <= cbBuf)
4289                strcpyW((LPWSTR)strPtr, DriverName);
4290     } else {
4291         *pcbNeeded = WideCharToMultiByte(CP_ACP, 0, DriverName, -1, NULL, 0,
4292                                           NULL, NULL);
4293         if(*pcbNeeded <= cbBuf)
4294             WideCharToMultiByte(CP_ACP, 0, DriverName, -1,
4295                                 (LPSTR)strPtr, *pcbNeeded, NULL, NULL);
4296     }
4297     if(Level == 1) {
4298        if(ptr)
4299           ((PDRIVER_INFO_1W) ptr)->pName = (LPWSTR) strPtr;
4300        return TRUE;
4301     } else {
4302        if(ptr)
4303           ((PDRIVER_INFO_2W) ptr)->pName = (LPWSTR) strPtr;
4304        strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4305     }
4306
4307     if(!DriverName[0] || RegOpenKeyW(hkeyDrivers, DriverName, &hkeyDriver) != ERROR_SUCCESS) {
4308         ERR("Can't find driver %s in registry\n", debugstr_w(DriverName));
4309         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER); /* ? */
4310         return FALSE;
4311     }
4312
4313     if(ptr)
4314         ((PDRIVER_INFO_2A) ptr)->cVersion = (GetVersion() & 0x80000000) ? 0 : 3; /* FIXME: add 1, 2 */
4315
4316     if(!pEnvironment)
4317         pEnvironment = DefaultEnvironmentW;
4318     if(unicode)
4319         size = (lstrlenW(pEnvironment) + 1) * sizeof(WCHAR);
4320     else
4321         size = WideCharToMultiByte(CP_ACP, 0, pEnvironment, -1, NULL, 0,
4322                                    NULL, NULL);
4323     *pcbNeeded += size;
4324     if(*pcbNeeded <= cbBuf) {
4325         if(unicode)
4326             strcpyW((LPWSTR)strPtr, pEnvironment);
4327         else
4328             WideCharToMultiByte(CP_ACP, 0, pEnvironment, -1,
4329                                 (LPSTR)strPtr, size, NULL, NULL);
4330         if(ptr)
4331             ((PDRIVER_INFO_2W) ptr)->pEnvironment = (LPWSTR)strPtr;
4332         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4333     }
4334
4335     if(WINSPOOL_GetStringFromReg(hkeyDriver, DriverW, strPtr, 0, &size,
4336                                  unicode)) {
4337         *pcbNeeded += size;
4338         if(*pcbNeeded <= cbBuf)
4339             WINSPOOL_GetStringFromReg(hkeyDriver, DriverW, strPtr, size, &tmp,
4340                                       unicode);
4341         if(ptr)
4342             ((PDRIVER_INFO_2W) ptr)->pDriverPath = (LPWSTR)strPtr;
4343         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4344     }
4345
4346     if(WINSPOOL_GetStringFromReg(hkeyDriver, Data_FileW, strPtr, 0, &size,
4347                                  unicode)) {
4348         *pcbNeeded += size;
4349         if(*pcbNeeded <= cbBuf)
4350             WINSPOOL_GetStringFromReg(hkeyDriver, Data_FileW, strPtr, size,
4351                                       &tmp, unicode);
4352         if(ptr)
4353             ((PDRIVER_INFO_2W) ptr)->pDataFile = (LPWSTR)strPtr;
4354         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4355     }
4356
4357     if(WINSPOOL_GetStringFromReg(hkeyDriver, Configuration_FileW, strPtr,
4358                                  0, &size, unicode)) {
4359         *pcbNeeded += size;
4360         if(*pcbNeeded <= cbBuf)
4361             WINSPOOL_GetStringFromReg(hkeyDriver, Configuration_FileW, strPtr,
4362                                       size, &tmp, unicode);
4363         if(ptr)
4364             ((PDRIVER_INFO_2W) ptr)->pConfigFile = (LPWSTR)strPtr;
4365         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4366     }
4367
4368     if(Level == 2 ) {
4369         RegCloseKey(hkeyDriver);
4370         TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4371         return TRUE;
4372     }
4373
4374     if (Level != 5 && WINSPOOL_GetStringFromReg(hkeyDriver, Help_FileW, strPtr, 0, &size,
4375                                  unicode)) {
4376         *pcbNeeded += size;
4377         if(*pcbNeeded <= cbBuf)
4378             WINSPOOL_GetStringFromReg(hkeyDriver, Help_FileW, strPtr,
4379                                       size, &tmp, unicode);
4380         if(ptr)
4381             ((PDRIVER_INFO_3W) ptr)->pHelpFile = (LPWSTR)strPtr;
4382         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4383     }
4384
4385     if (Level != 5 && WINSPOOL_GetStringFromReg(hkeyDriver, Dependent_FilesW, strPtr, 0,
4386                              &size, unicode)) {
4387         *pcbNeeded += size;
4388         if(*pcbNeeded <= cbBuf)
4389             WINSPOOL_GetStringFromReg(hkeyDriver, Dependent_FilesW, strPtr,
4390                                       size, &tmp, unicode);
4391         if(ptr)
4392             ((PDRIVER_INFO_3W) ptr)->pDependentFiles = (LPWSTR)strPtr;
4393         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4394     }
4395
4396     if (Level != 5 && WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, 0, &size,
4397                                  unicode)) {
4398         *pcbNeeded += size;
4399         if(*pcbNeeded <= cbBuf)
4400             WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr,
4401                                       size, &tmp, unicode);
4402         if(ptr)
4403             ((PDRIVER_INFO_3W) ptr)->pMonitorName = (LPWSTR)strPtr;
4404         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4405     }
4406
4407     if (Level != 5 && WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, 0, &size,
4408                                  unicode)) {
4409         *pcbNeeded += size;
4410         if(*pcbNeeded <= cbBuf)
4411             WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr,
4412                                       size, &tmp, unicode);
4413         if(ptr)
4414             ((PDRIVER_INFO_3W) ptr)->pDefaultDataType = (LPWSTR)strPtr;
4415         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4416     }
4417
4418     TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4419     RegCloseKey(hkeyDriver);
4420     return TRUE;
4421 }
4422
4423 /*****************************************************************************
4424  *          WINSPOOL_GetPrinterDriver
4425  */
4426 static BOOL WINSPOOL_GetPrinterDriver(HANDLE hPrinter, LPWSTR pEnvironment,
4427                                       DWORD Level, LPBYTE pDriverInfo,
4428                                       DWORD cbBuf, LPDWORD pcbNeeded,
4429                                       BOOL unicode)
4430 {
4431     LPCWSTR name;
4432     WCHAR DriverName[100];
4433     DWORD ret, type, size, needed = 0;
4434     LPBYTE ptr = NULL;
4435     HKEY hkeyPrinter, hkeyPrinters, hkeyDrivers;
4436
4437     TRACE("(%p,%s,%d,%p,%d,%p)\n",hPrinter,debugstr_w(pEnvironment),
4438           Level,pDriverInfo,cbBuf, pcbNeeded);
4439
4440     ZeroMemory(pDriverInfo, cbBuf);
4441
4442     if (!(name = get_opened_printer_name(hPrinter))) {
4443         SetLastError(ERROR_INVALID_HANDLE);
4444         return FALSE;
4445     }
4446     if(Level < 1 || Level > 6) {
4447         SetLastError(ERROR_INVALID_LEVEL);
4448         return FALSE;
4449     }
4450     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
4451        ERROR_SUCCESS) {
4452         ERR("Can't create Printers key\n");
4453         return FALSE;
4454     }
4455     if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter)
4456        != ERROR_SUCCESS) {
4457         ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
4458         RegCloseKey(hkeyPrinters);
4459         SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
4460         return FALSE;
4461     }
4462     size = sizeof(DriverName);
4463     DriverName[0] = 0;
4464     ret = RegQueryValueExW(hkeyPrinter, Printer_DriverW, 0, &type,
4465                            (LPBYTE)DriverName, &size);
4466     RegCloseKey(hkeyPrinter);
4467     RegCloseKey(hkeyPrinters);
4468     if(ret != ERROR_SUCCESS) {
4469         ERR("Can't get DriverName for printer %s\n", debugstr_w(name));
4470         return FALSE;
4471     }
4472
4473     hkeyDrivers = WINSPOOL_OpenDriverReg( pEnvironment, TRUE);
4474     if(!hkeyDrivers) {
4475         ERR("Can't create Drivers key\n");
4476         return FALSE;
4477     }
4478
4479     switch(Level) {
4480     case 1:
4481         size = sizeof(DRIVER_INFO_1W);
4482         break;
4483     case 2:
4484         size = sizeof(DRIVER_INFO_2W);
4485         break;
4486     case 3:
4487         size = sizeof(DRIVER_INFO_3W);
4488         break;
4489     case 4:
4490         size = sizeof(DRIVER_INFO_4W);
4491         break;
4492     case 5:
4493         size = sizeof(DRIVER_INFO_5W);
4494         break;
4495     case 6:
4496         size = sizeof(DRIVER_INFO_6W);
4497         break;
4498     default:
4499         ERR("Invalid level\n");
4500         return FALSE;
4501     }
4502
4503     if(size <= cbBuf)
4504         ptr = pDriverInfo + size;
4505
4506     if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverName,
4507                          pEnvironment, Level, pDriverInfo,
4508                          (cbBuf < size) ? NULL : ptr,
4509                          (cbBuf < size) ? 0 : cbBuf - size,
4510                          &needed, unicode)) {
4511             RegCloseKey(hkeyDrivers);
4512             return FALSE;
4513     }
4514
4515     RegCloseKey(hkeyDrivers);
4516
4517     if(pcbNeeded) *pcbNeeded = size + needed;
4518     TRACE("buffer space %d required %d\n", cbBuf, size + needed);
4519     if(cbBuf >= needed) return TRUE;
4520     SetLastError(ERROR_INSUFFICIENT_BUFFER);
4521     return FALSE;
4522 }
4523
4524 /*****************************************************************************
4525  *          GetPrinterDriverA  [WINSPOOL.@]
4526  */
4527 BOOL WINAPI GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment,
4528                               DWORD Level, LPBYTE pDriverInfo,
4529                               DWORD cbBuf, LPDWORD pcbNeeded)
4530 {
4531     BOOL ret;
4532     UNICODE_STRING pEnvW;
4533     PWSTR pwstrEnvW;
4534     
4535     pwstrEnvW = asciitounicode(&pEnvW, pEnvironment);
4536     ret = WINSPOOL_GetPrinterDriver(hPrinter, pwstrEnvW, Level, pDriverInfo,
4537                                     cbBuf, pcbNeeded, FALSE);
4538     RtlFreeUnicodeString(&pEnvW);
4539     return ret;
4540 }
4541 /*****************************************************************************
4542  *          GetPrinterDriverW  [WINSPOOL.@]
4543  */
4544 BOOL WINAPI GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment,
4545                                   DWORD Level, LPBYTE pDriverInfo,
4546                                   DWORD cbBuf, LPDWORD pcbNeeded)
4547 {
4548     return WINSPOOL_GetPrinterDriver(hPrinter, pEnvironment, Level,
4549                                      pDriverInfo, cbBuf, pcbNeeded, TRUE);
4550 }
4551
4552 /*****************************************************************************
4553  *       GetPrinterDriverDirectoryW  [WINSPOOL.@]
4554  *
4555  * Return the PATH for the Printer-Drivers (UNICODE)
4556  *
4557  * PARAMS
4558  *   pName            [I] Servername (NT only) or NULL (local Computer)
4559  *   pEnvironment     [I] Printing-Environment (see below) or NULL (Default)
4560  *   Level            [I] Structure-Level (must be 1)
4561  *   pDriverDirectory [O] PTR to Buffer that receives the Result
4562  *   cbBuf            [I] Size of Buffer at pDriverDirectory
4563  *   pcbNeeded        [O] PTR to DWORD that receives the size in Bytes used / 
4564  *                        required for pDriverDirectory
4565  *
4566  * RETURNS
4567  *   Success: TRUE  and in pcbNeeded the Bytes used in pDriverDirectory
4568  *   Failure: FALSE and in pcbNeeded the Bytes required for pDriverDirectory,
4569  *   if cbBuf is too small
4570  * 
4571  *   Native Values returned in pDriverDirectory on Success:
4572  *|  NT(Windows NT x86):  "%winsysdir%\\spool\\DRIVERS\\w32x86" 
4573  *|  NT(Windows 4.0):     "%winsysdir%\\spool\\DRIVERS\\win40" 
4574  *|  win9x(Windows 4.0):  "%winsysdir%" 
4575  *
4576  *   "%winsysdir%" is the Value from GetSystemDirectoryW()
4577  *
4578  * FIXME
4579  *-  Only NULL or "" is supported for pName
4580  *
4581  */
4582 BOOL WINAPI GetPrinterDriverDirectoryW(LPWSTR pName, LPWSTR pEnvironment,
4583                                        DWORD Level, LPBYTE pDriverDirectory,
4584                                        DWORD cbBuf, LPDWORD pcbNeeded)
4585 {
4586     DWORD needed;
4587     const printenv_t * env;
4588
4589     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName), 
4590           debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
4591     if(pName != NULL && pName[0]) {
4592         FIXME("pName unsupported: %s\n", debugstr_w(pName));
4593         SetLastError(ERROR_INVALID_PARAMETER);
4594         return FALSE;
4595     }
4596
4597     env = validate_envW(pEnvironment);
4598     if(!env) return FALSE;  /* pEnvironment invalid or unsupported */
4599
4600     if(Level != 1) {
4601         WARN("(Level: %d) is ignored in win9x\n", Level);
4602         SetLastError(ERROR_INVALID_LEVEL);
4603         return FALSE;
4604     }
4605
4606     /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
4607     needed = GetSystemDirectoryW(NULL, 0);
4608     /* add the Size for the Subdirectories */
4609     needed += lstrlenW(spooldriversW);
4610     needed += lstrlenW(env->subdir);
4611     needed *= sizeof(WCHAR);  /* return-value is size in Bytes */
4612
4613     if(pcbNeeded)
4614         *pcbNeeded = needed;
4615     TRACE("required: 0x%x/%d\n", needed, needed);
4616     if(needed > cbBuf) {
4617         SetLastError(ERROR_INSUFFICIENT_BUFFER);
4618         return FALSE;
4619     }
4620     if(pcbNeeded == NULL) {
4621         WARN("(pcbNeeded == NULL) is ignored in win9x\n");
4622         SetLastError(RPC_X_NULL_REF_POINTER);
4623         return FALSE;
4624     }
4625     if(pDriverDirectory == NULL) {
4626         /* ERROR_INVALID_USER_BUFFER is NT, ERROR_INVALID_PARAMETER is win9x */
4627         SetLastError(ERROR_INVALID_USER_BUFFER);
4628         return FALSE;
4629     }
4630     
4631     GetSystemDirectoryW((LPWSTR) pDriverDirectory, cbBuf/sizeof(WCHAR));
4632     /* add the Subdirectories */
4633     lstrcatW((LPWSTR) pDriverDirectory, spooldriversW);
4634     lstrcatW((LPWSTR) pDriverDirectory, env->subdir);
4635     TRACE(" => %s\n", debugstr_w((LPWSTR) pDriverDirectory));
4636     return TRUE;
4637 }
4638
4639
4640 /*****************************************************************************
4641  *       GetPrinterDriverDirectoryA  [WINSPOOL.@]
4642  *
4643  * Return the PATH for the Printer-Drivers (ANSI)
4644  *
4645  * See GetPrinterDriverDirectoryW.
4646  *
4647  * NOTES
4648  * On NT, pDriverDirectory need the same Size as the Unicode-Version
4649  *
4650  */
4651 BOOL WINAPI GetPrinterDriverDirectoryA(LPSTR pName, LPSTR pEnvironment,
4652                                        DWORD Level, LPBYTE pDriverDirectory,
4653                                        DWORD cbBuf, LPDWORD pcbNeeded)
4654 {
4655     UNICODE_STRING nameW, environmentW;
4656     BOOL ret;
4657     DWORD pcbNeededW;
4658     INT len = cbBuf * sizeof(WCHAR)/sizeof(CHAR);
4659     WCHAR *driverDirectoryW = NULL;
4660
4661     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_a(pName), 
4662           debugstr_a(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
4663  
4664     if (len) driverDirectoryW = HeapAlloc( GetProcessHeap(), 0, len );
4665
4666     if(pName) RtlCreateUnicodeStringFromAsciiz(&nameW, pName);
4667     else nameW.Buffer = NULL;
4668     if(pEnvironment) RtlCreateUnicodeStringFromAsciiz(&environmentW, pEnvironment);
4669     else environmentW.Buffer = NULL;
4670
4671     ret = GetPrinterDriverDirectoryW( nameW.Buffer, environmentW.Buffer, Level,
4672                                       (LPBYTE)driverDirectoryW, len, &pcbNeededW );
4673     if (ret) {
4674         DWORD needed;
4675         needed =  WideCharToMultiByte( CP_ACP, 0, driverDirectoryW, -1, 
4676                                    (LPSTR)pDriverDirectory, cbBuf, NULL, NULL);
4677         if(pcbNeeded)
4678             *pcbNeeded = needed;
4679         ret = (needed <= cbBuf) ? TRUE : FALSE;
4680     } else 
4681         if(pcbNeeded) *pcbNeeded = pcbNeededW * sizeof(CHAR)/sizeof(WCHAR);
4682
4683     TRACE("required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
4684
4685     HeapFree( GetProcessHeap(), 0, driverDirectoryW );
4686     RtlFreeUnicodeString(&environmentW);
4687     RtlFreeUnicodeString(&nameW);
4688
4689     return ret;
4690 }
4691
4692 /*****************************************************************************
4693  *          AddPrinterDriverA  [WINSPOOL.@]
4694  */
4695 BOOL WINAPI AddPrinterDriverA(LPSTR pName, DWORD level, LPBYTE pDriverInfo)
4696 {
4697     DRIVER_INFO_3A di3;
4698     HKEY hkeyDrivers, hkeyName;
4699     static CHAR empty[]    = "",
4700                 nullnull[] = "\0";
4701
4702     TRACE("(%s,%d,%p)\n",debugstr_a(pName),level,pDriverInfo);
4703
4704     if(level != 2 && level != 3) {
4705         SetLastError(ERROR_INVALID_LEVEL);
4706         return FALSE;
4707     }
4708     if ((pName) && (pName[0])) {
4709         FIXME("pName= %s - unsupported\n", debugstr_a(pName));
4710         SetLastError(ERROR_INVALID_PARAMETER);
4711         return FALSE;
4712     }
4713     if(!pDriverInfo) {
4714         WARN("pDriverInfo == NULL\n");
4715         SetLastError(ERROR_INVALID_PARAMETER);
4716         return FALSE;
4717     }
4718
4719     if(level == 3)
4720         di3 = *(DRIVER_INFO_3A *)pDriverInfo;
4721     else {
4722         memset(&di3, 0, sizeof(di3));
4723         memcpy(&di3, pDriverInfo, sizeof(DRIVER_INFO_2A));
4724     }
4725
4726     if(!di3.pName || !di3.pDriverPath || !di3.pConfigFile ||
4727        !di3.pDataFile) {
4728         SetLastError(ERROR_INVALID_PARAMETER);
4729         return FALSE;
4730     }
4731
4732     if(!di3.pDefaultDataType) di3.pDefaultDataType = empty;
4733     if(!di3.pDependentFiles) di3.pDependentFiles = nullnull;
4734     if(!di3.pHelpFile) di3.pHelpFile = empty;
4735     if(!di3.pMonitorName) di3.pMonitorName = empty;
4736
4737     hkeyDrivers = WINSPOOL_OpenDriverReg(di3.pEnvironment, FALSE);
4738
4739     if(!hkeyDrivers) {
4740         ERR("Can't create Drivers key\n");
4741         return FALSE;
4742     }
4743
4744     if(level == 2) { /* apparently can't overwrite with level2 */
4745         if(RegOpenKeyA(hkeyDrivers, di3.pName, &hkeyName) == ERROR_SUCCESS) {
4746             RegCloseKey(hkeyName);
4747             RegCloseKey(hkeyDrivers);
4748             WARN("Trying to create existing printer driver %s\n", debugstr_a(di3.pName));
4749             SetLastError(ERROR_PRINTER_DRIVER_ALREADY_INSTALLED);
4750             return FALSE;
4751         }
4752     }
4753     if(RegCreateKeyA(hkeyDrivers, di3.pName, &hkeyName) != ERROR_SUCCESS) {
4754         RegCloseKey(hkeyDrivers);
4755         ERR("Can't create Name key\n");
4756         return FALSE;
4757     }
4758     RegSetValueExA(hkeyName, "Configuration File", 0, REG_SZ, (LPBYTE) di3.pConfigFile,
4759                    lstrlenA(di3.pConfigFile) + 1);
4760     RegSetValueExA(hkeyName, "Data File", 0, REG_SZ, (LPBYTE) di3.pDataFile, lstrlenA(di3.pDataFile) + 1);
4761     RegSetValueExA(hkeyName, "Driver", 0, REG_SZ, (LPBYTE) di3.pDriverPath, lstrlenA(di3.pDriverPath) + 1);
4762     RegSetValueExA(hkeyName, "Version", 0, REG_DWORD, (LPBYTE) &di3.cVersion,
4763                    sizeof(DWORD));
4764     RegSetValueExA(hkeyName, "Datatype", 0, REG_SZ, (LPBYTE) di3.pDefaultDataType, lstrlenA(di3.pDefaultDataType));
4765     RegSetValueExA(hkeyName, "Dependent Files", 0, REG_MULTI_SZ,
4766                    (LPBYTE) di3.pDependentFiles, multi_sz_lenA(di3.pDependentFiles));
4767     RegSetValueExA(hkeyName, "Help File", 0, REG_SZ, (LPBYTE) di3.pHelpFile, lstrlenA(di3.pHelpFile) + 1);
4768     RegSetValueExA(hkeyName, "Monitor", 0, REG_SZ, (LPBYTE) di3.pMonitorName, lstrlenA(di3.pMonitorName) + 1);
4769     RegCloseKey(hkeyName);
4770     RegCloseKey(hkeyDrivers);
4771
4772     return TRUE;
4773 }
4774
4775 /*****************************************************************************
4776  *          AddPrinterDriverW  [WINSPOOL.@]
4777  */
4778 BOOL WINAPI AddPrinterDriverW(LPWSTR printerName,DWORD level,
4779                                    LPBYTE pDriverInfo)
4780 {
4781     FIXME("(%s,%d,%p): stub\n",debugstr_w(printerName),
4782           level,pDriverInfo);
4783     return FALSE;
4784 }
4785
4786 /*****************************************************************************
4787  *          AddPrintProcessorA  [WINSPOOL.@]
4788  */
4789 BOOL WINAPI AddPrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPathName,
4790                                LPSTR pPrintProcessorName)
4791 {
4792     FIXME("(%s,%s,%s,%s): stub\n", debugstr_a(pName), debugstr_a(pEnvironment),
4793           debugstr_a(pPathName), debugstr_a(pPrintProcessorName));
4794     return FALSE;
4795 }
4796
4797 /*****************************************************************************
4798  *          AddPrintProcessorW  [WINSPOOL.@]
4799  */
4800 BOOL WINAPI AddPrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPathName,
4801                                LPWSTR pPrintProcessorName)
4802 {
4803     FIXME("(%s,%s,%s,%s): stub\n", debugstr_w(pName), debugstr_w(pEnvironment),
4804           debugstr_w(pPathName), debugstr_w(pPrintProcessorName));
4805     return FALSE;
4806 }
4807
4808 /*****************************************************************************
4809  *          AddPrintProvidorA  [WINSPOOL.@]
4810  */
4811 BOOL WINAPI AddPrintProvidorA(LPSTR pName, DWORD Level, LPBYTE pProviderInfo)
4812 {
4813     FIXME("(%s,0x%08x,%p): stub\n", debugstr_a(pName), Level, pProviderInfo);
4814     return FALSE;
4815 }
4816
4817 /*****************************************************************************
4818  *          AddPrintProvidorW  [WINSPOOL.@]
4819  */
4820 BOOL WINAPI AddPrintProvidorW(LPWSTR pName, DWORD Level, LPBYTE pProviderInfo)
4821 {
4822     FIXME("(%s,0x%08x,%p): stub\n", debugstr_w(pName), Level, pProviderInfo);
4823     return FALSE;
4824 }
4825
4826 /*****************************************************************************
4827  *          AdvancedDocumentPropertiesA  [WINSPOOL.@]
4828  */
4829 LONG WINAPI AdvancedDocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName,
4830                                         PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput)
4831 {
4832     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_a(pDeviceName),
4833           pDevModeOutput, pDevModeInput);
4834     return 0;
4835 }
4836
4837 /*****************************************************************************
4838  *          AdvancedDocumentPropertiesW  [WINSPOOL.@]
4839  */
4840 LONG WINAPI AdvancedDocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName,
4841                                         PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput)
4842 {
4843     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_w(pDeviceName),
4844           pDevModeOutput, pDevModeInput);
4845     return 0;
4846 }
4847
4848 /*****************************************************************************
4849  *          PrinterProperties  [WINSPOOL.@]
4850  *
4851  *     Displays a dialog to set the properties of the printer.
4852  *
4853  * RETURNS
4854  *     nonzero on success or zero on failure
4855  *
4856  * BUGS
4857  *         implemented as stub only
4858  */
4859 BOOL WINAPI PrinterProperties(HWND hWnd,      /* [in] handle to parent window */
4860                               HANDLE hPrinter /* [in] handle to printer object */
4861 ){
4862     FIXME("(%p,%p): stub\n", hWnd, hPrinter);
4863     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4864     return FALSE;
4865 }
4866
4867 /*****************************************************************************
4868  *          EnumJobsA [WINSPOOL.@]
4869  *
4870  */
4871 BOOL WINAPI EnumJobsA(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
4872                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
4873                       LPDWORD pcReturned)
4874 {
4875     FIXME("(%p,first=%d,no=%d,level=%d,job=%p,cb=%d,%p,%p), stub!\n",
4876         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
4877     );
4878     if(pcbNeeded) *pcbNeeded = 0;
4879     if(pcReturned) *pcReturned = 0;
4880     return FALSE;
4881 }
4882
4883
4884 /*****************************************************************************
4885  *          EnumJobsW [WINSPOOL.@]
4886  *
4887  */
4888 BOOL WINAPI EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
4889                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
4890                       LPDWORD pcReturned)
4891 {
4892     FIXME("(%p,first=%d,no=%d,level=%d,job=%p,cb=%d,%p,%p), stub!\n",
4893         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
4894     );
4895     if(pcbNeeded) *pcbNeeded = 0;
4896     if(pcReturned) *pcReturned = 0;
4897     return FALSE;
4898 }
4899
4900 /*****************************************************************************
4901  *          WINSPOOL_EnumPrinterDrivers [internal]
4902  *
4903  *    Delivers information about all printer drivers installed on the
4904  *    localhost or a given server
4905  *
4906  * RETURNS
4907  *    nonzero on success or zero on failure. If the buffer for the returned
4908  *    information is too small the function will return an error
4909  *
4910  * BUGS
4911  *    - only implemented for localhost, foreign hosts will return an error
4912  */
4913 static BOOL WINSPOOL_EnumPrinterDrivers(LPWSTR pName, LPWSTR pEnvironment,
4914                                         DWORD Level, LPBYTE pDriverInfo,
4915                                         DWORD cbBuf, LPDWORD pcbNeeded,
4916                                         LPDWORD pcReturned, BOOL unicode)
4917
4918 {   HKEY  hkeyDrivers;
4919     DWORD i, needed, number = 0, size = 0;
4920     WCHAR DriverNameW[255];
4921     PBYTE ptr;
4922
4923     TRACE("%s,%s,%d,%p,%d,%d\n",
4924           debugstr_w(pName), debugstr_w(pEnvironment),
4925           Level, pDriverInfo, cbBuf, unicode);
4926
4927     /* check for local drivers */
4928     if((pName) && (pName[0])) {
4929         FIXME("remote drivers (%s) not supported!\n", debugstr_w(pName));
4930         SetLastError(ERROR_ACCESS_DENIED);
4931         return FALSE;
4932     }
4933
4934     /* check input parameter */
4935     if((Level < 1) || (Level > 3)) {
4936         ERR("unsupported level %d\n", Level);
4937         SetLastError(ERROR_INVALID_LEVEL);
4938         return FALSE;
4939     }
4940
4941     /* initialize return values */
4942     if(pDriverInfo)
4943         memset( pDriverInfo, 0, cbBuf);
4944     *pcbNeeded  = 0;
4945     *pcReturned = 0;
4946
4947     hkeyDrivers = WINSPOOL_OpenDriverReg(pEnvironment, TRUE);
4948     if(!hkeyDrivers) {
4949         ERR("Can't open Drivers key\n");
4950         return FALSE;
4951     }
4952
4953     if(RegQueryInfoKeyA(hkeyDrivers, NULL, NULL, NULL, &number, NULL, NULL,
4954                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
4955         RegCloseKey(hkeyDrivers);
4956         ERR("Can't query Drivers key\n");
4957         return FALSE;
4958     }
4959     TRACE("Found %d Drivers\n", number);
4960
4961     /* get size of single struct
4962      * unicode and ascii structure have the same size
4963      */
4964     switch (Level) {
4965         case 1:
4966             size = sizeof(DRIVER_INFO_1A);
4967             break;
4968         case 2:
4969             size = sizeof(DRIVER_INFO_2A);
4970             break;
4971         case 3:
4972             size = sizeof(DRIVER_INFO_3A);
4973             break;
4974     }
4975
4976     /* calculate required buffer size */
4977     *pcbNeeded = size * number;
4978
4979     for( i = 0,  ptr = (pDriverInfo && (cbBuf >= size)) ? pDriverInfo : NULL ;
4980          i < number;
4981          i++, ptr = (ptr && (cbBuf >= size * i)) ? ptr + size : NULL) {
4982         if(RegEnumKeyW(hkeyDrivers, i, DriverNameW, sizeof(DriverNameW))
4983                        != ERROR_SUCCESS) {
4984             ERR("Can't enum key number %d\n", i);
4985             RegCloseKey(hkeyDrivers);
4986             return FALSE;
4987         }
4988         if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverNameW,
4989                          pEnvironment, Level, ptr,
4990                          (cbBuf < *pcbNeeded) ? NULL : pDriverInfo + *pcbNeeded,
4991                          (cbBuf < *pcbNeeded) ? 0 : cbBuf - *pcbNeeded,
4992                          &needed, unicode)) {
4993             RegCloseKey(hkeyDrivers);
4994             return FALSE;
4995         }
4996         (*pcbNeeded) += needed;
4997     }
4998
4999     RegCloseKey(hkeyDrivers);
5000
5001     if(cbBuf < *pcbNeeded){
5002         SetLastError(ERROR_INSUFFICIENT_BUFFER);
5003         return FALSE;
5004     }
5005
5006     *pcReturned = number;
5007     return TRUE;
5008 }
5009
5010 /*****************************************************************************
5011  *          EnumPrinterDriversW  [WINSPOOL.@]
5012  *
5013  *    see function EnumPrinterDrivers for RETURNS, BUGS
5014  */
5015 BOOL WINAPI EnumPrinterDriversW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
5016                                 LPBYTE pDriverInfo, DWORD cbBuf,
5017                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
5018 {
5019     return WINSPOOL_EnumPrinterDrivers(pName, pEnvironment, Level, pDriverInfo,
5020                                        cbBuf, pcbNeeded, pcReturned, TRUE);
5021 }
5022
5023 /*****************************************************************************
5024  *          EnumPrinterDriversA  [WINSPOOL.@]
5025  *
5026  *    see function EnumPrinterDrivers for RETURNS, BUGS
5027  */
5028 BOOL WINAPI EnumPrinterDriversA(LPSTR pName, LPSTR pEnvironment, DWORD Level,
5029                                 LPBYTE pDriverInfo, DWORD cbBuf,
5030                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
5031 {   BOOL ret;
5032     UNICODE_STRING pNameW, pEnvironmentW;
5033     PWSTR pwstrNameW, pwstrEnvironmentW;
5034
5035     pwstrNameW = asciitounicode(&pNameW, pName);
5036     pwstrEnvironmentW = asciitounicode(&pEnvironmentW, pEnvironment);
5037
5038     ret = WINSPOOL_EnumPrinterDrivers(pwstrNameW, pwstrEnvironmentW,
5039                                       Level, pDriverInfo, cbBuf, pcbNeeded,
5040                                       pcReturned, FALSE);
5041     RtlFreeUnicodeString(&pNameW);
5042     RtlFreeUnicodeString(&pEnvironmentW);
5043
5044     return ret;
5045 }
5046
5047 /******************************************************************************
5048  *              EnumPortsA   (WINSPOOL.@)
5049  *
5050  * See EnumPortsW.
5051  *
5052  */
5053 BOOL WINAPI EnumPortsA( LPSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf,
5054                         LPDWORD pcbNeeded, LPDWORD pcReturned)
5055 {
5056     BOOL    res;
5057     LPBYTE  bufferW = NULL;
5058     LPWSTR  nameW = NULL;
5059     DWORD   needed = 0;
5060     DWORD   numentries = 0;
5061     INT     len;
5062
5063     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), Level, pPorts,
5064           cbBuf, pcbNeeded, pcReturned);
5065
5066     /* convert servername to unicode */
5067     if (pName) {
5068         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
5069         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5070         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
5071     }
5072     /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the Ports */
5073     needed = cbBuf * sizeof(WCHAR);    
5074     if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
5075     res = EnumPortsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5076
5077     if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
5078         if (pcbNeeded) needed = *pcbNeeded;
5079         /* HeapReAlloc return NULL, when bufferW was NULL */
5080         bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
5081                               HeapAlloc(GetProcessHeap(), 0, needed);
5082
5083         /* Try again with the large Buffer */
5084         res = EnumPortsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5085     }
5086     needed = pcbNeeded ? *pcbNeeded : 0;
5087     numentries = pcReturned ? *pcReturned : 0;
5088
5089     /*
5090        W2k require the buffersize from EnumPortsW also for EnumPortsA.
5091        We use the smaller Ansi-Size to avoid conflicts with fixed Buffers of old Apps.
5092      */
5093     if (res) {
5094         /* EnumPortsW collected all Data. Parse them to calculate ANSI-Size */
5095         DWORD   entrysize = 0;
5096         DWORD   index;
5097         LPSTR   ptr;
5098         LPPORT_INFO_2W pi2w;
5099         LPPORT_INFO_2A pi2a;
5100
5101         needed = 0;
5102         entrysize = (Level == 1) ? sizeof(PORT_INFO_1A) : sizeof(PORT_INFO_2A);
5103
5104         /* First pass: calculate the size for all Entries */
5105         pi2w = (LPPORT_INFO_2W) bufferW;
5106         pi2a = (LPPORT_INFO_2A) pPorts;
5107         index = 0;
5108         while (index < numentries) {
5109             index++;
5110             needed += entrysize;    /* PORT_INFO_?A */
5111             TRACE("%p: parsing #%d (%s)\n", pi2w, index, debugstr_w(pi2w->pPortName));
5112
5113             needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pPortName, -1,
5114                                             NULL, 0, NULL, NULL);
5115             if (Level > 1) {
5116                 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pMonitorName, -1,
5117                                                 NULL, 0, NULL, NULL);
5118                 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pDescription, -1,
5119                                                 NULL, 0, NULL, NULL);
5120             }
5121             /* use LPBYTE with entrysize to avoid double code (PORT_INFO_1 + PORT_INFO_2) */
5122             pi2w = (LPPORT_INFO_2W) (((LPBYTE)pi2w) + entrysize);
5123             pi2a = (LPPORT_INFO_2A) (((LPBYTE)pi2a) + entrysize);
5124         }
5125
5126         /* check for errors and quit on failure */
5127         if (cbBuf < needed) {
5128             SetLastError(ERROR_INSUFFICIENT_BUFFER);
5129             res = FALSE;
5130             goto cleanup;
5131         }
5132         len = entrysize * numentries;       /* room for all PORT_INFO_?A */
5133         ptr = (LPSTR) &pPorts[len];         /* room for strings */
5134         cbBuf -= len ;                      /* free Bytes in the user-Buffer */
5135         pi2w = (LPPORT_INFO_2W) bufferW;
5136         pi2a = (LPPORT_INFO_2A) pPorts;
5137         index = 0;
5138         /* Second Pass: Fill the User Buffer (if we have one) */
5139         while ((index < numentries) && pPorts) {
5140             index++;
5141             TRACE("%p: writing PORT_INFO_%dA #%d\n", pi2a, Level, index);
5142             pi2a->pPortName = ptr;
5143             len = WideCharToMultiByte(CP_ACP, 0, pi2w->pPortName, -1,
5144                                             ptr, cbBuf , NULL, NULL);
5145             ptr += len;
5146             cbBuf -= len;
5147             if (Level > 1) {
5148                 pi2a->pMonitorName = ptr;
5149                 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pMonitorName, -1,
5150                                             ptr, cbBuf, NULL, NULL);
5151                 ptr += len;
5152                 cbBuf -= len;
5153
5154                 pi2a->pDescription = ptr;
5155                 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pDescription, -1,
5156                                             ptr, cbBuf, NULL, NULL);
5157                 ptr += len;
5158                 cbBuf -= len;
5159
5160                 pi2a->fPortType = pi2w->fPortType;
5161                 pi2a->Reserved = 0;              /* documented: "must be zero" */
5162                 
5163             }
5164             /* use LPBYTE with entrysize to avoid double code (PORT_INFO_1 + PORT_INFO_2) */
5165             pi2w = (LPPORT_INFO_2W) (((LPBYTE)pi2w) + entrysize);
5166             pi2a = (LPPORT_INFO_2A) (((LPBYTE)pi2a) + entrysize);
5167         }
5168     }
5169
5170 cleanup:
5171     if (pcbNeeded)  *pcbNeeded = needed;
5172     if (pcReturned) *pcReturned = (res) ? numentries : 0;
5173
5174     HeapFree(GetProcessHeap(), 0, nameW);
5175     HeapFree(GetProcessHeap(), 0, bufferW);
5176
5177     TRACE("returning %d with %d (%d byte for %d of %d entries)\n", 
5178             (res), GetLastError(), needed, (res)? numentries : 0, numentries);
5179
5180     return (res);
5181
5182 }
5183
5184 /******************************************************************************
5185  *      EnumPortsW   (WINSPOOL.@)
5186  *
5187  * Enumerate available Ports
5188  *
5189  * PARAMS
5190  *  name        [I] Servername or NULL (local Computer)
5191  *  level       [I] Structure-Level (1 or 2)
5192  *  buffer      [O] PTR to Buffer that receives the Result
5193  *  bufsize     [I] Size of Buffer at buffer
5194  *  bufneeded   [O] PTR to DWORD that receives the size in Bytes used / required for buffer
5195  *  bufreturned [O] PTR to DWORD that receives the number of Ports in buffer
5196  *
5197  * RETURNS
5198  *  Success: TRUE
5199  *  Failure: FALSE and in bufneeded the Bytes required for buffer, if bufsize is too small
5200  *
5201  */
5202
5203 BOOL WINAPI EnumPortsW(LPWSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
5204 {
5205     DWORD   needed = 0;
5206     DWORD   numentries = 0;
5207     BOOL    res = FALSE;
5208
5209     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pPorts,
5210           cbBuf, pcbNeeded, pcReturned);
5211
5212     if (pName && (pName[0])) {
5213         FIXME("not implemented for Server %s\n", debugstr_w(pName));
5214         SetLastError(ERROR_ACCESS_DENIED);
5215         goto emP_cleanup;
5216     }
5217
5218     /* Level is not checked in win9x */
5219     if (!Level || (Level > 2)) {
5220         WARN("level (%d) is ignored in win9x\n", Level);
5221         SetLastError(ERROR_INVALID_LEVEL);
5222         goto emP_cleanup;
5223     }
5224     if (!pcbNeeded) {
5225         SetLastError(RPC_X_NULL_REF_POINTER);
5226         goto emP_cleanup;
5227     }
5228
5229     EnterCriticalSection(&monitor_handles_cs);
5230     monitor_loadall();
5231
5232     /* Scan all local Ports */
5233     numentries = 0;
5234     needed = get_ports_from_all_monitors(Level, NULL, 0, &numentries);
5235
5236     /* we calculated the needed buffersize. now do the error-checks */
5237     if (cbBuf < needed) {
5238         monitor_unloadall();
5239         SetLastError(ERROR_INSUFFICIENT_BUFFER);
5240         goto emP_cleanup_cs;
5241     }
5242     else if (!pPorts || !pcReturned) {
5243         monitor_unloadall();
5244         SetLastError(RPC_X_NULL_REF_POINTER);
5245         goto emP_cleanup_cs;
5246     }
5247
5248     /* Fill the Buffer */
5249     needed = get_ports_from_all_monitors(Level, pPorts, cbBuf, &numentries);
5250     res = TRUE;
5251     monitor_unloadall();
5252
5253 emP_cleanup_cs:
5254     LeaveCriticalSection(&monitor_handles_cs);
5255
5256 emP_cleanup:
5257     if (pcbNeeded)  *pcbNeeded = needed;
5258     if (pcReturned) *pcReturned = (res) ? numentries : 0;
5259
5260     TRACE("returning %d with %d (%d byte for %d of %d entries)\n", 
5261             (res), GetLastError(), needed, (res)? numentries : 0, numentries);
5262
5263     return (res);
5264 }
5265
5266 /******************************************************************************
5267  *              GetDefaultPrinterW   (WINSPOOL.@)
5268  *
5269  * FIXME
5270  *      This function must read the value from data 'device' of key
5271  *      HCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows
5272  */
5273 BOOL WINAPI GetDefaultPrinterW(LPWSTR name, LPDWORD namesize)
5274 {
5275     BOOL  retval = TRUE;
5276     DWORD insize, len;
5277     WCHAR *buffer, *ptr;
5278
5279     if (!namesize)
5280     {
5281         SetLastError(ERROR_INVALID_PARAMETER);
5282         return FALSE;
5283     }
5284
5285     /* make the buffer big enough for the stuff from the profile/registry,
5286      * the content must fit into the local buffer to compute the correct
5287      * size even if the extern buffer is too small or not given.
5288      * (20 for ,driver,port) */
5289     insize = *namesize;
5290     len = max(100, (insize + 20));
5291     buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
5292
5293     if (!GetProfileStringW(windowsW, deviceW, emptyStringW, buffer, len))
5294     {
5295         SetLastError (ERROR_FILE_NOT_FOUND);
5296         retval = FALSE;
5297         goto end;
5298     }
5299     TRACE("%s\n", debugstr_w(buffer));
5300
5301     if ((ptr = strchrW(buffer, ',')) == NULL)
5302     {
5303         SetLastError(ERROR_INVALID_NAME);
5304         retval = FALSE;
5305         goto end;
5306     }
5307
5308     *ptr = 0;
5309     *namesize = strlenW(buffer) + 1;
5310     if(!name || (*namesize > insize))
5311     {
5312         SetLastError(ERROR_INSUFFICIENT_BUFFER);
5313         retval = FALSE;
5314         goto end;
5315     }
5316     strcpyW(name, buffer);
5317
5318 end:
5319     HeapFree( GetProcessHeap(), 0, buffer);
5320     return retval;
5321 }
5322
5323
5324 /******************************************************************************
5325  *              GetDefaultPrinterA   (WINSPOOL.@)
5326  */
5327 BOOL WINAPI GetDefaultPrinterA(LPSTR name, LPDWORD namesize)
5328 {
5329     BOOL  retval = TRUE;
5330     DWORD insize = 0;
5331     WCHAR *bufferW = NULL;
5332
5333     if (!namesize)
5334     {
5335         SetLastError(ERROR_INVALID_PARAMETER);
5336         return FALSE;
5337     }
5338
5339     if(name && *namesize) {
5340         insize = *namesize;
5341         bufferW = HeapAlloc( GetProcessHeap(), 0, insize * sizeof(WCHAR));
5342     }
5343
5344     if(!GetDefaultPrinterW( bufferW, namesize)) {
5345         retval = FALSE;
5346         goto end;
5347     }
5348
5349     *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, name, insize,
5350                                     NULL, NULL);
5351     if (!*namesize)
5352     {
5353         *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
5354         retval = FALSE;
5355     }
5356     TRACE("0x%08x/0x%08x:%s\n", *namesize, insize, debugstr_w(bufferW));
5357
5358 end:
5359     HeapFree( GetProcessHeap(), 0, bufferW);
5360     return retval;
5361 }
5362
5363
5364 /******************************************************************************
5365  *              SetDefaultPrinterW   (WINSPOOL.204)
5366  *
5367  * Set the Name of the Default Printer
5368  *
5369  * PARAMS
5370  *  pszPrinter [I] Name of the Printer or NULL
5371  *
5372  * RETURNS
5373  *  Success:    True
5374  *  Failure:    FALSE
5375  *
5376  * NOTES
5377  *  When the Parameter is NULL or points to an Empty String and
5378  *  a Default Printer was already present, then this Function changes nothing.
5379  *  Without a Default Printer and NULL (or an Empty String) as Parameter,
5380  *  the First enumerated local Printer is used.
5381  *
5382  */
5383 BOOL WINAPI SetDefaultPrinterW(LPCWSTR pszPrinter)
5384 {
5385
5386     TRACE("(%s)\n", debugstr_w(pszPrinter));
5387
5388     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5389     return FALSE;
5390 }
5391
5392 /******************************************************************************
5393  *              SetDefaultPrinterA   (WINSPOOL.202)
5394  *
5395  * See SetDefaultPrinterW.
5396  *
5397  */
5398 BOOL WINAPI SetDefaultPrinterA(LPCSTR pszPrinter)
5399 {
5400
5401     TRACE("(%s)\n", debugstr_a(pszPrinter));
5402
5403     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5404     return FALSE;
5405 }
5406
5407
5408 /******************************************************************************
5409  *              SetPrinterDataExA   (WINSPOOL.@)
5410  */
5411 DWORD WINAPI SetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5412                                LPCSTR pValueName, DWORD Type,
5413                                LPBYTE pData, DWORD cbData)
5414 {
5415     HKEY hkeyPrinter, hkeySubkey;
5416     DWORD ret;
5417
5418     TRACE("(%p, %s, %s %08x, %p, %08x)\n", hPrinter, debugstr_a(pKeyName),
5419           debugstr_a(pValueName), Type, pData, cbData);
5420
5421     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5422        != ERROR_SUCCESS)
5423         return ret;
5424
5425     if((ret = RegCreateKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
5426        != ERROR_SUCCESS) {
5427         ERR("Can't create subkey %s\n", debugstr_a(pKeyName));
5428         RegCloseKey(hkeyPrinter);
5429         return ret;
5430     }
5431     ret = RegSetValueExA(hkeySubkey, pValueName, 0, Type, pData, cbData);
5432     RegCloseKey(hkeySubkey);
5433     RegCloseKey(hkeyPrinter);
5434     return ret;
5435 }
5436
5437 /******************************************************************************
5438  *              SetPrinterDataExW   (WINSPOOL.@)
5439  */
5440 DWORD WINAPI SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5441                                LPCWSTR pValueName, DWORD Type,
5442                                LPBYTE pData, DWORD cbData)
5443 {
5444     HKEY hkeyPrinter, hkeySubkey;
5445     DWORD ret;
5446
5447     TRACE("(%p, %s, %s %08x, %p, %08x)\n", hPrinter, debugstr_w(pKeyName),
5448           debugstr_w(pValueName), Type, pData, cbData);
5449
5450     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5451        != ERROR_SUCCESS)
5452         return ret;
5453
5454     if((ret = RegCreateKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
5455        != ERROR_SUCCESS) {
5456         ERR("Can't create subkey %s\n", debugstr_w(pKeyName));
5457         RegCloseKey(hkeyPrinter);
5458         return ret;
5459     }
5460     ret = RegSetValueExW(hkeySubkey, pValueName, 0, Type, pData, cbData);
5461     RegCloseKey(hkeySubkey);
5462     RegCloseKey(hkeyPrinter);
5463     return ret;
5464 }
5465
5466 /******************************************************************************
5467  *              SetPrinterDataA   (WINSPOOL.@)
5468  */
5469 DWORD WINAPI SetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, DWORD Type,
5470                                LPBYTE pData, DWORD cbData)
5471 {
5472     return SetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, Type,
5473                              pData, cbData);
5474 }
5475
5476 /******************************************************************************
5477  *              SetPrinterDataW   (WINSPOOL.@)
5478  */
5479 DWORD WINAPI SetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, DWORD Type,
5480                              LPBYTE pData, DWORD cbData)
5481 {
5482     return SetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, Type,
5483                              pData, cbData);
5484 }
5485
5486 /******************************************************************************
5487  *              GetPrinterDataExA   (WINSPOOL.@)
5488  */
5489 DWORD WINAPI GetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5490                                LPCSTR pValueName, LPDWORD pType,
5491                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5492 {
5493     HKEY hkeyPrinter, hkeySubkey;
5494     DWORD ret;
5495
5496     TRACE("(%p, %s, %s %p, %p, %08x, %p)\n", hPrinter,
5497           debugstr_a(pKeyName), debugstr_a(pValueName), pType, pData, nSize,
5498           pcbNeeded);
5499
5500     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5501        != ERROR_SUCCESS)
5502         return ret;
5503
5504     if((ret = RegOpenKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
5505        != ERROR_SUCCESS) {
5506         WARN("Can't open subkey %s\n", debugstr_a(pKeyName));
5507         RegCloseKey(hkeyPrinter);
5508         return ret;
5509     }
5510     *pcbNeeded = nSize;
5511     ret = RegQueryValueExA(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
5512     RegCloseKey(hkeySubkey);
5513     RegCloseKey(hkeyPrinter);
5514     return ret;
5515 }
5516
5517 /******************************************************************************
5518  *              GetPrinterDataExW   (WINSPOOL.@)
5519  */
5520 DWORD WINAPI GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5521                                LPCWSTR pValueName, LPDWORD pType,
5522                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5523 {
5524     HKEY hkeyPrinter, hkeySubkey;
5525     DWORD ret;
5526
5527     TRACE("(%p, %s, %s %p, %p, %08x, %p)\n", hPrinter,
5528           debugstr_w(pKeyName), debugstr_w(pValueName), pType, pData, nSize,
5529           pcbNeeded);
5530
5531     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5532        != ERROR_SUCCESS)
5533         return ret;
5534
5535     if((ret = RegOpenKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
5536        != ERROR_SUCCESS) {
5537         WARN("Can't open subkey %s\n", debugstr_w(pKeyName));
5538         RegCloseKey(hkeyPrinter);
5539         return ret;
5540     }
5541     *pcbNeeded = nSize;
5542     ret = RegQueryValueExW(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
5543     RegCloseKey(hkeySubkey);
5544     RegCloseKey(hkeyPrinter);
5545     return ret;
5546 }
5547
5548 /******************************************************************************
5549  *              GetPrinterDataA   (WINSPOOL.@)
5550  */
5551 DWORD WINAPI GetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, LPDWORD pType,
5552                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5553 {
5554     return GetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, pType,
5555                              pData, nSize, pcbNeeded);
5556 }
5557
5558 /******************************************************************************
5559  *              GetPrinterDataW   (WINSPOOL.@)
5560  */
5561 DWORD WINAPI GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType,
5562                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5563 {
5564     return GetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, pType,
5565                              pData, nSize, pcbNeeded);
5566 }
5567
5568 /*******************************************************************************
5569  *              EnumPrinterDataExW      [WINSPOOL.@]
5570  */
5571 DWORD WINAPI EnumPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5572                                 LPBYTE pEnumValues, DWORD cbEnumValues,
5573                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
5574 {
5575     HKEY                    hkPrinter, hkSubKey;
5576     DWORD                   r, ret, dwIndex, cValues, cbMaxValueNameLen,
5577                             cbValueNameLen, cbMaxValueLen, cbValueLen,
5578                             cbBufSize, dwType;
5579     LPWSTR                  lpValueName;
5580     HANDLE                  hHeap;
5581     PBYTE                   lpValue;
5582     PPRINTER_ENUM_VALUESW   ppev;
5583
5584     TRACE ("%p %s\n", hPrinter, debugstr_w (pKeyName));
5585
5586     if (pKeyName == NULL || *pKeyName == 0)
5587         return ERROR_INVALID_PARAMETER;
5588
5589     ret = WINSPOOL_GetOpenedPrinterRegKey (hPrinter, &hkPrinter);
5590     if (ret != ERROR_SUCCESS)
5591     {
5592         TRACE ("WINSPOOL_GetOpenedPrinterRegKey (%p) returned %i\n",
5593                 hPrinter, ret);
5594         return ret;
5595     }
5596
5597     ret = RegOpenKeyExW (hkPrinter, pKeyName, 0, KEY_READ, &hkSubKey);
5598     if (ret != ERROR_SUCCESS)
5599     {
5600         r = RegCloseKey (hkPrinter);
5601         if (r != ERROR_SUCCESS)
5602             WARN ("RegCloseKey returned %i\n", r);
5603         TRACE ("RegOpenKeyExW (%p, %s) returned %i\n", hPrinter,
5604                 debugstr_w (pKeyName), ret);
5605         return ret;
5606     }
5607
5608     ret = RegCloseKey (hkPrinter);
5609     if (ret != ERROR_SUCCESS)
5610     {
5611         ERR ("RegCloseKey returned %i\n", ret);
5612         r = RegCloseKey (hkSubKey);
5613         if (r != ERROR_SUCCESS)
5614             WARN ("RegCloseKey returned %i\n", r);
5615         return ret;
5616     }
5617
5618     ret = RegQueryInfoKeyW (hkSubKey, NULL, NULL, NULL, NULL, NULL, NULL,
5619             &cValues, &cbMaxValueNameLen, &cbMaxValueLen, NULL, NULL);
5620     if (ret != ERROR_SUCCESS)
5621     {
5622         r = RegCloseKey (hkSubKey);
5623         if (r != ERROR_SUCCESS)
5624             WARN ("RegCloseKey returned %i\n", r);
5625         TRACE ("RegQueryInfoKeyW (%p) returned %i\n", hkSubKey, ret);
5626         return ret;
5627     }
5628
5629     TRACE ("RegQueryInfoKeyW returned cValues = %i, cbMaxValueNameLen = %i, "
5630             "cbMaxValueLen = %i\n", cValues, cbMaxValueNameLen, cbMaxValueLen);
5631
5632     if (cValues == 0)                   /* empty key */
5633     {
5634         r = RegCloseKey (hkSubKey);
5635         if (r != ERROR_SUCCESS)
5636             WARN ("RegCloseKey returned %i\n", r);
5637         *pcbEnumValues = *pnEnumValues = 0;
5638         return ERROR_SUCCESS;
5639     }
5640
5641     ++cbMaxValueNameLen;                        /* allow for trailing '\0' */
5642
5643     hHeap = GetProcessHeap ();
5644     if (hHeap == NULL)
5645     {
5646         ERR ("GetProcessHeap failed\n");
5647         r = RegCloseKey (hkSubKey);
5648         if (r != ERROR_SUCCESS)
5649             WARN ("RegCloseKey returned %i\n", r);
5650         return ERROR_OUTOFMEMORY;
5651     }
5652
5653     lpValueName = HeapAlloc (hHeap, 0, cbMaxValueNameLen * sizeof (WCHAR));
5654     if (lpValueName == NULL)
5655     {
5656         ERR ("Failed to allocate %i WCHARs from process heap\n", cbMaxValueNameLen);
5657         r = RegCloseKey (hkSubKey);
5658         if (r != ERROR_SUCCESS)
5659             WARN ("RegCloseKey returned %i\n", r);
5660         return ERROR_OUTOFMEMORY;
5661     }
5662
5663     lpValue = HeapAlloc (hHeap, 0, cbMaxValueLen);
5664     if (lpValue == NULL)
5665     {
5666         ERR ("Failed to allocate %i bytes from process heap\n", cbMaxValueLen);
5667         if (HeapFree (hHeap, 0, lpValueName) == 0)
5668             WARN ("HeapFree failed with code %i\n", GetLastError ());
5669         r = RegCloseKey (hkSubKey);
5670         if (r != ERROR_SUCCESS)
5671             WARN ("RegCloseKey returned %i\n", r);
5672         return ERROR_OUTOFMEMORY;
5673     }
5674
5675     TRACE ("pass 1: calculating buffer required for all names and values\n");
5676
5677     cbBufSize = cValues * sizeof (PRINTER_ENUM_VALUESW);
5678
5679     TRACE ("%i bytes required for %i headers\n", cbBufSize, cValues);
5680
5681     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
5682     {
5683         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
5684         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
5685                 NULL, NULL, lpValue, &cbValueLen);
5686         if (ret != ERROR_SUCCESS)
5687         {
5688             if (HeapFree (hHeap, 0, lpValue) == 0)
5689                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5690             if (HeapFree (hHeap, 0, lpValueName) == 0)
5691                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5692             r = RegCloseKey (hkSubKey);
5693             if (r != ERROR_SUCCESS)
5694                 WARN ("RegCloseKey returned %i\n", r);
5695             TRACE ("RegEnumValueW (%i) returned %i\n", dwIndex, ret);
5696             return ret;
5697         }
5698
5699         TRACE ("%s [%i]: name needs %i WCHARs, data needs %i bytes\n",
5700                 debugstr_w (lpValueName), dwIndex,
5701                 cbValueNameLen + 1, cbValueLen);
5702
5703         cbBufSize += (cbValueNameLen + 1) * sizeof (WCHAR);
5704         cbBufSize += cbValueLen;
5705     }
5706
5707     TRACE ("%i bytes required for all %i values\n", cbBufSize, cValues);
5708
5709     *pcbEnumValues = cbBufSize;
5710     *pnEnumValues = cValues;
5711
5712     if (cbEnumValues < cbBufSize)       /* buffer too small */
5713     {
5714         if (HeapFree (hHeap, 0, lpValue) == 0)
5715             WARN ("HeapFree failed with code %i\n", GetLastError ());
5716         if (HeapFree (hHeap, 0, lpValueName) == 0)
5717             WARN ("HeapFree failed with code %i\n", GetLastError ());
5718         r = RegCloseKey (hkSubKey);
5719         if (r != ERROR_SUCCESS)
5720             WARN ("RegCloseKey returned %i\n", r);
5721         TRACE ("%i byte buffer is not large enough\n", cbEnumValues);
5722         return ERROR_MORE_DATA;
5723     }
5724
5725     TRACE ("pass 2: copying all names and values to buffer\n");
5726
5727     ppev = (PPRINTER_ENUM_VALUESW) pEnumValues;         /* array of structs */
5728     pEnumValues += cValues * sizeof (PRINTER_ENUM_VALUESW);
5729
5730     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
5731     {
5732         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
5733         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
5734                 NULL, &dwType, lpValue, &cbValueLen);
5735         if (ret != ERROR_SUCCESS)
5736         {
5737             if (HeapFree (hHeap, 0, lpValue) == 0)
5738                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5739             if (HeapFree (hHeap, 0, lpValueName) == 0)
5740                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5741             r = RegCloseKey (hkSubKey);
5742             if (r != ERROR_SUCCESS)
5743                 WARN ("RegCloseKey returned %i\n", r);
5744             TRACE ("RegEnumValueW (%i) returned %i\n", dwIndex, ret);
5745             return ret;
5746         }
5747
5748         cbValueNameLen = (cbValueNameLen + 1) * sizeof (WCHAR);
5749         memcpy (pEnumValues, lpValueName, cbValueNameLen);
5750         ppev[dwIndex].pValueName = (LPWSTR) pEnumValues;
5751         pEnumValues += cbValueNameLen;
5752
5753         /* return # of *bytes* (including trailing \0), not # of chars */
5754         ppev[dwIndex].cbValueName = cbValueNameLen;
5755
5756         ppev[dwIndex].dwType = dwType;
5757
5758         memcpy (pEnumValues, lpValue, cbValueLen);
5759         ppev[dwIndex].pData = pEnumValues;
5760         pEnumValues += cbValueLen;
5761
5762         ppev[dwIndex].cbData = cbValueLen;
5763
5764         TRACE ("%s [%i]: copied name (%i bytes) and data (%i bytes)\n",
5765                 debugstr_w (lpValueName), dwIndex, cbValueNameLen, cbValueLen);
5766     }
5767
5768     if (HeapFree (hHeap, 0, lpValue) == 0)
5769     {
5770         ret = GetLastError ();
5771         ERR ("HeapFree failed with code %i\n", ret);
5772         if (HeapFree (hHeap, 0, lpValueName) == 0)
5773             WARN ("HeapFree failed with code %i\n", GetLastError ());
5774         r = RegCloseKey (hkSubKey);
5775         if (r != ERROR_SUCCESS)
5776             WARN ("RegCloseKey returned %i\n", r);
5777         return ret;
5778     }
5779
5780     if (HeapFree (hHeap, 0, lpValueName) == 0)
5781     {
5782         ret = GetLastError ();
5783         ERR ("HeapFree failed with code %i\n", ret);
5784         r = RegCloseKey (hkSubKey);
5785         if (r != ERROR_SUCCESS)
5786             WARN ("RegCloseKey returned %i\n", r);
5787         return ret;
5788     }
5789
5790     ret = RegCloseKey (hkSubKey);
5791     if (ret != ERROR_SUCCESS)
5792     {
5793         ERR ("RegCloseKey returned %i\n", ret);
5794         return ret;
5795     }
5796
5797     return ERROR_SUCCESS;
5798 }
5799
5800 /*******************************************************************************
5801  *              EnumPrinterDataExA      [WINSPOOL.@]
5802  *
5803  * This functions returns value names and REG_SZ, REG_EXPAND_SZ, and
5804  * REG_MULTI_SZ values as ASCII strings in Unicode-sized buffers.  This is
5805  * what Windows 2000 SP1 does.
5806  *
5807  */
5808 DWORD WINAPI EnumPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5809                                 LPBYTE pEnumValues, DWORD cbEnumValues,
5810                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
5811 {
5812     INT     len;
5813     LPWSTR  pKeyNameW;
5814     DWORD   ret, dwIndex, dwBufSize;
5815     HANDLE  hHeap;
5816     LPSTR   pBuffer;
5817
5818     TRACE ("%p %s\n", hPrinter, pKeyName);
5819
5820     if (pKeyName == NULL || *pKeyName == 0)
5821         return ERROR_INVALID_PARAMETER;
5822
5823     len = MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, NULL, 0);
5824     if (len == 0)
5825     {
5826         ret = GetLastError ();
5827         ERR ("MultiByteToWideChar failed with code %i\n", ret);
5828         return ret;
5829     }
5830
5831     hHeap = GetProcessHeap ();
5832     if (hHeap == NULL)
5833     {
5834         ERR ("GetProcessHeap failed\n");
5835         return ERROR_OUTOFMEMORY;
5836     }
5837
5838     pKeyNameW = HeapAlloc (hHeap, 0, len * sizeof (WCHAR));
5839     if (pKeyNameW == NULL)
5840     {
5841         ERR ("Failed to allocate %i bytes from process heap\n",
5842              (LONG)(len * sizeof (WCHAR)));
5843         return ERROR_OUTOFMEMORY;
5844     }
5845
5846     if (MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, pKeyNameW, len) == 0)
5847     {
5848         ret = GetLastError ();
5849         ERR ("MultiByteToWideChar failed with code %i\n", ret);
5850         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
5851             WARN ("HeapFree failed with code %i\n", GetLastError ());
5852         return ret;
5853     }
5854
5855     ret = EnumPrinterDataExW (hPrinter, pKeyNameW, pEnumValues, cbEnumValues,
5856             pcbEnumValues, pnEnumValues);
5857     if (ret != ERROR_SUCCESS)
5858     {
5859         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
5860             WARN ("HeapFree failed with code %i\n", GetLastError ());
5861         TRACE ("EnumPrinterDataExW returned %i\n", ret);
5862         return ret;
5863     }
5864
5865     if (HeapFree (hHeap, 0, pKeyNameW) == 0)
5866     {
5867         ret = GetLastError ();
5868         ERR ("HeapFree failed with code %i\n", ret);
5869         return ret;
5870     }
5871
5872     if (*pnEnumValues == 0)     /* empty key */
5873         return ERROR_SUCCESS;
5874
5875     dwBufSize = 0;
5876     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
5877     {
5878         PPRINTER_ENUM_VALUESW ppev =
5879                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
5880
5881         if (dwBufSize < ppev->cbValueName)
5882             dwBufSize = ppev->cbValueName;
5883
5884         if (dwBufSize < ppev->cbData && (ppev->dwType == REG_SZ ||
5885                 ppev->dwType == REG_EXPAND_SZ || ppev->dwType == REG_MULTI_SZ))
5886             dwBufSize = ppev->cbData;
5887     }
5888
5889     TRACE ("Largest Unicode name or value is %i bytes\n", dwBufSize);
5890
5891     pBuffer = HeapAlloc (hHeap, 0, dwBufSize);
5892     if (pBuffer == NULL)
5893     {
5894         ERR ("Failed to allocate %i bytes from process heap\n", dwBufSize);
5895         return ERROR_OUTOFMEMORY;
5896     }
5897
5898     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
5899     {
5900         PPRINTER_ENUM_VALUESW ppev =
5901                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
5902
5903         len = WideCharToMultiByte (CP_ACP, 0, ppev->pValueName,
5904                 ppev->cbValueName / sizeof (WCHAR), pBuffer, dwBufSize, NULL,
5905                 NULL);
5906         if (len == 0)
5907         {
5908             ret = GetLastError ();
5909             ERR ("WideCharToMultiByte failed with code %i\n", ret);
5910             if (HeapFree (hHeap, 0, pBuffer) == 0)
5911                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5912             return ret;
5913         }
5914
5915         memcpy (ppev->pValueName, pBuffer, len);
5916
5917         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
5918
5919         if (ppev->dwType != REG_SZ && ppev->dwType != REG_EXPAND_SZ &&
5920                 ppev->dwType != REG_MULTI_SZ)
5921             continue;
5922
5923         len = WideCharToMultiByte (CP_ACP, 0, (LPWSTR) ppev->pData,
5924                 ppev->cbData / sizeof (WCHAR), pBuffer, dwBufSize, NULL, NULL);
5925         if (len == 0)
5926         {
5927             ret = GetLastError ();
5928             ERR ("WideCharToMultiByte failed with code %i\n", ret);
5929             if (HeapFree (hHeap, 0, pBuffer) == 0)
5930                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5931             return ret;
5932         }
5933
5934         memcpy (ppev->pData, pBuffer, len);
5935
5936         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
5937         TRACE ("  (only first string of REG_MULTI_SZ printed)\n");
5938     }
5939
5940     if (HeapFree (hHeap, 0, pBuffer) == 0)
5941     {
5942         ret = GetLastError ();
5943         ERR ("HeapFree failed with code %i\n", ret);
5944         return ret;
5945     }
5946
5947     return ERROR_SUCCESS;
5948 }
5949
5950 /******************************************************************************
5951  *      AbortPrinter (WINSPOOL.@)
5952  */
5953 BOOL WINAPI AbortPrinter( HANDLE hPrinter )
5954 {
5955     FIXME("(%p), stub!\n", hPrinter);
5956     return TRUE;
5957 }
5958
5959 /******************************************************************************
5960  *              AddPortA (WINSPOOL.@)
5961  *
5962  * See AddPortW.
5963  *
5964  */
5965 BOOL WINAPI AddPortA(LPSTR pName, HWND hWnd, LPSTR pMonitorName)
5966 {
5967     LPWSTR  nameW = NULL;
5968     LPWSTR  monitorW = NULL;
5969     DWORD   len;
5970     BOOL    res;
5971
5972     TRACE("(%s, %p, %s)\n",debugstr_a(pName), hWnd, debugstr_a(pMonitorName));
5973
5974     if (pName) {
5975         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
5976         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5977         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
5978     }
5979
5980     if (pMonitorName) {
5981         len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
5982         monitorW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5983         MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, monitorW, len);
5984     }
5985     res = AddPortW(nameW, hWnd, monitorW);
5986     HeapFree(GetProcessHeap(), 0, nameW);
5987     HeapFree(GetProcessHeap(), 0, monitorW);
5988     return res;
5989 }
5990
5991 /******************************************************************************
5992  *      AddPortW (WINSPOOL.@)
5993  *
5994  * Add a Port for a specific Monitor
5995  *
5996  * PARAMS
5997  *  pName        [I] Servername or NULL (local Computer)
5998  *  hWnd         [I] Handle to parent Window for the Dialog-Box
5999  *  pMonitorName [I] Name of the Monitor that manage the Port
6000  *
6001  * RETURNS
6002  *  Success: TRUE
6003  *  Failure: FALSE
6004  *
6005  */
6006 BOOL WINAPI AddPortW(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
6007 {
6008     monitor_t * pm;
6009     monitor_t * pui;
6010     DWORD       res;
6011
6012     TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pMonitorName));
6013
6014     if (pName && pName[0]) {
6015         SetLastError(ERROR_INVALID_PARAMETER);
6016         return FALSE;
6017     }
6018
6019     if (!pMonitorName) {
6020         SetLastError(RPC_X_NULL_REF_POINTER);
6021         return FALSE;
6022     }
6023
6024     /* an empty Monitorname is Invalid */
6025     if (!pMonitorName[0]) {
6026         SetLastError(ERROR_NOT_SUPPORTED);
6027         return FALSE;
6028     }
6029
6030     pm = monitor_load(pMonitorName, NULL);
6031     if (pm && pm->monitor && pm->monitor->pfnAddPort) {
6032         res = pm->monitor->pfnAddPort(pName, hWnd, pMonitorName);
6033         TRACE("got %d with %u\n", res, GetLastError());
6034         res = TRUE;
6035     }
6036     else
6037     {
6038         pui = monitor_loadui(pm);
6039         if (pui && pui->monitorUI && pui->monitorUI->pfnAddPortUI) {
6040             TRACE("use %p: %s\n", pui, debugstr_w(pui->dllname));
6041             res = pui->monitorUI->pfnAddPortUI(pName, hWnd, pMonitorName, NULL);
6042             TRACE("got %d with %u\n", res, GetLastError());
6043             res = TRUE;
6044         }
6045         else
6046         {
6047             FIXME("not implemented for %s (%p: %s => %p: %s)\n", debugstr_w(pMonitorName),
6048                 pm, pm ? debugstr_w(pm->dllname) : NULL, pui, pui ? debugstr_w(pui->dllname) : NULL);
6049
6050             /* XP: ERROR_NOT_SUPPORTED, NT351,9x: ERROR_INVALID_PARAMETER */
6051             SetLastError(ERROR_NOT_SUPPORTED);
6052             res = FALSE;
6053         }
6054         monitor_unload(pui);
6055     }
6056     /* invalidate cached PORT_INFO_2W */
6057     monitor_flush(pm);
6058
6059     monitor_unload(pm);
6060     TRACE("returning %d with %u\n", res, GetLastError());
6061     return res;
6062 }
6063
6064 /******************************************************************************
6065  *             AddPortExA (WINSPOOL.@)
6066  *
6067  * See AddPortExW.
6068  *
6069  */
6070 BOOL WINAPI AddPortExA(HANDLE hMonitor, LPSTR pName, DWORD Level, LPBYTE lpBuffer, LPSTR lpMonitorName)
6071 {
6072     FIXME("(%p, %s, %d, %p, %s), stub!\n",hMonitor, debugstr_a(pName), Level,
6073           lpBuffer, debugstr_a(lpMonitorName));
6074     return FALSE;
6075 }
6076
6077 /******************************************************************************
6078  *             AddPortExW (WINSPOOL.@)
6079  *
6080  * Add a Port for a specific Monitor, without presenting a user interface
6081  *
6082  * PARAMS
6083  *  hMonitor      [I] Handle from InitializePrintMonitor2()
6084  *  pName         [I] Servername or NULL (local Computer)
6085  *  Level         [I] Structure-Level (1 or 2) for lpBuffer
6086  *  lpBuffer      [I] PTR to: PORT_INFO_1 or PORT_INFO_2
6087  *  lpMonitorName [I] Name of the Monitor that manage the Port or NULL
6088  *
6089  * RETURNS
6090  *  Success: TRUE
6091  *  Failure: FALSE
6092  *
6093  * BUGS
6094  *  only a Stub
6095  *
6096  */
6097 BOOL WINAPI AddPortExW(HANDLE hMonitor, LPWSTR pName, DWORD Level, LPBYTE lpBuffer, LPWSTR lpMonitorName)
6098 {
6099     FIXME("(%p, %s, %d, %p, %s), stub!\n", hMonitor, debugstr_w(pName), Level,
6100           lpBuffer, debugstr_w(lpMonitorName));
6101     return FALSE;
6102 }
6103
6104 /******************************************************************************
6105  *      AddPrinterConnectionA (WINSPOOL.@)
6106  */
6107 BOOL WINAPI AddPrinterConnectionA( LPSTR pName )
6108 {
6109     FIXME("%s\n", debugstr_a(pName));
6110     return FALSE;
6111 }
6112
6113 /******************************************************************************
6114  *      AddPrinterConnectionW (WINSPOOL.@)
6115  */
6116 BOOL WINAPI AddPrinterConnectionW( LPWSTR pName )
6117 {
6118     FIXME("%s\n", debugstr_w(pName));
6119     return FALSE;
6120 }
6121
6122 /******************************************************************************
6123  *              AddPrinterDriverExW (WINSPOOL.@)
6124  */
6125 BOOL WINAPI AddPrinterDriverExW( LPWSTR pName, DWORD Level,
6126     LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
6127 {
6128     FIXME("%s %d %p %d\n", debugstr_w(pName),
6129            Level, pDriverInfo, dwFileCopyFlags);
6130     SetLastError(ERROR_PRINTER_DRIVER_BLOCKED);
6131     return FALSE;
6132 }
6133
6134 /******************************************************************************
6135  *              AddPrinterDriverExA (WINSPOOL.@)
6136  */
6137 BOOL WINAPI AddPrinterDriverExA( LPSTR pName, DWORD Level,
6138     LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
6139 {
6140     FIXME("%s %d %p %d\n", debugstr_a(pName),
6141            Level, pDriverInfo, dwFileCopyFlags);
6142     SetLastError(ERROR_PRINTER_DRIVER_BLOCKED);
6143     return FALSE;
6144 }
6145
6146 /******************************************************************************
6147  *      ConfigurePortA (WINSPOOL.@)
6148  *
6149  * See ConfigurePortW.
6150  *
6151  */
6152 BOOL WINAPI ConfigurePortA(LPSTR pName, HWND hWnd, LPSTR pPortName)
6153 {
6154     LPWSTR  nameW = NULL;
6155     LPWSTR  portW = NULL;
6156     INT     len;
6157     DWORD   res;
6158
6159     TRACE("(%s, %p, %s)\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
6160
6161     /* convert servername to unicode */
6162     if (pName) {
6163         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6164         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6165         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6166     }
6167
6168     /* convert portname to unicode */
6169     if (pPortName) {
6170         len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
6171         portW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6172         MultiByteToWideChar(CP_ACP, 0, pPortName, -1, portW, len);
6173     }
6174
6175     res = ConfigurePortW(nameW, hWnd, portW);
6176     HeapFree(GetProcessHeap(), 0, nameW);
6177     HeapFree(GetProcessHeap(), 0, portW);
6178     return res;
6179 }
6180
6181 /******************************************************************************
6182  *      ConfigurePortW (WINSPOOL.@)
6183  *
6184  * Display the Configuration-Dialog for a specific Port
6185  *
6186  * PARAMS
6187  *  pName     [I] Servername or NULL (local Computer)
6188  *  hWnd      [I] Handle to parent Window for the Dialog-Box
6189  *  pPortName [I] Name of the Port, that should be configured
6190  *
6191  * RETURNS
6192  *  Success: TRUE
6193  *  Failure: FALSE
6194  *
6195  */
6196 BOOL WINAPI ConfigurePortW(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
6197 {
6198     monitor_t * pm;
6199     monitor_t * pui;
6200     DWORD       res;
6201
6202     TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
6203
6204     if (pName && pName[0]) {
6205         SetLastError(ERROR_INVALID_PARAMETER);
6206         return FALSE;
6207     }
6208
6209     if (!pPortName) {
6210         SetLastError(RPC_X_NULL_REF_POINTER);
6211         return FALSE;
6212     }
6213
6214     /* an empty Portname is Invalid, but can popup a Dialog */
6215     if (!pPortName[0]) {
6216         SetLastError(ERROR_NOT_SUPPORTED);
6217         return FALSE;
6218     }
6219
6220     pm = monitor_load_by_port(pPortName);
6221     if (pm && pm->monitor && pm->monitor->pfnConfigurePort) {
6222         TRACE("Using %s for %s (%p: %s)\n", debugstr_w(pm->name), debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
6223         res = pm->monitor->pfnConfigurePort(pName, hWnd, pPortName);
6224         TRACE("got %d with %u\n", res, GetLastError());
6225     }
6226     else
6227     {
6228         pui = monitor_loadui(pm);
6229         if (pui && pui->monitorUI && pui->monitorUI->pfnConfigurePortUI) {
6230             TRACE("Use %s for %s (%p: %s)\n", debugstr_w(pui->name), debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
6231             res = pui->monitorUI->pfnConfigurePortUI(pName, hWnd, pPortName);
6232             TRACE("got %d with %u\n", res, GetLastError());
6233         }
6234         else
6235         {
6236             FIXME("not implemented for %s (%p: %s => %p: %s)\n", debugstr_w(pPortName),
6237                 pm, pm ? debugstr_w(pm->dllname) : NULL, pui, pui ? debugstr_w(pui->dllname) : NULL);
6238
6239             /* XP: ERROR_NOT_SUPPORTED, NT351,9x: ERROR_INVALID_PARAMETER */
6240             SetLastError(ERROR_NOT_SUPPORTED);
6241             res = FALSE;
6242         }
6243         monitor_unload(pui);
6244     }
6245     monitor_unload(pm);
6246
6247     TRACE("returning %d with %u\n", res, GetLastError());
6248     return res;
6249 }
6250
6251 /******************************************************************************
6252  *      ConnectToPrinterDlg (WINSPOOL.@)
6253  */
6254 HANDLE WINAPI ConnectToPrinterDlg( HWND hWnd, DWORD Flags )
6255 {
6256     FIXME("%p %x\n", hWnd, Flags);
6257     return NULL;
6258 }
6259
6260 /******************************************************************************
6261  *      DeletePrinterConnectionA (WINSPOOL.@)
6262  */
6263 BOOL WINAPI DeletePrinterConnectionA( LPSTR pName )
6264 {
6265     FIXME("%s\n", debugstr_a(pName));
6266     return TRUE;
6267 }
6268
6269 /******************************************************************************
6270  *      DeletePrinterConnectionW (WINSPOOL.@)
6271  */
6272 BOOL WINAPI DeletePrinterConnectionW( LPWSTR pName )
6273 {
6274     FIXME("%s\n", debugstr_w(pName));
6275     return TRUE;
6276 }
6277
6278 /******************************************************************************
6279  *              DeletePrinterDriverExW (WINSPOOL.@)
6280  */
6281 BOOL WINAPI DeletePrinterDriverExW( LPWSTR pName, LPWSTR pEnvironment,
6282     LPWSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
6283 {
6284     HKEY hkey_drivers;
6285     BOOL ret = FALSE;
6286
6287     TRACE("%s %s %s %x %x\n", debugstr_w(pName), debugstr_w(pEnvironment),
6288           debugstr_w(pDriverName), dwDeleteFlag, dwVersionFlag);
6289
6290     if(pName && pName[0])
6291     {
6292         FIXME("pName = %s - unsupported\n", debugstr_w(pName));
6293         SetLastError(ERROR_INVALID_PARAMETER);
6294         return FALSE;
6295     }
6296
6297     if(dwDeleteFlag)
6298     {
6299         FIXME("dwDeleteFlag = %x - unsupported\n", dwDeleteFlag);
6300         SetLastError(ERROR_INVALID_PARAMETER);
6301         return FALSE;
6302     }
6303
6304     hkey_drivers = WINSPOOL_OpenDriverReg(pEnvironment, TRUE);
6305
6306     if(!hkey_drivers)
6307     {
6308         ERR("Can't open drivers key\n");
6309         return FALSE;
6310     }
6311
6312     if(WINSPOOL_SHDeleteKeyW(hkey_drivers, pDriverName) == ERROR_SUCCESS)
6313         ret = TRUE;
6314
6315     RegCloseKey(hkey_drivers);
6316
6317     return ret;
6318 }
6319
6320 /******************************************************************************
6321  *              DeletePrinterDriverExA (WINSPOOL.@)
6322  */
6323 BOOL WINAPI DeletePrinterDriverExA( LPSTR pName, LPSTR pEnvironment,
6324     LPSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
6325 {
6326     UNICODE_STRING NameW, EnvW, DriverW;
6327     BOOL ret;
6328
6329     asciitounicode(&NameW, pName);
6330     asciitounicode(&EnvW, pEnvironment);
6331     asciitounicode(&DriverW, pDriverName);
6332
6333     ret = DeletePrinterDriverExW(NameW.Buffer, EnvW.Buffer, DriverW.Buffer, dwDeleteFlag, dwVersionFlag);
6334
6335     RtlFreeUnicodeString(&DriverW);
6336     RtlFreeUnicodeString(&EnvW);
6337     RtlFreeUnicodeString(&NameW);
6338
6339     return ret;
6340 }
6341
6342 /******************************************************************************
6343  *              DeletePrinterDataExW (WINSPOOL.@)
6344  */
6345 DWORD WINAPI DeletePrinterDataExW( HANDLE hPrinter, LPCWSTR pKeyName,
6346                                   LPCWSTR pValueName)
6347 {
6348     FIXME("%p %s %s\n", hPrinter, 
6349           debugstr_w(pKeyName), debugstr_w(pValueName));
6350     return ERROR_INVALID_PARAMETER;
6351 }
6352
6353 /******************************************************************************
6354  *              DeletePrinterDataExA (WINSPOOL.@)
6355  */
6356 DWORD WINAPI DeletePrinterDataExA( HANDLE hPrinter, LPCSTR pKeyName,
6357                                   LPCSTR pValueName)
6358 {
6359     FIXME("%p %s %s\n", hPrinter, 
6360           debugstr_a(pKeyName), debugstr_a(pValueName));
6361     return ERROR_INVALID_PARAMETER;
6362 }
6363
6364 /******************************************************************************
6365  *      DeletePrintProcessorA (WINSPOOL.@)
6366  */
6367 BOOL WINAPI DeletePrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProcessorName)
6368 {
6369     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
6370           debugstr_a(pPrintProcessorName));
6371     return TRUE;
6372 }
6373
6374 /******************************************************************************
6375  *      DeletePrintProcessorW (WINSPOOL.@)
6376  */
6377 BOOL WINAPI DeletePrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProcessorName)
6378 {
6379     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
6380           debugstr_w(pPrintProcessorName));
6381     return TRUE;
6382 }
6383
6384 /******************************************************************************
6385  *      DeletePrintProvidorA (WINSPOOL.@)
6386  */
6387 BOOL WINAPI DeletePrintProvidorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProviderName)
6388 {
6389     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
6390           debugstr_a(pPrintProviderName));
6391     return TRUE;
6392 }
6393
6394 /******************************************************************************
6395  *      DeletePrintProvidorW (WINSPOOL.@)
6396  */
6397 BOOL WINAPI DeletePrintProvidorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProviderName)
6398 {
6399     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
6400           debugstr_w(pPrintProviderName));
6401     return TRUE;
6402 }
6403
6404 /******************************************************************************
6405  *      EnumFormsA (WINSPOOL.@)
6406  */
6407 BOOL WINAPI EnumFormsA( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
6408     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
6409 {
6410     FIXME("%p %x %p %x %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
6411     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
6412     return FALSE;
6413 }
6414
6415 /******************************************************************************
6416  *      EnumFormsW (WINSPOOL.@)
6417  */
6418 BOOL WINAPI EnumFormsW( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
6419     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
6420 {
6421     FIXME("%p %x %p %x %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
6422     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
6423     return FALSE;
6424 }
6425
6426 /*****************************************************************************
6427  *          EnumMonitorsA [WINSPOOL.@]
6428  *
6429  * See EnumMonitorsW.
6430  *
6431  */
6432 BOOL WINAPI EnumMonitorsA(LPSTR pName, DWORD Level, LPBYTE pMonitors,
6433                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
6434 {
6435     BOOL    res;
6436     LPBYTE  bufferW = NULL;
6437     LPWSTR  nameW = NULL;
6438     DWORD   needed = 0;
6439     DWORD   numentries = 0;
6440     INT     len;
6441
6442     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), Level, pMonitors,
6443           cbBuf, pcbNeeded, pcReturned);
6444
6445     /* convert servername to unicode */
6446     if (pName) {
6447         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6448         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6449         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6450     }
6451     /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the monitors */
6452     needed = cbBuf * sizeof(WCHAR);    
6453     if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
6454     res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
6455
6456     if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
6457         if (pcbNeeded) needed = *pcbNeeded;
6458         /* HeapReAlloc return NULL, when bufferW was NULL */
6459         bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
6460                               HeapAlloc(GetProcessHeap(), 0, needed);
6461
6462         /* Try again with the large Buffer */
6463         res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
6464     }
6465     numentries = pcReturned ? *pcReturned : 0;
6466     needed = 0;
6467     /*
6468        W2k require the buffersize from EnumMonitorsW also for EnumMonitorsA.
6469        We use the smaller Ansi-Size to avoid conflicts with fixed Buffers of old Apps.
6470      */
6471     if (res) {
6472         /* EnumMonitorsW collected all Data. Parse them to calculate ANSI-Size */
6473         DWORD   entrysize = 0;
6474         DWORD   index;
6475         LPSTR   ptr;
6476         LPMONITOR_INFO_2W mi2w;
6477         LPMONITOR_INFO_2A mi2a;
6478
6479         /* MONITOR_INFO_*W and MONITOR_INFO_*A have the same size */
6480         entrysize = (Level == 1) ? sizeof(MONITOR_INFO_1A) : sizeof(MONITOR_INFO_2A);
6481
6482         /* First pass: calculate the size for all Entries */
6483         mi2w = (LPMONITOR_INFO_2W) bufferW;
6484         mi2a = (LPMONITOR_INFO_2A) pMonitors;
6485         index = 0;
6486         while (index < numentries) {
6487             index++;
6488             needed += entrysize;    /* MONITOR_INFO_?A */
6489             TRACE("%p: parsing #%d (%s)\n", mi2w, index, debugstr_w(mi2w->pName));
6490
6491             needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
6492                                             NULL, 0, NULL, NULL);
6493             if (Level > 1) {
6494                 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
6495                                                 NULL, 0, NULL, NULL);
6496                 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
6497                                                 NULL, 0, NULL, NULL);
6498             }
6499             /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
6500             mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
6501             mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
6502         }
6503
6504         /* check for errors and quit on failure */
6505         if (cbBuf < needed) {
6506             SetLastError(ERROR_INSUFFICIENT_BUFFER);
6507             res = FALSE;
6508             goto emA_cleanup;
6509         }
6510         len = entrysize * numentries;       /* room for all MONITOR_INFO_?A */
6511         ptr = (LPSTR) &pMonitors[len];      /* room for strings */
6512         cbBuf -= len ;                      /* free Bytes in the user-Buffer */
6513         mi2w = (LPMONITOR_INFO_2W) bufferW;
6514         mi2a = (LPMONITOR_INFO_2A) pMonitors;
6515         index = 0;
6516         /* Second Pass: Fill the User Buffer (if we have one) */
6517         while ((index < numentries) && pMonitors) {
6518             index++;
6519             TRACE("%p: writing MONITOR_INFO_%dA #%d\n", mi2a, Level, index);
6520             mi2a->pName = ptr;
6521             len = WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
6522                                             ptr, cbBuf , NULL, NULL);
6523             ptr += len;
6524             cbBuf -= len;
6525             if (Level > 1) {
6526                 mi2a->pEnvironment = ptr;
6527                 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
6528                                             ptr, cbBuf, NULL, NULL);
6529                 ptr += len;
6530                 cbBuf -= len;
6531
6532                 mi2a->pDLLName = ptr;
6533                 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
6534                                             ptr, cbBuf, NULL, NULL);
6535                 ptr += len;
6536                 cbBuf -= len;
6537             }
6538             /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
6539             mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
6540             mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
6541         }
6542     }
6543 emA_cleanup:
6544     if (pcbNeeded)  *pcbNeeded = needed;
6545     if (pcReturned) *pcReturned = (res) ? numentries : 0;
6546
6547     HeapFree(GetProcessHeap(), 0, nameW);
6548     HeapFree(GetProcessHeap(), 0, bufferW);
6549
6550     TRACE("returning %d with %d (%d byte for %d entries)\n", 
6551             (res), GetLastError(), needed, numentries);
6552
6553     return (res);
6554
6555 }
6556
6557 /*****************************************************************************
6558  *          EnumMonitorsW [WINSPOOL.@]
6559  *
6560  * Enumerate available Port-Monitors
6561  *
6562  * PARAMS
6563  *  pName       [I] Servername or NULL (local Computer)
6564  *  Level       [I] Structure-Level (1:Win9x+NT or 2:NT only)
6565  *  pMonitors   [O] PTR to Buffer that receives the Result
6566  *  cbBuf       [I] Size of Buffer at pMonitors
6567  *  pcbNeeded   [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
6568  *  pcReturned  [O] PTR to DWORD that receives the number of Monitors in pMonitors
6569  *
6570  * RETURNS
6571  *  Success: TRUE
6572  *  Failure: FALSE and in pcbNeeded the Bytes required for buffer, if cbBuf is too small
6573  *
6574  * NOTES
6575  *  Windows reads the Registry once and cache the Results.
6576  *
6577  *|  Language-Monitors are also installed in the same Registry-Location but 
6578  *|  they are filtered in Windows (not returned by EnumMonitors).
6579  *|  We do no filtering to simplify our Code.
6580  *
6581  */
6582 BOOL WINAPI EnumMonitorsW(LPWSTR pName, DWORD Level, LPBYTE pMonitors,
6583                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
6584 {
6585     DWORD   needed = 0;
6586     DWORD   numentries = 0;
6587     BOOL    res = FALSE;
6588
6589     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pMonitors,
6590           cbBuf, pcbNeeded, pcReturned);
6591
6592     if (pName && (lstrlenW(pName))) {
6593         FIXME("for Server %s not implemented\n", debugstr_w(pName));
6594         SetLastError(ERROR_ACCESS_DENIED);
6595         goto emW_cleanup;
6596     }
6597
6598     /* Level is not checked in win9x */
6599     if (!Level || (Level > 2)) {
6600         WARN("level (%d) is ignored in win9x\n", Level);
6601         SetLastError(ERROR_INVALID_LEVEL);
6602         goto emW_cleanup;
6603     }
6604     if (!pcbNeeded) {
6605         SetLastError(RPC_X_NULL_REF_POINTER);
6606         goto emW_cleanup;
6607     }
6608
6609     /* Scan all Monitor-Keys */
6610     numentries = 0;
6611     needed = get_local_monitors(Level, NULL, 0, &numentries);
6612
6613     /* we calculated the needed buffersize. now do the error-checks */
6614     if (cbBuf < needed) {
6615         SetLastError(ERROR_INSUFFICIENT_BUFFER);
6616         goto emW_cleanup;
6617     }
6618     else if (!pMonitors || !pcReturned) {
6619         SetLastError(RPC_X_NULL_REF_POINTER);
6620         goto emW_cleanup;
6621     }
6622
6623     /* fill the Buffer with the Monitor-Keys */
6624     needed = get_local_monitors(Level, pMonitors, cbBuf, &numentries);
6625     res = TRUE;
6626
6627 emW_cleanup:
6628     if (pcbNeeded)  *pcbNeeded = needed;
6629     if (pcReturned) *pcReturned = numentries;
6630
6631     TRACE("returning %d with %d (%d byte for %d entries)\n", 
6632             res, GetLastError(), needed, numentries);
6633
6634     return (res);
6635 }
6636
6637 /******************************************************************************
6638  *              XcvDataW (WINSPOOL.@)
6639  *
6640  * Execute commands in the Printmonitor DLL
6641  *
6642  * PARAMS
6643  *  hXcv            [i] Handle from OpenPrinter (with XcvMonitor or XcvPort)
6644  *  pszDataName     [i] Name of the command to execute
6645  *  pInputData      [i] Buffer for extra Input Data (needed only for some commands)
6646  *  cbInputData     [i] Size in Bytes of Buffer at pInputData
6647  *  pOutputData     [o] Buffer to receive additional Data (needed only for some commands)
6648  *  cbOutputData    [i] Size in Bytes of Buffer at pOutputData
6649  *  pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
6650  *  pdwStatus       [o] PTR to receive the win32 error code from the Printmonitor DLL
6651  *
6652  * RETURNS
6653  *  Success: TRUE
6654  *  Failure: FALSE
6655  *
6656  * NOTES
6657  *  Returning "TRUE" does mean, that the Printmonitor DLL was called successful.
6658  *  The execution of the command can still fail (check pdwStatus for ERROR_SUCCESS).
6659  *
6660  *  Minimal List of commands, that a Printmonitor DLL should support:
6661  *
6662  *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
6663  *| "AddPort"   : Add a Port
6664  *| "DeletePort": Delete a Port
6665  *
6666  *  Many Printmonitors support additional commands. Examples for localspl.dll:
6667  *  "GetDefaultCommConfig", "SetDefaultCommConfig",
6668  *  "GetTransmissionRetryTimeout", "ConfigureLPTPortCommandOK"
6669  *
6670  */
6671 BOOL WINAPI XcvDataW( HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData,
6672     DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData,
6673     PDWORD pcbOutputNeeded, PDWORD pdwStatus)
6674 {
6675     opened_printer_t *printer;
6676
6677     TRACE("(%p, %s, %p, %d, %p, %d, %p, %p)\n", hXcv, debugstr_w(pszDataName),
6678           pInputData, cbInputData, pOutputData,
6679           cbOutputData, pcbOutputNeeded, pdwStatus);
6680
6681     printer = get_opened_printer(hXcv);
6682     if (!printer || (!printer->hXcv)) {
6683         SetLastError(ERROR_INVALID_HANDLE);
6684         return FALSE;
6685     }
6686
6687     if (!pcbOutputNeeded) {
6688         SetLastError(ERROR_INVALID_PARAMETER);
6689         return FALSE;
6690     }
6691
6692     if (!pszDataName || !pdwStatus || (!pOutputData && (cbOutputData > 0))) {
6693         SetLastError(RPC_X_NULL_REF_POINTER);
6694         return FALSE;
6695     }
6696
6697     *pcbOutputNeeded = 0;
6698
6699     *pdwStatus = printer->pm->monitor->pfnXcvDataPort(printer->hXcv, pszDataName,
6700             pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
6701
6702     return TRUE;
6703 }
6704
6705 /*****************************************************************************
6706  *          EnumPrinterDataA [WINSPOOL.@]
6707  *
6708  */
6709 DWORD WINAPI EnumPrinterDataA( HANDLE hPrinter, DWORD dwIndex, LPSTR pValueName,
6710     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
6711     DWORD cbData, LPDWORD pcbData )
6712 {
6713     FIXME("%p %x %p %x %p %p %p %x %p\n", hPrinter, dwIndex, pValueName,
6714           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
6715     return ERROR_NO_MORE_ITEMS;
6716 }
6717
6718 /*****************************************************************************
6719  *          EnumPrinterDataW [WINSPOOL.@]
6720  *
6721  */
6722 DWORD WINAPI EnumPrinterDataW( HANDLE hPrinter, DWORD dwIndex, LPWSTR pValueName,
6723     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
6724     DWORD cbData, LPDWORD pcbData )
6725 {
6726     FIXME("%p %x %p %x %p %p %p %x %p\n", hPrinter, dwIndex, pValueName,
6727           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
6728     return ERROR_NO_MORE_ITEMS;
6729 }
6730
6731 /*****************************************************************************
6732  *          EnumPrintProcessorDatatypesA [WINSPOOL.@]
6733  *
6734  */
6735 BOOL WINAPI EnumPrintProcessorDatatypesA(LPSTR pName, LPSTR pPrintProcessorName,
6736                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
6737                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
6738 {
6739     FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_a(pName),
6740           debugstr_a(pPrintProcessorName), Level, pDatatypes, cbBuf,
6741           pcbNeeded, pcReturned);
6742     return FALSE;
6743 }
6744
6745 /*****************************************************************************
6746  *          EnumPrintProcessorDatatypesW [WINSPOOL.@]
6747  *
6748  */
6749 BOOL WINAPI EnumPrintProcessorDatatypesW(LPWSTR pName, LPWSTR pPrintProcessorName,
6750                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
6751                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
6752 {
6753     FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_w(pName),
6754           debugstr_w(pPrintProcessorName), Level, pDatatypes, cbBuf,
6755           pcbNeeded, pcReturned);
6756     return FALSE;
6757 }
6758
6759 /*****************************************************************************
6760  *          EnumPrintProcessorsA [WINSPOOL.@]
6761  *
6762  */
6763 BOOL WINAPI EnumPrintProcessorsA(LPSTR pName, LPSTR pEnvironment, DWORD Level, 
6764     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
6765 {
6766     FIXME("Stub: %s %s %d %p %d %p %p\n", pName, pEnvironment, Level,
6767         pPrintProcessorInfo, cbBuf, pcbNeeded, pcbReturned);
6768     return FALSE;
6769 }
6770
6771 /*****************************************************************************
6772  *          EnumPrintProcessorsW [WINSPOOL.@]
6773  *
6774  */
6775 BOOL WINAPI EnumPrintProcessorsW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
6776     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
6777 {
6778     FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_w(pName),
6779         debugstr_w(pEnvironment), Level, pPrintProcessorInfo,
6780         cbBuf, pcbNeeded, pcbReturned);
6781     return FALSE;
6782 }
6783
6784 /*****************************************************************************
6785  *          ExtDeviceMode [WINSPOOL.@]
6786  *
6787  */
6788 LONG WINAPI ExtDeviceMode( HWND hWnd, HANDLE hInst, LPDEVMODEA pDevModeOutput,
6789     LPSTR pDeviceName, LPSTR pPort, LPDEVMODEA pDevModeInput, LPSTR pProfile,
6790     DWORD fMode)
6791 {
6792     FIXME("Stub: %p %p %p %s %s %p %s %x\n", hWnd, hInst, pDevModeOutput,
6793           debugstr_a(pDeviceName), debugstr_a(pPort), pDevModeInput,
6794           debugstr_a(pProfile), fMode);
6795     return -1;
6796 }
6797
6798 /*****************************************************************************
6799  *          FindClosePrinterChangeNotification [WINSPOOL.@]
6800  *
6801  */
6802 BOOL WINAPI FindClosePrinterChangeNotification( HANDLE hChange )
6803 {
6804     FIXME("Stub: %p\n", hChange);
6805     return TRUE;
6806 }
6807
6808 /*****************************************************************************
6809  *          FindFirstPrinterChangeNotification [WINSPOOL.@]
6810  *
6811  */
6812 HANDLE WINAPI FindFirstPrinterChangeNotification( HANDLE hPrinter,
6813     DWORD fdwFlags, DWORD fdwOptions, LPVOID pPrinterNotifyOptions )
6814 {
6815     FIXME("Stub: %p %x %x %p\n",
6816           hPrinter, fdwFlags, fdwOptions, pPrinterNotifyOptions);
6817     return INVALID_HANDLE_VALUE;
6818 }
6819
6820 /*****************************************************************************
6821  *          FindNextPrinterChangeNotification [WINSPOOL.@]
6822  *
6823  */
6824 BOOL WINAPI FindNextPrinterChangeNotification( HANDLE hChange, PDWORD pdwChange,
6825     LPVOID pPrinterNotifyOptions, LPVOID *ppPrinterNotifyInfo )
6826 {
6827     FIXME("Stub: %p %p %p %p\n",
6828           hChange, pdwChange, pPrinterNotifyOptions, ppPrinterNotifyInfo);
6829     return FALSE;
6830 }
6831
6832 /*****************************************************************************
6833  *          FreePrinterNotifyInfo [WINSPOOL.@]
6834  *
6835  */
6836 BOOL WINAPI FreePrinterNotifyInfo( PPRINTER_NOTIFY_INFO pPrinterNotifyInfo )
6837 {
6838     FIXME("Stub: %p\n", pPrinterNotifyInfo);
6839     return TRUE;
6840 }
6841
6842 /*****************************************************************************
6843  *          string_to_buf
6844  *
6845  * Copies a unicode string into a buffer.  The buffer will either contain unicode or
6846  * ansi depending on the unicode parameter.
6847  */
6848 static BOOL string_to_buf(LPCWSTR str, LPBYTE ptr, DWORD cb, DWORD *size, BOOL unicode)
6849 {
6850     if(!str)
6851     {
6852         *size = 0;
6853         return TRUE;
6854     }
6855
6856     if(unicode)
6857     {
6858         *size = (strlenW(str) + 1) * sizeof(WCHAR);
6859         if(*size <= cb)
6860         {
6861             memcpy(ptr, str, *size);
6862             return TRUE;
6863         }
6864         return FALSE;
6865     }
6866     else
6867     {
6868         *size = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
6869         if(*size <= cb)
6870         {
6871             WideCharToMultiByte(CP_ACP, 0, str, -1, (LPSTR)ptr, *size, NULL, NULL);
6872             return TRUE;
6873         }
6874         return FALSE;
6875     }
6876 }
6877
6878 /*****************************************************************************
6879  *          get_job_info_1
6880  */
6881 static BOOL get_job_info_1(job_t *job, JOB_INFO_1W *ji1, LPBYTE buf, DWORD cbBuf,
6882                            LPDWORD pcbNeeded, BOOL unicode)
6883 {
6884     DWORD size, left = cbBuf;
6885     BOOL space = (cbBuf > 0);
6886     LPBYTE ptr = buf;
6887
6888     *pcbNeeded = 0;
6889
6890     if(space)
6891     {
6892         ji1->JobId = job->job_id;
6893     }
6894
6895     string_to_buf(job->document_title, ptr, left, &size, unicode);
6896     if(space && size <= left)
6897     {
6898         ji1->pDocument = (LPWSTR)ptr;
6899         ptr += size;
6900         left -= size;
6901     }
6902     else
6903         space = FALSE;
6904     *pcbNeeded += size;
6905
6906     return space;
6907 }
6908
6909 /*****************************************************************************
6910  *          get_job_info_2
6911  */
6912 static BOOL get_job_info_2(job_t *job, JOB_INFO_2W *ji2, LPBYTE buf, DWORD cbBuf,
6913                            LPDWORD pcbNeeded, BOOL unicode)
6914 {
6915     DWORD size, left = cbBuf;
6916     BOOL space = (cbBuf > 0);
6917     LPBYTE ptr = buf;
6918
6919     *pcbNeeded = 0;
6920
6921     if(space)
6922     {
6923         ji2->JobId = job->job_id;
6924     }
6925
6926     string_to_buf(job->document_title, ptr, left, &size, unicode);
6927     if(space && size <= left)
6928     {
6929         ji2->pDocument = (LPWSTR)ptr;
6930         ptr += size;
6931         left -= size;
6932     }
6933     else
6934         space = FALSE;
6935     *pcbNeeded += size;
6936
6937     return space;
6938 }
6939
6940 /*****************************************************************************
6941  *          get_job_info
6942  */
6943 static BOOL get_job_info(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
6944                          DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
6945 {
6946     BOOL ret = FALSE;
6947     DWORD needed = 0, size;
6948     job_t *job;
6949     LPBYTE ptr = pJob;
6950
6951     TRACE("%p %d %d %p %d %p\n", hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
6952
6953     EnterCriticalSection(&printer_handles_cs);
6954     job = get_job(hPrinter, JobId);
6955     if(!job)
6956         goto end;
6957
6958     switch(Level)
6959     {
6960     case 1:
6961         size = sizeof(JOB_INFO_1W);
6962         if(cbBuf >= size)
6963         {
6964             cbBuf -= size;
6965             ptr += size;
6966             memset(pJob, 0, size);
6967         }
6968         else
6969             cbBuf = 0;
6970         ret = get_job_info_1(job, (JOB_INFO_1W *)pJob, ptr, cbBuf, &needed, unicode);
6971         needed += size;
6972         break;
6973
6974     case 2:
6975         size = sizeof(JOB_INFO_2W);
6976         if(cbBuf >= size)
6977         {
6978             cbBuf -= size;
6979             ptr += size;
6980             memset(pJob, 0, size);
6981         }
6982         else
6983             cbBuf = 0;
6984         ret = get_job_info_2(job, (JOB_INFO_2W *)pJob, ptr, cbBuf, &needed, unicode);
6985         needed += size;
6986         break;
6987
6988     case 3:
6989         size = sizeof(JOB_INFO_3);
6990         if(cbBuf >= size)
6991         {
6992             cbBuf -= size;
6993             memset(pJob, 0, size);
6994             ret = TRUE;
6995         }
6996         else
6997             cbBuf = 0;
6998         needed = size;
6999         break;
7000
7001     default:
7002         SetLastError(ERROR_INVALID_LEVEL);
7003         goto end;
7004     }
7005     if(pcbNeeded)
7006         *pcbNeeded = needed;
7007 end:
7008     LeaveCriticalSection(&printer_handles_cs);
7009     return ret;
7010 }
7011
7012 /*****************************************************************************
7013  *          GetJobA [WINSPOOL.@]
7014  *
7015  */
7016 BOOL WINAPI GetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7017                     DWORD cbBuf, LPDWORD pcbNeeded)
7018 {
7019     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, FALSE);
7020 }
7021
7022 /*****************************************************************************
7023  *          GetJobW [WINSPOOL.@]
7024  *
7025  */
7026 BOOL WINAPI GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7027                     DWORD cbBuf, LPDWORD pcbNeeded)
7028 {
7029     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, TRUE);
7030 }
7031
7032 /*****************************************************************************
7033  *          schedule_lpr
7034  */
7035 static BOOL schedule_lpr(LPCWSTR printer_name, LPCWSTR filename)
7036 {
7037     char *unixname, *queue, *cmd;
7038     char fmt[] = "lpr -P%s %s";
7039     DWORD len;
7040
7041     if(!(unixname = wine_get_unix_file_name(filename)))
7042         return FALSE;
7043
7044     len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
7045     queue = HeapAlloc(GetProcessHeap(), 0, len);
7046     WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
7047
7048     cmd = HeapAlloc(GetProcessHeap(), 0, strlen(unixname) + len + sizeof(fmt) - 5);
7049     sprintf(cmd, fmt, queue, unixname);
7050
7051     TRACE("printing with: %s\n", cmd);
7052     system(cmd);
7053
7054     HeapFree(GetProcessHeap(), 0, cmd);
7055     HeapFree(GetProcessHeap(), 0, queue);
7056     HeapFree(GetProcessHeap(), 0, unixname);
7057     return TRUE;
7058 }
7059
7060 /*****************************************************************************
7061  *          schedule_cups
7062  */
7063 static BOOL schedule_cups(LPCWSTR printer_name, LPCWSTR filename, LPCWSTR document_title)
7064 {
7065 #if HAVE_CUPS_CUPS_H
7066     if(pcupsPrintFile)
7067     {
7068         char *unixname, *queue, *doc_titleA;
7069         DWORD len;
7070         BOOL ret;
7071
7072         if(!(unixname = wine_get_unix_file_name(filename)))
7073             return FALSE;
7074
7075         len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
7076         queue = HeapAlloc(GetProcessHeap(), 0, len);
7077         WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
7078
7079         len = WideCharToMultiByte(CP_ACP, 0, document_title, -1, NULL, 0, NULL, NULL);
7080         doc_titleA = HeapAlloc(GetProcessHeap(), 0, len);
7081         WideCharToMultiByte(CP_ACP, 0, document_title, -1, doc_titleA, len, NULL, NULL);
7082
7083         TRACE("printing via cups\n");
7084         ret = pcupsPrintFile(queue, unixname, doc_titleA, 0, NULL);
7085         HeapFree(GetProcessHeap(), 0, doc_titleA);
7086         HeapFree(GetProcessHeap(), 0, queue);
7087         HeapFree(GetProcessHeap(), 0, unixname);
7088         return ret;
7089     }
7090     else
7091 #endif
7092     {
7093         return schedule_lpr(printer_name, filename);
7094     }
7095 }
7096
7097 INT_PTR CALLBACK file_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
7098 {
7099     LPWSTR filename;
7100
7101     switch(msg)
7102     {
7103     case WM_INITDIALOG:
7104         SetWindowLongPtrW(hwnd, DWLP_USER, lparam);
7105         return TRUE;
7106
7107     case WM_COMMAND:
7108         if(HIWORD(wparam) == BN_CLICKED)
7109         {
7110             if(LOWORD(wparam) == IDOK)
7111             {
7112                 HANDLE hf;
7113                 DWORD len = SendDlgItemMessageW(hwnd, EDITBOX, WM_GETTEXTLENGTH, 0, 0);
7114                 LPWSTR *output;
7115
7116                 filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
7117                 GetDlgItemTextW(hwnd, EDITBOX, filename, len + 1);
7118
7119                 if(GetFileAttributesW(filename) != INVALID_FILE_ATTRIBUTES)
7120                 {
7121                     WCHAR caption[200], message[200];
7122                     int mb_ret;
7123
7124                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
7125                     LoadStringW(WINSPOOL_hInstance, IDS_FILE_EXISTS, message, sizeof(message) / sizeof(WCHAR));
7126                     mb_ret = MessageBoxW(hwnd, message, caption, MB_OKCANCEL | MB_ICONEXCLAMATION);
7127                     if(mb_ret == IDCANCEL)
7128                     {
7129                         HeapFree(GetProcessHeap(), 0, filename);
7130                         return TRUE;
7131                     }
7132                 }
7133                 hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
7134                 if(hf == INVALID_HANDLE_VALUE)
7135                 {
7136                     WCHAR caption[200], message[200];
7137
7138                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
7139                     LoadStringW(WINSPOOL_hInstance, IDS_CANNOT_OPEN, message, sizeof(message) / sizeof(WCHAR));
7140                     MessageBoxW(hwnd, message, caption, MB_OK | MB_ICONEXCLAMATION);
7141                     HeapFree(GetProcessHeap(), 0, filename);
7142                     return TRUE;
7143                 }
7144                 CloseHandle(hf);
7145                 DeleteFileW(filename);
7146                 output = (LPWSTR *)GetWindowLongPtrW(hwnd, DWLP_USER);
7147                 *output = filename;
7148                 EndDialog(hwnd, IDOK);
7149                 return TRUE;
7150             }
7151             if(LOWORD(wparam) == IDCANCEL)
7152             {
7153                 EndDialog(hwnd, IDCANCEL);
7154                 return TRUE;
7155             }
7156         }
7157         return FALSE;
7158     }
7159     return FALSE;
7160 }
7161
7162 /*****************************************************************************
7163  *          get_filename
7164  */
7165 static BOOL get_filename(LPWSTR *filename)
7166 {
7167     return DialogBoxParamW(WINSPOOL_hInstance, MAKEINTRESOURCEW(FILENAME_DIALOG), GetForegroundWindow(),
7168                            file_dlg_proc, (LPARAM)filename) == IDOK;
7169 }
7170
7171 /*****************************************************************************
7172  *          schedule_file
7173  */
7174 static BOOL schedule_file(LPCWSTR filename)
7175 {
7176     LPWSTR output = NULL;
7177
7178     if(get_filename(&output))
7179     {
7180         TRACE("copy to %s\n", debugstr_w(output));
7181         CopyFileW(filename, output, FALSE);
7182         HeapFree(GetProcessHeap(), 0, output);
7183         return TRUE;
7184     }
7185     return FALSE;
7186 }
7187
7188 /*****************************************************************************
7189  *          schedule_pipe
7190  */
7191 static BOOL schedule_pipe(LPCWSTR cmd, LPCWSTR filename)
7192 {
7193 #ifdef HAVE_FORK
7194     char *unixname, *cmdA;
7195     DWORD len;
7196     int fds[2] = {-1, -1}, file_fd = -1, no_read;
7197     BOOL ret = FALSE;
7198     char buf[1024];
7199
7200     if(!(unixname = wine_get_unix_file_name(filename)))
7201         return FALSE;
7202
7203     len = WideCharToMultiByte(CP_ACP, 0, cmd, -1, NULL, 0, NULL, NULL);
7204     cmdA = HeapAlloc(GetProcessHeap(), 0, len);
7205     WideCharToMultiByte(CP_ACP, 0, cmd, -1, cmdA, len, NULL, NULL);
7206
7207     TRACE("printing with: %s\n", cmdA);
7208
7209     if((file_fd = open(unixname, O_RDONLY)) == -1)
7210         goto end;
7211
7212     if (pipe(fds))
7213     {
7214         ERR("pipe() failed!\n"); 
7215         goto end;
7216     }
7217
7218     if (fork() == 0)
7219     {
7220         close(0);
7221         dup2(fds[0], 0);
7222         close(fds[1]);
7223
7224         /* reset signals that we previously set to SIG_IGN */
7225         signal(SIGPIPE, SIG_DFL);
7226         signal(SIGCHLD, SIG_DFL);
7227
7228         system(cmdA);
7229         exit(0);
7230     }
7231
7232     while((no_read = read(file_fd, buf, sizeof(buf))) > 0)
7233         write(fds[1], buf, no_read);
7234
7235     ret = TRUE;
7236
7237 end:
7238     if(file_fd != -1) close(file_fd);
7239     if(fds[0] != -1) close(fds[0]);
7240     if(fds[1] != -1) close(fds[1]);
7241
7242     HeapFree(GetProcessHeap(), 0, cmdA);
7243     HeapFree(GetProcessHeap(), 0, unixname);
7244     return ret;
7245 #else
7246     return FALSE;
7247 #endif
7248 }
7249
7250 /*****************************************************************************
7251  *          schedule_unixfile
7252  */
7253 static BOOL schedule_unixfile(LPCWSTR output, LPCWSTR filename)
7254 {
7255     int in_fd, out_fd, no_read;
7256     char buf[1024];
7257     BOOL ret = FALSE;
7258     char *unixname, *outputA;
7259     DWORD len;
7260
7261     if(!(unixname = wine_get_unix_file_name(filename)))
7262         return FALSE;
7263
7264     len = WideCharToMultiByte(CP_ACP, 0, output, -1, NULL, 0, NULL, NULL);
7265     outputA = HeapAlloc(GetProcessHeap(), 0, len);
7266     WideCharToMultiByte(CP_ACP, 0, output, -1, outputA, len, NULL, NULL);
7267     
7268     out_fd = open(outputA, O_CREAT | O_TRUNC | O_WRONLY, 0666);
7269     in_fd = open(unixname, O_RDONLY);
7270     if(out_fd == -1 || in_fd == -1)
7271         goto end;
7272
7273     while((no_read = read(in_fd, buf, sizeof(buf))) > 0)
7274         write(out_fd, buf, no_read);
7275
7276     ret = TRUE;
7277 end:
7278     if(in_fd != -1) close(in_fd);
7279     if(out_fd != -1) close(out_fd);
7280     HeapFree(GetProcessHeap(), 0, outputA);
7281     HeapFree(GetProcessHeap(), 0, unixname);
7282     return ret;
7283 }
7284
7285 /*****************************************************************************
7286  *          ScheduleJob [WINSPOOL.@]
7287  *
7288  */
7289 BOOL WINAPI ScheduleJob( HANDLE hPrinter, DWORD dwJobID )
7290 {
7291     opened_printer_t *printer;
7292     BOOL ret = FALSE;
7293     struct list *cursor, *cursor2;
7294
7295     TRACE("(%p, %x)\n", hPrinter, dwJobID);
7296     EnterCriticalSection(&printer_handles_cs);
7297     printer = get_opened_printer(hPrinter);
7298     if(!printer)
7299         goto end;
7300
7301     LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
7302     {
7303         job_t *job = LIST_ENTRY(cursor, job_t, entry);
7304         HANDLE hf;
7305
7306         if(job->job_id != dwJobID) continue;
7307
7308         hf = CreateFileW(job->filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
7309         if(hf != INVALID_HANDLE_VALUE)
7310         {
7311             PRINTER_INFO_5W *pi5;
7312             DWORD needed;
7313             HKEY hkey;
7314             WCHAR output[1024];
7315             static const WCHAR spooler_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
7316                                                 'P','r','i','n','t','i','n','g','\\','S','p','o','o','l','e','r',0};
7317
7318             GetPrinterW(hPrinter, 5, NULL, 0, &needed);
7319             pi5 = HeapAlloc(GetProcessHeap(), 0, needed);
7320             GetPrinterW(hPrinter, 5, (LPBYTE)pi5, needed, &needed);
7321             TRACE("need to schedule job %d filename %s to port %s\n", job->job_id, debugstr_w(job->filename),
7322                   debugstr_w(pi5->pPortName));
7323             
7324             output[0] = 0;
7325
7326             /* @@ Wine registry key: HKCU\Software\Wine\Printing\Spooler */
7327             if(RegOpenKeyW(HKEY_CURRENT_USER, spooler_key, &hkey) == ERROR_SUCCESS)
7328             {
7329                 DWORD type, count = sizeof(output);
7330                 RegQueryValueExW(hkey, pi5->pPortName, NULL, &type, (LPBYTE)output, &count);
7331                 RegCloseKey(hkey);
7332             }
7333             if(output[0] == '|')
7334             {
7335                 schedule_pipe(output + 1, job->filename);
7336             }
7337             else if(output[0])
7338             {
7339                 schedule_unixfile(output, job->filename);
7340             }
7341             else if(!strncmpW(pi5->pPortName, LPR_Port, strlenW(LPR_Port)))
7342             {
7343                 schedule_lpr(pi5->pPortName + strlenW(LPR_Port), job->filename);
7344             }
7345             else if(!strncmpW(pi5->pPortName, CUPS_Port, strlenW(CUPS_Port)))
7346             {
7347                 schedule_cups(pi5->pPortName + strlenW(CUPS_Port), job->filename, job->document_title);
7348             }
7349             else if(!strncmpW(pi5->pPortName, FILE_Port, strlenW(FILE_Port)))
7350             {
7351                 schedule_file(job->filename);
7352             }
7353             else
7354             {
7355                 FIXME("can't schedule to port %s\n", debugstr_w(pi5->pPortName));
7356             }
7357             HeapFree(GetProcessHeap(), 0, pi5);
7358             CloseHandle(hf);
7359             DeleteFileW(job->filename);
7360         }
7361         list_remove(cursor);
7362         HeapFree(GetProcessHeap(), 0, job->document_title);
7363         HeapFree(GetProcessHeap(), 0, job->filename);
7364         HeapFree(GetProcessHeap(), 0, job);
7365         ret = TRUE;
7366         break;
7367     }
7368 end:
7369     LeaveCriticalSection(&printer_handles_cs);
7370     return ret;
7371 }
7372
7373 /*****************************************************************************
7374  *          StartDocDlgA [WINSPOOL.@]
7375  */
7376 LPSTR WINAPI StartDocDlgA( HANDLE hPrinter, DOCINFOA *doc )
7377 {
7378     UNICODE_STRING usBuffer;
7379     DOCINFOW docW;
7380     LPWSTR retW;
7381     LPWSTR docnameW = NULL, outputW = NULL, datatypeW = NULL;
7382     LPSTR ret = NULL;
7383
7384     docW.cbSize = sizeof(docW);
7385     if (doc->lpszDocName)
7386     {
7387         docnameW = asciitounicode(&usBuffer, doc->lpszDocName);
7388         if (!(docW.lpszDocName = docnameW)) return NULL;
7389     }
7390     if (doc->lpszOutput)
7391     {
7392         outputW = asciitounicode(&usBuffer, doc->lpszOutput);
7393         if (!(docW.lpszOutput = outputW)) return NULL;
7394     }
7395     if (doc->lpszDatatype)
7396     {
7397         datatypeW = asciitounicode(&usBuffer, doc->lpszDatatype);
7398         if (!(docW.lpszDatatype = datatypeW)) return NULL;
7399     }
7400     docW.fwType = doc->fwType;
7401
7402     retW = StartDocDlgW(hPrinter, &docW);
7403
7404     if(retW)
7405     {
7406         DWORD len = WideCharToMultiByte(CP_ACP, 0, retW, -1, NULL, 0, NULL, NULL);
7407         ret = HeapAlloc(GetProcessHeap(), 0, len);
7408         WideCharToMultiByte(CP_ACP, 0, retW, -1, ret, len, NULL, NULL);
7409         HeapFree(GetProcessHeap(), 0, retW);
7410     }
7411
7412     HeapFree(GetProcessHeap(), 0, datatypeW);
7413     HeapFree(GetProcessHeap(), 0, outputW);
7414     HeapFree(GetProcessHeap(), 0, docnameW);
7415
7416     return ret;
7417 }
7418
7419 /*****************************************************************************
7420  *          StartDocDlgW [WINSPOOL.@]
7421  *
7422  * Undocumented: Apparently used by gdi32:StartDocW() to popup the file dialog
7423  * when lpszOutput is "FILE:" or if lpszOutput is NULL and the default printer
7424  * port is "FILE:". Also returns the full path if passed a relative path.
7425  *
7426  * The caller should free the returned string from the process heap.
7427  */
7428 LPWSTR WINAPI StartDocDlgW( HANDLE hPrinter, DOCINFOW *doc )
7429 {
7430     LPWSTR ret = NULL;
7431     DWORD len, attr;
7432
7433     if(doc->lpszOutput == NULL) /* Check whether default port is FILE: */
7434     {
7435         PRINTER_INFO_5W *pi5;
7436         GetPrinterW(hPrinter, 5, NULL, 0, &len);
7437         if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
7438             return NULL;
7439         pi5 = HeapAlloc(GetProcessHeap(), 0, len);
7440         GetPrinterW(hPrinter, 5, (LPBYTE)pi5, len, &len);
7441         if(!pi5->pPortName || strcmpW(pi5->pPortName, FILE_Port))
7442         {
7443             HeapFree(GetProcessHeap(), 0, pi5);
7444             return NULL;
7445         }
7446         HeapFree(GetProcessHeap(), 0, pi5);
7447     }
7448
7449     if(doc->lpszOutput == NULL || !strcmpW(doc->lpszOutput, FILE_Port))
7450     {
7451         LPWSTR name;
7452
7453         if (get_filename(&name))
7454         {
7455             if(!(len = GetFullPathNameW(name, 0, NULL, NULL)))
7456             {
7457                 HeapFree(GetProcessHeap(), 0, name);
7458                 return NULL;
7459             }
7460             ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7461             GetFullPathNameW(name, len, ret, NULL);
7462             HeapFree(GetProcessHeap(), 0, name);
7463         }
7464         return ret;
7465     }
7466
7467     if(!(len = GetFullPathNameW(doc->lpszOutput, 0, NULL, NULL)))
7468         return NULL;
7469
7470     ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7471     GetFullPathNameW(doc->lpszOutput, len, ret, NULL);
7472         
7473     attr = GetFileAttributesW(ret);
7474     if(attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY))
7475     {
7476         HeapFree(GetProcessHeap(), 0, ret);
7477         ret = NULL;
7478     }
7479     return ret;
7480 }