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