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