~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/winspool.drv/info.c

Version: ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

  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-2009 Detlef Riekenberg
 10  *
 11  * This library is free software; you can redistribute it and/or
 12  * modify it under the terms of the GNU Lesser General Public
 13  * License as published by the Free Software Foundation; either
 14  * version 2.1 of the License, or (at your option) any later version.
 15  *
 16  * This library is distributed in the hope that it will be useful,
 17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 19  * Lesser General Public License for more details.
 20  *
 21  * You should have received a copy of the GNU Lesser General Public
 22  * License along with this library; if not, write to the Free Software
 23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 24  */
 25 
 26 #include "config.h"
 27 #include "wine/port.h"
 28 
 29 #include <stdarg.h>
 30 #include <stdio.h>
 31 #include <stdlib.h>
 32 #include <string.h>
 33 #include <ctype.h>
 34 #include <stddef.h>
 35 #ifdef HAVE_UNISTD_H
 36 # include <unistd.h>
 37 #endif
 38 #include <signal.h>
 39 #ifdef HAVE_CUPS_CUPS_H
 40 # include <cups/cups.h>
 41 #endif
 42 
 43 #define NONAMELESSUNION
 44 #define NONAMELESSSTRUCT
 45 #include "wine/library.h"
 46 #include "windef.h"
 47 #include "winbase.h"
 48 #include "winuser.h"
 49 #include "winerror.h"
 50 #include "winreg.h"
 51 #include "wingdi.h"
 52 #include "winspool.h"
 53 #include "winternl.h"
 54 #include "wine/windef16.h"
 55 #include "wine/unicode.h"
 56 #include "wine/debug.h"
 57 #include "wine/list.h"
 58 #include "winnls.h"
 59 
 60 #include "ddk/winsplp.h"
 61 #include "wspool.h"
 62 
 63 WINE_DEFAULT_DEBUG_CHANNEL(winspool);
 64 
 65 /* ############################### */
 66 
 67 static CRITICAL_SECTION printer_handles_cs;
 68 static CRITICAL_SECTION_DEBUG printer_handles_cs_debug = 
 69 {
 70     0, 0, &printer_handles_cs,
 71     { &printer_handles_cs_debug.ProcessLocksList, &printer_handles_cs_debug.ProcessLocksList },
 72       0, 0, { (DWORD_PTR)(__FILE__ ": printer_handles_cs") }
 73 };
 74 static CRITICAL_SECTION printer_handles_cs = { &printer_handles_cs_debug, -1, 0, 0, 0, 0 };
 75 
 76 /* ############################### */
 77 
 78 typedef struct {
 79     DWORD job_id;
 80     HANDLE hf;
 81 } started_doc_t;
 82 
 83 typedef struct {
 84     struct list jobs;
 85     LONG ref;
 86 } jobqueue_t;
 87 
 88 typedef struct {
 89     LPWSTR name;
 90     LPWSTR printername;
 91     HANDLE backend_printer;
 92     jobqueue_t *queue;
 93     started_doc_t *doc;
 94 } opened_printer_t;
 95 
 96 typedef struct {
 97     struct list entry;
 98     DWORD job_id;
 99     WCHAR *filename;
100     WCHAR *document_title;
101 } job_t;
102 
103 
104 typedef struct {
105     LPCWSTR  envname;
106     LPCWSTR  subdir;
107     DWORD    driverversion;
108     LPCWSTR  versionregpath;
109     LPCWSTR  versionsubdir;
110 } printenv_t;
111 
112 /* ############################### */
113 
114 static opened_printer_t **printer_handles;
115 static UINT nb_printer_handles;
116 static LONG next_job_id = 1;
117 
118 static DWORD (WINAPI *GDI_CallDeviceCapabilities16)( LPCSTR lpszDevice, LPCSTR lpszPort,
119                                                      WORD fwCapability, LPSTR lpszOutput,
120                                                      LPDEVMODEA lpdm );
121 static INT (WINAPI *GDI_CallExtDeviceMode16)( HWND hwnd, LPDEVMODEA lpdmOutput,
122                                               LPSTR lpszDevice, LPSTR lpszPort,
123                                               LPDEVMODEA lpdmInput, LPSTR lpszProfile,
124                                               DWORD fwMode );
125 
126 static const WCHAR DriversW[] = { 'S','y','s','t','e','m','\\',
127                                   'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
128                                   'c','o','n','t','r','o','l','\\',
129                                   'P','r','i','n','t','\\',
130                                   'E','n','v','i','r','o','n','m','e','n','t','s','\\',
131                                   '%','s','\\','D','r','i','v','e','r','s','%','s',0 };
132 
133 static const WCHAR PrintersW[] = {'S','y','s','t','e','m','\\',
134                                   'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
135                                   'C','o','n','t','r','o','l','\\',
136                                   'P','r','i','n','t','\\',
137                                   'P','r','i','n','t','e','r','s',0};
138 
139 static const WCHAR LocalPortW[] = {'L','o','c','a','l',' ','P','o','r','t',0};
140 
141 static const WCHAR user_default_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
142                                               'M','i','c','r','o','s','o','f','t','\\',
143                                               'W','i','n','d','o','w','s',' ','N','T','\\',
144                                               'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
145                                               'W','i','n','d','o','w','s',0};
146 
147 static const WCHAR user_printers_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
148                                                'M','i','c','r','o','s','o','f','t','\\',
149                                                'W','i','n','d','o','w','s',' ','N','T','\\',
150                                                'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
151                                                'D','e','v','i','c','e','s',0};
152 
153 static const WCHAR WinNT_CV_PortsW[] = {'S','o','f','t','w','a','r','e','\\',
154                                         'M','i','c','r','o','s','o','f','t','\\',
155                                         'W','i','n','d','o','w','s',' ','N','T','\\',
156                                         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
157                                         'P','o','r','t','s',0};
158 
159 static const WCHAR WinNT_CV_PrinterPortsW[] = { 'S','o','f','t','w','a','r','e','\\',
160                                                 'M','i','c','r','o','s','o','f','t','\\',
161                                                 'W','i','n','d','o','w','s',' ','N','T','\\',
162                                                 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
163                                                 'P','r','i','n','t','e','r','P','o','r','t','s',0};
164 
165 static const WCHAR DefaultEnvironmentW[] = {'W','i','n','e',0};
166 static const WCHAR envname_win40W[] = {'W','i','n','d','o','w','s',' ','4','.','',0};
167 static const WCHAR envname_x64W[] =   {'W','i','n','d','o','w','s',' ','x','6','4',0};
168 static const WCHAR envname_x86W[] =   {'W','i','n','d','o','w','s',' ','N','T',' ','x','8','6',0};
169 static const WCHAR subdir_win40W[] = {'w','i','n','4','',0};
170 static const WCHAR subdir_x64W[] =   {'x','6','4',0};
171 static const WCHAR subdir_x86W[] =   {'w','3','2','x','8','6',0};
172 static const WCHAR Version0_RegPathW[] = {'\\','V','e','r','s','i','o','n','-','',0};
173 static const WCHAR Version0_SubdirW[] = {'\\','',0};
174 static const WCHAR Version3_RegPathW[] = {'\\','V','e','r','s','i','o','n','-','3',0};
175 static const WCHAR Version3_SubdirW[] = {'\\','3',0};
176 
177 static const WCHAR spooldriversW[] = {'\\','s','p','o','o','l','\\','d','r','i','v','e','r','s','\\',0};
178 static const WCHAR backslashW[] = {'\\',0};
179 static const WCHAR Configuration_FileW[] = {'C','o','n','f','i','g','u','r','a','t',
180                                       'i','o','n',' ','F','i','l','e',0};
181 static const WCHAR DatatypeW[] = {'D','a','t','a','t','y','p','e',0};
182 static const WCHAR Data_FileW[] = {'D','a','t','a',' ','F','i','l','e',0};
183 static const WCHAR Default_DevModeW[] = {'D','e','f','a','u','l','t',' ','D','e','v','M','o','d','e',0};
184 static const WCHAR Dependent_FilesW[] = {'D','e','p','e','n','d','e','n','t',' ','F','i','l','e','s',0};
185 static const WCHAR DescriptionW[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
186 static const WCHAR DriverW[] = {'D','r','i','v','e','r',0};
187 static const WCHAR HardwareIDW[] = {'H','a','r','d','w','a','r','e','I','D',0};
188 static const WCHAR Help_FileW[] = {'H','e','l','p',' ','F','i','l','e',0};
189 static const WCHAR LocationW[] = {'L','o','c','a','t','i','o','n',0};
190 static const WCHAR ManufacturerW[] = {'M','a','n','u','f','a','c','t','u','r','e','r',0};
191 static const WCHAR MonitorW[] = {'M','o','n','i','t','o','r',0};
192 static const WCHAR NameW[] = {'N','a','m','e',0};
193 static const WCHAR ObjectGUIDW[] = {'O','b','j','e','c','t','G','U','I','D',0};
194 static const WCHAR OEM_UrlW[] = {'O','E','M',' ','U','r','l',0};
195 static const WCHAR ParametersW[] = {'P','a','r','a','m','e','t','e','r','s',0};
196 static const WCHAR PortW[] = {'P','o','r','t',0};
197 static const WCHAR bs_Ports_bsW[] = {'\\','P','o','r','t','s','\\',0};
198 static const WCHAR Previous_NamesW[] = {'P','r','e','v','i','o','u','s',' ','N','a','m','e','s',0};
199 static const WCHAR Print_ProcessorW[] = {'P','r','i','n','t',' ','P','r','o','c','e','s','s','o','r',0};
200 static const WCHAR Printer_DriverW[] = {'P','r','i','n','t','e','r',' ','D','r','i','v','e','r',0};
201 static const WCHAR PrinterDriverDataW[] = {'P','r','i','n','t','e','r','D','r','i','v','e','r','D','a','t','a',0};
202 static const WCHAR PrinterPortsW[] = {'P','r','i','n','t','e','r','P','o','r','t','s',0};
203 static const WCHAR ProviderW[] = {'P','r','o','v','i','d','e','r',0};
204 static const WCHAR Separator_FileW[] = {'S','e','p','a','r','a','t','o','r',' ','F','i','l','e',0};
205 static const WCHAR Share_NameW[] = {'S','h','a','r','e',' ','N','a','m','e',0};
206 static const WCHAR VersionW[] = {'V','e','r','s','i','o','n',0};
207 static const WCHAR WinPrintW[] = {'W','i','n','P','r','i','n','t',0};
208 static const WCHAR deviceW[]  = {'d','e','v','i','c','e',0};
209 static const WCHAR devicesW[] = {'d','e','v','i','c','e','s',0};
210 static const WCHAR windowsW[] = {'w','i','n','d','o','w','s',0};
211 static const WCHAR emptyStringW[] = {0};
212 
213 static const WCHAR May_Delete_Value[] = {'W','i','n','e','M','a','y','D','e','l','e','t','e','M','e',0};
214 
215 static const WCHAR CUPS_Port[] = {'C','U','P','S',':',0};
216 static const WCHAR FILE_Port[] = {'F','I','L','E',':',0};
217 static const WCHAR LPR_Port[] = {'L','P','R',':',0};
218 
219 static const WCHAR default_doc_title[] = {'L','o','c','a','l',' ','D','o','w','n','l','e','v','e','l',' ',
220                                           'D','o','c','u','m','e','n','t',0};
221 
222 static const DWORD di_sizeof[] = {0, sizeof(DRIVER_INFO_1W), sizeof(DRIVER_INFO_2W),
223                                      sizeof(DRIVER_INFO_3W), sizeof(DRIVER_INFO_4W),
224                                      sizeof(DRIVER_INFO_5W), sizeof(DRIVER_INFO_6W),
225                                   0, sizeof(DRIVER_INFO_8W)};
226 
227 
228 static const DWORD pi_sizeof[] = {0, sizeof(PRINTER_INFO_1W), sizeof(PRINTER_INFO_2W),
229                                      sizeof(PRINTER_INFO_3),  sizeof(PRINTER_INFO_4W),
230                                      sizeof(PRINTER_INFO_5W), sizeof(PRINTER_INFO_6),
231                                      sizeof(PRINTER_INFO_7W), sizeof(PRINTER_INFO_8W),
232                                      sizeof(PRINTER_INFO_9W)};
233 
234 static const printenv_t env_x64 = {envname_x64W, subdir_x64W, 3, Version3_RegPathW, Version3_SubdirW};
235 static const printenv_t env_x86 = {envname_x86W, subdir_x86W, 3, Version3_RegPathW, Version3_SubdirW};
236 static const printenv_t env_win40 = {envname_win40W, subdir_win40W, 0, Version0_RegPathW, Version0_SubdirW};
237 
238 static const printenv_t * const all_printenv[] = {&env_x86, &env_x64, &env_win40};
239 
240 /******************************************************************
241  *  validate the user-supplied printing-environment [internal]
242  *
243  * PARAMS
244  *  env  [I] PTR to Environment-String or NULL
245  *
246  * RETURNS
247  *  Failure:  NULL
248  *  Success:  PTR to printenv_t
249  *
250  * NOTES
251  *  An empty string is handled the same way as NULL.
252  *  SetLastEror(ERROR_INVALID_ENVIRONMENT) is called on Failure
253  *  
254  */
255 
256 static const  printenv_t * validate_envW(LPCWSTR env)
257 {
258     const printenv_t *result = NULL;
259     unsigned int i;
260 
261     TRACE("testing %s\n", debugstr_w(env));
262     if (env && env[0])
263     {
264         for (i = 0; i < sizeof(all_printenv)/sizeof(all_printenv[0]); i++)
265         {
266             if (lstrcmpiW(env, all_printenv[i]->envname) == 0)
267             {
268                 result = all_printenv[i];
269                 break;
270             }
271         }
272 
273         if (result == NULL) {
274             FIXME("unsupported Environment: %s\n", debugstr_w(env));
275             SetLastError(ERROR_INVALID_ENVIRONMENT);
276         }
277         /* on win9x, only "Windows 4.0" is allowed, but we ignore this */
278     }
279     else
280     {
281         result = (GetVersion() & 0x80000000) ? &env_win40 : &env_x86;
282     }
283     TRACE("using %p: %s\n", result, debugstr_w(result ? result->envname : NULL));
284 
285     return result;
286 }
287 
288 
289 /* RtlCreateUnicodeStringFromAsciiz will return an empty string in the buffer
290    if passed a NULL string. This returns NULLs to the result. 
291 */
292 static inline PWSTR asciitounicode( UNICODE_STRING * usBufferPtr, LPCSTR src )
293 {
294     if ( (src) )
295     {
296         RtlCreateUnicodeStringFromAsciiz(usBufferPtr, src);
297         return usBufferPtr->Buffer;
298     }
299     usBufferPtr->Buffer = NULL; /* so that RtlFreeUnicodeString won't barf */
300     return NULL;
301 }
302             
303 static LPWSTR strdupW(LPCWSTR p)
304 {
305     LPWSTR ret;
306     DWORD len;
307 
308     if(!p) return NULL;
309     len = (strlenW(p) + 1) * sizeof(WCHAR);
310     ret = HeapAlloc(GetProcessHeap(), 0, len);
311     memcpy(ret, p, len);
312     return ret;
313 }
314 
315 static LPSTR strdupWtoA( LPCWSTR str )
316 {
317     LPSTR ret;
318     INT len;
319 
320     if (!str) return NULL;
321     len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
322     ret = HeapAlloc( GetProcessHeap(), 0, len );
323     if(ret) WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
324     return ret;
325 }
326 
327 /******************************************************************
328  * Return the number of bytes for an multi_sz string.
329  * The result includes all \0s
330  * (specifically the extra \0, that is needed as multi_sz terminator).
331  */
332 #if 0
333 static int multi_sz_lenW(const WCHAR *str)
334 {
335     const WCHAR *ptr = str;
336     if(!str) return 0;
337     do
338     {
339         ptr += lstrlenW(ptr) + 1;
340     } while(*ptr);
341 
342     return (ptr - str + 1) * sizeof(WCHAR);
343 }
344 #endif
345 /* ################################ */
346 
347 static int multi_sz_lenA(const char *str)
348 {
349     const char *ptr = str;
350     if(!str) return 0;
351     do
352     {
353         ptr += lstrlenA(ptr) + 1;
354     } while(*ptr);
355 
356     return ptr - str + 1;
357 }
358 
359 static void
360 WINSPOOL_SetDefaultPrinter(const char *devname, const char *name, BOOL force) {
361     char qbuf[200];
362 
363     /* If forcing, or no profile string entry for device yet, set the entry
364      *
365      * The always change entry if not WINEPS yet is discussable.
366      */
367     if (force                                                           ||
368         !GetProfileStringA("windows","device","*",qbuf,sizeof(qbuf))    ||
369         !strcmp(qbuf,"*")                                               ||
370         !strstr(qbuf,"WINEPS.DRV")
371     ) {
372         char *buf = HeapAlloc(GetProcessHeap(),0,strlen(name)+strlen(devname)+strlen(",WINEPS.DRV,LPR:")+1);
373         HKEY hkey;
374 
375         sprintf(buf,"%s,WINEPS.DRV,LPR:%s",devname,name);
376         WriteProfileStringA("windows","device",buf);
377         if(RegCreateKeyW(HKEY_CURRENT_USER, user_default_reg_key, &hkey) == ERROR_SUCCESS) {
378             RegSetValueExA(hkey, "Device", 0, REG_SZ, (LPBYTE)buf, strlen(buf) + 1);
379             RegCloseKey(hkey);
380         }
381         HeapFree(GetProcessHeap(),0,buf);
382     }
383 }
384 
385 static BOOL add_printer_driver(const char *name)
386 {
387     DRIVER_INFO_3A di3a;
388 
389     static char driver_9x[]         = "wineps16.drv",
390                 driver_nt[]         = "wineps.drv",
391                 env_9x[]            = "Windows 4.0",
392                 env_nt[]            = "Windows NT x86",
393                 data_file[]         = "generic.ppd",
394                 default_data_type[] = "RAW";
395 
396     ZeroMemory(&di3a, sizeof(DRIVER_INFO_3A));
397     di3a.cVersion         = 3;
398     di3a.pName            = (char *)name;
399     di3a.pEnvironment     = env_nt;
400     di3a.pDriverPath      = driver_nt;
401     di3a.pDataFile        = data_file;
402     di3a.pConfigFile      = driver_nt;
403     di3a.pDefaultDataType = default_data_type;
404 
405     if (AddPrinterDriverA(NULL, 3, (LPBYTE)&di3a) ||
406         (GetLastError() ==  ERROR_PRINTER_DRIVER_ALREADY_INSTALLED ))
407     {
408         di3a.cVersion     = 0;
409         di3a.pEnvironment = env_9x;
410         di3a.pDriverPath  = driver_9x;
411         di3a.pConfigFile  = driver_9x;
412         if (AddPrinterDriverA(NULL, 3, (LPBYTE)&di3a) ||
413             (GetLastError() ==  ERROR_PRINTER_DRIVER_ALREADY_INSTALLED ))
414         {
415             return TRUE;
416         }
417     }
418     ERR("Failed adding driver %s (%s): %u\n", debugstr_a(di3a.pDriverPath),
419         debugstr_a(di3a.pEnvironment), GetLastError());
420     return FALSE;
421 }
422 
423 #ifdef SONAME_LIBCUPS
424 static typeof(cupsFreeDests) *pcupsFreeDests;
425 static typeof(cupsGetDests)  *pcupsGetDests;
426 static typeof(cupsGetPPD)    *pcupsGetPPD;
427 static typeof(cupsPrintFile) *pcupsPrintFile;
428 static void *cupshandle;
429 
430 static BOOL CUPS_LoadPrinters(void)
431 {
432     int                   i, nrofdests;
433     BOOL                  hadprinter = FALSE, haddefault = FALSE;
434     cups_dest_t          *dests;
435     PRINTER_INFO_2A       pinfo2a;
436     char   *port,*devline;
437     HKEY hkeyPrinter, hkeyPrinters, hkey;
438     char    loaderror[256];
439 
440     cupshandle = wine_dlopen(SONAME_LIBCUPS, RTLD_NOW, loaderror, sizeof(loaderror));
441     if (!cupshandle) {
442         TRACE("%s\n", loaderror);
443         return FALSE;
444     }
445     TRACE("%p: %s loaded\n", cupshandle, SONAME_LIBCUPS);
446 
447 #define DYNCUPS(x)                                      \
448         p##x = wine_dlsym(cupshandle, #x, NULL,0);      \
449         if (!p##x) return FALSE;
450 
451     DYNCUPS(cupsFreeDests);
452     DYNCUPS(cupsGetPPD);
453     DYNCUPS(cupsGetDests);
454     DYNCUPS(cupsPrintFile);
455 #undef DYNCUPS
456 
457     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
458        ERROR_SUCCESS) {
459         ERR("Can't create Printers key\n");
460         return FALSE;
461     }
462 
463     nrofdests = pcupsGetDests(&dests);
464     TRACE("Found %d CUPS %s:\n", nrofdests, (nrofdests == 1) ? "printer" : "printers");
465     for (i=0;i<nrofdests;i++) {
466         /* FIXME: replace "LPR:" with "CUPS:". Fix printing output first */
467         port = HeapAlloc(GetProcessHeap(), 0, strlen("LPR:") + strlen(dests[i].name)+1);
468         sprintf(port,"LPR:%s", dests[i].name);
469         /* FIXME: remove extension. Fix gdi32/drivers and comdlg32/printdlg first */
470         devline = HeapAlloc(GetProcessHeap(), 0, sizeof("WINEPS.DRV,,15,45") + strlen(port));
471         sprintf(devline, "WINEPS.DRV,%s", port);
472         WriteProfileStringA("devices", dests[i].name, devline);
473         if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
474             RegSetValueExA(hkey, dests[i].name, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
475             RegCloseKey(hkey);
476         }
477 
478         lstrcatA(devline, ",15,45");
479         WriteProfileStringA("PrinterPorts", dests[i].name, devline);
480         if(RegCreateKeyW(HKEY_CURRENT_USER, WinNT_CV_PrinterPortsW, &hkey) == ERROR_SUCCESS) {
481             RegSetValueExA(hkey, dests[i].name, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
482             RegCloseKey(hkey);
483         }
484 
485         HeapFree(GetProcessHeap(), 0, devline);
486 
487         TRACE("Printer %d: %s\n", i, dests[i].name);
488         if(RegOpenKeyA(hkeyPrinters, dests[i].name, &hkeyPrinter) == ERROR_SUCCESS) {
489             /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
490                and continue */
491             TRACE("Printer already exists\n");
492             RegDeleteValueW(hkeyPrinter, May_Delete_Value);
493             RegCloseKey(hkeyPrinter);
494         } else {
495             static CHAR data_type[] = "RAW",
496                     print_proc[]    = "WinPrint",
497                     comment[]       = "WINEPS Printer using CUPS",
498                     location[]      = "<physical location of printer>",
499                     params[]        = "<parameters?>",
500                     share_name[]    = "<share name?>",
501                     sep_file[]      = "<sep file?>";
502 
503             add_printer_driver(dests[i].name);
504 
505             memset(&pinfo2a,0,sizeof(pinfo2a));
506             pinfo2a.pPrinterName    = dests[i].name;
507             pinfo2a.pDatatype       = data_type;
508             pinfo2a.pPrintProcessor = print_proc;
509             pinfo2a.pDriverName     = dests[i].name;
510             pinfo2a.pComment        = comment;
511             pinfo2a.pLocation       = location;
512             pinfo2a.pPortName       = port;
513             pinfo2a.pParameters     = params;
514             pinfo2a.pShareName      = share_name;
515             pinfo2a.pSepFile        = sep_file;
516 
517             if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
518                 if (GetLastError() != ERROR_PRINTER_ALREADY_EXISTS)
519                     ERR("printer '%s' not added by AddPrinterA (error %d)\n",dests[i].name,GetLastError());
520             }
521         }
522         HeapFree(GetProcessHeap(),0,port);
523 
524         hadprinter = TRUE;
525         if (dests[i].is_default) {
526             WINSPOOL_SetDefaultPrinter(dests[i].name, dests[i].name, TRUE);
527             haddefault = TRUE;
528         }
529     }
530     if (hadprinter & !haddefault)
531         WINSPOOL_SetDefaultPrinter(dests[0].name, dests[0].name, TRUE);
532     pcupsFreeDests(nrofdests, dests);
533     RegCloseKey(hkeyPrinters);
534     return hadprinter;
535 }
536 #endif
537 
538 static BOOL
539 PRINTCAP_ParseEntry(const char *pent, BOOL isfirst) {
540     PRINTER_INFO_2A     pinfo2a;
541     char                *e,*s,*name,*prettyname,*devname;
542     BOOL                ret = FALSE, set_default = FALSE;
543     char                *port = NULL, *devline,*env_default;
544     HKEY                hkeyPrinter, hkeyPrinters, hkey;
545 
546     while (isspace(*pent)) pent++;
547     s = strchr(pent,':');
548     if(s) *s='\0';
549     name = HeapAlloc(GetProcessHeap(), 0, strlen(pent) + 1);
550     strcpy(name,pent);
551     if(s) {
552         *s=':';
553         pent = s;
554     } else
555         pent = "";
556 
557     TRACE("name=%s entry=%s\n",name, pent);
558 
559     if(ispunct(*name)) { /* a tc entry, not a real printer */
560         TRACE("skipping tc entry\n");
561         goto end;
562     }
563 
564     if(strstr(pent,":server")) { /* server only version so skip */
565         TRACE("skipping server entry\n");
566         goto end;
567     }
568 
569     /* Determine whether this is a postscript printer. */
570 
571     ret = TRUE;
572     env_default = getenv("PRINTER");
573     prettyname = name;
574     /* Get longest name, usually the one at the right for later display. */
575     while((s=strchr(prettyname,'|'))) {
576         *s = '\0';
577         e = s;
578         while(isspace(*--e)) *e = '\0';
579         TRACE("\t%s\n", debugstr_a(prettyname));
580         if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
581         for(prettyname = s+1; isspace(*prettyname); prettyname++)
582             ;
583     }
584     e = prettyname + strlen(prettyname);
585     while(isspace(*--e)) *e = '\0';
586     TRACE("\t%s\n", debugstr_a(prettyname));
587     if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
588 
589     /* prettyname must fit into the dmDeviceName member of DEVMODE struct,
590      * if it is too long, we use it as comment below. */
591     devname = prettyname;
592     if (strlen(devname)>=CCHDEVICENAME-1)
593          devname = name;
594     if (strlen(devname)>=CCHDEVICENAME-1) {
595         ret = FALSE;
596         goto end;
597     }
598 
599     port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(name)+1);
600     sprintf(port,"LPR:%s",name);
601 
602     /* FIXME: remove extension. Fix gdi32/drivers and comdlg32/printdlg first */
603     devline = HeapAlloc(GetProcessHeap(), 0, sizeof("WINEPS.DRV,,15,45") + strlen(port));
604     sprintf(devline, "WINEPS.DRV,%s", port);
605     WriteProfileStringA("devices", devname, devline);
606     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
607         RegSetValueExA(hkey, devname, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
608         RegCloseKey(hkey);
609     }
610 
611     lstrcatA(devline, ",15,45");
612     WriteProfileStringA("PrinterPorts", devname, devline);
613     if(RegCreateKeyW(HKEY_CURRENT_USER, WinNT_CV_PrinterPortsW, &hkey) == ERROR_SUCCESS) {
614         RegSetValueExA(hkey, devname, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
615         RegCloseKey(hkey);
616     }
617 
618     HeapFree(GetProcessHeap(),0,devline);
619     
620     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
621        ERROR_SUCCESS) {
622         ERR("Can't create Printers key\n");
623         ret = FALSE;
624         goto end;
625     }
626     if(RegOpenKeyA(hkeyPrinters, devname, &hkeyPrinter) == ERROR_SUCCESS) {
627         /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
628            and continue */
629         TRACE("Printer already exists\n");
630         RegDeleteValueW(hkeyPrinter, May_Delete_Value);
631         RegCloseKey(hkeyPrinter);
632     } else {
633         static CHAR data_type[]   = "RAW",
634                     print_proc[]  = "WinPrint",
635                     comment[]     = "WINEPS Printer using LPR",
636                     params[]      = "<parameters?>",
637                     share_name[]  = "<share name?>",
638                     sep_file[]    = "<sep file?>";
639 
640         add_printer_driver(devname);
641 
642         memset(&pinfo2a,0,sizeof(pinfo2a));
643         pinfo2a.pPrinterName    = devname;
644         pinfo2a.pDatatype       = data_type;
645         pinfo2a.pPrintProcessor = print_proc;
646         pinfo2a.pDriverName     = devname;
647         pinfo2a.pComment        = comment;
648         pinfo2a.pLocation       = prettyname;
649         pinfo2a.pPortName       = port;
650         pinfo2a.pParameters     = params;
651         pinfo2a.pShareName      = share_name;
652         pinfo2a.pSepFile        = sep_file;
653 
654         if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
655             if (GetLastError()!=ERROR_PRINTER_ALREADY_EXISTS)
656                 ERR("%s not added by AddPrinterA (%d)\n",name,GetLastError());
657         }
658     }
659     RegCloseKey(hkeyPrinters);
660 
661     if (isfirst || set_default)
662         WINSPOOL_SetDefaultPrinter(devname,name,TRUE);
663 
664  end:
665     HeapFree(GetProcessHeap(), 0, port);
666     HeapFree(GetProcessHeap(), 0, name);
667     return ret;
668 }
669 
670 static BOOL
671 PRINTCAP_LoadPrinters(void) {
672     BOOL                hadprinter = FALSE;
673     char                buf[200];
674     FILE                *f;
675     char *pent = NULL;
676     BOOL had_bash = FALSE;
677 
678     f = fopen("/etc/printcap","r");
679     if (!f)
680         return FALSE;
681 
682     while(fgets(buf,sizeof(buf),f)) {
683         char *start, *end;
684 
685         end=strchr(buf,'\n');
686         if (end) *end='\0';
687     
688         start = buf;
689         while(isspace(*start)) start++;
690         if(*start == '#' || *start == '\0')
691             continue;
692 
693         if(pent && !had_bash && *start != ':' && *start != '|') { /* start of new entry, parse the previous one */
694             hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
695             HeapFree(GetProcessHeap(),0,pent);
696             pent = NULL;
697         }
698 
699         if (end && *--end == '\\') {
700             *end = '\0';
701             had_bash = TRUE;
702         } else
703             had_bash = FALSE;
704 
705         if (pent) {
706             pent=HeapReAlloc(GetProcessHeap(),0,pent,strlen(pent)+strlen(start)+1);
707             strcat(pent,start);
708         } else {
709             pent=HeapAlloc(GetProcessHeap(),0,strlen(start)+1);
710             strcpy(pent,start);
711         }
712 
713     }
714     if(pent) {
715         hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
716         HeapFree(GetProcessHeap(),0,pent);
717     }
718     fclose(f);
719     return hadprinter;
720 }
721 
722 static inline DWORD set_reg_szW(HKEY hkey, const WCHAR *keyname, const WCHAR *value)
723 {
724     if (value)
725         return RegSetValueExW(hkey, keyname, 0, REG_SZ, (const BYTE*)value,
726                               (lstrlenW(value) + 1) * sizeof(WCHAR));
727     else
728         return ERROR_FILE_NOT_FOUND;
729 }
730 
731 /******************************************************************
732  * get_servername_from_name  (internal)
733  *
734  * for an external server, a copy of the serverpart from the full name is returned
735  *
736  */
737 static LPWSTR get_servername_from_name(LPCWSTR name)
738 {
739     LPWSTR  server;
740     LPWSTR  ptr;
741     WCHAR   buffer[MAX_PATH];
742     DWORD   len;
743 
744     if (name == NULL) return NULL;
745     if ((name[0] != '\\') || (name[1] != '\\')) return NULL;
746 
747     server = strdupW(&name[2]);     /* skip over both backslash */
748     if (server == NULL) return NULL;
749 
750     /* strip '\' and the printername */
751     ptr = strchrW(server, '\\');
752     if (ptr) ptr[0] = '\0';
753 
754     TRACE("found %s\n", debugstr_w(server));
755 
756     len = sizeof(buffer)/sizeof(buffer[0]);
757     if (GetComputerNameW(buffer, &len)) {
758         if (lstrcmpW(buffer, server) == 0) {
759             /* The requested Servername is our computername */
760             HeapFree(GetProcessHeap(), 0, server);
761             return NULL;
762         }
763     }
764     return server;
765 }
766 
767 /******************************************************************
768  * get_basename_from_name  (internal)
769  *
770  * skip over the serverpart from the full name
771  *
772  */
773 static LPCWSTR get_basename_from_name(LPCWSTR name)
774 {
775     if (name == NULL)  return NULL;
776     if ((name[0] == '\\') && (name[1] == '\\')) {
777         /* skip over the servername and search for the following '\'  */
778         name = strchrW(&name[2], '\\');
779         if ((name) && (name[1])) {
780             /* found a separator ('\') followed by a name:
781                skip over the separator and return the rest */
782             name++;
783         }
784         else
785         {
786             /* no basename present (we found only a servername) */
787             return NULL;
788         }
789     }
790     return name;
791 }
792 
793 /******************************************************************
794  *  get_opened_printer_entry
795  *  Get the first place empty in the opened printer table
796  *
797  * ToDo:
798  *  - pDefault is ignored
799  */
800 static HANDLE get_opened_printer_entry(LPWSTR name, LPPRINTER_DEFAULTSW pDefault)
801 {
802     UINT_PTR handle = nb_printer_handles, i;
803     jobqueue_t *queue = NULL;
804     opened_printer_t *printer = NULL;
805     LPWSTR  servername;
806     LPCWSTR printername;
807 
808     if ((backend == NULL)  && !load_backend()) return NULL;
809 
810     servername = get_servername_from_name(name);
811     if (servername) {
812         FIXME("server %s not supported\n", debugstr_w(servername));
813         HeapFree(GetProcessHeap(), 0, servername);
814         SetLastError(ERROR_INVALID_PRINTER_NAME);
815         return NULL;
816     }
817 
818     printername = get_basename_from_name(name);
819     if (name != printername) TRACE("converted %s to %s\n", debugstr_w(name), debugstr_w(printername));
820 
821     /* an empty printername is invalid */
822     if (printername && (!printername[0])) {
823         SetLastError(ERROR_INVALID_PARAMETER);
824         return NULL;
825     }
826 
827     EnterCriticalSection(&printer_handles_cs);
828 
829     for (i = 0; i < nb_printer_handles; i++)
830     {
831         if (!printer_handles[i])
832         {
833             if(handle == nb_printer_handles)
834                 handle = i;
835         }
836         else
837         {
838             if(!queue && (name) && !lstrcmpW(name, printer_handles[i]->name))
839                 queue = printer_handles[i]->queue;
840         }
841     }
842 
843     if (handle >= nb_printer_handles)
844     {
845         opened_printer_t **new_array;
846         if (printer_handles)
847             new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, printer_handles,
848                                      (nb_printer_handles + 16) * sizeof(*new_array) );
849         else
850             new_array = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
851                                    (nb_printer_handles + 16) * sizeof(*new_array) );
852 
853         if (!new_array)
854         {
855             handle = 0;
856             goto end;
857         }
858         printer_handles = new_array;
859         nb_printer_handles += 16;
860     }
861 
862     if (!(printer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*printer))))
863     {
864         handle = 0;
865         goto end;
866     }
867 
868     /* get a printer handle from the backend */
869     if (! backend->fpOpenPrinter(name, &printer->backend_printer, pDefault)) {
870         handle = 0;
871         goto end;
872     }
873 
874     /* clone the base name. This is NULL for the printserver */
875     printer->printername = strdupW(printername);
876 
877     /* clone the full name */
878     printer->name = strdupW(name);
879     if (name && (!printer->name)) {
880         handle = 0;
881         goto end;
882     }
883 
884     if(queue)
885         printer->queue = queue;
886     else
887     {
888         printer->queue = HeapAlloc(GetProcessHeap(), 0, sizeof(*queue));
889         if (!printer->queue) {
890             handle = 0;
891             goto end;
892         }
893         list_init(&printer->queue->jobs);
894         printer->queue->ref = 0;
895     }
896     InterlockedIncrement(&printer->queue->ref);
897 
898     printer_handles[handle] = printer;
899     handle++;
900 end:
901     LeaveCriticalSection(&printer_handles_cs);
902     if (!handle && printer) {
903         /* Something failed: Free all resources */
904         HeapFree(GetProcessHeap(), 0, printer->printername);
905         HeapFree(GetProcessHeap(), 0, printer->name);
906         if (!queue) HeapFree(GetProcessHeap(), 0, printer->queue);
907         HeapFree(GetProcessHeap(), 0, printer);
908     }
909 
910     return (HANDLE)handle;
911 }
912 
913 /******************************************************************
914  *  get_opened_printer
915  *  Get the pointer to the opened printer referred by the handle
916  */
917 static opened_printer_t *get_opened_printer(HANDLE hprn)
918 {
919     UINT_PTR idx = (UINT_PTR)hprn;
920     opened_printer_t *ret = NULL;
921 
922     EnterCriticalSection(&printer_handles_cs);
923 
924     if ((idx > 0) && (idx <= nb_printer_handles)) {
925         ret = printer_handles[idx - 1];
926     }
927     LeaveCriticalSection(&printer_handles_cs);
928     return ret;
929 }
930 
931 /******************************************************************
932  *  get_opened_printer_name
933  *  Get the pointer to the opened printer name referred by the handle
934  */
935 static LPCWSTR get_opened_printer_name(HANDLE hprn)
936 {
937     opened_printer_t *printer = get_opened_printer(hprn);
938     if(!printer) return NULL;
939     return printer->name;
940 }
941 
942 /******************************************************************
943  *  WINSPOOL_GetOpenedPrinterRegKey
944  *
945  */
946 static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey)
947 {
948     LPCWSTR name = get_opened_printer_name(hPrinter);
949     DWORD ret;
950     HKEY hkeyPrinters;
951 
952     if(!name) return ERROR_INVALID_HANDLE;
953 
954     if((ret = RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters)) !=
955        ERROR_SUCCESS)
956         return ret;
957 
958     if(RegOpenKeyW(hkeyPrinters, name, phkey) != ERROR_SUCCESS)
959     {
960         ERR("Can't find opened printer %s in registry\n",
961             debugstr_w(name));
962         RegCloseKey(hkeyPrinters);
963         return ERROR_INVALID_PRINTER_NAME; /* ? */
964     }
965     RegCloseKey(hkeyPrinters);
966     return ERROR_SUCCESS;
967 }
968 
969 void WINSPOOL_LoadSystemPrinters(void)
970 {
971     HKEY                hkey, hkeyPrinters;
972     HANDLE              hprn;
973     DWORD               needed, num, i;
974     WCHAR               PrinterName[256];
975     BOOL                done = FALSE;
976 
977     /* This ensures that all printer entries have a valid Name value.  If causes
978        problems later if they don't.  If one is found to be missed we create one
979        and set it equal to the name of the key */
980     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
981         if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &num, NULL, NULL,
982                             NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
983             for(i = 0; i < num; i++) {
984                 if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)/sizeof(PrinterName[0])) == ERROR_SUCCESS) {
985                     if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkey) == ERROR_SUCCESS) {
986                         if(RegQueryValueExW(hkey, NameW, 0, 0, 0, &needed) == ERROR_FILE_NOT_FOUND) {
987                             set_reg_szW(hkey, NameW, PrinterName);
988                         }
989                         RegCloseKey(hkey);
990                     }
991                 }
992             }
993         }
994         RegCloseKey(hkeyPrinters);
995     }
996 
997     /* We want to avoid calling AddPrinter on printers as much as
998        possible, because on cups printers this will (eventually) lead
999        to a call to cupsGetPPD which takes forever, even with non-cups
1000        printers AddPrinter takes a while.  So we'll tag all printers that
1001        were automatically added last time around, if they still exist
1002        we'll leave them be otherwise we'll delete them. */
1003     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
1004     if(needed) {
1005         PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
1006         if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
1007             for(i = 0; i < num; i++) {
1008                 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
1009                     if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
1010                         if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
1011                             DWORD dw = 1;
1012                             RegSetValueExW(hkey, May_Delete_Value, 0, REG_DWORD, (LPBYTE)&dw, sizeof(dw));
1013                             RegCloseKey(hkey);
1014                         }
1015                         ClosePrinter(hprn);
1016                     }
1017                 }
1018             }
1019         }
1020         HeapFree(GetProcessHeap(), 0, pi);
1021     }
1022 
1023 
1024 #ifdef SONAME_LIBCUPS
1025     done = CUPS_LoadPrinters();
1026 #endif
1027 
1028     if(!done) /* If we have any CUPS based printers, skip looking for printcap printers */
1029         PRINTCAP_LoadPrinters();
1030 
1031     /* Now enumerate the list again and delete any printers that are still tagged */
1032     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
1033     if(needed) {
1034         PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
1035         if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
1036             for(i = 0; i < num; i++) {
1037                 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
1038                     if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
1039                         BOOL delete_driver = FALSE;
1040                         if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
1041                             DWORD dw, type, size = sizeof(dw);
1042                             if(RegQueryValueExW(hkey, May_Delete_Value, NULL, &type, (LPBYTE)&dw, &size) == ERROR_SUCCESS) {
1043                                 TRACE("Deleting old printer %s\n", pi[i].pPrinterName);
1044                                 DeletePrinter(hprn);
1045                                 delete_driver = TRUE;
1046                             }
1047                             RegCloseKey(hkey);
1048                         }
1049                         ClosePrinter(hprn);
1050                         if(delete_driver)
1051                             DeletePrinterDriverExA(NULL, NULL, pi[i].pPrinterName, 0, 0);
1052                     }
1053                 }
1054             }
1055         }
1056         HeapFree(GetProcessHeap(), 0, pi);
1057     }
1058 
1059     return;
1060 
1061 }
1062 
1063 /******************************************************************
1064  *                  get_job
1065  *
1066  *  Get the pointer to the specified job.
1067  *  Should hold the printer_handles_cs before calling.
1068  */
1069 static job_t *get_job(HANDLE hprn, DWORD JobId)
1070 {
1071     opened_printer_t *printer = get_opened_printer(hprn);
1072     job_t *job;
1073 
1074     if(!printer) return NULL;
1075     LIST_FOR_EACH_ENTRY(job, &printer->queue->jobs, job_t, entry)
1076     {
1077         if(job->job_id == JobId)
1078             return job;
1079     }
1080     return NULL;
1081 }
1082 
1083 /***********************************************************
1084  *      DEVMODEcpyAtoW
1085  */
1086 static LPDEVMODEW DEVMODEcpyAtoW(DEVMODEW *dmW, const DEVMODEA *dmA)
1087 {
1088     BOOL Formname;
1089     ptrdiff_t off_formname = (const char *)dmA->dmFormName - (const char *)dmA;
1090     DWORD size;
1091 
1092     Formname = (dmA->dmSize > off_formname);
1093     size = dmA->dmSize + CCHDEVICENAME + (Formname ? CCHFORMNAME : 0);
1094     MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmDeviceName, -1,
1095                         dmW->dmDeviceName, CCHDEVICENAME);
1096     if(!Formname) {
1097       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
1098              dmA->dmSize - CCHDEVICENAME);
1099     } else {
1100       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
1101              off_formname - CCHDEVICENAME);
1102       MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmFormName, -1,
1103                           dmW->dmFormName, CCHFORMNAME);
1104       memcpy(&dmW->dmLogPixels, &dmA->dmLogPixels, dmA->dmSize -
1105              (off_formname + CCHFORMNAME));
1106     }
1107     dmW->dmSize = size;
1108     memcpy((char *)dmW + dmW->dmSize, (const char *)dmA + dmA->dmSize,
1109            dmA->dmDriverExtra);
1110     return dmW;
1111 }
1112 
1113 /***********************************************************
1114  * DEVMODEdupWtoA
1115  * Creates an ansi copy of supplied devmode
1116  */
1117 static LPDEVMODEA DEVMODEdupWtoA(const DEVMODEW *dmW)
1118 {
1119     LPDEVMODEA dmA;
1120     DWORD size;
1121 
1122     if (!dmW) return NULL;
1123     size = dmW->dmSize - CCHDEVICENAME -
1124                         ((dmW->dmSize > FIELD_OFFSET(DEVMODEW, dmFormName)) ? CCHFORMNAME : 0);
1125 
1126     dmA = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size + dmW->dmDriverExtra);
1127     if (!dmA) return NULL;
1128 
1129     WideCharToMultiByte(CP_ACP, 0, dmW->dmDeviceName, -1,
1130                         (LPSTR)dmA->dmDeviceName, CCHDEVICENAME, NULL, NULL);
1131 
1132     if (FIELD_OFFSET(DEVMODEW, dmFormName) >= dmW->dmSize) {
1133         memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
1134                dmW->dmSize - FIELD_OFFSET(DEVMODEW, dmSpecVersion));
1135     }
1136     else
1137     {
1138         memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
1139                FIELD_OFFSET(DEVMODEW, dmFormName) - FIELD_OFFSET(DEVMODEW, dmSpecVersion));
1140         WideCharToMultiByte(CP_ACP, 0, dmW->dmFormName, -1,
1141                             (LPSTR)dmA->dmFormName, CCHFORMNAME, NULL, NULL);
1142 
1143         memcpy(&dmA->dmLogPixels, &dmW->dmLogPixels, dmW->dmSize - FIELD_OFFSET(DEVMODEW, dmLogPixels));
1144     }
1145 
1146     dmA->dmSize = size;
1147     memcpy((char *)dmA + dmA->dmSize, (const char *)dmW + dmW->dmSize, dmW->dmDriverExtra);
1148     return dmA;
1149 }
1150 
1151 /******************************************************************
1152  * convert_printerinfo_W_to_A [internal]
1153  *
1154  */
1155 static void convert_printerinfo_W_to_A(LPBYTE out, LPBYTE pPrintersW,
1156                                        DWORD level, DWORD outlen, DWORD numentries)
1157 {
1158     DWORD id = 0;
1159     LPSTR ptr;
1160     INT len;
1161 
1162     TRACE("(%p, %p, %d, %u, %u)\n", out, pPrintersW, level, outlen, numentries);
1163 
1164     len = pi_sizeof[level] * numentries;
1165     ptr = (LPSTR) out + len;
1166     outlen -= len;
1167 
1168     /* copy the numbers of all PRINTER_INFO_* first */
1169     memcpy(out, pPrintersW, len);
1170 
1171     while (id < numentries) {
1172         switch (level) {
1173             case 1:
1174                 {
1175                     PRINTER_INFO_1W * piW = (PRINTER_INFO_1W *) pPrintersW;
1176                     PRINTER_INFO_1A * piA = (PRINTER_INFO_1A *) out;
1177 
1178                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(piW->pName));
1179                     if (piW->pDescription) {
1180                         piA->pDescription = ptr;
1181                         len = WideCharToMultiByte(CP_ACP, 0, piW->pDescription, -1,
1182                                                   ptr, outlen, NULL, NULL);
1183                         ptr += len;
1184                         outlen -= len;
1185                     }
1186                     if (piW->pName) {
1187                         piA->pName = ptr;
1188                         len = WideCharToMultiByte(CP_ACP, 0, piW->pName, -1,
1189                                                   ptr, outlen, NULL, NULL);
1190                         ptr += len;
1191                         outlen -= len;
1192                     }
1193                     if (piW->pComment) {
1194                         piA->pComment = ptr;
1195                         len = WideCharToMultiByte(CP_ACP, 0, piW->pComment, -1,
1196                                                   ptr, outlen, NULL, NULL);
1197                         ptr += len;
1198                         outlen -= len;
1199                     }
1200                     break;
1201                 }
1202 
1203             case 2:
1204                 {
1205                     PRINTER_INFO_2W * piW = (PRINTER_INFO_2W *) pPrintersW;
1206                     PRINTER_INFO_2A * piA = (PRINTER_INFO_2A *) out;
1207                     LPDEVMODEA dmA;
1208 
1209                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(piW->pPrinterName));
1210                     if (piW->pServerName) {
1211                         piA->pServerName = ptr;
1212                         len = WideCharToMultiByte(CP_ACP, 0, piW->pServerName, -1,
1213                                                   ptr, outlen, NULL, NULL);
1214                         ptr += len;
1215                         outlen -= len;
1216                     }
1217                     if (piW->pPrinterName) {
1218                         piA->pPrinterName = ptr;
1219                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPrinterName, -1,
1220                                                   ptr, outlen, NULL, NULL);
1221                         ptr += len;
1222                         outlen -= len;
1223                     }
1224                     if (piW->pShareName) {
1225                         piA->pShareName = ptr;
1226                         len = WideCharToMultiByte(CP_ACP, 0, piW->pShareName, -1,
1227                                                   ptr, outlen, NULL, NULL);
1228                         ptr += len;
1229                         outlen -= len;
1230                     }
1231                     if (piW->pPortName) {
1232                         piA->pPortName = ptr;
1233                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPortName, -1,
1234                                                   ptr, outlen, NULL, NULL);
1235                         ptr += len;
1236                         outlen -= len;
1237                     }
1238                     if (piW->pDriverName) {
1239                         piA->pDriverName = ptr;
1240                         len = WideCharToMultiByte(CP_ACP, 0, piW->pDriverName, -1,
1241                                                   ptr, outlen, NULL, NULL);
1242                         ptr += len;
1243                         outlen -= len;
1244                     }
1245                     if (piW->pComment) {
1246                         piA->pComment = ptr;
1247                         len = WideCharToMultiByte(CP_ACP, 0, piW->pComment, -1,
1248                                                   ptr, outlen, NULL, NULL);
1249                         ptr += len;
1250                         outlen -= len;
1251                     }
1252                     if (piW->pLocation) {
1253                         piA->pLocation = ptr;
1254                         len = WideCharToMultiByte(CP_ACP, 0, piW->pLocation, -1,
1255                                                   ptr, outlen, NULL, NULL);
1256                         ptr += len;
1257                         outlen -= len;
1258                     }
1259 
1260                     dmA = DEVMODEdupWtoA(piW->pDevMode);
1261                     if (dmA) {
1262                         /* align DEVMODEA to a DWORD boundary */
1263                         len = (4 - ( (DWORD_PTR) ptr & 3)) & 3;
1264                         ptr += len;
1265                         outlen -= len;
1266 
1267                         piA->pDevMode = (LPDEVMODEA) ptr;
1268                         len = dmA->dmSize + dmA->dmDriverExtra;
1269                         memcpy(ptr, dmA, len);
1270                         HeapFree(GetProcessHeap(), 0, dmA);
1271 
1272                         ptr += len;
1273                         outlen -= len;
1274                     }
1275 
1276                     if (piW->pSepFile) {
1277                         piA->pSepFile = ptr;
1278                         len = WideCharToMultiByte(CP_ACP, 0, piW->pSepFile, -1,
1279                                                   ptr, outlen, NULL, NULL);
1280                         ptr += len;
1281                         outlen -= len;
1282                     }
1283                     if (piW->pPrintProcessor) {
1284                         piA->pPrintProcessor = ptr;
1285                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPrintProcessor, -1,
1286                                                   ptr, outlen, NULL, NULL);
1287                         ptr += len;
1288                         outlen -= len;
1289                     }
1290                     if (piW->pDatatype) {
1291                         piA->pDatatype = ptr;
1292                         len = WideCharToMultiByte(CP_ACP, 0, piW->pDatatype, -1,
1293                                                   ptr, outlen, NULL, NULL);
1294                         ptr += len;
1295                         outlen -= len;
1296                     }
1297                     if (piW->pParameters) {
1298                         piA->pParameters = ptr;
1299                         len = WideCharToMultiByte(CP_ACP, 0, piW->pParameters, -1,
1300                                                   ptr, outlen, NULL, NULL);
1301                         ptr += len;
1302                         outlen -= len;
1303                     }
1304                     if (piW->pSecurityDescriptor) {
1305                         piA->pSecurityDescriptor = NULL;
1306                         FIXME("pSecurityDescriptor ignored: %s\n", debugstr_w(piW->pPrinterName));
1307                     }
1308                     break;
1309                 }
1310 
1311             case 4:
1312                 {
1313                     PRINTER_INFO_4W * piW = (PRINTER_INFO_4W *) pPrintersW;
1314                     PRINTER_INFO_4A * piA = (PRINTER_INFO_4A *) out;
1315 
1316                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(piW->pPrinterName));
1317 
1318                     if (piW->pPrinterName) {
1319                         piA->pPrinterName = ptr;
1320                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPrinterName, -1,
1321                                                   ptr, outlen, NULL, NULL);
1322                         ptr += len;
1323                         outlen -= len;
1324                     }
1325                     if (piW->pServerName) {
1326                         piA->pServerName = ptr;
1327                         len = WideCharToMultiByte(CP_ACP, 0, piW->pServerName, -1,
1328                                                   ptr, outlen, NULL, NULL);
1329                         ptr += len;
1330                         outlen -= len;
1331                     }
1332                     break;
1333                 }
1334 
1335             case 5:
1336                 {
1337                     PRINTER_INFO_5W * piW = (PRINTER_INFO_5W *) pPrintersW;
1338                     PRINTER_INFO_5A * piA = (PRINTER_INFO_5A *) out;
1339 
1340                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(piW->pPrinterName));
1341 
1342                     if (piW->pPrinterName) {
1343                         piA->pPrinterName = ptr;
1344                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPrinterName, -1,
1345                                                   ptr, outlen, NULL, NULL);
1346                         ptr += len;
1347                         outlen -= len;
1348                     }
1349                     if (piW->pPortName) {
1350                         piA->pPortName = ptr;
1351                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPortName, -1,
1352                                                   ptr, outlen, NULL, NULL);
1353                         ptr += len;
1354                         outlen -= len;
1355                     }
1356                     break;
1357                 }
1358 
1359             default:
1360                 FIXME("for level %u\n", level);
1361         }
1362         pPrintersW += pi_sizeof[level];
1363         out += pi_sizeof[level];
1364         id++;
1365     }
1366 }
1367 
1368 /***********************************************************
1369  *             PRINTER_INFO_2AtoW
1370  * Creates a unicode copy of PRINTER_INFO_2A on heap
1371  */
1372 static LPPRINTER_INFO_2W PRINTER_INFO_2AtoW(HANDLE heap, LPPRINTER_INFO_2A piA)
1373 {
1374     LPPRINTER_INFO_2W piW;
1375     UNICODE_STRING usBuffer;
1376 
1377     if(!piA) return NULL;
1378     piW = HeapAlloc(heap, 0, sizeof(*piW));
1379     memcpy(piW, piA, sizeof(*piW)); /* copy everything first */
1380     
1381     piW->pServerName = asciitounicode(&usBuffer,piA->pServerName);
1382     piW->pPrinterName = asciitounicode(&usBuffer,piA->pPrinterName);
1383     piW->pShareName = asciitounicode(&usBuffer,piA->pShareName);
1384     piW->pPortName = asciitounicode(&usBuffer,piA->pPortName);
1385     piW->pDriverName = asciitounicode(&usBuffer,piA->pDriverName);
1386     piW->pComment = asciitounicode(&usBuffer,piA->pComment);
1387     piW->pLocation = asciitounicode(&usBuffer,piA->pLocation);
1388     piW->pDevMode = piA->pDevMode ? GdiConvertToDevmodeW(piA->pDevMode) : NULL;
1389     piW->pSepFile = asciitounicode(&usBuffer,piA->pSepFile);
1390     piW->pPrintProcessor = asciitounicode(&usBuffer,piA->pPrintProcessor);
1391     piW->pDatatype = asciitounicode(&usBuffer,piA->pDatatype);
1392     piW->pParameters = asciitounicode(&usBuffer,piA->pParameters);
1393     return piW;
1394 }
1395 
1396 /***********************************************************
1397  *       FREE_PRINTER_INFO_2W
1398  * Free PRINTER_INFO_2W and all strings
1399  */
1400 static void FREE_PRINTER_INFO_2W(HANDLE heap, LPPRINTER_INFO_2W piW)
1401 {
1402     if(!piW) return;
1403 
1404     HeapFree(heap,0,piW->pServerName);
1405     HeapFree(heap,0,piW->pPrinterName);
1406     HeapFree(heap,0,piW->pShareName);
1407     HeapFree(heap,0,piW->pPortName);
1408     HeapFree(heap,0,piW->pDriverName);
1409     HeapFree(heap,0,piW->pComment);
1410     HeapFree(heap,0,piW->pLocation);
1411     HeapFree(heap,0,piW->pDevMode);
1412     HeapFree(heap,0,piW->pSepFile);
1413     HeapFree(heap,0,piW->pPrintProcessor);
1414     HeapFree(heap,0,piW->pDatatype);
1415     HeapFree(heap,0,piW->pParameters);
1416     HeapFree(heap,0,piW);
1417     return;
1418 }
1419 
1420 /******************************************************************
1421  *              DeviceCapabilities     [WINSPOOL.@]
1422  *              DeviceCapabilitiesA    [WINSPOOL.@]
1423  *
1424  */
1425 INT WINAPI DeviceCapabilitiesA(LPCSTR pDevice,LPCSTR pPort, WORD cap,
1426                                LPSTR pOutput, LPDEVMODEA lpdm)
1427 {
1428     INT ret;
1429 
1430     if (!GDI_CallDeviceCapabilities16)
1431     {
1432         GDI_CallDeviceCapabilities16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1433                                                               (LPCSTR)104 );
1434         if (!GDI_CallDeviceCapabilities16) return -1;
1435     }
1436     ret = GDI_CallDeviceCapabilities16(pDevice, pPort, cap, pOutput, lpdm);
1437 
1438     /* If DC_PAPERSIZE map POINT16s to POINTs */
1439     if(ret != -1 && cap == DC_PAPERSIZE && pOutput) {
1440         POINT16 *tmp = HeapAlloc( GetProcessHeap(), 0, ret * sizeof(POINT16) );
1441         POINT *pt = (POINT *)pOutput;
1442         INT i;
1443         memcpy(tmp, pOutput, ret * sizeof(POINT16));
1444         for(i = 0; i < ret; i++, pt++)
1445         {
1446             pt->x = tmp[i].x;
1447             pt->y = tmp[i].y;
1448         }
1449         HeapFree( GetProcessHeap(), 0, tmp );
1450     }
1451     return ret;
1452 }
1453 
1454 
1455 /*****************************************************************************
1456  *          DeviceCapabilitiesW        [WINSPOOL.@]
1457  *
1458  * Call DeviceCapabilitiesA since we later call 16bit stuff anyway
1459  *
1460  */
1461 INT WINAPI DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort,
1462                                WORD fwCapability, LPWSTR pOutput,
1463                                const DEVMODEW *pDevMode)
1464 {
1465     LPDEVMODEA dmA = DEVMODEdupWtoA(pDevMode);
1466     LPSTR pDeviceA = strdupWtoA(pDevice);
1467     LPSTR pPortA = strdupWtoA(pPort);
1468     INT ret;
1469 
1470     if(pOutput && (fwCapability == DC_BINNAMES ||
1471                    fwCapability == DC_FILEDEPENDENCIES ||
1472                    fwCapability == DC_PAPERNAMES)) {
1473       /* These need A -> W translation */
1474         INT size = 0, i;
1475         LPSTR pOutputA;
1476         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, NULL,
1477                                   dmA);
1478         if(ret == -1)
1479             return ret;
1480         switch(fwCapability) {
1481         case DC_BINNAMES:
1482             size = 24;
1483             break;
1484         case DC_PAPERNAMES:
1485         case DC_FILEDEPENDENCIES:
1486             size = 64;
1487             break;
1488         }
1489         pOutputA = HeapAlloc(GetProcessHeap(), 0, size * ret);
1490         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, pOutputA,
1491                                   dmA);
1492         for(i = 0; i < ret; i++)
1493             MultiByteToWideChar(CP_ACP, 0, pOutputA + (i * size), -1,
1494                                 pOutput + (i * size), size);
1495         HeapFree(GetProcessHeap(), 0, pOutputA);
1496     } else {
1497         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability,
1498                                   (LPSTR)pOutput, dmA);
1499     }
1500     HeapFree(GetProcessHeap(),0,pPortA);
1501     HeapFree(GetProcessHeap(),0,pDeviceA);
1502     HeapFree(GetProcessHeap(),0,dmA);
1503     return ret;
1504 }
1505 
1506 /******************************************************************
1507  *              DocumentPropertiesA   [WINSPOOL.@]
1508  *
1509  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1510  */
1511 LONG WINAPI DocumentPropertiesA(HWND hWnd,HANDLE hPrinter,
1512                                 LPSTR pDeviceName, LPDEVMODEA pDevModeOutput,
1513                                 LPDEVMODEA pDevModeInput,DWORD fMode )
1514 {
1515     LPSTR lpName = pDeviceName;
1516     static CHAR port[] = "LPT1:";
1517     LONG ret;
1518 
1519     TRACE("(%p,%p,%s,%p,%p,%d)\n",
1520         hWnd,hPrinter,pDeviceName,pDevModeOutput,pDevModeInput,fMode
1521     );
1522 
1523     if(!pDeviceName) {
1524         LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
1525         if(!lpNameW) {
1526                 ERR("no name from hPrinter?\n");
1527                 SetLastError(ERROR_INVALID_HANDLE);
1528                 return -1;
1529         }
1530         lpName = strdupWtoA(lpNameW);
1531     }
1532 
1533     if (!GDI_CallExtDeviceMode16)
1534     {
1535         GDI_CallExtDeviceMode16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1536                                                          (LPCSTR)102 );
1537         if (!GDI_CallExtDeviceMode16) {
1538                 ERR("No CallExtDeviceMode16?\n");
1539                 return -1;
1540         }
1541     }
1542     ret = GDI_CallExtDeviceMode16(hWnd, pDevModeOutput, lpName, port,
1543                                   pDevModeInput, NULL, fMode);
1544 
1545     if(!pDeviceName)
1546         HeapFree(GetProcessHeap(),0,lpName);
1547     return ret;
1548 }
1549 
1550 
1551 /*****************************************************************************
1552  *          DocumentPropertiesW (WINSPOOL.@)
1553  *
1554  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1555  */
1556 LONG WINAPI DocumentPropertiesW(HWND hWnd, HANDLE hPrinter,
1557                                 LPWSTR pDeviceName,
1558                                 LPDEVMODEW pDevModeOutput,
1559                                 LPDEVMODEW pDevModeInput, DWORD fMode)
1560 {
1561 
1562     LPSTR pDeviceNameA = strdupWtoA(pDeviceName);
1563     LPDEVMODEA pDevModeInputA = DEVMODEdupWtoA(pDevModeInput);
1564     LPDEVMODEA pDevModeOutputA = NULL;
1565     LONG ret;
1566 
1567     TRACE("(%p,%p,%s,%p,%p,%d)\n",
1568           hWnd,hPrinter,debugstr_w(pDeviceName),pDevModeOutput,pDevModeInput,
1569           fMode);
1570     if(pDevModeOutput) {
1571         ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, NULL, NULL, 0);
1572         if(ret < 0) return ret;
1573         pDevModeOutputA = HeapAlloc(GetProcessHeap(), 0, ret);
1574     }
1575     ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, pDevModeOutputA,
1576                               pDevModeInputA, fMode);
1577     if(pDevModeOutput) {
1578         DEVMODEcpyAtoW(pDevModeOutput, pDevModeOutputA);
1579         HeapFree(GetProcessHeap(),0,pDevModeOutputA);
1580     }
1581     if(fMode == 0 && ret > 0)
1582         ret += (CCHDEVICENAME + CCHFORMNAME);
1583     HeapFree(GetProcessHeap(),0,pDevModeInputA);
1584     HeapFree(GetProcessHeap(),0,pDeviceNameA);
1585     return ret;
1586 }
1587 
1588 /******************************************************************
1589  *              OpenPrinterA        [WINSPOOL.@]
1590  *
1591  * See OpenPrinterW.
1592  *
1593  */
1594 BOOL WINAPI OpenPrinterA(LPSTR lpPrinterName,HANDLE *phPrinter,
1595                          LPPRINTER_DEFAULTSA pDefault)
1596 {
1597     UNICODE_STRING lpPrinterNameW;
1598     UNICODE_STRING usBuffer;
1599     PRINTER_DEFAULTSW DefaultW, *pDefaultW = NULL;
1600     PWSTR pwstrPrinterNameW;
1601     BOOL ret;
1602 
1603     pwstrPrinterNameW = asciitounicode(&lpPrinterNameW,lpPrinterName);
1604 
1605     if(pDefault) {
1606         DefaultW.pDatatype = asciitounicode(&usBuffer,pDefault->pDatatype);
1607         DefaultW.pDevMode = pDefault->pDevMode ? GdiConvertToDevmodeW(pDefault->pDevMode) : NULL;
1608         DefaultW.DesiredAccess = pDefault->DesiredAccess;
1609         pDefaultW = &DefaultW;
1610     }
1611     ret = OpenPrinterW(pwstrPrinterNameW, phPrinter, pDefaultW);
1612     if(pDefault) {
1613         RtlFreeUnicodeString(&usBuffer);
1614         HeapFree(GetProcessHeap(), 0, DefaultW.pDevMode);
1615     }
1616     RtlFreeUnicodeString(&lpPrinterNameW);
1617     return ret;
1618 }
1619 
1620 /******************************************************************
1621  *              OpenPrinterW        [WINSPOOL.@]
1622  *
1623  * Open a Printer / Printserver or a Printer-Object
1624  *
1625  * PARAMS
1626  *  lpPrinterName [I] Name of Printserver, Printer, or Printer-Object
1627  *  phPrinter     [O] The resulting Handle is stored here
1628  *  pDefault      [I] PTR to Default Printer Settings or NULL
1629  *
1630  * RETURNS
1631  *  Success: TRUE
1632  *  Failure: FALSE
1633  *
1634  * NOTES
1635  *  lpPrinterName is one of:
1636  *|  Printserver (NT only): "Servername" or NULL for the local Printserver
1637  *|  Printer: "PrinterName"
1638  *|  Printer-Object: "PrinterName,Job xxx"
1639  *|  XcvMonitor: "Servername,XcvMonitor MonitorName"
1640  *|  XcvPort: "Servername,XcvPort PortName"
1641  *
1642  * BUGS
1643  *|  Printer-Object not supported
1644  *|  pDefaults is ignored
1645  *
1646  */
1647 BOOL WINAPI OpenPrinterW(LPWSTR lpPrinterName,HANDLE *phPrinter, LPPRINTER_DEFAULTSW pDefault)
1648 {
1649 
1650     TRACE("(%s, %p, %p)\n", debugstr_w(lpPrinterName), phPrinter, pDefault);
1651     if (pDefault) {
1652         FIXME("PRINTER_DEFAULTS ignored => %s,%p,0x%08x\n",
1653         debugstr_w(pDefault->pDatatype), pDefault->pDevMode, pDefault->DesiredAccess);
1654     }
1655 
1656     if(!phPrinter) {
1657         /* NT: FALSE with ERROR_INVALID_PARAMETER, 9x: TRUE */
1658         SetLastError(ERROR_INVALID_PARAMETER);
1659         return FALSE;
1660     }
1661 
1662     /* Get the unique handle of the printer or Printserver */
1663     *phPrinter = get_opened_printer_entry(lpPrinterName, pDefault);
1664     TRACE("returning %d with %u and %p\n", *phPrinter != NULL, GetLastError(), *phPrinter);
1665     return (*phPrinter != 0);
1666 }
1667 
1668 /******************************************************************
1669  *              AddMonitorA        [WINSPOOL.@]
1670  *
1671  * See AddMonitorW.
1672  *
1673  */
1674 BOOL WINAPI AddMonitorA(LPSTR pName, DWORD Level, LPBYTE pMonitors)
1675 {
1676     LPWSTR  nameW = NULL;
1677     INT     len;
1678     BOOL    res;
1679     LPMONITOR_INFO_2A mi2a;
1680     MONITOR_INFO_2W mi2w;
1681 
1682     mi2a = (LPMONITOR_INFO_2A) pMonitors;
1683     TRACE("(%s, %d, %p) :  %s %s %s\n", debugstr_a(pName), Level, pMonitors,
1684           debugstr_a(mi2a ? mi2a->pName : NULL),
1685           debugstr_a(mi2a ? mi2a->pEnvironment : NULL),
1686           debugstr_a(mi2a ? mi2a->pDLLName : NULL));
1687 
1688     if  (Level != 2) {
1689         SetLastError(ERROR_INVALID_LEVEL);
1690         return FALSE;
1691     }
1692 
1693     /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
1694     if (mi2a == NULL) {
1695         return FALSE;
1696     }
1697 
1698     if (pName) {
1699         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
1700         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1701         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
1702     }
1703 
1704     memset(&mi2w, 0, sizeof(MONITOR_INFO_2W));
1705     if (mi2a->pName) {
1706         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, NULL, 0);
1707         mi2w.pName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1708         MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, mi2w.pName, len);
1709     }
1710     if (mi2a->pEnvironment) {
1711         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, NULL, 0);
1712         mi2w.pEnvironment = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1713         MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, mi2w.pEnvironment, len);
1714     }
1715     if (mi2a->pDLLName) {
1716         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, NULL, 0);
1717         mi2w.pDLLName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1718         MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, mi2w.pDLLName, len);
1719     }
1720 
1721     res = AddMonitorW(nameW, Level, (LPBYTE) &mi2w);
1722 
1723     HeapFree(GetProcessHeap(), 0, mi2w.pName); 
1724     HeapFree(GetProcessHeap(), 0, mi2w.pEnvironment); 
1725     HeapFree(GetProcessHeap(), 0, mi2w.pDLLName); 
1726 
1727     HeapFree(GetProcessHeap(), 0, nameW); 
1728     return (res);
1729 }
1730 
1731 /******************************************************************************
1732  *              AddMonitorW        [WINSPOOL.@]
1733  *
1734  * Install a Printmonitor
1735  *
1736  * PARAMS
1737  *  pName       [I] Servername or NULL (local Computer)
1738  *  Level       [I] Structure-Level (Must be 2)
1739  *  pMonitors   [I] PTR to MONITOR_INFO_2
1740  *
1741  * RETURNS
1742  *  Success: TRUE
1743  *  Failure: FALSE
1744  *
1745  * NOTES
1746  *  All Files for the Monitor must already be copied to %winsysdir% ("%SystemRoot%\system32")
1747  *
1748  */
1749 BOOL WINAPI AddMonitorW(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
1750 {
1751     LPMONITOR_INFO_2W mi2w;
1752 
1753     mi2w = (LPMONITOR_INFO_2W) pMonitors;
1754     TRACE("(%s, %d, %p) :  %s %s %s\n", debugstr_w(pName), Level, pMonitors,
1755           debugstr_w(mi2w ? mi2w->pName : NULL),
1756           debugstr_w(mi2w ? mi2w->pEnvironment : NULL),
1757           debugstr_w(mi2w ? mi2w->pDLLName : NULL));
1758 
1759     if ((backend == NULL)  && !load_backend()) return FALSE;
1760 
1761     if (Level != 2) {
1762         SetLastError(ERROR_INVALID_LEVEL);
1763         return FALSE;
1764     }
1765 
1766     /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
1767     if (mi2w == NULL) {
1768         return FALSE;
1769     }
1770 
1771     return backend->fpAddMonitor(pName, Level, pMonitors);
1772 }
1773 
1774 /******************************************************************
1775  *              DeletePrinterDriverA        [WINSPOOL.@]
1776  *
1777  */
1778 BOOL WINAPI DeletePrinterDriverA (LPSTR pName, LPSTR pEnvironment, LPSTR pDriverName)
1779 {
1780     return DeletePrinterDriverExA(pName, pEnvironment, pDriverName, 0, 0);
1781 }
1782 
1783 /******************************************************************
1784  *              DeletePrinterDriverW        [WINSPOOL.@]
1785  *
1786  */
1787 BOOL WINAPI DeletePrinterDriverW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pDriverName)
1788 {
1789     return DeletePrinterDriverExW(pName, pEnvironment, pDriverName, 0, 0);
1790 }
1791 
1792 /******************************************************************
1793  *              DeleteMonitorA        [WINSPOOL.@]
1794  *
1795  * See DeleteMonitorW.
1796  *
1797  */
1798 BOOL WINAPI DeleteMonitorA (LPSTR pName, LPSTR pEnvironment, LPSTR pMonitorName)
1799 {
1800     LPWSTR  nameW = NULL;
1801     LPWSTR  EnvironmentW = NULL;
1802     LPWSTR  MonitorNameW = NULL;
1803     BOOL    res;
1804     INT     len;
1805 
1806     if (pName) {
1807         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
1808         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1809         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
1810     }
1811 
1812     if (pEnvironment) {
1813         len = MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, NULL, 0);
1814         EnvironmentW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1815         MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, EnvironmentW, len);
1816     }
1817     if (pMonitorName) {
1818         len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
1819         MonitorNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1820         MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, MonitorNameW, len);
1821     }
1822 
1823     res = DeleteMonitorW(nameW, EnvironmentW, MonitorNameW);
1824 
1825     HeapFree(GetProcessHeap(), 0, MonitorNameW); 
1826     HeapFree(GetProcessHeap(), 0, EnvironmentW);
1827     HeapFree(GetProcessHeap(), 0, nameW); 
1828     return (res);
1829 }
1830 
1831 /******************************************************************
1832  *              DeleteMonitorW        [WINSPOOL.@]
1833  *
1834  * Delete a specific Printmonitor from a Printing-Environment
1835  *
1836  * PARAMS
1837  *  pName        [I] Servername or NULL (local Computer)
1838  *  pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
1839  *  pMonitorName [I] Name of the Monitor, that should be deleted
1840  *
1841  * RETURNS
1842  *  Success: TRUE
1843  *  Failure: FALSE
1844  *
1845  * NOTES
1846  *  pEnvironment is ignored in Windows for the local Computer.
1847  *
1848  */
1849 BOOL WINAPI DeleteMonitorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
1850 {
1851 
1852     TRACE("(%s, %s, %s)\n",debugstr_w(pName),debugstr_w(pEnvironment),
1853            debugstr_w(pMonitorName));
1854 
1855     if ((backend == NULL)  && !load_backend()) return FALSE;
1856 
1857     return backend->fpDeleteMonitor(pName, pEnvironment, pMonitorName);
1858 }
1859 
1860 
1861 /******************************************************************
1862  *              DeletePortA        [WINSPOOL.@]
1863  *
1864  * See DeletePortW.
1865  *
1866  */
1867 BOOL WINAPI DeletePortA (LPSTR pName, HWND hWnd, LPSTR pPortName)
1868 {
1869     LPWSTR  nameW = NULL;
1870     LPWSTR  portW = NULL;
1871     INT     len;
1872     DWORD   res;
1873 
1874     TRACE("(%s, %p, %s)\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
1875 
1876     /* convert servername to unicode */
1877     if (pName) {
1878         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
1879         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1880         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
1881     }
1882 
1883     /* convert portname to unicode */
1884     if (pPortName) {
1885         len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
1886         portW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1887         MultiByteToWideChar(CP_ACP, 0, pPortName, -1, portW, len);
1888     }
1889 
1890     res = DeletePortW(nameW, hWnd, portW);
1891     HeapFree(GetProcessHeap(), 0, nameW);
1892     HeapFree(GetProcessHeap(), 0, portW);
1893     return res;
1894 }
1895 
1896 /******************************************************************
1897  *              DeletePortW        [WINSPOOL.@]
1898  *
1899  * Delete a specific Port
1900  *
1901  * PARAMS
1902  *  pName     [I] Servername or NULL (local Computer)
1903  *  hWnd      [I] Handle to parent Window for the Dialog-Box
1904  *  pPortName [I] Name of the Port, that should be deleted
1905  *
1906  * RETURNS
1907  *  Success: TRUE
1908  *  Failure: FALSE
1909  *
1910  */
1911 BOOL WINAPI DeletePortW (LPWSTR pName, HWND hWnd, LPWSTR pPortName)
1912 {
1913     TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
1914 
1915     if ((backend == NULL)  && !load_backend()) return FALSE;
1916 
1917     if (!pPortName) {
1918         SetLastError(RPC_X_NULL_REF_POINTER);
1919         return FALSE;
1920     }
1921 
1922     return backend->fpDeletePort(pName, hWnd, pPortName);
1923 }
1924 
1925 /******************************************************************************
1926  *    SetPrinterW  [WINSPOOL.@]
1927  */
1928 BOOL WINAPI SetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD Command)
1929 {
1930     FIXME("(%p, %d, %p, %d): stub\n", hPrinter, Level, pPrinter, Command);
1931     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1932     return FALSE;
1933 }
1934 
1935 /******************************************************************************
1936  *    WritePrinter  [WINSPOOL.@]
1937  */
1938 BOOL WINAPI WritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
1939 {
1940     opened_printer_t *printer;
1941     BOOL ret = FALSE;
1942 
1943     TRACE("(%p, %p, %d, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
1944 
1945     EnterCriticalSection(&printer_handles_cs);
1946     printer = get_opened_printer(hPrinter);
1947     if(!printer)
1948     {
1949         SetLastError(ERROR_INVALID_HANDLE);
1950         goto end;
1951     }
1952 
1953     if(!printer->doc)
1954     {
1955         SetLastError(ERROR_SPL_NO_STARTDOC);
1956         goto end;
1957     }
1958 
1959     ret = WriteFile(printer->doc->hf, pBuf, cbBuf, pcWritten, NULL);
1960 end:
1961     LeaveCriticalSection(&printer_handles_cs);
1962     return ret;
1963 }
1964 
1965 /*****************************************************************************
1966  *          AddFormA  [WINSPOOL.@]
1967  */
1968 BOOL WINAPI AddFormA(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
1969 {
1970     FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
1971     return 1;
1972 }
1973 
1974 /*****************************************************************************
1975  *          AddFormW  [WINSPOOL.@]
1976  */
1977 BOOL WINAPI AddFormW(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
1978 {
1979     FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
1980     return 1;
1981 }
1982 
1983 /*****************************************************************************
1984  *          AddJobA  [WINSPOOL.@]
1985  */
1986 BOOL WINAPI AddJobA(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
1987 {
1988     BOOL ret;
1989     BYTE buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
1990     DWORD needed;
1991 
1992     if(Level != 1) {
1993         SetLastError(ERROR_INVALID_LEVEL);
1994         return FALSE;
1995     }
1996 
1997     ret = AddJobW(hPrinter, Level, buf, sizeof(buf), &needed);
1998 
1999     if(ret) {
2000         ADDJOB_INFO_1W *addjobW = (ADDJOB_INFO_1W*)buf;
2001         DWORD len = WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, NULL, 0, NULL, NULL);
2002         *pcbNeeded = len + sizeof(ADDJOB_INFO_1A);
2003         if(*pcbNeeded > cbBuf) {
2004             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2005             ret = FALSE;
2006         } else {
2007             ADDJOB_INFO_1A *addjobA = (ADDJOB_INFO_1A*)pData;
2008             addjobA->JobId = addjobW->JobId;
2009             addjobA->Path = (char *)(addjobA + 1);
2010             WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, addjobA->Path, len, NULL, NULL);
2011         }
2012     }
2013     return ret;
2014 }
2015 
2016 /*****************************************************************************
2017  *          AddJobW  [WINSPOOL.@]
2018  */
2019 BOOL WINAPI AddJobW(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
2020 {
2021     opened_printer_t *printer;
2022     job_t *job;
2023     BOOL ret = FALSE;
2024     static const WCHAR spool_path[] = {'s','p','o','o','l','\\','P','R','I','N','T','E','R','S','\\',0};
2025     static const WCHAR fmtW[] = {'%','s','%','','5','d','.','S','P','L',0};
2026     WCHAR path[MAX_PATH], filename[MAX_PATH];
2027     DWORD len;
2028     ADDJOB_INFO_1W *addjob;
2029 
2030     TRACE("(%p,%d,%p,%d,%p)\n", hPrinter, Level, pData, cbBuf, pcbNeeded);
2031     
2032     EnterCriticalSection(&printer_handles_cs);
2033 
2034     printer = get_opened_printer(hPrinter);
2035 
2036     if(!printer) {
2037         SetLastError(ERROR_INVALID_HANDLE);
2038         goto end;
2039     }
2040 
2041     if(Level != 1) {
2042         SetLastError(ERROR_INVALID_LEVEL);
2043         goto end;
2044     }
2045 
2046     job = HeapAlloc(GetProcessHeap(), 0, sizeof(*job));
2047     if(!job)
2048         goto end;
2049 
2050     job->job_id = InterlockedIncrement(&next_job_id);
2051 
2052     len = GetSystemDirectoryW(path, sizeof(path) / sizeof(WCHAR));
2053     if(path[len - 1] != '\\')
2054         path[len++] = '\\';
2055     memcpy(path + len, spool_path, sizeof(spool_path));    
2056     sprintfW(filename, fmtW, path, job->job_id);
2057 
2058     len = strlenW(filename);
2059     job->filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
2060     memcpy(job->filename, filename, (len + 1) * sizeof(WCHAR));
2061     job->document_title = strdupW(default_doc_title);
2062     list_add_tail(&printer->queue->jobs, &job->entry);
2063 
2064     *pcbNeeded = (len + 1) * sizeof(WCHAR) + sizeof(*addjob);
2065     if(*pcbNeeded <= cbBuf) {
2066         addjob = (ADDJOB_INFO_1W*)pData;
2067         addjob->JobId = job->job_id;
2068         addjob->Path = (WCHAR *)(addjob + 1);
2069         memcpy(addjob->Path, filename, (len + 1) * sizeof(WCHAR));
2070         ret = TRUE;
2071     } else 
2072         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2073 
2074 end:
2075     LeaveCriticalSection(&printer_handles_cs);
2076     return ret;
2077 }
2078 
2079 /*****************************************************************************
2080  *          GetPrintProcessorDirectoryA  [WINSPOOL.@]
2081  *
2082  * Return the PATH for the Print-Processors
2083  *
2084  * See GetPrintProcessorDirectoryW.
2085  *
2086  *
2087  */
2088 BOOL WINAPI GetPrintProcessorDirectoryA(LPSTR server, LPSTR env,
2089                                         DWORD level,  LPBYTE Info,
2090                                         DWORD cbBuf,  LPDWORD pcbNeeded)
2091 {
2092     LPWSTR  serverW = NULL;
2093     LPWSTR  envW = NULL;
2094     BOOL    ret;
2095     INT     len;
2096 
2097     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_a(server), 
2098           debugstr_a(env), level, Info, cbBuf, pcbNeeded);
2099  
2100 
2101     if (server) {
2102         len = MultiByteToWideChar(CP_ACP, 0, server, -1, NULL, 0);
2103         serverW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2104         MultiByteToWideChar(CP_ACP, 0, server, -1, serverW, len);
2105     }
2106 
2107     if (env) {
2108         len = MultiByteToWideChar(CP_ACP, 0, env, -1, NULL, 0);
2109         envW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2110         MultiByteToWideChar(CP_ACP, 0, env, -1, envW, len);
2111     }
2112 
2113     /* NT requires the buffersize from GetPrintProcessorDirectoryW also
2114        for GetPrintProcessorDirectoryA and WC2MB is done in-place.
2115      */
2116     ret = GetPrintProcessorDirectoryW(serverW, envW, level, Info, 
2117                                       cbBuf, pcbNeeded);
2118 
2119     if (ret) ret = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)Info, -1, (LPSTR)Info,
2120                                        cbBuf, NULL, NULL) > 0;
2121 
2122 
2123     TRACE(" required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
2124     HeapFree(GetProcessHeap(), 0, envW);
2125     HeapFree(GetProcessHeap(), 0, serverW);
2126     return ret;
2127 }
2128 
2129 /*****************************************************************************
2130  *          GetPrintProcessorDirectoryW  [WINSPOOL.@]
2131  *
2132  * Return the PATH for the Print-Processors
2133  *
2134  * PARAMS
2135  *   server     [I] Servername (NT only) or NULL (local Computer)
2136  *   env        [I] Printing-Environment (see below) or NULL (Default)
2137  *   level      [I] Structure-Level (must be 1)
2138  *   Info       [O] PTR to Buffer that receives the Result
2139  *   cbBuf      [I] Size of Buffer at "Info"
2140  *   pcbNeeded  [O] PTR to DWORD that receives the size in Bytes used / 
2141  *                  required for the Buffer at "Info"
2142  *
2143  * RETURNS
2144  *   Success: TRUE  and in pcbNeeded the Bytes used in Info
2145  *   Failure: FALSE and in pcbNeeded the Bytes required for Info,
2146  *   if cbBuf is too small
2147  * 
2148  *   Native Values returned in Info on Success:
2149  *|  NT(Windows NT x86):  "%winsysdir%\\spool\\PRTPROCS\\w32x86" 
2150  *|  NT(Windows 4.0):     "%winsysdir%\\spool\\PRTPROCS\\win40" 
2151  *|  win9x(Windows 4.0):  "%winsysdir%" 
2152  *
2153  *   "%winsysdir%" is the Value from GetSystemDirectoryW()
2154  *
2155  * BUGS
2156  *  Only NULL or "" is supported for server
2157  *
2158  */
2159 BOOL WINAPI GetPrintProcessorDirectoryW(LPWSTR server, LPWSTR env,
2160                                         DWORD level,  LPBYTE Info,
2161                                         DWORD cbBuf,  LPDWORD pcbNeeded)
2162 {
2163 
2164     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(server), debugstr_w(env), level,
2165                                         Info, cbBuf, pcbNeeded);
2166 
2167     if ((backend == NULL)  && !load_backend()) return FALSE;
2168 
2169     if (level != 1) {
2170         /* (Level != 1) is ignored in win9x */
2171         SetLastError(ERROR_INVALID_LEVEL);
2172         return FALSE;
2173     }
2174 
2175     if (pcbNeeded == NULL) {
2176         /* (pcbNeeded == NULL) is ignored in win9x */
2177         SetLastError(RPC_X_NULL_REF_POINTER);
2178         return FALSE;
2179     }
2180 
2181     return backend->fpGetPrintProcessorDirectory(server, env, level, Info, cbBuf, pcbNeeded);
2182 }
2183 
2184 /*****************************************************************************
2185  *          WINSPOOL_OpenDriverReg [internal]
2186  *
2187  * opens the registry for the printer drivers depending on the given input
2188  * variable pEnvironment
2189  *
2190  * RETURNS:
2191  *    the opened hkey on success
2192  *    NULL on error
2193  */
2194 static HKEY WINSPOOL_OpenDriverReg( LPCVOID pEnvironment, BOOL unicode)
2195 {   
2196     HKEY  retval = NULL;
2197     LPWSTR buffer;
2198     const printenv_t * env;
2199 
2200     TRACE("(%s, %d)\n",
2201           (unicode) ? debugstr_w(pEnvironment) : debugstr_a(pEnvironment), unicode);
2202 
2203     if (!pEnvironment || unicode) {
2204         /* pEnvironment was NULL or a Unicode-String: use it direct */
2205         env = validate_envW(pEnvironment);
2206     }
2207     else
2208     {
2209         /* pEnvironment was an ANSI-String: convert to unicode first */
2210         LPWSTR  buffer;
2211         INT len = MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, NULL, 0);
2212         buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2213         if (buffer) MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, buffer, len);
2214         env = validate_envW(buffer);
2215         HeapFree(GetProcessHeap(), 0, buffer);
2216     }
2217     if (!env) return NULL;
2218 
2219     buffer = HeapAlloc( GetProcessHeap(), 0,
2220                 (strlenW(DriversW) + strlenW(env->envname) + 
2221                  strlenW(env->versionregpath) + 1) * sizeof(WCHAR));
2222     if(buffer) {
2223         wsprintfW(buffer, DriversW, env->envname, env->versionregpath);
2224         RegCreateKeyW(HKEY_LOCAL_MACHINE, buffer, &retval);
2225         HeapFree(GetProcessHeap(), 0, buffer);
2226     }
2227     return retval;
2228 }
2229 
2230 /*****************************************************************************
2231  *          AddPrinterW  [WINSPOOL.@]
2232  */
2233 HANDLE WINAPI AddPrinterW(LPWSTR pName, DWORD Level, LPBYTE pPrinter)
2234 {
2235     PRINTER_INFO_2W *pi = (PRINTER_INFO_2W *) pPrinter;
2236     LPDEVMODEA dmA;
2237     LPDEVMODEW dmW;
2238     HANDLE retval;
2239     HKEY hkeyPrinter, hkeyPrinters, hkeyDriver, hkeyDrivers;
2240     LONG size;
2241     static const WCHAR attributesW[]      = {'A','t','t','r','i','b','u','t','e','s',0},
2242                        default_devmodeW[] = {'D','e','f','a','u','l','t',' ','D','e','v','M','o','d','e',0},
2243                        priorityW[]        = {'P','r','i','o','r','i','t','y',0},
2244                        start_timeW[]      = {'S','t','a','r','t','T','i','m','e',0},
2245                        statusW[]          = {'S','t','a','t','u','s',0},
2246                        until_timeW[]      = {'U','n','t','i','l','T','i','m','e',0};
2247 
2248     TRACE("(%s,%d,%p)\n", debugstr_w(pName), Level, pPrinter);
2249 
2250     if(pName != NULL) {
2251         ERR("pName = %s - unsupported\n", debugstr_w(pName));
2252         SetLastError(ERROR_INVALID_PARAMETER);
2253         return 0;
2254     }
2255     if(Level != 2) {
2256         ERR("Level = %d, unsupported!\n", Level);
2257         SetLastError(ERROR_INVALID_LEVEL);
2258         return 0;
2259     }
2260     if(!pPrinter) {
2261         SetLastError(ERROR_INVALID_PARAMETER);
2262         return 0;
2263     }
2264     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
2265        ERROR_SUCCESS) {
2266         ERR("Can't create Printers key\n");
2267         return 0;
2268     }
2269     if(!RegOpenKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter)) {
2270         if (!RegQueryValueW(hkeyPrinter, attributesW, NULL, NULL)) {
2271             SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
2272             RegCloseKey(hkeyPrinter);
2273             RegCloseKey(hkeyPrinters);
2274             return 0;
2275         }
2276         RegCloseKey(hkeyPrinter);
2277     }
2278     hkeyDrivers = WINSPOOL_OpenDriverReg( NULL, TRUE);
2279     if(!hkeyDrivers) {
2280         ERR("Can't create Drivers key\n");
2281         RegCloseKey(hkeyPrinters);
2282         return 0;
2283     }
2284     if(RegOpenKeyW(hkeyDrivers, pi->pDriverName, &hkeyDriver) !=
2285        ERROR_SUCCESS) {
2286         WARN("Can't find driver %s\n", debugstr_w(pi->pDriverName));
2287         RegCloseKey(hkeyPrinters);
2288         RegCloseKey(hkeyDrivers);
2289         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER);
2290         return 0;
2291     }
2292     RegCloseKey(hkeyDriver);
2293     RegCloseKey(hkeyDrivers);
2294 
2295     if(lstrcmpiW(pi->pPrintProcessor, WinPrintW)) {  /* FIXME */
2296         FIXME("Can't find processor %s\n", debugstr_w(pi->pPrintProcessor));
2297         SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR);
2298         RegCloseKey(hkeyPrinters);
2299         return 0;
2300     }
2301 
2302     if(RegCreateKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter) !=
2303        ERROR_SUCCESS) {
2304         FIXME("Can't create printer %s\n", debugstr_w(pi->pPrinterName));
2305         SetLastError(ERROR_INVALID_PRINTER_NAME);
2306         RegCloseKey(hkeyPrinters);
2307         return 0;
2308     }
2309     RegSetValueExW(hkeyPrinter, attributesW, 0, REG_DWORD,
2310                    (LPBYTE)&pi->Attributes, sizeof(DWORD));
2311     set_reg_szW(hkeyPrinter, DatatypeW, pi->pDatatype);
2312 
2313     /* See if we can load the driver.  We may need the devmode structure anyway
2314      *
2315      * FIXME:
2316      * Note that DocumentPropertiesW will briefly try to open the printer we
2317      * just create to find a DEVMODEA struct (it will use the WINEPS default
2318      * one in case it is not there, so we are ok).
2319      */
2320     size = DocumentPropertiesW(0, 0, pi->pPrinterName, NULL, NULL, 0);
2321 
2322     if(size < 0) {
2323         FIXME("DocumentPropertiesW on printer %s fails\n", debugstr_w(pi->pPrinterName));
2324         size = sizeof(DEVMODEW);
2325     }
2326     if(pi->pDevMode)
2327         dmW = pi->pDevMode;
2328     else
2329     {
2330         dmW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
2331         dmW->dmSize = size;
2332         if (0>DocumentPropertiesW(0,0,pi->pPrinterName,dmW,NULL,DM_OUT_BUFFER))
2333         {
2334             WARN("DocumentPropertiesW on printer %s failed!\n", debugstr_w(pi->pPrinterName));
2335             HeapFree(GetProcessHeap(),0,dmW);
2336             dmW=NULL;
2337         }
2338         else
2339         {
2340             /* set devmode to printer name */
2341             lstrcpynW(dmW->dmDeviceName, pi->pPrinterName, CCHDEVICENAME);
2342         }
2343     }
2344 
2345     /* Write DEVMODEA not DEVMODEW into reg.  This is what win9x does
2346        and we support these drivers.  NT writes DEVMODEW so somehow
2347        we'll need to distinguish between these when we support NT
2348        drivers */
2349     if (dmW)
2350     {
2351         dmA = DEVMODEdupWtoA(dmW);
2352         RegSetValueExW(hkeyPrinter, default_devmodeW, 0, REG_BINARY,
2353                        (LPBYTE)dmA, dmA->dmSize + dmA->dmDriverExtra);
2354         HeapFree(GetProcessHeap(), 0, dmA);
2355         if(!pi->pDevMode)
2356             HeapFree(GetProcessHeap(), 0, dmW);
2357     }
2358     set_reg_szW(hkeyPrinter, DescriptionW, pi->pComment);
2359     set_reg_szW(hkeyPrinter, LocationW, pi->pLocation);
2360     set_reg_szW(hkeyPrinter, NameW, pi->pPrinterName);
2361     set_reg_szW(hkeyPrinter, ParametersW, pi->pParameters);
2362 
2363     set_reg_szW(hkeyPrinter, PortW, pi->pPortName);
2364     set_reg_szW(hkeyPrinter, Print_ProcessorW, pi->pPrintProcessor);
2365     set_reg_szW(hkeyPrinter, Printer_DriverW, pi->pDriverName);
2366     RegSetValueExW(hkeyPrinter, priorityW, 0, REG_DWORD,
2367                    (LPBYTE)&pi->Priority, sizeof(DWORD));
2368     set_reg_szW(hkeyPrinter, Separator_FileW, pi->pSepFile);
2369     set_reg_szW(hkeyPrinter, Share_NameW, pi->pShareName);
2370     RegSetValueExW(hkeyPrinter, start_timeW, 0, REG_DWORD,
2371                    (LPBYTE)&pi->StartTime, sizeof(DWORD));
2372     RegSetValueExW(hkeyPrinter, statusW, 0, REG_DWORD,
2373                    (LPBYTE)&pi->Status, sizeof(DWORD));
2374     RegSetValueExW(hkeyPrinter, until_timeW, 0, REG_DWORD,
2375                    (LPBYTE)&pi->UntilTime, sizeof(DWORD));
2376 
2377     RegCloseKey(hkeyPrinter);
2378     RegCloseKey(hkeyPrinters);
2379     if(!OpenPrinterW(pi->pPrinterName, &retval, NULL)) {
2380         ERR("OpenPrinter failing\n");
2381         return 0;
2382     }
2383     return retval;
2384 }
2385 
2386 /*****************************************************************************
2387  *          AddPrinterA  [WINSPOOL.@]
2388  */
2389 HANDLE WINAPI AddPrinterA(LPSTR pName, DWORD Level, LPBYTE pPrinter)
2390 {
2391     UNICODE_STRING pNameW;
2392     PWSTR pwstrNameW;
2393     PRINTER_INFO_2W *piW;
2394     PRINTER_INFO_2A *piA = (PRINTER_INFO_2A*)pPrinter;
2395     HANDLE ret;
2396 
2397     TRACE("(%s,%d,%p): stub\n", debugstr_a(pName), Level, pPrinter);
2398     if(Level != 2) {
2399         ERR("Level = %d, unsupported!\n", Level);
2400         SetLastError(ERROR_INVALID_LEVEL);
2401         return 0;
2402     }
2403     pwstrNameW = asciitounicode(&pNameW,pName);
2404     piW = PRINTER_INFO_2AtoW(GetProcessHeap(), piA);
2405 
2406     ret = AddPrinterW(pwstrNameW, Level, (LPBYTE)piW);
2407 
2408     FREE_PRINTER_INFO_2W(GetProcessHeap(), piW);
2409     RtlFreeUnicodeString(&pNameW);
2410     return ret;
2411 }
2412 
2413 
2414 /*****************************************************************************
2415  *          ClosePrinter  [WINSPOOL.@]
2416  */
2417 BOOL WINAPI ClosePrinter(HANDLE hPrinter)
2418 {
2419     UINT_PTR i = (UINT_PTR)hPrinter;
2420     opened_printer_t *printer = NULL;
2421     BOOL ret = FALSE;
2422 
2423     TRACE("(%p)\n", hPrinter);
2424 
2425     EnterCriticalSection(&printer_handles_cs);
2426 
2427     if ((i > 0) && (i <= nb_printer_handles))
2428         printer = printer_handles[i - 1];
2429 
2430 
2431     if(printer)
2432     {
2433         struct list *cursor, *cursor2;
2434 
2435         TRACE("closing %s (doc: %p)\n", debugstr_w(printer->name), printer->doc);
2436 
2437         if (printer->backend_printer) {
2438             backend->fpClosePrinter(printer->backend_printer);
2439         }
2440 
2441         if(printer->doc)
2442             EndDocPrinter(hPrinter);
2443 
2444         if(InterlockedDecrement(&printer->queue->ref) == 0)
2445         {
2446             LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
2447             {
2448                 job_t *job = LIST_ENTRY(cursor, job_t, entry);
2449                 ScheduleJob(hPrinter, job->job_id);
2450             }
2451             HeapFree(GetProcessHeap(), 0, printer->queue);
2452         }
2453 
2454         HeapFree(GetProcessHeap(), 0, printer->printername);
2455         HeapFree(GetProcessHeap(), 0, printer->name);
2456         HeapFree(GetProcessHeap(), 0, printer);
2457         printer_handles[i - 1] = NULL;
2458         ret = TRUE;
2459     }
2460     LeaveCriticalSection(&printer_handles_cs);
2461     return ret;
2462 }
2463 
2464 /*****************************************************************************
2465  *          DeleteFormA  [WINSPOOL.@]
2466  */
2467 BOOL WINAPI DeleteFormA(HANDLE hPrinter, LPSTR pFormName)
2468 {
2469     FIXME("(%p,%s): stub\n", hPrinter, pFormName);
2470     return 1;
2471 }
2472 
2473 /*****************************************************************************
2474  *          DeleteFormW  [WINSPOOL.@]
2475  */
2476 BOOL WINAPI DeleteFormW(HANDLE hPrinter, LPWSTR pFormName)
2477 {
2478     FIXME("(%p,%s): stub\n", hPrinter, debugstr_w(pFormName));
2479     return 1;
2480 }
2481 
2482 /*****************************************************************************
2483  *          DeletePrinter  [WINSPOOL.@]
2484  */
2485 BOOL WINAPI DeletePrinter(HANDLE hPrinter)
2486 {
2487     LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
2488     HKEY hkeyPrinters, hkey;
2489 
2490     if(!lpNameW) {
2491         SetLastError(ERROR_INVALID_HANDLE);
2492         return FALSE;
2493     }
2494     if(RegOpenKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
2495         RegDeleteTreeW(hkeyPrinters, lpNameW);
2496         RegCloseKey(hkeyPrinters);
2497     }
2498     WriteProfileStringW(devicesW, lpNameW, NULL);
2499     WriteProfileStringW(PrinterPortsW, lpNameW, NULL);
2500 
2501     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
2502         RegDeleteValueW(hkey, lpNameW);
2503         RegCloseKey(hkey);
2504     }
2505 
2506     if(RegCreateKeyW(HKEY_CURRENT_USER, WinNT_CV_PrinterPortsW, &hkey) == ERROR_SUCCESS) {
2507         RegDeleteValueW(hkey, lpNameW);
2508         RegCloseKey(hkey);
2509     }
2510     return TRUE;
2511 }
2512 
2513 /*****************************************************************************
2514  *          SetPrinterA  [WINSPOOL.@]
2515  */
2516 BOOL WINAPI SetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
2517                            DWORD Command)
2518 {
2519     FIXME("(%p,%d,%p,%d): stub\n",hPrinter,Level,pPrinter,Command);
2520     return FALSE;
2521 }
2522 
2523 /*****************************************************************************
2524  *          SetJobA  [WINSPOOL.@]
2525  */
2526 BOOL WINAPI SetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level,
2527                     LPBYTE pJob, DWORD Command)
2528 {
2529     BOOL ret;
2530     LPBYTE JobW;
2531     UNICODE_STRING usBuffer;
2532 
2533     TRACE("(%p, %d, %d, %p, %d)\n",hPrinter, JobId, Level, pJob, Command);
2534 
2535     /* JobId, pPrinterName, pMachineName, pDriverName, Size, Submitted, Time and TotalPages
2536        are all ignored by SetJob, so we don't bother copying them */
2537     switch(Level)
2538     {
2539     case 0:
2540         JobW = NULL;
2541         break;
2542     case 1:
2543       {
2544         JOB_INFO_1W *info1W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info1W));
2545         JOB_INFO_1A *info1A = (JOB_INFO_1A*)pJob;
2546 
2547         JobW = (LPBYTE)info1W;
2548         info1W->pUserName = asciitounicode(&usBuffer, info1A->pUserName);
2549         info1W->pDocument = asciitounicode(&usBuffer, info1A->pDocument);
2550         info1W->pDatatype = asciitounicode(&usBuffer, info1A->pDatatype);
2551         info1W->pStatus = asciitounicode(&usBuffer, info1A->pStatus);
2552         info1W->Status = info1A->Status;
2553         info1W->Priority = info1A->Priority;
2554         info1W->Position = info1A->Position;
2555         info1W->PagesPrinted = info1A->PagesPrinted;
2556         break;
2557       }
2558     case 2:
2559       {
2560         JOB_INFO_2W *info2W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info2W));
2561         JOB_INFO_2A *info2A = (JOB_INFO_2A*)pJob;
2562 
2563         JobW = (LPBYTE)info2W;
2564         info2W->pUserName = asciitounicode(&usBuffer, info2A->pUserName);
2565         info2W->pDocument = asciitounicode(&usBuffer, info2A->pDocument);
2566         info2W->pNotifyName = asciitounicode(&usBuffer, info2A->pNotifyName);
2567         info2W->pDatatype = asciitounicode(&usBuffer, info2A->pDatatype);
2568         info2W->pPrintProcessor = asciitounicode(&usBuffer, info2A->pPrintProcessor);
2569         info2W->pParameters = asciitounicode(&usBuffer, info2A->pParameters);
2570         info2W->pDevMode = info2A->pDevMode ? GdiConvertToDevmodeW(info2A->pDevMode) : NULL;
2571         info2W->pStatus = asciitounicode(&usBuffer, info2A->pStatus);
2572         info2W->pSecurityDescriptor = info2A->pSecurityDescriptor;
2573         info2W->Status = info2A->Status;
2574         info2W->Priority = info2A->Priority;
2575         info2W->Position = info2A->Position;
2576         info2W->StartTime = info2A->StartTime;
2577         info2W->UntilTime = info2A->UntilTime;
2578         info2W->PagesPrinted = info2A->PagesPrinted;
2579         break;
2580       }
2581     case 3:
2582         JobW = HeapAlloc(GetProcessHeap(), 0, sizeof(JOB_INFO_3));
2583         memcpy(JobW, pJob, sizeof(JOB_INFO_3));
2584         break;
2585     default:
2586         SetLastError(ERROR_INVALID_LEVEL);
2587         return FALSE;
2588     }
2589 
2590     ret = SetJobW(hPrinter, JobId, Level, JobW, Command);
2591 
2592     switch(Level)
2593     {
2594     case 1:
2595       {
2596         JOB_INFO_1W *info1W = (JOB_INFO_1W*)JobW;
2597         HeapFree(GetProcessHeap(), 0, info1W->pUserName);
2598         HeapFree(GetProcessHeap(), 0, info1W->pDocument); 
2599         HeapFree(GetProcessHeap(), 0, info1W->pDatatype);
2600         HeapFree(GetProcessHeap(), 0, info1W->pStatus);
2601         break;
2602       }
2603     case 2:
2604       {
2605         JOB_INFO_2W *info2W = (JOB_INFO_2W*)JobW;
2606         HeapFree(GetProcessHeap(), 0, info2W->pUserName);
2607         HeapFree(GetProcessHeap(), 0, info2W->pDocument); 
2608         HeapFree(GetProcessHeap(), 0, info2W->pNotifyName);
2609         HeapFree(GetProcessHeap(), 0, info2W->pDatatype);
2610         HeapFree(GetProcessHeap(), 0, info2W->pPrintProcessor);
2611         HeapFree(GetProcessHeap(), 0, info2W->pParameters);
2612         HeapFree(GetProcessHeap(), 0, info2W->pDevMode);
2613         HeapFree(GetProcessHeap(), 0, info2W->pStatus);
2614         break;
2615       }
2616     }
2617     HeapFree(GetProcessHeap(), 0, JobW);
2618 
2619     return ret;
2620 }
2621 
2622 /*****************************************************************************
2623  *          SetJobW  [WINSPOOL.@]
2624  */
2625 BOOL WINAPI SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level,
2626                     LPBYTE pJob, DWORD Command)
2627 {
2628     BOOL ret = FALSE;
2629     job_t *job;
2630 
2631     TRACE("(%p, %d, %d, %p, %d)\n", hPrinter, JobId, Level, pJob, Command);
2632     FIXME("Ignoring everything other than document title\n");
2633 
2634     EnterCriticalSection(&printer_handles_cs);
2635     job = get_job(hPrinter, JobId);
2636     if(!job)
2637         goto end;
2638 
2639     switch(Level)
2640     {
2641     case 0:
2642         break;
2643     case 1:
2644       {
2645         JOB_INFO_1W *info1 = (JOB_INFO_1W*)pJob;
2646         HeapFree(GetProcessHeap(), 0, job->document_title);
2647         job->document_title = strdupW(info1->pDocument);
2648         break;
2649       }
2650     case 2:
2651       {
2652         JOB_INFO_2W *info2 = (JOB_INFO_2W*)pJob;
2653         HeapFree(GetProcessHeap(), 0, job->document_title);
2654         job->document_title = strdupW(info2->pDocument);
2655         break;
2656       }
2657     case 3:
2658         break;
2659     default:
2660         SetLastError(ERROR_INVALID_LEVEL);
2661         goto end;
2662     }
2663     ret = TRUE;
2664 end:
2665     LeaveCriticalSection(&printer_handles_cs);
2666     return ret;
2667 }
2668 
2669 /*****************************************************************************
2670  *          EndDocPrinter  [WINSPOOL.@]
2671  */
2672 BOOL WINAPI EndDocPrinter(HANDLE hPrinter)
2673 {
2674     opened_printer_t *printer;
2675     BOOL ret = FALSE;
2676     TRACE("(%p)\n", hPrinter);
2677 
2678     EnterCriticalSection(&printer_handles_cs);
2679 
2680     printer = get_opened_printer(hPrinter);
2681     if(!printer)
2682     {
2683         SetLastError(ERROR_INVALID_HANDLE);
2684         goto end;
2685     }
2686 
2687     if(!printer->doc)    
2688     {
2689         SetLastError(ERROR_SPL_NO_STARTDOC);
2690         goto end;
2691     }
2692 
2693     CloseHandle(printer->doc->hf);
2694     ScheduleJob(hPrinter, printer->doc->job_id);
2695     HeapFree(GetProcessHeap(), 0, printer->doc);
2696     printer->doc = NULL;
2697     ret = TRUE;
2698 end:
2699     LeaveCriticalSection(&printer_handles_cs);
2700     return ret;
2701 }
2702 
2703 /*****************************************************************************
2704  *          EndPagePrinter  [WINSPOOL.@]
2705  */
2706 BOOL WINAPI EndPagePrinter(HANDLE hPrinter)
2707 {
2708     FIXME("(%p): stub\n", hPrinter);
2709     return TRUE;
2710 }
2711 
2712 /*****************************************************************************
2713  *          StartDocPrinterA  [WINSPOOL.@]
2714  */
2715 DWORD WINAPI StartDocPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
2716 {
2717     UNICODE_STRING usBuffer;
2718     DOC_INFO_2W doc2W;
2719     DOC_INFO_2A *doc2 = (DOC_INFO_2A*)pDocInfo;
2720     DWORD ret;
2721 
2722     /* DOC_INFO_1, 2 and 3 all have the strings in the same place with either two (DOC_INFO_2)
2723        or one (DOC_INFO_3) extra DWORDs */
2724 
2725     switch(Level) {
2726     case 2:
2727         doc2W.JobId = doc2->JobId;
2728         /* fall through */
2729     case 3:
2730         doc2W.dwMode = doc2->dwMode;
2731         /* fall through */
2732     case 1:
2733         doc2W.pDocName = asciitounicode(&usBuffer, doc2->pDocName);
2734         doc2W.pOutputFile = asciitounicode(&usBuffer, doc2->pOutputFile);
2735         doc2W.pDatatype = asciitounicode(&usBuffer, doc2->pDatatype);
2736         break;
2737 
2738     default:
2739         SetLastError(ERROR_INVALID_LEVEL);
2740         return FALSE;
2741     }
2742 
2743     ret = StartDocPrinterW(hPrinter, Level, (LPBYTE)&doc2W);
2744 
2745     HeapFree(GetProcessHeap(), 0, doc2W.pDatatype);
2746     HeapFree(GetProcessHeap(), 0, doc2W.pOutputFile);
2747     HeapFree(GetProcessHeap(), 0, doc2W.pDocName);
2748 
2749     return ret;
2750 }
2751 
2752 /*****************************************************************************
2753  *          StartDocPrinterW  [WINSPOOL.@]
2754  */
2755 DWORD WINAPI StartDocPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
2756 {
2757     DOC_INFO_2W *doc = (DOC_INFO_2W *)pDocInfo;
2758     opened_printer_t *printer;
2759     BYTE addjob_buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
2760     ADDJOB_INFO_1W *addjob = (ADDJOB_INFO_1W*) addjob_buf;
2761     JOB_INFO_1W job_info;
2762     DWORD needed, ret = 0;
2763     HANDLE hf;
2764     WCHAR *filename;
2765 
2766     TRACE("(hPrinter = %p, Level = %d, pDocInfo = %p {pDocName = %s, pOutputFile = %s, pDatatype = %s}):\n",
2767           hPrinter, Level, doc, debugstr_w(doc->pDocName), debugstr_w(doc->pOutputFile),
2768           debugstr_w(doc->pDatatype));
2769 
2770     if(Level < 1 || Level > 3)
2771     {
2772         SetLastError(ERROR_INVALID_LEVEL);
2773         return 0;
2774     }
2775 
2776     EnterCriticalSection(&printer_handles_cs);
2777     printer = get_opened_printer(hPrinter);
2778     if(!printer)
2779     {
2780         SetLastError(ERROR_INVALID_HANDLE);
2781         goto end;
2782     }
2783 
2784     if(printer->doc)
2785     {
2786         SetLastError(ERROR_INVALID_PRINTER_STATE);
2787         goto end;
2788     }
2789 
2790     /* Even if we're printing to a file we still add a print job, we'll
2791        just ignore the spool file name */
2792 
2793     if(!AddJobW(hPrinter, 1, addjob_buf, sizeof(addjob_buf), &needed))
2794     {
2795         ERR("AddJob failed gle %u\n", GetLastError());
2796         goto end;
2797     }
2798 
2799     if(doc->pOutputFile)
2800         filename = doc->pOutputFile;
2801     else
2802         filename = addjob->Path;
2803 
2804     hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2805     if(hf == INVALID_HANDLE_VALUE)
2806         goto end;
2807 
2808     memset(&job_info, 0, sizeof(job_info));
2809     job_info.pDocument = doc->pDocName;
2810     SetJobW(hPrinter, addjob->JobId, 1, (LPBYTE)&job_info, 0);
2811 
2812     printer->doc = HeapAlloc(GetProcessHeap(), 0, sizeof(*printer->doc));
2813     printer->doc->hf = hf;
2814     ret = printer->doc->job_id = addjob->JobId;
2815 end:
2816     LeaveCriticalSection(&printer_handles_cs);
2817 
2818     return ret;
2819 }
2820 
2821 /*****************************************************************************
2822  *          StartPagePrinter  [WINSPOOL.@]
2823  */
2824 BOOL WINAPI StartPagePrinter(HANDLE hPrinter)
2825 {
2826     FIXME("(%p): stub\n", hPrinter);
2827     return TRUE;
2828 }
2829 
2830 /*****************************************************************************
2831  *          GetFormA  [WINSPOOL.@]
2832  */
2833 BOOL WINAPI GetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
2834                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
2835 {
2836     FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,pFormName,
2837          Level,pForm,cbBuf,pcbNeeded);
2838     return FALSE;
2839 }
2840 
2841 /*****************************************************************************
2842  *          GetFormW  [WINSPOOL.@]
2843  */
2844 BOOL WINAPI GetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
2845                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
2846 {
2847     FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,
2848           debugstr_w(pFormName),Level,pForm,cbBuf,pcbNeeded);
2849     return FALSE;
2850 }
2851 
2852 /*****************************************************************************
2853  *          SetFormA  [WINSPOOL.@]
2854  */
2855 BOOL WINAPI SetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
2856                         LPBYTE pForm)
2857 {
2858     FIXME("(%p,%s,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
2859     return FALSE;
2860 }
2861 
2862 /*****************************************************************************
2863  *          SetFormW  [WINSPOOL.@]
2864  */
2865 BOOL WINAPI SetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
2866                         LPBYTE pForm)
2867 {
2868     FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
2869     return FALSE;
2870 }
2871 
2872 /*****************************************************************************
2873  *          ReadPrinter  [WINSPOOL.@]
2874  */
2875 BOOL WINAPI ReadPrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf,
2876                            LPDWORD pNoBytesRead)
2877 {
2878     FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pBuf,cbBuf,pNoBytesRead);
2879     return FALSE;
2880 }
2881 
2882 /*****************************************************************************
2883  *          ResetPrinterA  [WINSPOOL.@]
2884  */
2885 BOOL WINAPI ResetPrinterA(HANDLE hPrinter, LPPRINTER_DEFAULTSA pDefault)
2886 {
2887     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
2888     return FALSE;
2889 }
2890 
2891 /*****************************************************************************
2892  *          ResetPrinterW  [WINSPOOL.@]
2893  */
2894 BOOL WINAPI ResetPrinterW(HANDLE hPrinter, LPPRINTER_DEFAULTSW pDefault)
2895 {
2896     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
2897     return FALSE;
2898 }
2899 
2900 /*****************************************************************************
2901  *    WINSPOOL_GetDWORDFromReg
2902  *
2903  * Return DWORD associated with ValueName from hkey.
2904  */
2905 static DWORD WINSPOOL_GetDWORDFromReg(HKEY hkey, LPCSTR ValueName)
2906 {
2907     DWORD sz = sizeof(DWORD), type, value = 0;
2908     LONG ret;
2909 
2910     ret = RegQueryValueExA(hkey, ValueName, 0, &type, (LPBYTE)&value, &sz);
2911 
2912     if(ret != ERROR_SUCCESS) {
2913         WARN("Got ret = %d on name %s\n", ret, ValueName);
2914         return 0;
2915     }
2916     if(type != REG_DWORD) {
2917         ERR("Got type %d\n", type);
2918         return 0;
2919     }
2920     return value;
2921 }
2922 
2923 
2924 /*****************************************************************************
2925  * get_filename_from_reg [internal]
2926  *
2927  * Get ValueName from hkey storing result in out
2928  * when the Value in the registry has only a filename, use driverdir as prefix
2929  * outlen is space left in out
2930  * String is stored either as unicode or ascii
2931  *
2932  */
2933 
2934 static BOOL get_filename_from_reg(HKEY hkey, LPCWSTR driverdir, DWORD dirlen, LPCWSTR ValueName,
2935                                   LPBYTE out, DWORD outlen, LPDWORD needed, BOOL unicode)
2936 {
2937     WCHAR   filename[MAX_PATH];
2938     DWORD   size;
2939     DWORD   type;
2940     LONG    ret;
2941     LPWSTR  buffer = filename;
2942     LPWSTR  ptr;
2943 
2944     *needed = 0;
2945     size = sizeof(filename);
2946     buffer[0] = '\0';
2947     ret = RegQueryValueExW(hkey, ValueName, NULL, &type, (LPBYTE) buffer, &size);
2948     if (ret == ERROR_MORE_DATA) {
2949         TRACE("need dynamic buffer: %u\n", size);
2950         buffer = HeapAlloc(GetProcessHeap(), 0, size);
2951         if (!buffer) {
2952             /* No Memory is bad */
2953             return FALSE;
2954         }
2955         buffer[0] = '\0';
2956         ret = RegQueryValueExW(hkey, ValueName, NULL, &type, (LPBYTE) buffer, &size);
2957     }
2958 
2959     if ((ret != ERROR_SUCCESS) || (!buffer[0])) {
2960         if (buffer != filename) HeapFree(GetProcessHeap(), 0, buffer);
2961         return FALSE;
2962     }
2963 
2964     ptr = buffer;
2965     while (ptr) {
2966         /* do we have a full path ? */
2967         ret = (((buffer[0] == '\\') && (buffer[1] == '\\')) ||
2968                 (buffer[0] && (buffer[1] == ':') && (buffer[2] == '\\')) );
2969 
2970         if (!ret) {
2971             /* we must build the full Path */
2972             *needed += dirlen;
2973             if ((out) && (outlen > dirlen)) {
2974                 if (unicode) {
2975                     lstrcpyW((LPWSTR)out, driverdir);
2976                 }
2977                 else
2978                 {
2979                     WideCharToMultiByte(CP_ACP, 0, driverdir, -1, (LPSTR)out, outlen, NULL, NULL);
2980                 }
2981                 out += dirlen;
2982                 outlen -= dirlen;
2983             }
2984             else
2985                 out = NULL;
2986         }
2987 
2988         /* write the filename */
2989         if (unicode) {
2990             size = (lstrlenW(ptr) + 1) * sizeof(WCHAR);
2991             if ((out) && (outlen >= size)) {
2992                 lstrcpyW((LPWSTR)out, ptr);
2993                 out += size;
2994                 outlen -= size;
2995             }
2996             else
2997                 out = NULL;
2998         }
2999         else
3000         {
3001             size = WideCharToMultiByte(CP_ACP, 0, ptr, -1, NULL, 0, NULL, NULL);
3002             if ((out) && (outlen >= size)) {
3003                 WideCharToMultiByte(CP_ACP, 0, ptr, -1, (LPSTR)out, outlen, NULL, NULL);
3004                 out += size;
3005                 outlen -= size;
3006             }
3007             else
3008                 out = NULL;
3009         }
3010         *needed += size;
3011         ptr +=  lstrlenW(ptr)+1;
3012         if ((type != REG_MULTI_SZ) || (!ptr[0]))  ptr = NULL;
3013     }
3014 
3015     if (buffer != filename) HeapFree(GetProcessHeap(), 0, buffer);
3016 
3017     /* write the multisz-termination */
3018     if (type == REG_MULTI_SZ) {
3019         size = (unicode) ? sizeof(WCHAR) : 1;
3020 
3021         *needed += size;
3022         if (out && (outlen >= size)) {
3023             memset (out, 0, size);
3024         }
3025     }
3026     return TRUE;
3027 }
3028 
3029 /*****************************************************************************
3030  *    WINSPOOL_GetStringFromReg
3031  *
3032  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
3033  * String is stored either as unicode or ascii.
3034  * Bit of a hack here to get the ValueName if we want ascii.
3035  */
3036 static BOOL WINSPOOL_GetStringFromReg(HKEY hkey, LPCWSTR ValueName, LPBYTE ptr,
3037                                       DWORD buflen, DWORD *needed,
3038                                       BOOL unicode)
3039 {
3040     DWORD sz = buflen, type;
3041     LONG ret;
3042 
3043     if(unicode)
3044         ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3045     else {
3046         LPSTR ValueNameA = strdupWtoA(ValueName);
3047         ret = RegQueryValueExA(hkey, ValueNameA, 0, &type, ptr, &sz);
3048         HeapFree(GetProcessHeap(),0,ValueNameA);
3049     }
3050     if(ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) {
3051         WARN("Got ret = %d\n", ret);
3052         *needed = 0;
3053         return FALSE;
3054     }
3055     /* add space for terminating '\0' */
3056     sz += unicode ? sizeof(WCHAR) : 1;
3057     *needed = sz;
3058 
3059     if (ptr)
3060         TRACE("%s: %s\n", debugstr_w(ValueName), unicode ? debugstr_w((LPCWSTR)ptr) : debugstr_a((LPCSTR)ptr));
3061 
3062     return TRUE;
3063 }
3064 
3065 /*****************************************************************************
3066  *    WINSPOOL_GetDefaultDevMode
3067  *
3068  * Get a default DevMode values for wineps.
3069  * FIXME - use ppd.
3070  */
3071 
3072 static void WINSPOOL_GetDefaultDevMode(
3073         LPBYTE ptr,
3074         DWORD buflen, DWORD *needed,
3075         BOOL unicode)
3076 {
3077     DEVMODEA    dm;
3078     static const char szwps[] = "wineps.drv";
3079 
3080         /* fill default DEVMODE - should be read from ppd... */
3081         ZeroMemory( &dm, sizeof(dm) );
3082         memcpy(dm.dmDeviceName,szwps,sizeof szwps);
3083         dm.dmSpecVersion = DM_SPECVERSION;
3084         dm.dmDriverVersion = 1;
3085         dm.dmSize = sizeof(DEVMODEA);
3086         dm.dmDriverExtra = 0;
3087         dm.dmFields =
3088                 DM_ORIENTATION | DM_PAPERSIZE |
3089                 DM_PAPERLENGTH | DM_PAPERWIDTH |
3090                 DM_SCALE |
3091                 DM_COPIES |
3092                 DM_DEFAULTSOURCE | DM_PRINTQUALITY |
3093                 DM_YRESOLUTION | DM_TTOPTION;
3094 
3095         dm.u1.s1.dmOrientation = DMORIENT_PORTRAIT;
3096         dm.u1.s1.dmPaperSize = DMPAPER_A4;
3097         dm.u1.s1.dmPaperLength = 2970;
3098         dm.u1.s1.dmPaperWidth = 2100;
3099 
3100         dm.u1.s1.dmScale = 100;
3101         dm.u1.s1.dmCopies = 1;
3102         dm.u1.s1.dmDefaultSource = DMBIN_AUTO;
3103         dm.u1.s1.dmPrintQuality = DMRES_MEDIUM;
3104         /* dm.dmColor */
3105         /* dm.dmDuplex */
3106         dm.dmYResolution = 300; /* 300dpi */
3107         dm.dmTTOption = DMTT_BITMAP;
3108         /* dm.dmCollate */
3109         /* dm.dmFormName */
3110         /* dm.dmLogPixels */
3111         /* dm.dmBitsPerPel */
3112         /* dm.dmPelsWidth */
3113         /* dm.dmPelsHeight */
3114         /* dm.u2.dmDisplayFlags */
3115         /* dm.dmDisplayFrequency */
3116         /* dm.dmICMMethod */
3117         /* dm.dmICMIntent */
3118         /* dm.dmMediaType */
3119         /* dm.dmDitherType */
3120         /* dm.dmReserved1 */
3121         /* dm.dmReserved2 */
3122         /* dm.dmPanningWidth */
3123         /* dm.dmPanningHeight */
3124 
3125     if(unicode) {
3126         if(buflen >= sizeof(DEVMODEW)) {
3127             DEVMODEW *pdmW = GdiConvertToDevmodeW(&dm);
3128             memcpy(ptr, pdmW, sizeof(DEVMODEW));
3129             HeapFree(GetProcessHeap(),0,pdmW);
3130         }
3131         *needed = sizeof(DEVMODEW);
3132     }
3133     else
3134     {
3135         if(buflen >= sizeof(DEVMODEA)) {
3136             memcpy(ptr, &dm, sizeof(DEVMODEA));
3137         }
3138         *needed = sizeof(DEVMODEA);
3139     }
3140 }
3141 
3142 /*****************************************************************************
3143  *    WINSPOOL_GetDevModeFromReg
3144  *
3145  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
3146  * DevMode is stored either as unicode or ascii.
3147  */
3148 static BOOL WINSPOOL_GetDevModeFromReg(HKEY hkey, LPCWSTR ValueName,
3149                                        LPBYTE ptr,
3150                                        DWORD buflen, DWORD *needed,
3151                                        BOOL unicode)
3152 {
3153     DWORD sz = buflen, type;
3154     LONG ret;
3155 
3156     if (ptr && buflen>=sizeof(DEVMODEA)) memset(ptr, 0, sizeof(DEVMODEA));
3157     ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3158     if ((ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA)) sz = 0;
3159     if (sz < sizeof(DEVMODEA))
3160     {
3161         TRACE("corrupted registry for %s ( size %d)\n",debugstr_w(ValueName),sz);
3162         return FALSE;
3163     }
3164     /* ensures that dmSize is not erratically bogus if registry is invalid */
3165     if (ptr && ((DEVMODEA*)ptr)->dmSize < sizeof(DEVMODEA))
3166         ((DEVMODEA*)ptr)->dmSize = sizeof(DEVMODEA);
3167     if(unicode) {
3168         sz += (CCHDEVICENAME + CCHFORMNAME);
3169         if(buflen >= sz) {
3170             DEVMODEW *dmW = GdiConvertToDevmodeW((DEVMODEA*)ptr);
3171             memcpy(ptr, dmW, sz);
3172             HeapFree(GetProcessHeap(),0,dmW);
3173         }
3174     }
3175     *needed = sz;
3176     return TRUE;
3177 }
3178 
3179 /*********************************************************************
3180  *    WINSPOOL_GetPrinter_1
3181  *
3182  * Fills out a PRINTER_INFO_1A|W struct storing the strings in buf.
3183  * The strings are either stored as unicode or ascii.
3184  */
3185 static BOOL WINSPOOL_GetPrinter_1(HKEY hkeyPrinter, PRINTER_INFO_1W *pi1,
3186                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3187                                   BOOL unicode)
3188 {
3189     DWORD size, left = cbBuf;
3190     BOOL space = (cbBuf > 0);
3191     LPBYTE ptr = buf;
3192 
3193     *pcbNeeded = 0;
3194 
3195     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3196                                  unicode)) {
3197         if(space && size <= left) {
3198             pi1->pName = (LPWSTR)ptr;
3199             ptr += size;
3200             left -= size;
3201         } else
3202             space = FALSE;
3203         *pcbNeeded += size;
3204     }
3205 
3206     /* FIXME: pDescription should be something like "Name,Driver_Name,". */
3207     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3208                                  unicode)) {
3209         if(space && size <= left) {
3210             pi1->pDescription = (LPWSTR)ptr;
3211             ptr += size;
3212             left -= size;
3213         } else
3214             space = FALSE;
3215         *pcbNeeded += size;
3216     }
3217 
3218     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size,
3219                                  unicode)) {
3220         if(space && size <= left) {
3221             pi1->pComment = (LPWSTR)ptr;
3222             ptr += size;
3223             left -= size;
3224         } else
3225             space = FALSE;
3226         *pcbNeeded += size;
3227     }
3228 
3229     if(pi1) pi1->Flags = PRINTER_ENUM_ICON8; /* We're a printer */
3230 
3231     if(!space && pi1) /* zero out pi1 if we can't completely fill buf */
3232         memset(pi1, 0, sizeof(*pi1));
3233 
3234     return space;
3235 }
3236 /*********************************************************************
3237  *    WINSPOOL_GetPrinter_2
3238  *
3239  * Fills out a PRINTER_INFO_2A|W struct storing the strings in buf.
3240  * The strings are either stored as unicode or ascii.
3241  */
3242 static BOOL WINSPOOL_GetPrinter_2(HKEY hkeyPrinter, PRINTER_INFO_2W *pi2,
3243                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3244                                   BOOL unicode)
3245 {
3246     DWORD size, left = cbBuf;
3247     BOOL space = (cbBuf > 0);
3248     LPBYTE ptr = buf;
3249 
3250     *pcbNeeded = 0;
3251 
3252     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3253                                  unicode)) {
3254         if(space && size <= left) {
3255             pi2->pPrinterName = (LPWSTR)ptr;
3256             ptr += size;
3257             left -= size;
3258         } else
3259             space = FALSE;
3260         *pcbNeeded += size;
3261     }
3262     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Share_NameW, ptr, left, &size,
3263                                  unicode)) {
3264         if(space && size <= left) {
3265             pi2->pShareName = (LPWSTR)ptr;
3266             ptr += size;
3267             left -= size;
3268         } else
3269             space = FALSE;
3270         *pcbNeeded += size;
3271     }
3272     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
3273                                  unicode)) {
3274         if(space && size <= left) {
3275             pi2->pPortName = (LPWSTR)ptr;
3276             ptr += size;
3277             left -= size;
3278         } else
3279             space = FALSE;
3280         *pcbNeeded += size;
3281     }
3282     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Printer_DriverW, ptr, left,
3283                                  &size, unicode)) {
3284         if(space && size <= left) {
3285             pi2->pDriverName = (LPWSTR)ptr;
3286             ptr += size;
3287             left -= size;
3288         } else
3289             space = FALSE;
3290         *pcbNeeded += size;
3291     }
3292     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size,
3293                                  unicode)) {
3294         if(space && size <= left) {
3295             pi2->pComment = (LPWSTR)ptr;
3296             ptr += size;
3297             left -= size;
3298         } else
3299             space = FALSE;
3300         *pcbNeeded += size;
3301     }
3302     if(WINSPOOL_GetStringFromReg(hkeyPrinter, LocationW, ptr, left, &size,
3303                                  unicode)) {
3304         if(space && size <= left) {
3305             pi2->pLocation = (LPWSTR)ptr;
3306             ptr += size;
3307             left -= size;
3308         } else
3309             space = FALSE;
3310         *pcbNeeded += size;
3311     }
3312     if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, ptr, left,
3313                                   &size, unicode)) {
3314         if(space && size <= left) {
3315             pi2->pDevMode = (LPDEVMODEW)ptr;
3316             ptr += size;
3317             left -= size;
3318         } else
3319             space = FALSE;
3320         *pcbNeeded += size;
3321     }
3322     else
3323     {
3324         WINSPOOL_GetDefaultDevMode(ptr, left, &size, unicode);
3325         if(space && size <= left) {
3326             pi2->pDevMode = (LPDEVMODEW)ptr;
3327             ptr += size;
3328             left -= size;
3329         } else
3330             space = FALSE;
3331         *pcbNeeded += size;
3332     }
3333     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Separator_FileW, ptr, left,
3334                                  &size, unicode)) {
3335         if(space && size <= left) {
3336             pi2->pSepFile = (LPWSTR)ptr;
3337             ptr += size;
3338             left -= size;
3339         } else
3340             space = FALSE;
3341         *pcbNeeded += size;
3342     }
3343     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Print_ProcessorW, ptr, left,
3344                                  &size, unicode)) {
3345         if(space && size <= left) {
3346             pi2->pPrintProcessor = (LPWSTR)ptr;
3347             ptr += size;
3348             left -= size;
3349         } else
3350             space = FALSE;
3351         *pcbNeeded += size;
3352     }
3353     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DatatypeW, ptr, left,
3354                                  &size, unicode)) {
3355         if(space && size <= left) {
3356             pi2->pDatatype = (LPWSTR)ptr;
3357             ptr += size;
3358             left -= size;
3359         } else
3360             space = FALSE;
3361         *pcbNeeded += size;
3362     }
3363     if(WINSPOOL_GetStringFromReg(hkeyPrinter, ParametersW, ptr, left,
3364                                  &size, unicode)) {
3365         if(space && size <= left) {
3366             pi2->pParameters = (LPWSTR)ptr;
3367             ptr += size;
3368             left -= size;
3369         } else
3370             space = FALSE;
3371         *pcbNeeded += size;
3372     }
3373     if(pi2) {
3374         pi2->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3375         pi2->Priority = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Priority");
3376         pi2->DefaultPriority = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3377                                                         "Default Priority");
3378         pi2->StartTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "StartTime");
3379         pi2->UntilTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "UntilTime");
3380     }
3381 
3382     if(!space && pi2) /* zero out pi2 if we can't completely fill buf */
3383         memset(pi2, 0, sizeof(*pi2));
3384 
3385     return space;
3386 }
3387 
3388 /*********************************************************************
3389  *    WINSPOOL_GetPrinter_4
3390  *
3391  * Fills out a PRINTER_INFO_4 struct storing the strings in buf.
3392  */
3393 static BOOL WINSPOOL_GetPrinter_4(HKEY hkeyPrinter, PRINTER_INFO_4W *pi4,
3394                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3395                                   BOOL unicode)
3396 {
3397     DWORD size, left = cbBuf;
3398     BOOL space = (cbBuf > 0);
3399     LPBYTE ptr = buf;
3400 
3401     *pcbNeeded = 0;
3402 
3403     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3404                                  unicode)) {
3405         if(space && size <= left) {
3406             pi4->pPrinterName = (LPWSTR)ptr;
3407             ptr += size;
3408             left -= size;
3409         } else
3410             space = FALSE;
3411         *pcbNeeded += size;
3412     }
3413     if(pi4) {
3414         pi4->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3415     }
3416 
3417     if(!space && pi4) /* zero out pi4 if we can't completely fill buf */
3418         memset(pi4, 0, sizeof(*pi4));
3419 
3420     return space;
3421 }
3422 
3423 /*********************************************************************
3424  *    WINSPOOL_GetPrinter_5
3425  *
3426  * Fills out a PRINTER_INFO_5 struct storing the strings in buf.
3427  */
3428 static BOOL WINSPOOL_GetPrinter_5(HKEY hkeyPrinter, PRINTER_INFO_5W *pi5,
3429                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3430                                   BOOL unicode)
3431 {
3432     DWORD size, left = cbBuf;
3433     BOOL space = (cbBuf > 0);
3434     LPBYTE ptr = buf;
3435 
3436     *pcbNeeded = 0;
3437 
3438     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3439                                  unicode)) {
3440         if(space && size <= left) {
3441             pi5->pPrinterName = (LPWSTR)ptr;
3442             ptr += size;
3443             left -= size;
3444         } else
3445             space = FALSE;
3446         *pcbNeeded += size;
3447     }
3448     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
3449                                  unicode)) {
3450         if(space && size <= left) {
3451             pi5->pPortName = (LPWSTR)ptr;
3452             ptr += size;
3453             left -= size;
3454         } else
3455             space = FALSE;
3456         *pcbNeeded += size;
3457     }
3458     if(pi5) {
3459         pi5->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3460         pi5->DeviceNotSelectedTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3461                                                                 "dnsTimeout");
3462         pi5->TransmissionRetryTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3463                                                                  "txTimeout");
3464     }
3465 
3466     if(!space && pi5) /* zero out pi5 if we can't completely fill buf */
3467         memset(pi5, 0, sizeof(*pi5));
3468 
3469     return space;
3470 }
3471 
3472 /*********************************************************************
3473  *    WINSPOOL_GetPrinter_7
3474  *
3475  * Fills out a PRINTER_INFO_7 struct storing the strings in buf.
3476  */
3477 static BOOL WINSPOOL_GetPrinter_7(HKEY hkeyPrinter, PRINTER_INFO_7W *pi7, LPBYTE buf,
3478                                   DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
3479 {
3480     DWORD size, left = cbBuf;
3481     BOOL space = (cbBuf > 0);
3482     LPBYTE ptr = buf;
3483 
3484     *pcbNeeded = 0;
3485 
3486     if (WINSPOOL_GetStringFromReg(hkeyPrinter, ObjectGUIDW, ptr, left, &size, unicode))
3487     {
3488         if (space && size <= left) {
3489             pi7->pszObjectGUID = (LPWSTR)ptr;
3490             ptr += size;
3491             left -= size;
3492         } else
3493             space = FALSE;
3494         *pcbNeeded += size;
3495     }
3496     if (pi7) {
3497         /* We do not have a Directory Service */
3498         pi7->dwAction = DSPRINT_UNPUBLISH;
3499     }
3500 
3501     if (!space && pi7) /* zero out pi7 if we can't completely fill buf */
3502         memset(pi7, 0, sizeof(*pi7));
3503 
3504     return space;
3505 }
3506 
3507 /*********************************************************************
3508  *    WINSPOOL_GetPrinter_9
3509  *
3510  * Fills out a PRINTER_INFO_9A|W struct storing the strings in buf.
3511  * The strings are either stored as unicode or ascii.
3512  */
3513 static BOOL WINSPOOL_GetPrinter_9(HKEY hkeyPrinter, PRINTER_INFO_9W *pi9, LPBYTE buf,
3514                                   DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
3515 {
3516     DWORD size;
3517     BOOL space = (cbBuf > 0);
3518 
3519     *pcbNeeded = 0;
3520 
3521     if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, buf, cbBuf, &size, unicode)) {
3522         if(space && size <= cbBuf) {
3523             pi9->pDevMode = (LPDEVMODEW)buf;
3524         } else
3525             space = FALSE;
3526         *pcbNeeded += size;
3527     }
3528     else
3529     {
3530         WINSPOOL_GetDefaultDevMode(buf, cbBuf, &size, unicode);
3531         if(space && size <= cbBuf) {
3532             pi9->pDevMode = (LPDEVMODEW)buf;
3533         } else
3534             space = FALSE;
3535         *pcbNeeded += size;
3536     }
3537 
3538     if(!space && pi9) /* zero out pi9 if we can't completely fill buf */
3539         memset(pi9, 0, sizeof(*pi9));
3540 
3541     return space;
3542 }
3543 
3544 /*****************************************************************************
3545  *          WINSPOOL_GetPrinter
3546  *
3547  *    Implementation of GetPrinterA|W.  Relies on PRINTER_INFO_*W being
3548  *    essentially the same as PRINTER_INFO_*A. i.e. the structure itself is
3549  *    just a collection of pointers to strings.
3550  */
3551 static BOOL WINSPOOL_GetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
3552                                 DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
3553 {
3554     LPCWSTR name;
3555     DWORD size, needed = 0;
3556     LPBYTE ptr = NULL;
3557     HKEY hkeyPrinter, hkeyPrinters;
3558     BOOL ret;
3559 
3560     TRACE("(%p,%d,%p,%d,%p)\n",hPrinter,Level,pPrinter,cbBuf, pcbNeeded);
3561 
3562     if (!(name = get_opened_printer_name(hPrinter))) {
3563         SetLastError(ERROR_INVALID_HANDLE);
3564         return FALSE;
3565     }
3566 
3567     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
3568        ERROR_SUCCESS) {
3569         ERR("Can't create Printers key\n");
3570         return FALSE;
3571     }
3572     if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter) != ERROR_SUCCESS)
3573     {
3574         ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
3575         RegCloseKey(hkeyPrinters);
3576         SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
3577         return FALSE;
3578     }
3579 
3580     switch(Level) {
3581     case 2:
3582       {
3583         PRINTER_INFO_2W *pi2 = (PRINTER_INFO_2W *)pPrinter;
3584 
3585         size = sizeof(PRINTER_INFO_2W);
3586         if(size <= cbBuf) {
3587             ptr = pPrinter + size;
3588             cbBuf -= size;
3589             memset(pPrinter, 0, size);
3590         } else {
3591             pi2 = NULL;
3592             cbBuf = 0;
3593         }
3594         ret = WINSPOOL_GetPrinter_2(hkeyPrinter, pi2, ptr, cbBuf, &needed,
3595                                     unicode);
3596         needed += size;
3597         break;
3598       }
3599 
3600     case 4:
3601       {
3602         PRINTER_INFO_4W *pi4 = (PRINTER_INFO_4W *)pPrinter;
3603 
3604         size = sizeof(PRINTER_INFO_4W);
3605         if(size <= cbBuf) {
3606             ptr = pPrinter + size;
3607             cbBuf -= size;
3608             memset(pPrinter, 0, size);
3609         } else {
3610             pi4 = NULL;
3611             cbBuf = 0;
3612         }
3613         ret = WINSPOOL_GetPrinter_4(hkeyPrinter, pi4, ptr, cbBuf, &needed,
3614                                     unicode);
3615         needed += size;
3616         break;
3617       }
3618 
3619 
3620     case 5:
3621       {
3622         PRINTER_INFO_5W *pi5 = (PRINTER_INFO_5W *)pPrinter;
3623 
3624         size = sizeof(PRINTER_INFO_5W);
3625         if(size <= cbBuf) {
3626             ptr = pPrinter + size;
3627             cbBuf -= size;
3628             memset(pPrinter, 0, size);
3629         } else {
3630             pi5 = NULL;
3631             cbBuf = 0;
3632         }
3633 
3634         ret = WINSPOOL_GetPrinter_5(hkeyPrinter, pi5, ptr, cbBuf, &needed,
3635                                     unicode);
3636         needed += size;
3637         break;
3638       }
3639 
3640 
3641     case 6:
3642       {
3643         PRINTER_INFO_6 *pi6 = (PRINTER_INFO_6 *) pPrinter;
3644 
3645         size = sizeof(PRINTER_INFO_6);
3646         if (size <= cbBuf) {
3647             /* FIXME: We do not update the status yet */
3648             pi6->dwStatus = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Status");
3649             ret = TRUE;
3650         } else {
3651             ret = FALSE;
3652         }
3653 
3654         needed += size;
3655         break;
3656       }
3657 
3658     case 7:
3659       {
3660         PRINTER_INFO_7W *pi7 = (PRINTER_INFO_7W *) pPrinter;
3661 
3662         size = sizeof(PRINTER_INFO_7W);
3663         if (size <= cbBuf) {
3664             ptr = pPrinter + size;
3665             cbBuf -= size;
3666             memset(pPrinter, 0, size);
3667         } else {
3668             pi7 = NULL;
3669             cbBuf = 0;
3670         }
3671 
3672         ret = WINSPOOL_GetPrinter_7(hkeyPrinter, pi7, ptr, cbBuf, &needed, unicode);
3673         needed += size;
3674         break;
3675       }
3676 
3677 
3678     case 9:
3679       {
3680         PRINTER_INFO_9W *pi9 = (PRINTER_INFO_9W *)pPrinter;
3681 
3682         size = sizeof(PRINTER_INFO_9W);
3683         if(size <= cbBuf) {
3684             ptr = pPrinter + size;
3685             cbBuf -= size;
3686             memset(pPrinter, 0, size);
3687         } else {
3688             pi9 = NULL;
3689             cbBuf = 0;
3690         }
3691 
3692         ret = WINSPOOL_GetPrinter_9(hkeyPrinter, pi9, ptr, cbBuf, &needed, unicode);
3693         needed += size;
3694         break;
3695       }
3696 
3697 
3698     default:
3699         FIXME("Unimplemented level %d\n", Level);
3700         SetLastError(ERROR_INVALID_LEVEL);
3701         RegCloseKey(hkeyPrinters);
3702         RegCloseKey(hkeyPrinter);
3703         return FALSE;
3704     }
3705 
3706     RegCloseKey(hkeyPrinter);
3707     RegCloseKey(hkeyPrinters);
3708 
3709     TRACE("returning %d needed = %d\n", ret, needed);
3710     if(pcbNeeded) *pcbNeeded = needed;
3711     if(!ret)
3712         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3713     return ret;
3714 }
3715 
3716 /*****************************************************************************
3717  *          GetPrinterW  [WINSPOOL.@]
3718  */
3719 BOOL WINAPI GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
3720                         DWORD cbBuf, LPDWORD pcbNeeded)
3721 {
3722     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
3723                                TRUE);
3724 }
3725 
3726 /*****************************************************************************
3727  *          GetPrinterA  [WINSPOOL.@]
3728  */
3729 BOOL WINAPI GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
3730                     DWORD cbBuf, LPDWORD pcbNeeded)
3731 {
3732     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
3733                                FALSE);
3734 }
3735 
3736 /*****************************************************************************
3737  *          WINSPOOL_EnumPrinters
3738  *
3739  *    Implementation of EnumPrintersA|W
3740  */
3741 static BOOL WINSPOOL_EnumPrinters(DWORD dwType, LPWSTR lpszName,
3742                                   DWORD dwLevel, LPBYTE lpbPrinters,
3743                                   DWORD cbBuf, LPDWORD lpdwNeeded,
3744                                   LPDWORD lpdwReturned, BOOL unicode)
3745 
3746 {
3747     HKEY hkeyPrinters, hkeyPrinter;
3748     WCHAR PrinterName[255];
3749     DWORD needed = 0, number = 0;
3750     DWORD used, i, left;
3751     PBYTE pi, buf;
3752 
3753     if(lpbPrinters)
3754         memset(lpbPrinters, 0, cbBuf);
3755     if(lpdwReturned)
3756         *lpdwReturned = 0;
3757     if(lpdwNeeded)
3758         *lpdwNeeded = 0;
3759 
3760     /* PRINTER_ENUM_DEFAULT is only supported under win9x, we behave like NT */
3761     if(dwType == PRINTER_ENUM_DEFAULT)
3762         return TRUE;
3763 
3764     if (dwType & PRINTER_ENUM_CONNECTIONS) {
3765         TRACE("ignoring PRINTER_ENUM_CONNECTIONS\n");
3766         dwType &= ~PRINTER_ENUM_CONNECTIONS; /* we don't handle that */
3767         if (!dwType) {
3768             FIXME("We don't handle PRINTER_ENUM_CONNECTIONS\n");
3769             *lpdwNeeded = 0;
3770             *lpdwReturned = 0;
3771             return TRUE;
3772         }
3773 
3774     }
3775 
3776     if (!((dwType & PRINTER_ENUM_LOCAL) || (dwType & PRINTER_ENUM_NAME))) {
3777         FIXME("dwType = %08x\n", dwType);
3778         SetLastError(ERROR_INVALID_FLAGS);
3779         return FALSE;
3780     }
3781 
3782     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
3783        ERROR_SUCCESS) {
3784         ERR("Can't create Printers key\n");
3785         return FALSE;
3786     }
3787 
3788     if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &number, NULL, NULL,
3789                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
3790         RegCloseKey(hkeyPrinters);
3791         ERR("Can't query Printers key\n");
3792         return FALSE;
3793     }
3794     TRACE("Found %d printers\n", number);
3795 
3796     switch(dwLevel) {
3797     case 1:
3798         used = number * sizeof(PRINTER_INFO_1W);
3799         break;
3800     case 2:
3801         used = number * sizeof(PRINTER_INFO_2W);
3802         break;
3803     case 4:
3804         used = number * sizeof(PRINTER_INFO_4W);
3805         break;
3806     case 5:
3807         used = number * sizeof(PRINTER_INFO_5W);
3808         break;
3809 
3810     default:
3811         SetLastError(ERROR_INVALID_LEVEL);
3812         RegCloseKey(hkeyPrinters);
3813         return FALSE;
3814     }
3815     pi = (used <= cbBuf) ? lpbPrinters : NULL;
3816 
3817     for(i = 0; i < number; i++) {
3818         if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)/sizeof(PrinterName[0])) !=
3819            ERROR_SUCCESS) {
3820             ERR("Can't enum key number %d\n", i);
3821             RegCloseKey(hkeyPrinters);
3822             return FALSE;
3823         }
3824         TRACE("Printer %d is %s\n", i, debugstr_w(PrinterName));
3825         if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkeyPrinter) !=
3826            ERROR_SUCCESS) {
3827             ERR("Can't open key %s\n", debugstr_w(PrinterName));
3828             RegCloseKey(hkeyPrinters);
3829             return FALSE;
3830         }
3831 
3832         if(cbBuf > used) {
3833             buf = lpbPrinters + used;
3834             left = cbBuf - used;
3835         } else {
3836             buf = NULL;
3837             left = 0;
3838         }
3839 
3840         switch(dwLevel) {
3841         case 1:
3842             WINSPOOL_GetPrinter_1(hkeyPrinter, (PRINTER_INFO_1W *)pi, buf,
3843                                   left, &needed, unicode);
3844             used += needed;
3845             if(pi) pi += sizeof(PRINTER_INFO_1W);
3846             break;
3847         case 2:
3848             WINSPOOL_GetPrinter_2(hkeyPrinter, (PRINTER_INFO_2W *)pi, buf,
3849                                   left, &needed, unicode);
3850             used += needed;
3851             if(pi) pi += sizeof(PRINTER_INFO_2W);
3852             break;
3853         case 4:
3854             WINSPOOL_GetPrinter_4(hkeyPrinter, (PRINTER_INFO_4W *)pi, buf,
3855                                   left, &needed, unicode);
3856             used += needed;
3857             if(pi) pi += sizeof(PRINTER_INFO_4W);
3858             break;
3859         case 5:
3860             WINSPOOL_GetPrinter_5(hkeyPrinter, (PRINTER_INFO_5W *)pi, buf,
3861                                   left, &needed, unicode);
3862             used += needed;
3863             if(pi) pi += sizeof(PRINTER_INFO_5W);
3864             break;
3865         default:
3866             ERR("Shouldn't be here!\n");
3867             RegCloseKey(hkeyPrinter);
3868             RegCloseKey(hkeyPrinters);
3869             return FALSE;
3870         }
3871         RegCloseKey(hkeyPrinter);
3872     }
3873     RegCloseKey(hkeyPrinters);
3874 
3875     if(lpdwNeeded)
3876         *lpdwNeeded = used;
3877 
3878     if(used > cbBuf) {
3879         if(lpbPrinters)
3880             memset(lpbPrinters, 0, cbBuf);
3881         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3882         return FALSE;
3883     }
3884     if(lpdwReturned)
3885         *lpdwReturned = number;
3886     SetLastError(ERROR_SUCCESS);
3887     return TRUE;
3888 }
3889 
3890 
3891 /******************************************************************
3892  *              EnumPrintersW        [WINSPOOL.@]
3893  *
3894  *    Enumerates the available printers, print servers and print
3895  *    providers, depending on the specified flags, name and level.
3896  *
3897  * RETURNS:
3898  *
3899  *    If level is set to 1:
3900  *      Returns an array of PRINTER_INFO_1 data structures in the
3901  *      lpbPrinters buffer.
3902  *
3903  *    If level is set to 2:
3904  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
3905  *      Returns an array of PRINTER_INFO_2 data structures in the
3906  *      lpbPrinters buffer. Note that according to MSDN also an
3907  *      OpenPrinter should be performed on every remote printer.
3908  *
3909  *    If level i