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