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

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

Version: ~ [ wine-1.1.33 ] ~ [ 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 spoolprtprocsW[] = {'\\','s','p','o','o','l','\\','p','r','t','p','r','o','c','s','\\',0};
179 
180 static const WCHAR backslashW[] = {'\\',0};
181 static const WCHAR Configuration_FileW[] = {'C','o','n','f','i','g','u','r','a','t',
182                                       'i','o','n',' ','F','i','l','e',0};
183 static const WCHAR DatatypeW[] = {'D','a','t','a','t','y','p','e',0};
184 static const WCHAR Data_FileW[] = {'D','a','t','a',' ','F','i','l','e',0};
185 static const WCHAR Default_DevModeW[] = {'D','e','f','a','u','l','t',' ','D','e','v','M','o','d','e',0};
186 static const WCHAR Dependent_FilesW[] = {'D','e','p','e','n','d','e','n','t',' ','F','i','l','e','s',0};
187 static const WCHAR DescriptionW[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
188 static const WCHAR DriverW[] = {'D','r','i','v','e','r',0};
189 static const WCHAR HardwareIDW[] = {'H','a','r','d','w','a','r','e','I','D',0};
190 static const WCHAR Help_FileW[] = {'H','e','l','p',' ','F','i','l','e',0};
191 static const WCHAR LocationW[] = {'L','o','c','a','t','i','o','n',0};
192 static const WCHAR ManufacturerW[] = {'M','a','n','u','f','a','c','t','u','r','e','r',0};
193 static const WCHAR MonitorW[] = {'M','o','n','i','t','o','r',0};
194 static const WCHAR NameW[] = {'N','a','m','e',0};
195 static const WCHAR ObjectGUIDW[] = {'O','b','j','e','c','t','G','U','I','D',0};
196 static const WCHAR OEM_UrlW[] = {'O','E','M',' ','U','r','l',0};
197 static const WCHAR ParametersW[] = {'P','a','r','a','m','e','t','e','r','s',0};
198 static const WCHAR PortW[] = {'P','o','r','t',0};
199 static const WCHAR bs_Ports_bsW[] = {'\\','P','o','r','t','s','\\',0};
200 static const WCHAR Previous_NamesW[] = {'P','r','e','v','i','o','u','s',' ','N','a','m','e','s',0};
201 static const WCHAR Print_ProcessorW[] = {'P','r','i','n','t',' ','P','r','o','c','e','s','s','o','r',0};
202 static const WCHAR Printer_DriverW[] = {'P','r','i','n','t','e','r',' ','D','r','i','v','e','r',0};
203 static const WCHAR PrinterDriverDataW[] = {'P','r','i','n','t','e','r','D','r','i','v','e','r','D','a','t','a',0};
204 static const WCHAR PrinterPortsW[] = {'P','r','i','n','t','e','r','P','o','r','t','s',0};
205 static const WCHAR ProviderW[] = {'P','r','o','v','i','d','e','r',0};
206 static const WCHAR Separator_FileW[] = {'S','e','p','a','r','a','t','o','r',' ','F','i','l','e',0};
207 static const WCHAR Share_NameW[] = {'S','h','a','r','e',' ','N','a','m','e',0};
208 static const WCHAR VersionW[] = {'V','e','r','s','i','o','n',0};
209 static const WCHAR WinPrintW[] = {'W','i','n','P','r','i','n','t',0};
210 static const WCHAR deviceW[]  = {'d','e','v','i','c','e',0};
211 static const WCHAR devicesW[] = {'d','e','v','i','c','e','s',0};
212 static const WCHAR windowsW[] = {'w','i','n','d','o','w','s',0};
213 static const WCHAR emptyStringW[] = {0};
214 
215 static const WCHAR May_Delete_Value[] = {'W','i','n','e','M','a','y','D','e','l','e','t','e','M','e',0};
216 
217 static const WCHAR CUPS_Port[] = {'C','U','P','S',':',0};
218 static const WCHAR FILE_Port[] = {'F','I','L','E',':',0};
219 static const WCHAR LPR_Port[] = {'L','P','R',':',0};
220 
221 static const WCHAR default_doc_title[] = {'L','o','c','a','l',' ','D','o','w','n','l','e','v','e','l',' ',
222                                           'D','o','c','u','m','e','n','t',0};
223 
224 static const DWORD di_sizeof[] = {0, sizeof(DRIVER_INFO_1W), sizeof(DRIVER_INFO_2W),
225                                      sizeof(DRIVER_INFO_3W), sizeof(DRIVER_INFO_4W),
226                                      sizeof(DRIVER_INFO_5W), sizeof(DRIVER_INFO_6W),
227                                   0, sizeof(DRIVER_INFO_8W)};
228 
229 
230 static const DWORD pi_sizeof[] = {0, sizeof(PRINTER_INFO_1W), sizeof(PRINTER_INFO_2W),
231                                      sizeof(PRINTER_INFO_3),  sizeof(PRINTER_INFO_4W),
232                                      sizeof(PRINTER_INFO_5W), sizeof(PRINTER_INFO_6),
233                                      sizeof(PRINTER_INFO_7W), sizeof(PRINTER_INFO_8W),
234                                      sizeof(PRINTER_INFO_9W)};
235 
236 static const printenv_t env_x64 = {envname_x64W, subdir_x64W, 3, Version3_RegPathW, Version3_SubdirW};
237 static const printenv_t env_x86 = {envname_x86W, subdir_x86W, 3, Version3_RegPathW, Version3_SubdirW};
238 static const printenv_t env_win40 = {envname_win40W, subdir_win40W, 0, Version0_RegPathW, Version0_SubdirW};
239 
240 static const printenv_t * const all_printenv[] = {&env_x86, &env_x64, &env_win40};
241 
242 /******************************************************************
243  *  validate the user-supplied printing-environment [internal]
244  *
245  * PARAMS
246  *  env  [I] PTR to Environment-String or NULL
247  *
248  * RETURNS
249  *  Failure:  NULL
250  *  Success:  PTR to printenv_t
251  *
252  * NOTES
253  *  An empty string is handled the same way as NULL.
254  *  SetLastEror(ERROR_INVALID_ENVIRONMENT) is called on Failure
255  *  
256  */
257 
258 static const  printenv_t * validate_envW(LPCWSTR env)
259 {
260     const printenv_t *result = NULL;
261     unsigned int i;
262 
263     TRACE("testing %s\n", debugstr_w(env));
264     if (env && env[0])
265     {
266         for (i = 0; i < sizeof(all_printenv)/sizeof(all_printenv[0]); i++)
267         {
268             if (lstrcmpiW(env, all_printenv[i]->envname) == 0)
269             {
270                 result = all_printenv[i];
271                 break;
272             }
273         }
274 
275         if (result == NULL) {
276             FIXME("unsupported Environment: %s\n", debugstr_w(env));
277             SetLastError(ERROR_INVALID_ENVIRONMENT);
278         }
279         /* on win9x, only "Windows 4.0" is allowed, but we ignore this */
280     }
281     else
282     {
283         result = (GetVersion() & 0x80000000) ? &env_win40 : &env_x86;
284     }
285     TRACE("using %p: %s\n", result, debugstr_w(result ? result->envname : NULL));
286 
287     return result;
288 }
289 
290 
291 /* RtlCreateUnicodeStringFromAsciiz will return an empty string in the buffer
292    if passed a NULL string. This returns NULLs to the result. 
293 */
294 static inline PWSTR asciitounicode( UNICODE_STRING * usBufferPtr, LPCSTR src )
295 {
296     if ( (src) )
297     {
298         RtlCreateUnicodeStringFromAsciiz(usBufferPtr, src);
299         return usBufferPtr->Buffer;
300     }
301     usBufferPtr->Buffer = NULL; /* so that RtlFreeUnicodeString won't barf */
302     return NULL;
303 }
304             
305 static LPWSTR strdupW(LPCWSTR p)
306 {
307     LPWSTR ret;
308     DWORD len;
309 
310     if(!p) return NULL;
311     len = (strlenW(p) + 1) * sizeof(WCHAR);
312     ret = HeapAlloc(GetProcessHeap(), 0, len);
313     memcpy(ret, p, len);
314     return ret;
315 }
316 
317 static LPSTR strdupWtoA( LPCWSTR str )
318 {
319     LPSTR ret;
320     INT len;
321 
322     if (!str) return NULL;
323     len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
324     ret = HeapAlloc( GetProcessHeap(), 0, len );
325     if(ret) WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
326     return ret;
327 }
328 
329 /******************************************************************
330  * Return the number of bytes for an multi_sz string.
331  * The result includes all \0s
332  * (specifically the extra \0, that is needed as multi_sz terminator).
333  */
334 #if 0
335 static int multi_sz_lenW(const WCHAR *str)
336 {
337     const WCHAR *ptr = str;
338     if(!str) return 0;
339     do
340     {
341         ptr += lstrlenW(ptr) + 1;
342     } while(*ptr);
343 
344     return (ptr - str + 1) * sizeof(WCHAR);
345 }
346 #endif
347 /* ################################ */
348 
349 static int multi_sz_lenA(const char *str)
350 {
351     const char *ptr = str;
352     if(!str) return 0;
353     do
354     {
355         ptr += lstrlenA(ptr) + 1;
356     } while(*ptr);
357 
358     return ptr - str + 1;
359 }
360 
361 static void
362 WINSPOOL_SetDefaultPrinter(const char *devname, const char *name, BOOL force) {
363     char qbuf[200];
364 
365     /* If forcing, or no profile string entry for device yet, set the entry
366      *
367      * The always change entry if not WINEPS yet is discussable.
368      */
369     if (force                                                           ||
370         !GetProfileStringA("windows","device","*",qbuf,sizeof(qbuf))    ||
371         !strcmp(qbuf,"*")                                               ||
372         !strstr(qbuf,"WINEPS.DRV")
373     ) {
374         char *buf = HeapAlloc(GetProcessHeap(),0,strlen(name)+strlen(devname)+strlen(",WINEPS.DRV,LPR:")+1);
375         HKEY hkey;
376 
377         sprintf(buf,"%s,WINEPS.DRV,LPR:%s",devname,name);
378         WriteProfileStringA("windows","device",buf);
379         if(RegCreateKeyW(HKEY_CURRENT_USER, user_default_reg_key, &hkey) == ERROR_SUCCESS) {
380             RegSetValueExA(hkey, "Device", 0, REG_SZ, (LPBYTE)buf, strlen(buf) + 1);
381             RegCloseKey(hkey);
382         }
383         HeapFree(GetProcessHeap(),0,buf);
384     }
385 }
386 
387 static BOOL add_printer_driver(const char *name)
388 {
389     DRIVER_INFO_3A di3a;
390 
391     static char driver_9x[]         = "wineps16.drv",
392                 driver_nt[]         = "wineps.drv",
393                 env_9x[]            = "Windows 4.0",
394                 env_nt[]            = "Windows NT x86",
395                 data_file[]         = "generic.ppd",
396                 default_data_type[] = "RAW";
397 
398     ZeroMemory(&di3a, sizeof(DRIVER_INFO_3A));
399     di3a.cVersion         = 3;
400     di3a.pName            = (char *)name;
401     di3a.pEnvironment     = env_nt;
402     di3a.pDriverPath      = driver_nt;
403     di3a.pDataFile        = data_file;
404     di3a.pConfigFile      = driver_nt;
405     di3a.pDefaultDataType = default_data_type;
406 
407     if (AddPrinterDriverA(NULL, 3, (LPBYTE)&di3a) ||
408         (GetLastError() ==  ERROR_PRINTER_DRIVER_ALREADY_INSTALLED ))
409     {
410         di3a.cVersion     = 0;
411         di3a.pEnvironment = env_9x;
412         di3a.pDriverPath  = driver_9x;
413         di3a.pConfigFile  = driver_9x;
414         if (AddPrinterDriverA(NULL, 3, (LPBYTE)&di3a) ||
415             (GetLastError() ==  ERROR_PRINTER_DRIVER_ALREADY_INSTALLED ))
416         {
417             return TRUE;
418         }
419     }
420     ERR("Failed adding driver %s (%s): %u\n", debugstr_a(di3a.pDriverPath),
421         debugstr_a(di3a.pEnvironment), GetLastError());
422     return FALSE;
423 }
424 
425 #ifdef SONAME_LIBCUPS
426 static typeof(cupsFreeDests) *pcupsFreeDests;
427 static typeof(cupsGetDests)  *pcupsGetDests;
428 static typeof(cupsGetPPD)    *pcupsGetPPD;
429 static typeof(cupsPrintFile) *pcupsPrintFile;
430 static void *cupshandle;
431 
432 static BOOL CUPS_LoadPrinters(void)
433 {
434     int                   i, nrofdests;
435     BOOL                  hadprinter = FALSE, haddefault = FALSE;
436     cups_dest_t          *dests;
437     PRINTER_INFO_2A       pinfo2a;
438     char   *port,*devline;
439     HKEY hkeyPrinter, hkeyPrinters, hkey;
440     char    loaderror[256];
441 
442     cupshandle = wine_dlopen(SONAME_LIBCUPS, RTLD_NOW, loaderror, sizeof(loaderror));
443     if (!cupshandle) {
444         TRACE("%s\n", loaderror);
445         return FALSE;
446     }
447     TRACE("%p: %s loaded\n", cupshandle, SONAME_LIBCUPS);
448 
449 #define DYNCUPS(x)                                      \
450         p##x = wine_dlsym(cupshandle, #x, NULL,0);      \
451         if (!p##x) return FALSE;
452 
453     DYNCUPS(cupsFreeDests);
454     DYNCUPS(cupsGetPPD);
455     DYNCUPS(cupsGetDests);
456     DYNCUPS(cupsPrintFile);
457 #undef DYNCUPS
458 
459     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
460        ERROR_SUCCESS) {
461         ERR("Can't create Printers key\n");
462         return FALSE;
463     }
464 
465     nrofdests = pcupsGetDests(&dests);
466     TRACE("Found %d CUPS %s:\n", nrofdests, (nrofdests == 1) ? "printer" : "printers");
467     for (i=0;i<nrofdests;i++) {
468         /* FIXME: replace "LPR:" with "CUPS:". Fix printing output first */
469         port = HeapAlloc(GetProcessHeap(), 0, strlen("LPR:") + strlen(dests[i].name)+1);
470         sprintf(port,"LPR:%s", dests[i].name);
471         /* FIXME: remove extension. Fix gdi32/drivers and comdlg32/printdlg first */
472         devline = HeapAlloc(GetProcessHeap(), 0, sizeof("WINEPS.DRV,,15,45") + strlen(port));
473         sprintf(devline, "WINEPS.DRV,%s", port);
474         WriteProfileStringA("devices", dests[i].name, devline);
475         if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
476             RegSetValueExA(hkey, dests[i].name, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
477             RegCloseKey(hkey);
478         }
479 
480         lstrcatA(devline, ",15,45");
481         WriteProfileStringA("PrinterPorts", dests[i].name, devline);
482         if(RegCreateKeyW(HKEY_CURRENT_USER, WinNT_CV_PrinterPortsW, &hkey) == ERROR_SUCCESS) {
483             RegSetValueExA(hkey, dests[i].name, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
484             RegCloseKey(hkey);
485         }
486 
487         HeapFree(GetProcessHeap(), 0, devline);
488 
489         TRACE("Printer %d: %s\n", i, dests[i].name);
490         if(RegOpenKeyA(hkeyPrinters, dests[i].name, &hkeyPrinter) == ERROR_SUCCESS) {
491             /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
492                and continue */
493             TRACE("Printer already exists\n");
494             RegDeleteValueW(hkeyPrinter, May_Delete_Value);
495             RegCloseKey(hkeyPrinter);
496         } else {
497             static CHAR data_type[] = "RAW",
498                     print_proc[]    = "WinPrint",
499                     comment[]       = "WINEPS Printer using CUPS",
500                     location[]      = "<physical location of printer>",
501                     params[]        = "<parameters?>",
502                     share_name[]    = "<share name?>",
503                     sep_file[]      = "<sep file?>";
504 
505             add_printer_driver(dests[i].name);
506 
507             memset(&pinfo2a,0,sizeof(pinfo2a));
508             pinfo2a.pPrinterName    = dests[i].name;
509             pinfo2a.pDatatype       = data_type;
510             pinfo2a.pPrintProcessor = print_proc;
511             pinfo2a.pDriverName     = dests[i].name;
512             pinfo2a.pComment        = comment;
513             pinfo2a.pLocation       = location;
514             pinfo2a.pPortName       = port;
515             pinfo2a.pParameters     = params;
516             pinfo2a.pShareName      = share_name;
517             pinfo2a.pSepFile        = sep_file;
518 
519             if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
520                 if (GetLastError() != ERROR_PRINTER_ALREADY_EXISTS)
521                     ERR("printer '%s' not added by AddPrinterA (error %d)\n",dests[i].name,GetLastError());
522             }
523         }
524         HeapFree(GetProcessHeap(),0,port);
525 
526         hadprinter = TRUE;
527         if (dests[i].is_default) {
528             WINSPOOL_SetDefaultPrinter(dests[i].name, dests[i].name, TRUE);
529             haddefault = TRUE;
530         }
531     }
532     if (hadprinter & !haddefault)
533         WINSPOOL_SetDefaultPrinter(dests[0].name, dests[0].name, TRUE);
534     pcupsFreeDests(nrofdests, dests);
535     RegCloseKey(hkeyPrinters);
536     return hadprinter;
537 }
538 #endif
539 
540 static BOOL
541 PRINTCAP_ParseEntry(const char *pent, BOOL isfirst) {
542     PRINTER_INFO_2A     pinfo2a;
543     char                *e,*s,*name,*prettyname,*devname;
544     BOOL                ret = FALSE, set_default = FALSE;
545     char                *port = NULL, *devline,*env_default;
546     HKEY                hkeyPrinter, hkeyPrinters, hkey;
547 
548     while (isspace(*pent)) pent++;
549     s = strchr(pent,':');
550     if(s) *s='\0';
551     name = HeapAlloc(GetProcessHeap(), 0, strlen(pent) + 1);
552     strcpy(name,pent);
553     if(s) {
554         *s=':';
555         pent = s;
556     } else
557         pent = "";
558 
559     TRACE("name=%s entry=%s\n",name, pent);
560 
561     if(ispunct(*name)) { /* a tc entry, not a real printer */
562         TRACE("skipping tc entry\n");
563         goto end;
564     }
565 
566     if(strstr(pent,":server")) { /* server only version so skip */
567         TRACE("skipping server entry\n");
568         goto end;
569     }
570 
571     /* Determine whether this is a postscript printer. */
572 
573     ret = TRUE;
574     env_default = getenv("PRINTER");
575     prettyname = name;
576     /* Get longest name, usually the one at the right for later display. */
577     while((s=strchr(prettyname,'|'))) {
578         *s = '\0';
579         e = s;
580         while(isspace(*--e)) *e = '\0';
581         TRACE("\t%s\n", debugstr_a(prettyname));
582         if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
583         for(prettyname = s+1; isspace(*prettyname); prettyname++)
584             ;
585     }
586     e = prettyname + strlen(prettyname);
587     while(isspace(*--e)) *e = '\0';
588     TRACE("\t%s\n", debugstr_a(prettyname));
589     if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
590 
591     /* prettyname must fit into the dmDeviceName member of DEVMODE struct,
592      * if it is too long, we use it as comment below. */
593     devname = prettyname;
594     if (strlen(devname)>=CCHDEVICENAME-1)
595          devname = name;
596     if (strlen(devname)>=CCHDEVICENAME-1) {
597         ret = FALSE;
598         goto end;
599     }
600 
601     port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(name)+1);
602     sprintf(port,"LPR:%s",name);
603 
604     /* FIXME: remove extension. Fix gdi32/drivers and comdlg32/printdlg first */
605     devline = HeapAlloc(GetProcessHeap(), 0, sizeof("WINEPS.DRV,,15,45") + strlen(port));
606     sprintf(devline, "WINEPS.DRV,%s", port);
607     WriteProfileStringA("devices", devname, devline);
608     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
609         RegSetValueExA(hkey, devname, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
610         RegCloseKey(hkey);
611     }
612 
613     lstrcatA(devline, ",15,45");
614     WriteProfileStringA("PrinterPorts", devname, devline);
615     if(RegCreateKeyW(HKEY_CURRENT_USER, WinNT_CV_PrinterPortsW, &hkey) == ERROR_SUCCESS) {
616         RegSetValueExA(hkey, devname, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
617         RegCloseKey(hkey);
618     }
619 
620     HeapFree(GetProcessHeap(),0,devline);
621     
622     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
623        ERROR_SUCCESS) {
624         ERR("Can't create Printers key\n");
625         ret = FALSE;
626         goto end;
627     }
628     if(RegOpenKeyA(hkeyPrinters, devname, &hkeyPrinter) == ERROR_SUCCESS) {
629         /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
630            and continue */
631         TRACE("Printer already exists\n");
632         RegDeleteValueW(hkeyPrinter, May_Delete_Value);
633         RegCloseKey(hkeyPrinter);
634     } else {
635         static CHAR data_type[]   = "RAW",
636                     print_proc[]  = "WinPrint",
637                     comment[]     = "WINEPS Printer using LPR",
638                     params[]      = "<parameters?>",
639                     share_name[]  = "<share name?>",
640                     sep_file[]    = "<sep file?>";
641 
642         add_printer_driver(devname);
643 
644         memset(&pinfo2a,0,sizeof(pinfo2a));
645         pinfo2a.pPrinterName    = devname;
646         pinfo2a.pDatatype       = data_type;
647         pinfo2a.pPrintProcessor = print_proc;
648         pinfo2a.pDriverName     = devname;
649         pinfo2a.pComment        = comment;
650         pinfo2a.pLocation       = prettyname;
651         pinfo2a.pPortName       = port;
652         pinfo2a.pParameters     = params;
653         pinfo2a.pShareName      = share_name;
654         pinfo2a.pSepFile        = sep_file;
655 
656         if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
657             if (GetLastError()!=ERROR_PRINTER_ALREADY_EXISTS)
658                 ERR("%s not added by AddPrinterA (%d)\n",name,GetLastError());
659         }
660     }
661     RegCloseKey(hkeyPrinters);
662 
663     if (isfirst || set_default)
664         WINSPOOL_SetDefaultPrinter(devname,name,TRUE);
665 
666  end:
667     HeapFree(GetProcessHeap(), 0, port);
668     HeapFree(GetProcessHeap(), 0, name);
669     return ret;
670 }
671 
672 static BOOL
673 PRINTCAP_LoadPrinters(void) {
674     BOOL                hadprinter = FALSE;
675     char                buf[200];
676     FILE                *f;
677     char *pent = NULL;
678     BOOL had_bash = FALSE;
679 
680     f = fopen("/etc/printcap","r");
681     if (!f)
682         return FALSE;
683 
684     while(fgets(buf,sizeof(buf),f)) {
685         char *start, *end;
686 
687         end=strchr(buf,'\n');
688         if (end) *end='\0';
689     
690         start = buf;
691         while(isspace(*start)) start++;
692         if(*start == '#' || *start == '\0')
693             continue;
694 
695         if(pent && !had_bash && *start != ':' && *start != '|') { /* start of new entry, parse the previous one */
696             hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
697             HeapFree(GetProcessHeap(),0,pent);
698             pent = NULL;
699         }
700 
701         if (end && *--end == '\\') {
702             *end = '\0';
703             had_bash = TRUE;
704         } else
705             had_bash = FALSE;
706 
707         if (pent) {
708             pent=HeapReAlloc(GetProcessHeap(),0,pent,strlen(pent)+strlen(start)+1);
709             strcat(pent,start);
710         } else {
711             pent=HeapAlloc(GetProcessHeap(),0,strlen(start)+1);
712             strcpy(pent,start);
713         }
714 
715     }
716     if(pent) {
717         hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
718         HeapFree(GetProcessHeap(),0,pent);
719     }
720     fclose(f);
721     return hadprinter;
722 }
723 
724 static inline DWORD set_reg_szW(HKEY hkey, const WCHAR *keyname, const WCHAR *value)
725 {
726     if (value)
727         return RegSetValueExW(hkey, keyname, 0, REG_SZ, (const BYTE*)value,
728                               (lstrlenW(value) + 1) * sizeof(WCHAR));
729     else
730         return ERROR_FILE_NOT_FOUND;
731 }
732 
733 /******************************************************************
734  * get_servername_from_name  (internal)
735  *
736  * for an external server, a copy of the serverpart from the full name is returned
737  *
738  */
739 static LPWSTR get_servername_from_name(LPCWSTR name)
740 {
741     LPWSTR  server;
742     LPWSTR  ptr;
743     WCHAR   buffer[MAX_PATH];
744     DWORD   len;
745 
746     if (name == NULL) return NULL;
747     if ((name[0] != '\\') || (name[1] != '\\')) return NULL;
748 
749     server = strdupW(&name[2]);     /* skip over both backslash */
750     if (server == NULL) return NULL;
751 
752     /* strip '\' and the printername */
753     ptr = strchrW(server, '\\');
754     if (ptr) ptr[0] = '\0';
755 
756     TRACE("found %s\n", debugstr_w(server));
757 
758     len = sizeof(buffer)/sizeof(buffer[0]);
759     if (GetComputerNameW(buffer, &len)) {
760         if (lstrcmpW(buffer, server) == 0) {
761             /* The requested Servername is our computername */
762             HeapFree(GetProcessHeap(), 0, server);
763             return NULL;
764         }
765     }
766     return server;
767 }
768 
769 /******************************************************************
770  * get_basename_from_name  (internal)
771  *
772  * skip over the serverpart from the full name
773  *
774  */
775 static LPCWSTR get_basename_from_name(LPCWSTR name)
776 {
777     if (name == NULL)  return NULL;
778     if ((name[0] == '\\') && (name[1] == '\\')) {
779         /* skip over the servername and search for the following '\'  */
780         name = strchrW(&name[2], '\\');
781         if ((name) && (name[1])) {
782             /* found a separator ('\') followed by a name:
783                skip over the separator and return the rest */
784             name++;
785         }
786         else
787         {
788             /* no basename present (we found only a servername) */
789             return NULL;
790         }
791     }
792     return name;
793 }
794 
795 /******************************************************************
796  *  get_opened_printer_entry
797  *  Get the first place empty in the opened printer table
798  *
799  * ToDo:
800  *  - pDefault is ignored
801  */
802 static HANDLE get_opened_printer_entry(LPWSTR name, LPPRINTER_DEFAULTSW pDefault)
803 {
804     UINT_PTR handle = nb_printer_handles, i;
805     jobqueue_t *queue = NULL;
806     opened_printer_t *printer = NULL;
807     LPWSTR  servername;
808     LPCWSTR printername;
809 
810     if ((backend == NULL)  && !load_backend()) return NULL;
811 
812     servername = get_servername_from_name(name);
813     if (servername) {
814         FIXME("server %s not supported\n", debugstr_w(servername));
815         HeapFree(GetProcessHeap(), 0, servername);
816         SetLastError(ERROR_INVALID_PRINTER_NAME);
817         return NULL;
818     }
819 
820     printername = get_basename_from_name(name);
821     if (name != printername) TRACE("converted %s to %s\n", debugstr_w(name), debugstr_w(printername));
822 
823     /* an empty printername is invalid */
824     if (printername && (!printername[0])) {
825         SetLastError(ERROR_INVALID_PARAMETER);
826         return NULL;
827     }
828 
829     EnterCriticalSection(&printer_handles_cs);
830 
831     for (i = 0; i < nb_printer_handles; i++)
832     {
833         if (!printer_handles[i])
834         {
835             if(handle == nb_printer_handles)
836                 handle = i;
837         }
838         else
839         {
840             if(!queue && (name) && !lstrcmpW(name, printer_handles[i]->name))
841                 queue = printer_handles[i]->queue;
842         }
843     }
844 
845     if (handle >= nb_printer_handles)
846     {
847         opened_printer_t **new_array;
848         if (printer_handles)
849             new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, printer_handles,
850                                      (nb_printer_handles + 16) * sizeof(*new_array) );
851         else
852             new_array = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
853                                    (nb_printer_handles + 16) * sizeof(*new_array) );
854 
855         if (!new_array)
856         {
857             handle = 0;
858             goto end;
859         }
860         printer_handles = new_array;
861         nb_printer_handles += 16;
862     }
863 
864     if (!(printer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*printer))))
865     {
866         handle = 0;
867         goto end;
868     }
869 
870     /* get a printer handle from the backend */
871     if (! backend->fpOpenPrinter(name, &printer->backend_printer, pDefault)) {
872         handle = 0;
873         goto end;
874     }
875 
876     /* clone the base name. This is NULL for the printserver */
877     printer->printername = strdupW(printername);
878 
879     /* clone the full name */
880     printer->name = strdupW(name);
881     if (name && (!printer->name)) {
882         handle = 0;
883         goto end;
884     }
885 
886     if(queue)
887         printer->queue = queue;
888     else
889     {
890         printer->queue = HeapAlloc(GetProcessHeap(), 0, sizeof(*queue));
891         if (!printer->queue) {
892             handle = 0;
893             goto end;
894         }
895         list_init(&printer->queue->jobs);
896         printer->queue->ref = 0;
897     }
898     InterlockedIncrement(&printer->queue->ref);
899 
900     printer_handles[handle] = printer;
901     handle++;
902 end:
903     LeaveCriticalSection(&printer_handles_cs);
904     if (!handle && printer) {
905         /* Something failed: Free all resources */
906         HeapFree(GetProcessHeap(), 0, printer->printername);
907         HeapFree(GetProcessHeap(), 0, printer->name);
908         if (!queue) HeapFree(GetProcessHeap(), 0, printer->queue);
909         HeapFree(GetProcessHeap(), 0, printer);
910     }
911 
912     return (HANDLE)handle;
913 }
914 
915 /******************************************************************
916  *  get_opened_printer
917  *  Get the pointer to the opened printer referred by the handle
918  */
919 static opened_printer_t *get_opened_printer(HANDLE hprn)
920 {
921     UINT_PTR idx = (UINT_PTR)hprn;
922     opened_printer_t *ret = NULL;
923 
924     EnterCriticalSection(&printer_handles_cs);
925 
926     if ((idx > 0) && (idx <= nb_printer_handles)) {
927         ret = printer_handles[idx - 1];
928     }
929     LeaveCriticalSection(&printer_handles_cs);
930     return ret;
931 }
932 
933 /******************************************************************
934  *  get_opened_printer_name
935  *  Get the pointer to the opened printer name referred by the handle
936  */
937 static LPCWSTR get_opened_printer_name(HANDLE hprn)
938 {
939     opened_printer_t *printer = get_opened_printer(hprn);
940     if(!printer) return NULL;
941     return printer->name;
942 }
943 
944 /******************************************************************
945  *  WINSPOOL_GetOpenedPrinterRegKey
946  *
947  */
948 static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey)
949 {
950     LPCWSTR name = get_opened_printer_name(hPrinter);
951     DWORD ret;
952     HKEY hkeyPrinters;
953 
954     if(!name) return ERROR_INVALID_HANDLE;
955 
956     if((ret = RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters)) !=
957        ERROR_SUCCESS)
958         return ret;
959 
960     if(RegOpenKeyW(hkeyPrinters, name, phkey) != ERROR_SUCCESS)
961     {
962         ERR("Can't find opened printer %s in registry\n",
963             debugstr_w(name));
964         RegCloseKey(hkeyPrinters);
965         return ERROR_INVALID_PRINTER_NAME; /* ? */
966     }
967     RegCloseKey(hkeyPrinters);
968     return ERROR_SUCCESS;
969 }
970 
971 void WINSPOOL_LoadSystemPrinters(void)
972 {
973     HKEY                hkey, hkeyPrinters;
974     HANDLE              hprn;
975     DWORD               needed, num, i;
976     WCHAR               PrinterName[256];
977     BOOL                done = FALSE;
978 
979     /* This ensures that all printer entries have a valid Name value.  If causes
980        problems later if they don't.  If one is found to be missed we create one
981        and set it equal to the name of the key */
982     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
983         if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &num, NULL, NULL,
984                             NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
985             for(i = 0; i < num; i++) {
986                 if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)/sizeof(PrinterName[0])) == ERROR_SUCCESS) {
987                     if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkey) == ERROR_SUCCESS) {
988                         if(RegQueryValueExW(hkey, NameW, 0, 0, 0, &needed) == ERROR_FILE_NOT_FOUND) {
989                             set_reg_szW(hkey, NameW, PrinterName);
990                         }
991                         RegCloseKey(hkey);
992                     }
993                 }
994             }
995         }
996         RegCloseKey(hkeyPrinters);
997     }
998 
999     /* We want to avoid calling AddPrinter on printers as much as
1000        possible, because on cups printers this will (eventually) lead
1001        to a call to cupsGetPPD which takes forever, even with non-cups
1002        printers AddPrinter takes a while.  So we'll tag all printers that
1003        were automatically added last time around, if they still exist
1004        we'll leave them be otherwise we'll delete them. */
1005     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
1006     if(needed) {
1007         PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
1008         if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
1009             for(i = 0; i < num; i++) {
1010                 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
1011                     if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
1012                         if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
1013                             DWORD dw = 1;
1014                             RegSetValueExW(hkey, May_Delete_Value, 0, REG_DWORD, (LPBYTE)&dw, sizeof(dw));
1015                             RegCloseKey(hkey);
1016                         }
1017                         ClosePrinter(hprn);
1018                     }
1019                 }
1020             }
1021         }
1022         HeapFree(GetProcessHeap(), 0, pi);
1023     }
1024 
1025 
1026 #ifdef SONAME_LIBCUPS
1027     done = CUPS_LoadPrinters();
1028 #endif
1029 
1030     if(!done) /* If we have any CUPS based printers, skip looking for printcap printers */
1031         PRINTCAP_LoadPrinters();
1032 
1033     /* Now enumerate the list again and delete any printers that are still tagged */
1034     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
1035     if(needed) {
1036         PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
1037         if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
1038             for(i = 0; i < num; i++) {
1039                 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
1040                     if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
1041                         BOOL delete_driver = FALSE;
1042                         if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
1043                             DWORD dw, type, size = sizeof(dw);
1044                             if(RegQueryValueExW(hkey, May_Delete_Value, NULL, &type, (LPBYTE)&dw, &size) == ERROR_SUCCESS) {
1045                                 TRACE("Deleting old printer %s\n", pi[i].pPrinterName);
1046                                 DeletePrinter(hprn);
1047                                 delete_driver = TRUE;
1048                             }
1049                             RegCloseKey(hkey);
1050                         }
1051                         ClosePrinter(hprn);
1052                         if(delete_driver)
1053                             DeletePrinterDriverExA(NULL, NULL, pi[i].pPrinterName, 0, 0);
1054                     }
1055                 }
1056             }
1057         }
1058         HeapFree(GetProcessHeap(), 0, pi);
1059     }
1060 
1061     return;
1062 
1063 }
1064 
1065 /******************************************************************
1066  *                  get_job
1067  *
1068  *  Get the pointer to the specified job.
1069  *  Should hold the printer_handles_cs before calling.
1070  */
1071 static job_t *get_job(HANDLE hprn, DWORD JobId)
1072 {
1073     opened_printer_t *printer = get_opened_printer(hprn);
1074     job_t *job;
1075 
1076     if(!printer) return NULL;
1077     LIST_FOR_EACH_ENTRY(job, &printer->queue->jobs, job_t, entry)
1078     {
1079         if(job->job_id == JobId)
1080             return job;
1081     }
1082     return NULL;
1083 }
1084 
1085 /***********************************************************
1086  *      DEVMODEcpyAtoW
1087  */
1088 static LPDEVMODEW DEVMODEcpyAtoW(DEVMODEW *dmW, const DEVMODEA *dmA)
1089 {
1090     BOOL Formname;
1091     ptrdiff_t off_formname = (const char *)dmA->dmFormName - (const char *)dmA;
1092     DWORD size;
1093 
1094     Formname = (dmA->dmSize > off_formname);
1095     size = dmA->dmSize + CCHDEVICENAME + (Formname ? CCHFORMNAME : 0);
1096     MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmDeviceName, -1,
1097                         dmW->dmDeviceName, CCHDEVICENAME);
1098     if(!Formname) {
1099       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
1100              dmA->dmSize - CCHDEVICENAME);
1101     } else {
1102       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
1103              off_formname - CCHDEVICENAME);
1104       MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmFormName, -1,
1105                           dmW->dmFormName, CCHFORMNAME);
1106       memcpy(&dmW->dmLogPixels, &dmA->dmLogPixels, dmA->dmSize -
1107              (off_formname + CCHFORMNAME));
1108     }
1109     dmW->dmSize = size;
1110     memcpy((char *)dmW + dmW->dmSize, (const char *)dmA + dmA->dmSize,
1111            dmA->dmDriverExtra);
1112     return dmW;
1113 }
1114 
1115 /***********************************************************
1116  * DEVMODEdupWtoA
1117  * Creates an ansi copy of supplied devmode
1118  */
1119 static LPDEVMODEA DEVMODEdupWtoA(const DEVMODEW *dmW)
1120 {
1121     LPDEVMODEA dmA;
1122     DWORD size;
1123 
1124     if (!dmW) return NULL;
1125     size = dmW->dmSize - CCHDEVICENAME -
1126                         ((dmW->dmSize > FIELD_OFFSET(DEVMODEW, dmFormName)) ? CCHFORMNAME : 0);
1127 
1128     dmA = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size + dmW->dmDriverExtra);
1129     if (!dmA) return NULL;
1130 
1131     WideCharToMultiByte(CP_ACP, 0, dmW->dmDeviceName, -1,
1132                         (LPSTR)dmA->dmDeviceName, CCHDEVICENAME, NULL, NULL);
1133 
1134     if (FIELD_OFFSET(DEVMODEW, dmFormName) >= dmW->dmSize) {
1135         memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
1136                dmW->dmSize - FIELD_OFFSET(DEVMODEW, dmSpecVersion));
1137     }
1138     else
1139     {
1140         memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
1141                FIELD_OFFSET(DEVMODEW, dmFormName) - FIELD_OFFSET(DEVMODEW, dmSpecVersion));
1142         WideCharToMultiByte(CP_ACP, 0, dmW->dmFormName, -1,
1143                             (LPSTR)dmA->dmFormName, CCHFORMNAME, NULL, NULL);
1144 
1145         memcpy(&dmA->dmLogPixels, &dmW->dmLogPixels, dmW->dmSize - FIELD_OFFSET(DEVMODEW, dmLogPixels));
1146     }
1147 
1148     dmA->dmSize = size;
1149     memcpy((char *)dmA + dmA->dmSize, (const char *)dmW + dmW->dmSize, dmW->dmDriverExtra);
1150     return dmA;
1151 }
1152 
1153 /******************************************************************
1154  * convert_printerinfo_W_to_A [internal]
1155  *
1156  */
1157 static void convert_printerinfo_W_to_A(LPBYTE out, LPBYTE pPrintersW,
1158                                        DWORD level, DWORD outlen, DWORD numentries)
1159 {
1160     DWORD id = 0;
1161     LPSTR ptr;
1162     INT len;
1163 
1164     TRACE("(%p, %p, %d, %u, %u)\n", out, pPrintersW, level, outlen, numentries);
1165 
1166     len = pi_sizeof[level] * numentries;
1167     ptr = (LPSTR) out + len;
1168     outlen -= len;
1169 
1170     /* copy the numbers of all PRINTER_INFO_* first */
1171     memcpy(out, pPrintersW, len);
1172 
1173     while (id < numentries) {
1174         switch (level) {
1175             case 1:
1176                 {
1177                     PRINTER_INFO_1W * piW = (PRINTER_INFO_1W *) pPrintersW;
1178                     PRINTER_INFO_1A * piA = (PRINTER_INFO_1A *) out;
1179 
1180                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(piW->pName));
1181                     if (piW->pDescription) {
1182                         piA->pDescription = ptr;
1183                         len = WideCharToMultiByte(CP_ACP, 0, piW->pDescription, -1,
1184                                                   ptr, outlen, NULL, NULL);
1185                         ptr += len;
1186                         outlen -= len;
1187                     }
1188                     if (piW->pName) {
1189                         piA->pName = ptr;
1190                         len = WideCharToMultiByte(CP_ACP, 0, piW->pName, -1,
1191                                                   ptr, outlen, NULL, NULL);
1192                         ptr += len;
1193                         outlen -= len;
1194                     }
1195                     if (piW->pComment) {
1196                         piA->pComment = ptr;
1197                         len = WideCharToMultiByte(CP_ACP, 0, piW->pComment, -1,
1198                                                   ptr, outlen, NULL, NULL);
1199                         ptr += len;
1200                         outlen -= len;
1201                     }
1202                     break;
1203                 }
1204 
1205             case 2:
1206                 {
1207                     PRINTER_INFO_2W * piW = (PRINTER_INFO_2W *) pPrintersW;
1208                     PRINTER_INFO_2A * piA = (PRINTER_INFO_2A *) out;
1209                     LPDEVMODEA dmA;
1210 
1211                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(piW->pPrinterName));
1212                     if (piW->pServerName) {
1213                         piA->pServerName = ptr;
1214                         len = WideCharToMultiByte(CP_ACP, 0, piW->pServerName, -1,
1215                                                   ptr, outlen, NULL, NULL);
1216                         ptr += len;
1217                         outlen -= len;
1218                     }
1219                     if (piW->pPrinterName) {
1220                         piA->pPrinterName = ptr;
1221                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPrinterName, -1,
1222                                                   ptr, outlen, NULL, NULL);
1223                         ptr += len;
1224                         outlen -= len;
1225                     }
1226                     if (piW->pShareName) {
1227                         piA->pShareName = ptr;
1228                         len = WideCharToMultiByte(CP_ACP, 0, piW->pShareName, -1,
1229                                                   ptr, outlen, NULL, NULL);
1230                         ptr += len;
1231                         outlen -= len;
1232                     }
1233                     if (piW->pPortName) {
1234                         piA->pPortName = ptr;
1235                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPortName, -1,
1236                                                   ptr, outlen, NULL, NULL);
1237                         ptr += len;
1238                         outlen -= len;
1239                     }
1240                     if (piW->pDriverName) {
1241                         piA->pDriverName = ptr;
1242                         len = WideCharToMultiByte(CP_ACP, 0, piW->pDriverName, -1,
1243                                                   ptr, outlen, NULL, NULL);
1244                         ptr += len;
1245                         outlen -= len;
1246                     }
1247                     if (piW->pComment) {
1248                         piA->pComment = ptr;
1249                         len = WideCharToMultiByte(CP_ACP, 0, piW->pComment, -1,
1250                                                   ptr, outlen, NULL, NULL);
1251                         ptr += len;
1252                         outlen -= len;
1253                     }
1254                     if (piW->pLocation) {
1255                         piA->pLocation = ptr;
1256                         len = WideCharToMultiByte(CP_ACP, 0, piW->pLocation, -1,
1257                                                   ptr, outlen, NULL, NULL);
1258                         ptr += len;
1259                         outlen -= len;
1260                     }
1261 
1262                     dmA = DEVMODEdupWtoA(piW->pDevMode);
1263                     if (dmA) {
1264                         /* align DEVMODEA to a DWORD boundary */
1265                         len = (4 - ( (DWORD_PTR) ptr & 3)) & 3;
1266                         ptr += len;
1267                         outlen -= len;
1268 
1269                         piA->pDevMode = (LPDEVMODEA) ptr;
1270                         len = dmA->dmSize + dmA->dmDriverExtra;
1271                         memcpy(ptr, dmA, len);
1272                         HeapFree(GetProcessHeap(), 0, dmA);
1273 
1274                         ptr += len;
1275                         outlen -= len;
1276                     }
1277 
1278                     if (piW->pSepFile) {
1279                         piA->pSepFile = ptr;
1280                         len = WideCharToMultiByte(CP_ACP, 0, piW->pSepFile, -1,
1281                                                   ptr, outlen, NULL, NULL);
1282                         ptr += len;
1283                         outlen -= len;
1284                     }
1285                     if (piW->pPrintProcessor) {
1286                         piA->pPrintProcessor = ptr;
1287                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPrintProcessor, -1,
1288                                                   ptr, outlen, NULL, NULL);
1289                         ptr += len;
1290                         outlen -= len;
1291                     }
1292                     if (piW->pDatatype) {
1293                         piA->pDatatype = ptr;
1294                         len = WideCharToMultiByte(CP_ACP, 0, piW->pDatatype, -1,
1295                                                   ptr, outlen, NULL, NULL);
1296                         ptr += len;
1297                         outlen -= len;
1298                     }
1299                     if (piW->pParameters) {
1300                         piA->pParameters = ptr;
1301                         len = WideCharToMultiByte(CP_ACP, 0, piW->pParameters, -1,
1302                                                   ptr, outlen, NULL, NULL);
1303                         ptr += len;
1304                         outlen -= len;
1305                     }
1306                     if (piW->pSecurityDescriptor) {
1307                         piA->pSecurityDescriptor = NULL;
1308                         FIXME("pSecurityDescriptor ignored: %s\n", debugstr_w(piW->pPrinterName));
1309                     }
1310                     break;
1311                 }
1312 
1313             case 4:
1314                 {
1315                     PRINTER_INFO_4W * piW = (PRINTER_INFO_4W *) pPrintersW;
1316                     PRINTER_INFO_4A * piA = (PRINTER_INFO_4A *) out;
1317 
1318                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(piW->pPrinterName));
1319 
1320                     if (piW->pPrinterName) {
1321                         piA->pPrinterName = ptr;
1322                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPrinterName, -1,
1323                                                   ptr, outlen, NULL, NULL);
1324                         ptr += len;
1325                         outlen -= len;
1326                     }
1327                     if (piW->pServerName) {
1328                         piA->pServerName = ptr;
1329                         len = WideCharToMultiByte(CP_ACP, 0, piW->pServerName, -1,
1330                                                   ptr, outlen, NULL, NULL);
1331                         ptr += len;
1332                         outlen -= len;
1333                     }
1334                     break;
1335                 }
1336 
1337             case 5:
1338                 {
1339                     PRINTER_INFO_5W * piW = (PRINTER_INFO_5W *) pPrintersW;
1340                     PRINTER_INFO_5A * piA = (PRINTER_INFO_5A *) out;
1341 
1342                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(piW->pPrinterName));
1343 
1344                     if (piW->pPrinterName) {
1345                         piA->pPrinterName = ptr;
1346                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPrinterName, -1,
1347                                                   ptr, outlen, NULL, NULL);
1348                         ptr += len;
1349                         outlen -= len;
1350                     }
1351                     if (piW->pPortName) {
1352                         piA->pPortName = ptr;
1353                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPortName, -1,
1354                                                   ptr, outlen, NULL, NULL);
1355                         ptr += len;
1356                         outlen -= len;
1357                     }
1358                     break;
1359                 }
1360 
1361             default:
1362                 FIXME("for level %u\n", level);
1363         }
1364         pPrintersW += pi_sizeof[level];
1365         out += pi_sizeof[level];
1366         id++;
1367     }
1368 }
1369 
1370 /***********************************************************
1371  *             PRINTER_INFO_2AtoW
1372  * Creates a unicode copy of PRINTER_INFO_2A on heap
1373  */
1374 static LPPRINTER_INFO_2W PRINTER_INFO_2AtoW(HANDLE heap, LPPRINTER_INFO_2A piA)
1375 {
1376     LPPRINTER_INFO_2W piW;
1377     UNICODE_STRING usBuffer;
1378 
1379     if(!piA) return NULL;
1380     piW = HeapAlloc(heap, 0, sizeof(*piW));
1381     memcpy(piW, piA, sizeof(*piW)); /* copy everything first */
1382     
1383     piW->pServerName = asciitounicode(&usBuffer,piA->pServerName);
1384     piW->pPrinterName = asciitounicode(&usBuffer,piA->pPrinterName);
1385     piW->pShareName = asciitounicode(&usBuffer,piA->pShareName);
1386     piW->pPortName = asciitounicode(&usBuffer,piA->pPortName);
1387     piW->pDriverName = asciitounicode(&usBuffer,piA->pDriverName);
1388     piW->pComment = asciitounicode(&usBuffer,piA->pComment);
1389     piW->pLocation = asciitounicode(&usBuffer,piA->pLocation);
1390     piW->pDevMode = piA->pDevMode ? GdiConvertToDevmodeW(piA->pDevMode) : NULL;
1391     piW->pSepFile = asciitounicode(&usBuffer,piA->pSepFile);
1392     piW->pPrintProcessor = asciitounicode(&usBuffer,piA->pPrintProcessor);
1393     piW->pDatatype = asciitounicode(&usBuffer,piA->pDatatype);
1394     piW->pParameters = asciitounicode(&usBuffer,piA->pParameters);
1395     return piW;
1396 }
1397 
1398 /***********************************************************
1399  *       FREE_PRINTER_INFO_2W
1400  * Free PRINTER_INFO_2W and all strings
1401  */
1402 static void FREE_PRINTER_INFO_2W(HANDLE heap, LPPRINTER_INFO_2W piW)
1403 {
1404     if(!piW) return;
1405 
1406     HeapFree(heap,0,piW->pServerName);
1407     HeapFree(heap,0,piW->pPrinterName);
1408     HeapFree(heap,0,piW->pShareName);
1409     HeapFree(heap,0,piW->pPortName);
1410     HeapFree(heap,0,piW->pDriverName);
1411     HeapFree(heap,0,piW->pComment);
1412     HeapFree(heap,0,piW->pLocation);
1413     HeapFree(heap,0,piW->pDevMode);
1414     HeapFree(heap,0,piW->pSepFile);
1415     HeapFree(heap,0,piW->pPrintProcessor);
1416     HeapFree(heap,0,piW->pDatatype);
1417     HeapFree(heap,0,piW->pParameters);
1418     HeapFree(heap,0,piW);
1419     return;
1420 }
1421 
1422 /******************************************************************
1423  *              DeviceCapabilities     [WINSPOOL.@]
1424  *              DeviceCapabilitiesA    [WINSPOOL.@]
1425  *
1426  */
1427 INT WINAPI DeviceCapabilitiesA(LPCSTR pDevice,LPCSTR pPort, WORD cap,
1428                                LPSTR pOutput, LPDEVMODEA lpdm)
1429 {
1430     INT ret;
1431 
1432     if (!GDI_CallDeviceCapabilities16)
1433     {
1434         GDI_CallDeviceCapabilities16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1435                                                               (LPCSTR)104 );
1436         if (!GDI_CallDeviceCapabilities16) return -1;
1437     }
1438     ret = GDI_CallDeviceCapabilities16(pDevice, pPort, cap, pOutput, lpdm);
1439 
1440     /* If DC_PAPERSIZE map POINT16s to POINTs */
1441     if(ret != -1 && cap == DC_PAPERSIZE && pOutput) {
1442         POINT16 *tmp = HeapAlloc( GetProcessHeap(), 0, ret * sizeof(POINT16) );
1443         POINT *pt = (POINT *)pOutput;
1444         INT i;
1445         memcpy(tmp, pOutput, ret * sizeof(POINT16));
1446         for(i = 0; i < ret; i++, pt++)
1447         {
1448             pt->x = tmp[i].x;
1449             pt->y = tmp[i].y;
1450         }
1451         HeapFree( GetProcessHeap(), 0, tmp );
1452     }
1453     return ret;
1454 }
1455 
1456 
1457 /*****************************************************************************
1458  *          DeviceCapabilitiesW        [WINSPOOL.@]
1459  *
1460  * Call DeviceCapabilitiesA since we later call 16bit stuff anyway
1461  *
1462  */
1463 INT WINAPI DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort,
1464                                WORD fwCapability, LPWSTR pOutput,
1465                                const DEVMODEW *pDevMode)
1466 {
1467     LPDEVMODEA dmA = DEVMODEdupWtoA(pDevMode);
1468     LPSTR pDeviceA = strdupWtoA(pDevice);
1469     LPSTR pPortA = strdupWtoA(pPort);
1470     INT ret;
1471 
1472     if(pOutput && (fwCapability == DC_BINNAMES ||
1473                    fwCapability == DC_FILEDEPENDENCIES ||
1474                    fwCapability == DC_PAPERNAMES)) {
1475       /* These need A -> W translation */
1476         INT size = 0, i;
1477         LPSTR pOutputA;
1478         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, NULL,
1479                                   dmA);
1480         if(ret == -1)
1481             return ret;
1482         switch(fwCapability) {
1483         case DC_BINNAMES:
1484             size = 24;
1485             break;
1486         case DC_PAPERNAMES:
1487         case DC_FILEDEPENDENCIES:
1488             size = 64;
1489             break;
1490         }
1491         pOutputA = HeapAlloc(GetProcessHeap(), 0, size * ret);
1492         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, pOutputA,
1493                                   dmA);
1494         for(i = 0; i < ret; i++)
1495             MultiByteToWideChar(CP_ACP, 0, pOutputA + (i * size), -1,
1496                                 pOutput + (i * size), size);
1497         HeapFree(GetProcessHeap(), 0, pOutputA);
1498     } else {
1499         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability,
1500                                   (LPSTR)pOutput, dmA);
1501     }
1502     HeapFree(GetProcessHeap(),0,pPortA);
1503     HeapFree(GetProcessHeap(),0,pDeviceA);
1504     HeapFree(GetProcessHeap(),0,dmA);
1505     return ret;
1506 }
1507 
1508 /******************************************************************
1509  *              DocumentPropertiesA   [WINSPOOL.@]
1510  *
1511  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1512  */
1513 LONG WINAPI DocumentPropertiesA(HWND hWnd,HANDLE hPrinter,
1514                                 LPSTR pDeviceName, LPDEVMODEA pDevModeOutput,
1515                                 LPDEVMODEA pDevModeInput,DWORD fMode )
1516 {
1517     LPSTR lpName = pDeviceName;
1518     static CHAR port[] = "LPT1:";
1519     LONG ret;
1520 
1521     TRACE("(%p,%p,%s,%p,%p,%d)\n",
1522         hWnd,hPrinter,pDeviceName,pDevModeOutput,pDevModeInput,fMode
1523     );
1524 
1525     if(!pDeviceName) {
1526         LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
1527         if(!lpNameW) {
1528                 ERR("no name from hPrinter?\n");
1529                 SetLastError(ERROR_INVALID_HANDLE);
1530                 return -1;
1531         }
1532         lpName = strdupWtoA(lpNameW);
1533     }
1534 
1535     if (!GDI_CallExtDeviceMode16)
1536     {
1537         GDI_CallExtDeviceMode16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1538                                                          (LPCSTR)102 );
1539         if (!GDI_CallExtDeviceMode16) {
1540                 ERR("No CallExtDeviceMode16?\n");
1541                 return -1;
1542         }
1543     }
1544     ret = GDI_CallExtDeviceMode16(hWnd, pDevModeOutput, lpName, port,
1545                                   pDevModeInput, NULL, fMode);
1546 
1547     if(!pDeviceName)
1548         HeapFree(GetProcessHeap(),0,lpName);
1549     return ret;
1550 }
1551 
1552 
1553 /*****************************************************************************
1554  *          DocumentPropertiesW (WINSPOOL.@)
1555  *
1556  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1557  */
1558 LONG WINAPI DocumentPropertiesW(HWND hWnd, HANDLE hPrinter,
1559                                 LPWSTR pDeviceName,
1560                                 LPDEVMODEW pDevModeOutput,
1561                                 LPDEVMODEW pDevModeInput, DWORD fMode)
1562 {
1563 
1564     LPSTR pDeviceNameA = strdupWtoA(pDeviceName);
1565     LPDEVMODEA pDevModeInputA = DEVMODEdupWtoA(pDevModeInput);
1566     LPDEVMODEA pDevModeOutputA = NULL;
1567     LONG ret;
1568 
1569     TRACE("(%p,%p,%s,%p,%p,%d)\n",
1570           hWnd,hPrinter,debugstr_w(pDeviceName),pDevModeOutput,pDevModeInput,
1571           fMode);
1572     if(pDevModeOutput) {
1573         ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, NULL, NULL, 0);
1574         if(ret < 0) return ret;
1575         pDevModeOutputA = HeapAlloc(GetProcessHeap(), 0, ret);
1576     }
1577     ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, pDevModeOutputA,
1578                               pDevModeInputA, fMode);
1579     if(pDevModeOutput) {
1580         DEVMODEcpyAtoW(pDevModeOutput, pDevModeOutputA);
1581         HeapFree(GetProcessHeap(),0,pDevModeOutputA);
1582     }
1583     if(fMode == 0 && ret > 0)
1584         ret += (CCHDEVICENAME + CCHFORMNAME);
1585     HeapFree(GetProcessHeap(),0,pDevModeInputA);
1586     HeapFree(GetProcessHeap(),0,pDeviceNameA);
1587     return ret;
1588 }
1589 
1590 /******************************************************************
1591  *              OpenPrinterA        [WINSPOOL.@]
1592  *
1593  * See OpenPrinterW.
1594  *
1595  */
1596 BOOL WINAPI OpenPrinterA(LPSTR lpPrinterName,HANDLE *phPrinter,
1597                          LPPRINTER_DEFAULTSA pDefault)
1598 {
1599     UNICODE_STRING lpPrinterNameW;
1600     UNICODE_STRING usBuffer;
1601     PRINTER_DEFAULTSW DefaultW, *pDefaultW = NULL;
1602     PWSTR pwstrPrinterNameW;
1603     BOOL ret;
1604 
1605     pwstrPrinterNameW = asciitounicode(&lpPrinterNameW,lpPrinterName);
1606 
1607     if(pDefault) {
1608         DefaultW.pDatatype = asciitounicode(&usBuffer,pDefault->pDatatype);
1609         DefaultW.pDevMode = pDefault->pDevMode ? GdiConvertToDevmodeW(pDefault->pDevMode) : NULL;
1610         DefaultW.DesiredAccess = pDefault->DesiredAccess;
1611         pDefaultW = &DefaultW;
1612     }
1613     ret = OpenPrinterW(pwstrPrinterNameW, phPrinter, pDefaultW);
1614     if(pDefault) {
1615         RtlFreeUnicodeString(&usBuffer);
1616         HeapFree(GetProcessHeap(), 0, DefaultW.pDevMode);
1617     }
1618     RtlFreeUnicodeString(&lpPrinterNameW);
1619     return ret;
1620 }
1621 
1622 /******************************************************************
1623  *              OpenPrinterW        [WINSPOOL.@]
1624  *
1625  * Open a Printer / Printserver or a Printer-Object
1626  *
1627  * PARAMS
1628  *  lpPrinterName [I] Name of Printserver, Printer, or Printer-Object
1629  *  phPrinter     [O] The resulting Handle is stored here
1630  *  pDefault      [I] PTR to Default Printer Settings or NULL
1631  *
1632  * RETURNS
1633  *  Success: TRUE
1634  *  Failure: FALSE
1635  *
1636  * NOTES
1637  *  lpPrinterName is one of:
1638  *|  Printserver (NT only): "Servername" or NULL for the local Printserver
1639  *|  Printer: "PrinterName"
1640  *|  Printer-Object: "PrinterName,Job xxx"
1641  *|  XcvMonitor: "Servername,XcvMonitor MonitorName"
1642  *|  XcvPort: "Servername,XcvPort PortName"
1643  *
1644  * BUGS
1645  *|  Printer-Object not supported
1646  *|  pDefaults is ignored
1647  *
1648  */
1649 BOOL WINAPI OpenPrinterW(LPWSTR lpPrinterName,HANDLE *phPrinter, LPPRINTER_DEFAULTSW pDefault)
1650 {
1651 
1652     TRACE("(%s, %p, %p)\n", debugstr_w(lpPrinterName), phPrinter, pDefault);
1653     if (pDefault) {
1654         FIXME("PRINTER_DEFAULTS ignored => %s,%p,0x%08x\n",
1655         debugstr_w(pDefault->pDatatype), pDefault->pDevMode, pDefault->DesiredAccess);
1656     }
1657 
1658     if(!phPrinter) {
1659         /* NT: FALSE with ERROR_INVALID_PARAMETER, 9x: TRUE */
1660         SetLastError(ERROR_INVALID_PARAMETER);
1661         return FALSE;
1662     }
1663 
1664     /* Get the unique handle of the printer or Printserver */
1665     *phPrinter = get_opened_printer_entry(lpPrinterName, pDefault);
1666     TRACE("returning %d with %u and %p\n", *phPrinter != NULL, GetLastError(), *phPrinter);
1667     return (*phPrinter != 0);
1668 }
1669 
1670 /******************************************************************
1671  *              AddMonitorA        [WINSPOOL.@]
1672  *
1673  * See AddMonitorW.
1674  *
1675  */
1676 BOOL WINAPI AddMonitorA(LPSTR pName, DWORD Level, LPBYTE pMonitors)
1677 {
1678     LPWSTR  nameW = NULL;
1679     INT     len;
1680     BOOL    res;
1681     LPMONITOR_INFO_2A mi2a;
1682     MONITOR_INFO_2W mi2w;
1683 
1684     mi2a = (LPMONITOR_INFO_2A) pMonitors;
1685     TRACE("(%s, %d, %p) :  %s %s %s\n", debugstr_a(pName), Level, pMonitors,
1686           debugstr_a(mi2a ? mi2a->pName : NULL),
1687           debugstr_a(mi2a ? mi2a->pEnvironment : NULL),
1688           debugstr_a(mi2a ? mi2a->pDLLName : NULL));
1689 
1690     if  (Level != 2) {
1691         SetLastError(ERROR_INVALID_LEVEL);
1692         return FALSE;
1693     }
1694 
1695     /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
1696     if (mi2a == NULL) {
1697         return FALSE;
1698     }
1699 
1700     if (pName) {
1701         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
1702         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1703         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
1704     }
1705 
1706     memset(&mi2w, 0, sizeof(MONITOR_INFO_2W));
1707     if (mi2a->pName) {
1708         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, NULL, 0);
1709         mi2w.pName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1710         MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, mi2w.pName, len);
1711     }
1712     if (mi2a->pEnvironment) {
1713         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, NULL, 0);
1714         mi2w.pEnvironment = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1715         MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, mi2w.pEnvironment, len);
1716     }
1717     if (mi2a->pDLLName) {
1718         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, NULL, 0);
1719         mi2w.pDLLName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1720         MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, mi2w.pDLLName, len);
1721     }
1722 
1723     res = AddMonitorW(nameW, Level, (LPBYTE) &mi2w);
1724 
1725     HeapFree(GetProcessHeap(), 0, mi2w.pName); 
1726     HeapFree(GetProcessHeap(), 0, mi2w.pEnvironment); 
1727     HeapFree(GetProcessHeap(), 0, mi2w.pDLLName); 
1728 
1729     HeapFree(GetProcessHeap(), 0, nameW); 
1730     return (res);
1731 }
1732 
1733 /******************************************************************************
1734  *              AddMonitorW        [WINSPOOL.@]
1735  *
1736  * Install a Printmonitor
1737  *
1738  * PARAMS
1739  *  pName       [I] Servername or NULL (local Computer)
1740  *  Level       [I] Structure-Level (Must be 2)
1741  *  pMonitors   [I] PTR to MONITOR_INFO_2
1742  *
1743  * RETURNS
1744  *  Success: TRUE
1745  *  Failure: FALSE
1746  *
1747  * NOTES
1748  *  All Files for the Monitor must already be copied to %winsysdir% ("%SystemRoot%\system32")
1749  *
1750  */
1751 BOOL WINAPI AddMonitorW(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
1752 {
1753     LPMONITOR_INFO_2W mi2w;
1754 
1755     mi2w = (LPMONITOR_INFO_2W) pMonitors;
1756     TRACE("(%s, %d, %p) :  %s %s %s\n", debugstr_w(pName), Level, pMonitors,
1757           debugstr_w(mi2w ? mi2w->pName : NULL),
1758           debugstr_w(mi2w ? mi2w->pEnvironment : NULL),
1759           debugstr_w(mi2w ? mi2w->pDLLName : NULL));
1760 
1761     if ((backend == NULL)  && !load_backend()) return FALSE;
1762 
1763     if (Level != 2) {
1764         SetLastError(ERROR_INVALID_LEVEL);
1765         return FALSE;
1766     }
1767 
1768     /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
1769     if (mi2w == NULL) {
1770         return FALSE;
1771     }
1772 
1773     return backend->fpAddMonitor(pName, Level, pMonitors);
1774 }
1775 
1776 /******************************************************************
1777  *              DeletePrinterDriverA        [WINSPOOL.@]
1778  *
1779  */
1780 BOOL WINAPI DeletePrinterDriverA (LPSTR pName, LPSTR pEnvironment, LPSTR pDriverName)
1781 {
1782     return DeletePrinterDriverExA(pName, pEnvironment, pDriverName, 0, 0);
1783 }
1784 
1785 /******************************************************************
1786  *              DeletePrinterDriverW        [WINSPOOL.@]
1787  *
1788  */
1789 BOOL WINAPI DeletePrinterDriverW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pDriverName)
1790 {
1791     return DeletePrinterDriverExW(pName, pEnvironment, pDriverName, 0, 0);
1792 }
1793 
1794 /******************************************************************
1795  *              DeleteMonitorA        [WINSPOOL.@]
1796  *
1797  * See DeleteMonitorW.
1798  *
1799  */
1800 BOOL WINAPI DeleteMonitorA (LPSTR pName, LPSTR pEnvironment, LPSTR pMonitorName)
1801 {
1802     LPWSTR  nameW = NULL;
1803     LPWSTR  EnvironmentW = NULL;
1804     LPWSTR  MonitorNameW = NULL;
1805     BOOL    res;
1806     INT     len;
1807 
1808     if (pName) {
1809         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
1810         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1811         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
1812     }
1813 
1814     if (pEnvironment) {
1815         len = MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, NULL, 0);
1816         EnvironmentW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1817         MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, EnvironmentW, len);
1818     }
1819     if (pMonitorName) {
1820         len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
1821         MonitorNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1822         MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, MonitorNameW, len);
1823     }
1824 
1825     res = DeleteMonitorW(nameW, EnvironmentW, MonitorNameW);
1826 
1827     HeapFree(GetProcessHeap(), 0, MonitorNameW); 
1828     HeapFree(GetProcessHeap(), 0, EnvironmentW);
1829     HeapFree(GetProcessHeap(), 0, nameW); 
1830     return (res);
1831 }
1832 
1833 /******************************************************************
1834  *              DeleteMonitorW        [WINSPOOL.@]
1835  *
1836  * Delete a specific Printmonitor from a Printing-Environment
1837  *
1838  * PARAMS
1839  *  pName        [I] Servername or NULL (local Computer)
1840  *  pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
1841  *  pMonitorName [I] Name of the Monitor, that should be deleted
1842  *
1843  * RETURNS
1844  *  Success: TRUE
1845  *  Failure: FALSE
1846  *
1847  * NOTES
1848  *  pEnvironment is ignored in Windows for the local Computer.
1849  *
1850  */
1851 BOOL WINAPI DeleteMonitorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
1852 {
1853 
1854     TRACE("(%s, %s, %s)\n",debugstr_w(pName),debugstr_w(pEnvironment),
1855            debugstr_w(pMonitorName));
1856 
1857     if ((backend == NULL)  && !load_backend()) return FALSE;
1858 
1859     return backend->fpDeleteMonitor(pName, pEnvironment, pMonitorName);
1860 }
1861 
1862 
1863 /******************************************************************
1864  *              DeletePortA        [WINSPOOL.@]
1865  *
1866  * See DeletePortW.
1867  *
1868  */
1869 BOOL WINAPI DeletePortA (LPSTR pName, HWND hWnd, LPSTR pPortName)
1870 {
1871     LPWSTR  nameW = NULL;
1872     LPWSTR  portW = NULL;
1873     INT     len;
1874     DWORD   res;
1875 
1876     TRACE("(%s, %p, %s)\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
1877 
1878     /* convert servername to unicode */
1879     if (pName) {
1880         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
1881         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1882         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
1883     }
1884 
1885     /* convert portname to unicode */
1886     if (pPortName) {
1887         len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
1888         portW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1889         MultiByteToWideChar(CP_ACP, 0, pPortName, -1, portW, len);
1890     }
1891 
1892     res = DeletePortW(nameW, hWnd, portW);
1893     HeapFree(GetProcessHeap(), 0, nameW);
1894     HeapFree(GetProcessHeap(), 0, portW);
1895     return res;
1896 }
1897 
1898 /******************************************************************
1899  *              DeletePortW        [WINSPOOL.@]
1900  *
1901  * Delete a specific Port
1902  *
1903  * PARAMS
1904  *  pName     [I] Servername or NULL (local Computer)
1905  *  hWnd      [I] Handle to parent Window for the Dialog-Box
1906  *  pPortName [I] Name of the Port, that should be deleted
1907  *
1908  * RETURNS
1909  *  Success: TRUE
1910  *  Failure: FALSE
1911  *
1912  */
1913 BOOL WINAPI DeletePortW (LPWSTR pName, HWND hWnd, LPWSTR pPortName)
1914 {
1915     TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
1916 
1917     if ((backend == NULL)  && !load_backend()) return FALSE;
1918 
1919     if (!pPortName) {
1920         SetLastError(RPC_X_NULL_REF_POINTER);
1921         return FALSE;
1922     }
1923 
1924     return backend->fpDeletePort(pName, hWnd, pPortName);
1925 }
1926 
1927 /******************************************************************************
1928  *    SetPrinterW  [WINSPOOL.@]
1929  */
1930 BOOL WINAPI SetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD Command)
1931 {
1932     FIXME("(%p, %d, %p, %d): stub\n", hPrinter, Level, pPrinter, Command);
1933     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1934     return FALSE;
1935 }
1936 
1937 /******************************************************************************
1938  *    WritePrinter  [WINSPOOL.@]
1939  */
1940 BOOL WINAPI WritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
1941 {
1942     opened_printer_t *printer;
1943     BOOL ret = FALSE;
1944 
1945     TRACE("(%p, %p, %d, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
1946 
1947     EnterCriticalSection(&printer_handles_cs);
1948     printer = get_opened_printer(hPrinter);
1949     if(!printer)
1950     {
1951         SetLastError(ERROR_INVALID_HANDLE);
1952         goto end;
1953     }
1954 
1955     if(!printer->doc)
1956     {
1957         SetLastError(ERROR_SPL_NO_STARTDOC);
1958         goto end;
1959     }
1960 
1961     ret = WriteFile(printer->doc->hf, pBuf, cbBuf, pcWritten, NULL);
1962 end:
1963     LeaveCriticalSection(&printer_handles_cs);
1964     return ret;
1965 }
1966 
1967 /*****************************************************************************
1968  *          AddFormA  [WINSPOOL.@]
1969  */
1970 BOOL WINAPI AddFormA(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
1971 {
1972     FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
1973     return 1;
1974 }
1975 
1976 /*****************************************************************************
1977  *          AddFormW  [WINSPOOL.@]
1978  */
1979 BOOL WINAPI AddFormW(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
1980 {
1981     FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
1982     return 1;
1983 }
1984 
1985 /*****************************************************************************
1986  *          AddJobA  [WINSPOOL.@]
1987  */
1988 BOOL WINAPI AddJobA(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
1989 {
1990     BOOL ret;
1991     BYTE buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
1992     DWORD needed;
1993 
1994     if(Level != 1) {
1995         SetLastError(ERROR_INVALID_LEVEL);
1996         return FALSE;
1997     }
1998 
1999     ret = AddJobW(hPrinter, Level, buf, sizeof(buf), &needed);
2000 
2001     if(ret) {
2002         ADDJOB_INFO_1W *addjobW = (ADDJOB_INFO_1W*)buf;
2003         DWORD len = WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, NULL, 0, NULL, NULL);
2004         *pcbNeeded = len + sizeof(ADDJOB_INFO_1A);
2005         if(*pcbNeeded > cbBuf) {
2006             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2007             ret = FALSE;
2008         } else {
2009             ADDJOB_INFO_1A *addjobA = (ADDJOB_INFO_1A*)pData;
2010             addjobA->JobId = addjobW->JobId;
2011             addjobA->Path = (char *)(addjobA + 1);
2012             WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, addjobA->Path, len, NULL, NULL);
2013         }
2014     }
2015     return ret;
2016 }
2017 
2018 /*****************************************************************************
2019  *          AddJobW  [WINSPOOL.@]
2020  */
2021 BOOL WINAPI AddJobW(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
2022 {
2023     opened_printer_t *printer;
2024     job_t *job;
2025     BOOL ret = FALSE;
2026     static const WCHAR spool_path[] = {'s','p','o','o','l','\\','P','R','I','N','T','E','R','S','\\',0};
2027     static const WCHAR fmtW[] = {'%','s','%','','5','d','.','S','P','L',0};
2028     WCHAR path[MAX_PATH], filename[MAX_PATH];
2029     DWORD len;
2030     ADDJOB_INFO_1W *addjob;
2031 
2032     TRACE("(%p,%d,%p,%d,%p)\n", hPrinter, Level, pData, cbBuf, pcbNeeded);
2033     
2034     EnterCriticalSection(&printer_handles_cs);
2035 
2036     printer = get_opened_printer(hPrinter);
2037 
2038     if(!printer) {
2039         SetLastError(ERROR_INVALID_HANDLE);
2040         goto end;
2041     }
2042 
2043     if(Level != 1) {
2044         SetLastError(ERROR_INVALID_LEVEL);
2045         goto end;
2046     }
2047 
2048     job = HeapAlloc(GetProcessHeap(), 0, sizeof(*job));
2049     if(!job)
2050         goto end;
2051 
2052     job->job_id = InterlockedIncrement(&next_job_id);
2053 
2054     len = GetSystemDirectoryW(path, sizeof(path) / sizeof(WCHAR));
2055     if(path[len - 1] != '\\')
2056         path[len++] = '\\';
2057     memcpy(path + len, spool_path, sizeof(spool_path));    
2058     sprintfW(filename, fmtW, path, job->job_id);
2059 
2060     len = strlenW(filename);
2061     job->filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
2062     memcpy(job->filename, filename, (len + 1) * sizeof(WCHAR));
2063     job->document_title = strdupW(default_doc_title);
2064     list_add_tail(&printer->queue->jobs, &job->entry);
2065 
2066     *pcbNeeded = (len + 1) * sizeof(WCHAR) + sizeof(*addjob);
2067     if(*pcbNeeded <= cbBuf) {
2068         addjob = (ADDJOB_INFO_1W*)pData;
2069         addjob->JobId = job->job_id;
2070         addjob->Path = (WCHAR *)(addjob + 1);
2071         memcpy(addjob->Path, filename, (len + 1) * sizeof(WCHAR));
2072         ret = TRUE;
2073     } else 
2074         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2075 
2076 end:
2077     LeaveCriticalSection(&printer_handles_cs);
2078     return ret;
2079 }
2080 
2081 /*****************************************************************************
2082  *          GetPrintProcessorDirectoryA  [WINSPOOL.@]
2083  *
2084  * Return the PATH for the Print-Processors
2085  *
2086  * See GetPrintProcessorDirectoryW.
2087  *
2088  *
2089  */
2090 BOOL WINAPI GetPrintProcessorDirectoryA(LPSTR server, LPSTR env,
2091                                         DWORD level,  LPBYTE Info,
2092                                         DWORD cbBuf,  LPDWORD pcbNeeded)
2093 {
2094     LPWSTR  serverW = NULL;
2095     LPWSTR  envW = NULL;
2096     BOOL    ret;
2097     INT     len;
2098 
2099     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_a(server), 
2100           debugstr_a(env), level, Info, cbBuf, pcbNeeded);
2101  
2102 
2103     if (server) {
2104         len = MultiByteToWideChar(CP_ACP, 0, server, -1, NULL, 0);
2105         serverW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2106         MultiByteToWideChar(CP_ACP, 0, server, -1, serverW, len);
2107     }
2108 
2109     if (env) {
2110         len = MultiByteToWideChar(CP_ACP, 0, env, -1, NULL, 0);
2111         envW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2112         MultiByteToWideChar(CP_ACP, 0, env, -1, envW, len);
2113     }
2114 
2115     /* NT requires the buffersize from GetPrintProcessorDirectoryW also
2116        for GetPrintProcessorDirectoryA and WC2MB is done in-place.
2117      */
2118     ret = GetPrintProcessorDirectoryW(serverW, envW, level, Info, 
2119                                       cbBuf, pcbNeeded);
2120 
2121     if (ret) ret = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)Info, -1, (LPSTR)Info,
2122                                        cbBuf, NULL, NULL) > 0;
2123 
2124 
2125     TRACE(" required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
2126     HeapFree(GetProcessHeap(), 0, envW);
2127     HeapFree(GetProcessHeap(), 0, serverW);
2128     return ret;
2129 }
2130 
2131 /*****************************************************************************
2132  *          GetPrintProcessorDirectoryW  [WINSPOOL.@]
2133  *
2134  * Return the PATH for the Print-Processors
2135  *
2136  * PARAMS
2137  *   server     [I] Servername (NT only) or NULL (local Computer)
2138  *   env        [I] Printing-Environment (see below) or NULL (Default)
2139  *   level      [I] Structure-Level (must be 1)
2140  *   Info       [O] PTR to Buffer that receives the Result
2141  *   cbBuf      [I] Size of Buffer at "Info"
2142  *   pcbNeeded  [O] PTR to DWORD that receives the size in Bytes used / 
2143  *                  required for the Buffer at "Info"
2144  *
2145  * RETURNS
2146  *   Success: TRUE  and in pcbNeeded the Bytes used in Info
2147  *   Failure: FALSE and in pcbNeeded the Bytes required for Info,
2148  *   if cbBuf is too small
2149  * 
2150  *   Native Values returned in Info on Success:
2151  *|  NT(Windows NT x86):  "%winsysdir%\\spool\\PRTPROCS\\w32x86" 
2152  *|  NT(Windows 4.0):     "%winsysdir%\\spool\\PRTPROCS\\win40" 
2153  *|  win9x(Windows 4.0):  "%winsysdir%" 
2154  *
2155  *   "%winsysdir%" is the Value from GetSystemDirectoryW()
2156  *
2157  * BUGS
2158  *  Only NULL or "" is supported for server
2159  *
2160  */
2161 BOOL WINAPI GetPrintProcessorDirectoryW(LPWSTR server, LPWSTR env,
2162                                         DWORD level,  LPBYTE Info,
2163                                         DWORD cbBuf,  LPDWORD pcbNeeded)
2164 {
2165     DWORD needed;
2166     const printenv_t * env_t;
2167 
2168     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(server),
2169             debugstr_w(env), level, Info, cbBuf, pcbNeeded);
2170 
2171     if(server != NULL && server[0]) {
2172         FIXME("server not supported: %s\n", debugstr_w(server));
2173         SetLastError(ERROR_INVALID_PARAMETER);
2174         return FALSE;
2175     }
2176 
2177     env_t = validate_envW(env);
2178     if(!env_t) return FALSE;  /* environment invalid or unsupported */
2179 
2180     if(level != 1) {
2181         WARN("(Level: %d) is ignored in win9x\n", level);
2182         SetLastError(ERROR_INVALID_LEVEL);
2183         return FALSE;
2184     }
2185 
2186     /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
2187     needed = GetSystemDirectoryW(NULL, 0);
2188     /* add the Size for the Subdirectories */
2189     needed += lstrlenW(spoolprtprocsW);
2190     needed += lstrlenW(env_t->subdir);
2191     needed *= sizeof(WCHAR);  /* return-value is size in Bytes */
2192 
2193     if(pcbNeeded) *pcbNeeded = needed;
2194     TRACE ("required: 0x%x/%d\n", needed, needed);
2195     if (needed > cbBuf) {
2196         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2197         return FALSE;
2198     }
2199     if(pcbNeeded == NULL) {
2200         /* NT: RPC_X_NULL_REF_POINTER, 9x: ignored */
2201         WARN("(pcbNeeded == NULL) is ignored in win9x\n");
2202         SetLastError(RPC_X_NULL_REF_POINTER);
2203         return FALSE;
2204     }
2205     if(Info == NULL) {
2206         /* NT: RPC_X_NULL_REF_POINTER, 9x: ERROR_INVALID_PARAMETER */
2207         SetLastError(RPC_X_NULL_REF_POINTER);
2208         return FALSE;
2209     }
2210     
2211     GetSystemDirectoryW((LPWSTR) Info, cbBuf/sizeof(WCHAR));
2212     /* add the Subdirectories */
2213     lstrcatW((LPWSTR) Info, spoolprtprocsW);
2214     lstrcatW((LPWSTR) Info, env_t->subdir);
2215     TRACE(" => %s\n", debugstr_w((LPWSTR) Info));
2216     return TRUE;
2217 }
2218 
2219 /*****************************************************************************
2220  *          WINSPOOL_OpenDriverReg [internal]
2221  *
2222  * opens the registry for the printer drivers depending on the given input
2223  * variable pEnvironment
2224  *
2225  * RETURNS:
2226  *    the opened hkey on success
2227  *    NULL on error
2228  */
2229 static HKEY WINSPOOL_OpenDriverReg( LPCVOID pEnvironment, BOOL unicode)
2230 {   
2231     HKEY  retval = NULL;
2232     LPWSTR buffer;
2233     const printenv_t * env;
2234 
2235     TRACE("(%s, %d)\n",
2236           (unicode) ? debugstr_w(pEnvironment) : debugstr_a(pEnvironment), unicode);
2237 
2238     if (!pEnvironment || unicode) {
2239         /* pEnvironment was NULL or a Unicode-String: use it direct */
2240         env = validate_envW(pEnvironment);
2241     }
2242     else
2243     {
2244         /* pEnvironment was an ANSI-String: convert to unicode first */
2245         LPWSTR  buffer;
2246         INT len = MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, NULL, 0);
2247         buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2248         if (buffer) MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, buffer, len);
2249         env = validate_envW(buffer);
2250         HeapFree(GetProcessHeap(), 0, buffer);
2251     }
2252     if (!env) return NULL;
2253 
2254     buffer = HeapAlloc( GetProcessHeap(), 0,
2255                 (strlenW(DriversW) + strlenW(env->envname) + 
2256                  strlenW(env->versionregpath) + 1) * sizeof(WCHAR));
2257     if(buffer) {
2258         wsprintfW(buffer, DriversW, env->envname, env->versionregpath);
2259         RegCreateKeyW(HKEY_LOCAL_MACHINE, buffer, &retval);
2260         HeapFree(GetProcessHeap(), 0, buffer);
2261     }
2262     return retval;
2263 }
2264 
2265 /*****************************************************************************
2266  *          AddPrinterW  [WINSPOOL.@]
2267  */
2268 HANDLE WINAPI AddPrinterW(LPWSTR pName, DWORD Level, LPBYTE pPrinter)
2269 {
2270     PRINTER_INFO_2W *pi = (PRINTER_INFO_2W *) pPrinter;
2271     LPDEVMODEA dmA;
2272     LPDEVMODEW dmW;
2273     HANDLE retval;
2274     HKEY hkeyPrinter, hkeyPrinters, hkeyDriver, hkeyDrivers;
2275     LONG size;
2276     static const WCHAR attributesW[]      = {'A','t','t','r','i','b','u','t','e','s',0},
2277                        default_devmodeW[] = {'D','e','f','a','u','l','t',' ','D','e','v','M','o','d','e',0},
2278                        priorityW[]        = {'P','r','i','o','r','i','t','y',0},
2279                        start_timeW[]      = {'S','t','a','r','t','T','i','m','e',0},
2280                        statusW[]          = {'S','t','a','t','u','s',0},
2281                        until_timeW[]      = {'U','n','t','i','l','T','i','m','e',0};
2282 
2283     TRACE("(%s,%d,%p)\n", debugstr_w(pName), Level, pPrinter);
2284 
2285     if(pName != NULL) {
2286         ERR("pName = %s - unsupported\n", debugstr_w(pName));
2287         SetLastError(ERROR_INVALID_PARAMETER);
2288         return 0;
2289     }
2290     if(Level != 2) {
2291         ERR("Level = %d, unsupported!\n", Level);
2292         SetLastError(ERROR_INVALID_LEVEL);
2293         return 0;
2294     }
2295     if(!pPrinter) {
2296         SetLastError(ERROR_INVALID_PARAMETER);
2297         return 0;
2298     }
2299     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
2300        ERROR_SUCCESS) {
2301         ERR("Can't create Printers key\n");
2302         return 0;
2303     }
2304     if(!RegOpenKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter)) {
2305         if (!RegQueryValueW(hkeyPrinter, attributesW, NULL, NULL)) {
2306             SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
2307             RegCloseKey(hkeyPrinter);
2308             RegCloseKey(hkeyPrinters);
2309             return 0;
2310         }
2311         RegCloseKey(hkeyPrinter);
2312     }
2313     hkeyDrivers = WINSPOOL_OpenDriverReg( NULL, TRUE);
2314     if(!hkeyDrivers) {
2315         ERR("Can't create Drivers key\n");
2316         RegCloseKey(hkeyPrinters);
2317         return 0;
2318     }
2319     if(RegOpenKeyW(hkeyDrivers, pi->pDriverName, &hkeyDriver) !=
2320        ERROR_SUCCESS) {
2321         WARN("Can't find driver %s\n", debugstr_w(pi->pDriverName));
2322         RegCloseKey(hkeyPrinters);
2323         RegCloseKey(hkeyDrivers);
2324         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER);
2325         return 0;
2326     }
2327     RegCloseKey(hkeyDriver);
2328     RegCloseKey(hkeyDrivers);
2329 
2330     if(lstrcmpiW(pi->pPrintProcessor, WinPrintW)) {  /* FIXME */
2331         FIXME("Can't find processor %s\n", debugstr_w(pi->pPrintProcessor));
2332         SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR);
2333         RegCloseKey(hkeyPrinters);
2334         return 0;
2335     }
2336 
2337     if(RegCreateKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter) !=
2338        ERROR_SUCCESS) {
2339         FIXME("Can't create printer %s\n", debugstr_w(pi->pPrinterName));
2340         SetLastError(ERROR_INVALID_PRINTER_NAME);
2341         RegCloseKey(hkeyPrinters);
2342         return 0;
2343     }
2344     RegSetValueExW(hkeyPrinter, attributesW, 0, REG_DWORD,
2345                    (LPBYTE)&pi->Attributes, sizeof(DWORD));
2346     set_reg_szW(hkeyPrinter, DatatypeW, pi->pDatatype);
2347 
2348     /* See if we can load the driver.  We may need the devmode structure anyway
2349      *
2350      * FIXME:
2351      * Note that DocumentPropertiesW will briefly try to open the printer we
2352      * just create to find a DEVMODEA struct (it will use the WINEPS default
2353      * one in case it is not there, so we are ok).
2354      */
2355     size = DocumentPropertiesW(0, 0, pi->pPrinterName, NULL, NULL, 0);
2356 
2357     if(size < 0) {
2358         FIXME("DocumentPropertiesW on printer %s fails\n", debugstr_w(pi->pPrinterName));
2359         size = sizeof(DEVMODEW);
2360     }
2361     if(pi->pDevMode)
2362         dmW = pi->pDevMode;
2363     else
2364     {
2365         dmW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
2366         dmW->dmSize = size;
2367         if (0>DocumentPropertiesW(0,0,pi->pPrinterName,dmW,NULL,DM_OUT_BUFFER))
2368         {
2369             WARN("DocumentPropertiesW on printer %s failed!\n", debugstr_w(pi->pPrinterName));
2370             HeapFree(GetProcessHeap(),0,dmW);
2371             dmW=NULL;
2372         }
2373         else
2374         {
2375             /* set devmode to printer name */
2376             lstrcpynW(dmW->dmDeviceName, pi->pPrinterName, CCHDEVICENAME);
2377         }
2378     }
2379 
2380     /* Write DEVMODEA not DEVMODEW into reg.  This is what win9x does
2381        and we support these drivers.  NT writes DEVMODEW so somehow
2382        we'll need to distinguish between these when we support NT
2383        drivers */
2384     if (dmW)
2385     {
2386         dmA = DEVMODEdupWtoA(dmW);
2387         RegSetValueExW(hkeyPrinter, default_devmodeW, 0, REG_BINARY,
2388                        (LPBYTE)dmA, dmA->dmSize + dmA->dmDriverExtra);
2389         HeapFree(GetProcessHeap(), 0, dmA);
2390         if(!pi->pDevMode)
2391             HeapFree(GetProcessHeap(), 0, dmW);
2392     }
2393     set_reg_szW(hkeyPrinter, DescriptionW, pi->pComment);
2394     set_reg_szW(hkeyPrinter, LocationW, pi->pLocation);
2395     set_reg_szW(hkeyPrinter, NameW, pi->pPrinterName);
2396     set_reg_szW(hkeyPrinter, ParametersW, pi->pParameters);
2397 
2398     set_reg_szW(hkeyPrinter, PortW, pi->pPortName);
2399     set_reg_szW(hkeyPrinter, Print_ProcessorW, pi->pPrintProcessor);
2400     set_reg_szW(hkeyPrinter, Printer_DriverW, pi->pDriverName);
2401     RegSetValueExW(hkeyPrinter, priorityW, 0, REG_DWORD,
2402                    (LPBYTE)&pi->Priority, sizeof(DWORD));
2403     set_reg_szW(hkeyPrinter, Separator_FileW, pi->pSepFile);
2404     set_reg_szW(hkeyPrinter, Share_NameW, pi->pShareName);
2405     RegSetValueExW(hkeyPrinter, start_timeW, 0, REG_DWORD,
2406                    (LPBYTE)&pi->StartTime, sizeof(DWORD));
2407     RegSetValueExW(hkeyPrinter, statusW, 0, REG_DWORD,
2408                    (LPBYTE)&pi->Status, sizeof(DWORD));
2409     RegSetValueExW(hkeyPrinter, until_timeW, 0, REG_DWORD,
2410                    (LPBYTE)&pi->UntilTime, sizeof(DWORD));
2411 
2412     RegCloseKey(hkeyPrinter);
2413     RegCloseKey(hkeyPrinters);
2414     if(!OpenPrinterW(pi->pPrinterName, &retval, NULL)) {
2415         ERR("OpenPrinter failing\n");
2416         return 0;
2417     }
2418     return retval;
2419 }
2420 
2421 /*****************************************************************************
2422  *          AddPrinterA  [WINSPOOL.@]
2423  */
2424 HANDLE WINAPI AddPrinterA(LPSTR pName, DWORD Level, LPBYTE pPrinter)
2425 {
2426     UNICODE_STRING pNameW;
2427     PWSTR pwstrNameW;
2428     PRINTER_INFO_2W *piW;
2429     PRINTER_INFO_2A *piA = (PRINTER_INFO_2A*)pPrinter;
2430     HANDLE ret;
2431 
2432     TRACE("(%s,%d,%p): stub\n", debugstr_a(pName), Level, pPrinter);
2433     if(Level != 2) {
2434         ERR("Level = %d, unsupported!\n", Level);
2435         SetLastError(ERROR_INVALID_LEVEL);
2436         return 0;
2437     }
2438     pwstrNameW = asciitounicode(&pNameW,pName);
2439     piW = PRINTER_INFO_2AtoW(GetProcessHeap(), piA);
2440 
2441     ret = AddPrinterW(pwstrNameW, Level, (LPBYTE)piW);
2442 
2443     FREE_PRINTER_INFO_2W(GetProcessHeap(), piW);
2444     RtlFreeUnicodeString(&pNameW);
2445     return ret;
2446 }
2447 
2448 
2449 /*****************************************************************************
2450  *          ClosePrinter  [WINSPOOL.@]
2451  */
2452 BOOL WINAPI ClosePrinter(HANDLE hPrinter)
2453 {
2454     UINT_PTR i = (UINT_PTR)hPrinter;
2455     opened_printer_t *printer = NULL;
2456     BOOL ret = FALSE;
2457 
2458     TRACE("(%p)\n", hPrinter);
2459 
2460     EnterCriticalSection(&printer_handles_cs);
2461 
2462     if ((i > 0) && (i <= nb_printer_handles))
2463         printer = printer_handles[i - 1];
2464 
2465 
2466     if(printer)
2467     {
2468         struct list *cursor, *cursor2;
2469 
2470         TRACE("closing %s (doc: %p)\n", debugstr_w(printer->name), printer->doc);
2471 
2472         if (printer->backend_printer) {
2473             backend->fpClosePrinter(printer->backend_printer);
2474         }
2475 
2476         if(printer->doc)
2477             EndDocPrinter(hPrinter);
2478 
2479         if(InterlockedDecrement(&printer->queue->ref) == 0)
2480         {
2481             LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
2482             {
2483                 job_t *job = LIST_ENTRY(cursor, job_t, entry);
2484                 ScheduleJob(hPrinter, job->job_id);
2485             }
2486             HeapFree(GetProcessHeap(), 0, printer->queue);
2487         }
2488 
2489         HeapFree(GetProcessHeap(), 0, printer->printername);
2490         HeapFree(GetProcessHeap(), 0, printer->name);
2491         HeapFree(GetProcessHeap(), 0, printer);
2492         printer_handles[i - 1] = NULL;
2493         ret = TRUE;
2494     }
2495     LeaveCriticalSection(&printer_handles_cs);
2496     return ret;
2497 }
2498 
2499 /*****************************************************************************
2500  *          DeleteFormA  [WINSPOOL.@]
2501  */
2502 BOOL WINAPI DeleteFormA(HANDLE hPrinter, LPSTR pFormName)
2503 {
2504     FIXME("(%p,%s): stub\n", hPrinter, pFormName);
2505     return 1;
2506 }
2507 
2508 /*****************************************************************************
2509  *          DeleteFormW  [WINSPOOL.@]
2510  */
2511 BOOL WINAPI DeleteFormW(HANDLE hPrinter, LPWSTR pFormName)
2512 {
2513     FIXME("(%p,%s): stub\n", hPrinter, debugstr_w(pFormName));
2514     return 1;
2515 }
2516 
2517 /*****************************************************************************
2518  *          DeletePrinter  [WINSPOOL.@]
2519  */
2520 BOOL WINAPI DeletePrinter(HANDLE hPrinter)
2521 {
2522     LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
2523     HKEY hkeyPrinters, hkey;
2524 
2525     if(!lpNameW) {
2526         SetLastError(ERROR_INVALID_HANDLE);
2527         return FALSE;
2528     }
2529     if(RegOpenKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
2530         RegDeleteTreeW(hkeyPrinters, lpNameW);
2531         RegCloseKey(hkeyPrinters);
2532     }
2533     WriteProfileStringW(devicesW, lpNameW, NULL);
2534     WriteProfileStringW(PrinterPortsW, lpNameW, NULL);
2535 
2536     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
2537         RegDeleteValueW(hkey, lpNameW);
2538         RegCloseKey(hkey);
2539     }
2540 
2541     if(RegCreateKeyW(HKEY_CURRENT_USER, WinNT_CV_PrinterPortsW, &hkey) == ERROR_SUCCESS) {
2542         RegDeleteValueW(hkey, lpNameW);
2543         RegCloseKey(hkey);
2544     }
2545     return TRUE;
2546 }
2547 
2548 /*****************************************************************************
2549  *          SetPrinterA  [WINSPOOL.@]
2550  */
2551 BOOL WINAPI SetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
2552                            DWORD Command)
2553 {
2554     FIXME("(%p,%d,%p,%d): stub\n",hPrinter,Level,pPrinter,Command);
2555     return FALSE;
2556 }
2557 
2558 /*****************************************************************************
2559  *          SetJobA  [WINSPOOL.@]
2560  */
2561 BOOL WINAPI SetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level,
2562                     LPBYTE pJob, DWORD Command)
2563 {
2564     BOOL ret;
2565     LPBYTE JobW;
2566     UNICODE_STRING usBuffer;
2567 
2568     TRACE("(%p, %d, %d, %p, %d)\n",hPrinter, JobId, Level, pJob, Command);
2569 
2570     /* JobId, pPrinterName, pMachineName, pDriverName, Size, Submitted, Time and TotalPages
2571        are all ignored by SetJob, so we don't bother copying them */
2572     switch(Level)
2573     {
2574     case 0:
2575         JobW = NULL;
2576         break;
2577     case 1:
2578       {
2579         JOB_INFO_1W *info1W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info1W));
2580         JOB_INFO_1A *info1A = (JOB_INFO_1A*)pJob;
2581 
2582         JobW = (LPBYTE)info1W;
2583         info1W->pUserName = asciitounicode(&usBuffer, info1A->pUserName);
2584         info1W->pDocument = asciitounicode(&usBuffer, info1A->pDocument);
2585         info1W->pDatatype = asciitounicode(&usBuffer, info1A->pDatatype);
2586         info1W->pStatus = asciitounicode(&usBuffer, info1A->pStatus);
2587         info1W->Status = info1A->Status;
2588         info1W->Priority = info1A->Priority;
2589         info1W->Position = info1A->Position;
2590         info1W->PagesPrinted = info1A->PagesPrinted;
2591         break;
2592       }
2593     case 2:
2594       {
2595         JOB_INFO_2W *info2W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info2W));
2596         JOB_INFO_2A *info2A = (JOB_INFO_2A*)pJob;
2597 
2598         JobW = (LPBYTE)info2W;
2599         info2W->pUserName = asciitounicode(&usBuffer, info2A->pUserName);
2600         info2W->pDocument = asciitounicode(&usBuffer, info2A->pDocument);
2601         info2W->pNotifyName = asciitounicode(&usBuffer, info2A->pNotifyName);
2602         info2W->pDatatype = asciitounicode(&usBuffer, info2A->pDatatype);
2603         info2W->pPrintProcessor = asciitounicode(&usBuffer, info2A->pPrintProcessor);
2604         info2W->pParameters = asciitounicode(&usBuffer, info2A->pParameters);
2605         info2W->pDevMode = info2A->pDevMode ? GdiConvertToDevmodeW(info2A->pDevMode) : NULL;
2606         info2W->pStatus = asciitounicode(&usBuffer, info2A->pStatus);
2607         info2W->pSecurityDescriptor = info2A->pSecurityDescriptor;
2608         info2W->Status = info2A->Status;
2609         info2W->Priority = info2A->Priority;
2610         info2W->Position = info2A->Position;
2611         info2W->StartTime = info2A->StartTime;
2612         info2W->UntilTime = info2A->UntilTime;
2613         info2W->PagesPrinted = info2A->PagesPrinted;
2614         break;
2615       }
2616     case 3:
2617         JobW = HeapAlloc(GetProcessHeap(), 0, sizeof(JOB_INFO_3));
2618         memcpy(JobW, pJob, sizeof(JOB_INFO_3));
2619         break;
2620     default:
2621         SetLastError(ERROR_INVALID_LEVEL);
2622         return FALSE;
2623     }
2624 
2625     ret = SetJobW(hPrinter, JobId, Level, JobW, Command);
2626 
2627     switch(Level)
2628     {
2629     case 1:
2630       {
2631         JOB_INFO_1W *info1W = (JOB_INFO_1W*)JobW;
2632         HeapFree(GetProcessHeap(), 0, info1W->pUserName);
2633         HeapFree(GetProcessHeap(), 0, info1W->pDocument); 
2634         HeapFree(GetProcessHeap(), 0, info1W->pDatatype);
2635         HeapFree(GetProcessHeap(), 0, info1W->pStatus);
2636         break;
2637       }
2638     case 2:
2639       {
2640         JOB_INFO_2W *info2W = (JOB_INFO_2W*)JobW;
2641         HeapFree(GetProcessHeap(), 0, info2W->pUserName);
2642         HeapFree(GetProcessHeap(), 0, info2W->pDocument); 
2643         HeapFree(GetProcessHeap(), 0, info2W->pNotifyName);
2644         HeapFree(GetProcessHeap(), 0, info2W->pDatatype);
2645         HeapFree(GetProcessHeap(), 0, info2W->pPrintProcessor);
2646         HeapFree(GetProcessHeap(), 0, info2W->pParameters);
2647         HeapFree(GetProcessHeap(), 0, info2W->pDevMode);
2648         HeapFree(GetProcessHeap(), 0, info2W->pStatus);
2649         break;
2650       }
2651     }
2652     HeapFree(GetProcessHeap(), 0, JobW);
2653 
2654     return ret;
2655 }
2656 
2657 /*****************************************************************************
2658  *          SetJobW  [WINSPOOL.@]
2659  */
2660 BOOL WINAPI SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level,
2661                     LPBYTE pJob, DWORD Command)
2662 {
2663     BOOL ret = FALSE;
2664     job_t *job;
2665 
2666     TRACE("(%p, %d, %d, %p, %d)\n", hPrinter, JobId, Level, pJob, Command);
2667     FIXME("Ignoring everything other than document title\n");
2668 
2669     EnterCriticalSection(&printer_handles_cs);
2670     job = get_job(hPrinter, JobId);
2671     if(!job)
2672         goto end;
2673 
2674     switch(Level)
2675     {
2676     case 0:
2677         break;
2678     case 1:
2679       {
2680         JOB_INFO_1W *info1 = (JOB_INFO_1W*)pJob;
2681         HeapFree(GetProcessHeap(), 0, job->document_title);
2682         job->document_title = strdupW(info1->pDocument);
2683         break;
2684       }
2685     case 2:
2686       {
2687         JOB_INFO_2W *info2 = (JOB_INFO_2W*)pJob;
2688         HeapFree(GetProcessHeap(), 0, job->document_title);
2689         job->document_title = strdupW(info2->pDocument);
2690         break;
2691       }
2692     case 3:
2693         break;
2694     default:
2695         SetLastError(ERROR_INVALID_LEVEL);
2696         goto end;
2697     }
2698     ret = TRUE;
2699 end:
2700     LeaveCriticalSection(&printer_handles_cs);
2701     return ret;
2702 }
2703 
2704 /*****************************************************************************
2705  *          EndDocPrinter  [WINSPOOL.@]
2706  */
2707 BOOL WINAPI EndDocPrinter(HANDLE hPrinter)
2708 {
2709     opened_printer_t *printer;
2710     BOOL ret = FALSE;
2711     TRACE("(%p)\n", hPrinter);
2712 
2713     EnterCriticalSection(&printer_handles_cs);
2714 
2715     printer = get_opened_printer(hPrinter);
2716     if(!printer)
2717     {
2718         SetLastError(ERROR_INVALID_HANDLE);
2719         goto end;
2720     }
2721 
2722     if(!printer->doc)    
2723     {
2724         SetLastError(ERROR_SPL_NO_STARTDOC);
2725         goto end;
2726     }
2727 
2728     CloseHandle(printer->doc->hf);
2729     ScheduleJob(hPrinter, printer->doc->job_id);
2730     HeapFree(GetProcessHeap(), 0, printer->doc);
2731     printer->doc = NULL;
2732     ret = TRUE;
2733 end:
2734     LeaveCriticalSection(&printer_handles_cs);
2735     return ret;
2736 }
2737 
2738 /*****************************************************************************
2739  *          EndPagePrinter  [WINSPOOL.@]
2740  */
2741 BOOL WINAPI EndPagePrinter(HANDLE hPrinter)
2742 {
2743     FIXME("(%p): stub\n", hPrinter);
2744     return TRUE;
2745 }
2746 
2747 /*****************************************************************************
2748  *          StartDocPrinterA  [WINSPOOL.@]
2749  */
2750 DWORD WINAPI StartDocPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
2751 {
2752     UNICODE_STRING usBuffer;
2753     DOC_INFO_2W doc2W;
2754     DOC_INFO_2A *doc2 = (DOC_INFO_2A*)pDocInfo;
2755     DWORD ret;
2756 
2757     /* DOC_INFO_1, 2 and 3 all have the strings in the same place with either two (DOC_INFO_2)
2758        or one (DOC_INFO_3) extra DWORDs */
2759 
2760     switch(Level) {
2761     case 2:
2762         doc2W.JobId = doc2->JobId;
2763         /* fall through */
2764     case 3:
2765         doc2W.dwMode = doc2->dwMode;
2766         /* fall through */
2767     case 1:
2768         doc2W.pDocName = asciitounicode(&usBuffer, doc2->pDocName);
2769         doc2W.pOutputFile = asciitounicode(&usBuffer, doc2->pOutputFile);
2770         doc2W.pDatatype = asciitounicode(&usBuffer, doc2->pDatatype);
2771         break;
2772 
2773     default:
2774         SetLastError(ERROR_INVALID_LEVEL);
2775         return FALSE;
2776     }
2777 
2778     ret = StartDocPrinterW(hPrinter, Level, (LPBYTE)&doc2W);
2779 
2780     HeapFree(GetProcessHeap(), 0, doc2W.pDatatype);
2781     HeapFree(GetProcessHeap(), 0, doc2W.pOutputFile);
2782     HeapFree(GetProcessHeap(), 0, doc2W.pDocName);
2783 
2784     return ret;
2785 }
2786 
2787 /*****************************************************************************
2788  *          StartDocPrinterW  [WINSPOOL.@]
2789  */
2790 DWORD WINAPI StartDocPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
2791 {
2792     DOC_INFO_2W *doc = (DOC_INFO_2W *)pDocInfo;
2793     opened_printer_t *printer;
2794     BYTE addjob_buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
2795     ADDJOB_INFO_1W *addjob = (ADDJOB_INFO_1W*) addjob_buf;
2796     JOB_INFO_1W job_info;
2797     DWORD needed, ret = 0;
2798     HANDLE hf;
2799     WCHAR *filename;
2800 
2801     TRACE("(hPrinter = %p, Level = %d, pDocInfo = %p {pDocName = %s, pOutputFile = %s, pDatatype = %s}):\n",
2802           hPrinter, Level, doc, debugstr_w(doc->pDocName), debugstr_w(doc->pOutputFile),
2803           debugstr_w(doc->pDatatype));
2804 
2805     if(Level < 1 || Level > 3)
2806     {
2807         SetLastError(ERROR_INVALID_LEVEL);
2808         return 0;
2809     }
2810 
2811     EnterCriticalSection(&printer_handles_cs);
2812     printer = get_opened_printer(hPrinter);
2813     if(!printer)
2814     {
2815         SetLastError(ERROR_INVALID_HANDLE);
2816         goto end;
2817     }
2818 
2819     if(printer->doc)
2820     {
2821         SetLastError(ERROR_INVALID_PRINTER_STATE);
2822         goto end;
2823     }
2824 
2825     /* Even if we're printing to a file we still add a print job, we'll
2826        just ignore the spool file name */
2827 
2828     if(!AddJobW(hPrinter, 1, addjob_buf, sizeof(addjob_buf), &needed))
2829     {
2830         ERR("AddJob failed gle %u\n", GetLastError());
2831         goto end;
2832     }
2833 
2834     if(doc->pOutputFile)
2835         filename = doc->pOutputFile;
2836     else
2837         filename = addjob->Path;
2838 
2839     hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2840     if(hf == INVALID_HANDLE_VALUE)
2841         goto end;
2842 
2843     memset(&job_info, 0, sizeof(job_info));
2844     job_info.pDocument = doc->pDocName;
2845     SetJobW(hPrinter, addjob->JobId, 1, (LPBYTE)&job_info, 0);
2846 
2847     printer->doc = HeapAlloc(GetProcessHeap(), 0, sizeof(*printer->doc));
2848     printer->doc->hf = hf;
2849     ret = printer->doc->job_id = addjob->JobId;
2850 end:
2851     LeaveCriticalSection(&printer_handles_cs);
2852 
2853     return ret;
2854 }
2855 
2856 /*****************************************************************************
2857  *          StartPagePrinter  [WINSPOOL.@]
2858  */
2859 BOOL WINAPI StartPagePrinter(HANDLE hPrinter)
2860 {
2861     FIXME("(%p): stub\n", hPrinter);
2862     return TRUE;
2863 }
2864 
2865 /*****************************************************************************
2866  *          GetFormA  [WINSPOOL.@]
2867  */
2868 BOOL WINAPI GetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
2869                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
2870 {
2871     FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,pFormName,
2872          Level,pForm,cbBuf,pcbNeeded);
2873     return FALSE;
2874 }
2875 
2876 /*****************************************************************************
2877  *          GetFormW  [WINSPOOL.@]
2878  */
2879 BOOL WINAPI GetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
2880                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
2881 {
2882     FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,
2883           debugstr_w(pFormName),Level,pForm,cbBuf,pcbNeeded);
2884     return FALSE;
2885 }
2886 
2887 /*****************************************************************************
2888  *          SetFormA  [WINSPOOL.@]
2889  */
2890 BOOL WINAPI SetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
2891                         LPBYTE pForm)
2892 {
2893     FIXME("(%p,%s,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
2894     return FALSE;
2895 }
2896 
2897 /*****************************************************************************
2898  *          SetFormW  [WINSPOOL.@]
2899  */
2900 BOOL WINAPI SetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
2901                         LPBYTE pForm)
2902 {
2903     FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
2904     return FALSE;
2905 }
2906 
2907 /*****************************************************************************
2908  *          ReadPrinter  [WINSPOOL.@]
2909  */
2910 BOOL WINAPI ReadPrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf,
2911                            LPDWORD pNoBytesRead)
2912 {
2913     FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pBuf,cbBuf,pNoBytesRead);
2914     return FALSE;
2915 }
2916 
2917 /*****************************************************************************
2918  *          ResetPrinterA  [WINSPOOL.@]
2919  */
2920 BOOL WINAPI ResetPrinterA(HANDLE hPrinter, LPPRINTER_DEFAULTSA pDefault)
2921 {
2922     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
2923     return FALSE;
2924 }
2925 
2926 /*****************************************************************************
2927  *          ResetPrinterW  [WINSPOOL.@]
2928  */
2929 BOOL WINAPI ResetPrinterW(HANDLE hPrinter, LPPRINTER_DEFAULTSW pDefault)
2930 {
2931     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
2932     return FALSE;
2933 }
2934 
2935 /*****************************************************************************
2936  *    WINSPOOL_GetDWORDFromReg
2937  *
2938  * Return DWORD associated with ValueName from hkey.
2939  */
2940 static DWORD WINSPOOL_GetDWORDFromReg(HKEY hkey, LPCSTR ValueName)
2941 {
2942     DWORD sz = sizeof(DWORD), type, value = 0;
2943     LONG ret;
2944 
2945     ret = RegQueryValueExA(hkey, ValueName, 0, &type, (LPBYTE)&value, &sz);
2946 
2947     if(ret != ERROR_SUCCESS) {
2948         WARN("Got ret = %d on name %s\n", ret, ValueName);
2949         return 0;
2950     }
2951     if(type != REG_DWORD) {
2952         ERR("Got type %d\n", type);
2953         return 0;
2954     }
2955     return value;
2956 }
2957 
2958 
2959 /*****************************************************************************
2960  * get_filename_from_reg [internal]
2961  *
2962  * Get ValueName from hkey storing result in out
2963  * when the Value in the registry has only a filename, use driverdir as prefix
2964  * outlen is space left in out
2965  * String is stored either as unicode or ascii
2966  *
2967  */
2968 
2969 static BOOL get_filename_from_reg(HKEY hkey, LPCWSTR driverdir, DWORD dirlen, LPCWSTR ValueName,
2970                                   LPBYTE out, DWORD outlen, LPDWORD needed, BOOL unicode)
2971 {
2972     WCHAR   filename[MAX_PATH];
2973     DWORD   size;
2974     DWORD   type;
2975     LONG    ret;
2976     LPWSTR  buffer = filename;
2977     LPWSTR  ptr;
2978 
2979     *needed = 0;
2980     size = sizeof(filename);
2981     buffer[0] = '\0';
2982     ret = RegQueryValueExW(hkey, ValueName, NULL, &type, (LPBYTE) buffer, &size);
2983     if (ret == ERROR_MORE_DATA) {
2984         TRACE("need dynamic buffer: %u\n", size);
2985         buffer = HeapAlloc(GetProcessHeap(), 0, size);
2986         if (!buffer) {
2987             /* No Memory is bad */
2988             return FALSE;
2989         }
2990         buffer[0] = '\0';
2991         ret = RegQueryValueExW(hkey, ValueName, NULL, &type, (LPBYTE) buffer, &size);
2992     }
2993 
2994     if ((ret != ERROR_SUCCESS) || (!buffer[0])) {
2995         if (buffer != filename) HeapFree(GetProcessHeap(), 0, buffer);
2996         return FALSE;
2997     }
2998 
2999     ptr = buffer;
3000     while (ptr) {
3001         /* do we have a full path ? */
3002         ret = (((buffer[0] == '\\') && (buffer[1] == '\\')) ||
3003                 (buffer[0] && (buffer[1] == ':') && (buffer[2] == '\\')) );
3004 
3005         if (!ret) {
3006             /* we must build the full Path */
3007             *needed += dirlen;
3008             if ((out) && (outlen > dirlen)) {
3009                 if (unicode) {
3010                     lstrcpyW((LPWSTR)out, driverdir);
3011                 }
3012                 else
3013                 {
3014                     WideCharToMultiByte(CP_ACP, 0, driverdir, -1, (LPSTR)out, outlen, NULL, NULL);
3015                 }
3016                 out += dirlen;
3017                 outlen -= dirlen;
3018             }
3019             else
3020                 out = NULL;
3021         }
3022 
3023         /* write the filename */
3024         if (unicode) {
3025             size = (lstrlenW(ptr) + 1) * sizeof(WCHAR);
3026             if ((out) && (outlen >= size)) {
3027                 lstrcpyW((LPWSTR)out, ptr);
3028                 out += size;
3029                 outlen -= size;
3030             }
3031             else
3032                 out = NULL;
3033         }
3034         else
3035         {
3036             size = WideCharToMultiByte(CP_ACP, 0, ptr, -1, NULL, 0, NULL, NULL);
3037             if ((out) && (outlen >= size)) {
3038                 WideCharToMultiByte(CP_ACP, 0, ptr, -1, (LPSTR)out, outlen, NULL, NULL);
3039                 out += size;
3040                 outlen -= size;
3041             }
3042             else
3043                 out = NULL;
3044         }
3045         *needed += size;
3046         ptr +=  lstrlenW(ptr)+1;
3047         if ((type != REG_MULTI_SZ) || (!ptr[0]))  ptr = NULL;
3048     }
3049 
3050     if (buffer != filename) HeapFree(GetProcessHeap(), 0, buffer);
3051 
3052     /* write the multisz-termination */
3053     if (type == REG_MULTI_SZ) {
3054         size = (unicode) ? sizeof(WCHAR) : 1;
3055 
3056         *needed += size;
3057         if (out && (outlen >= size)) {
3058             memset (out, 0, size);
3059         }
3060     }
3061     return TRUE;
3062 }
3063 
3064 /*****************************************************************************
3065  *    WINSPOOL_GetStringFromReg
3066  *
3067  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
3068  * String is stored either as unicode or ascii.
3069  * Bit of a hack here to get the ValueName if we want ascii.
3070  */
3071 static BOOL WINSPOOL_GetStringFromReg(HKEY hkey, LPCWSTR ValueName, LPBYTE ptr,
3072                                       DWORD buflen, DWORD *needed,
3073                                       BOOL unicode)
3074 {
3075     DWORD sz = buflen, type;
3076     LONG ret;
3077 
3078     if(unicode)
3079         ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3080     else {
3081         LPSTR ValueNameA = strdupWtoA(ValueName);
3082         ret = RegQueryValueExA(hkey, ValueNameA, 0, &type, ptr, &sz);
3083         HeapFree(GetProcessHeap(),0,ValueNameA);
3084     }
3085     if(ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) {
3086         WARN("Got ret = %d\n", ret);
3087         *needed = 0;
3088         return FALSE;
3089     }
3090     /* add space for terminating '\0' */
3091     sz += unicode ? sizeof(WCHAR) : 1;
3092     *needed = sz;
3093 
3094     if (ptr)
3095         TRACE("%s: %s\n", debugstr_w(ValueName), unicode ? debugstr_w((LPCWSTR)ptr) : debugstr_a((LPCSTR)ptr));
3096 
3097     return TRUE;
3098 }
3099 
3100 /*****************************************************************************
3101  *    WINSPOOL_GetDefaultDevMode
3102  *
3103  * Get a default DevMode values for wineps.
3104  * FIXME - use ppd.
3105  */
3106 
3107 static void WINSPOOL_GetDefaultDevMode(
3108         LPBYTE ptr,
3109         DWORD buflen, DWORD *needed,
3110         BOOL unicode)
3111 {
3112     DEVMODEA    dm;
3113     static const char szwps[] = "wineps.drv";
3114 
3115         /* fill default DEVMODE - should be read from ppd... */
3116         ZeroMemory( &dm, sizeof(dm) );
3117         memcpy(dm.dmDeviceName,szwps,sizeof szwps);
3118         dm.dmSpecVersion = DM_SPECVERSION;
3119         dm.dmDriverVersion = 1;
3120         dm.dmSize = sizeof(DEVMODEA);
3121         dm.dmDriverExtra = 0;
3122         dm.dmFields =
3123                 DM_ORIENTATION | DM_PAPERSIZE |
3124                 DM_PAPERLENGTH | DM_PAPERWIDTH |
3125                 DM_SCALE |
3126                 DM_COPIES |
3127                 DM_DEFAULTSOURCE | DM_PRINTQUALITY |
3128                 DM_YRESOLUTION | DM_TTOPTION;
3129 
3130         dm.u1.s1.dmOrientation = DMORIENT_PORTRAIT;
3131         dm.u1.s1.dmPaperSize = DMPAPER_A4;
3132         dm.u1.s1.dmPaperLength = 2970;
3133         dm.u1.s1.dmPaperWidth = 2100;
3134 
3135         dm.u1.s1.dmScale = 100;
3136         dm.u1.s1.dmCopies = 1;
3137         dm.u1.s1.dmDefaultSource = DMBIN_AUTO;
3138         dm.u1.s1.dmPrintQuality = DMRES_MEDIUM;
3139         /* dm.dmColor */
3140         /* dm.dmDuplex */
3141         dm.dmYResolution = 300; /* 300dpi */
3142         dm.dmTTOption = DMTT_BITMAP;
3143         /* dm.dmCollate */
3144         /* dm.dmFormName */
3145         /* dm.dmLogPixels */
3146         /* dm.dmBitsPerPel */
3147         /* dm.dmPelsWidth */
3148         /* dm.dmPelsHeight */
3149         /* dm.u2.dmDisplayFlags */
3150         /* dm.dmDisplayFrequency */
3151         /* dm.dmICMMethod */
3152         /* dm.dmICMIntent */
3153         /* dm.dmMediaType */
3154         /* dm.dmDitherType */
3155         /* dm.dmReserved1 */
3156         /* dm.dmReserved2 */
3157         /* dm.dmPanningWidth */
3158         /* dm.dmPanningHeight */
3159 
3160     if(unicode) {
3161         if(buflen >= sizeof(DEVMODEW)) {
3162             DEVMODEW *pdmW = GdiConvertToDevmodeW(&dm);
3163             memcpy(ptr, pdmW, sizeof(DEVMODEW));
3164             HeapFree(GetProcessHeap(),0,pdmW);
3165         }
3166         *needed = sizeof(DEVMODEW);
3167     }
3168     else
3169     {
3170         if(buflen >= sizeof(DEVMODEA)) {
3171             memcpy(ptr, &dm, sizeof(DEVMODEA));
3172         }
3173         *needed = sizeof(DEVMODEA);
3174     }
3175 }
3176 
3177 /*****************************************************************************
3178  *    WINSPOOL_GetDevModeFromReg
3179  *
3180  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
3181  * DevMode is stored either as unicode or ascii.
3182  */
3183 static BOOL WINSPOOL_GetDevModeFromReg(HKEY hkey, LPCWSTR ValueName,
3184                                        LPBYTE ptr,
3185                                        DWORD buflen, DWORD *needed,
3186                                        BOOL unicode)
3187 {
3188     DWORD sz = buflen, type;
3189     LONG ret;
3190 
3191     if (ptr && buflen>=sizeof(DEVMODEA)) memset(ptr, 0, sizeof(DEVMODEA));
3192     ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3193     if ((ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA)) sz = 0;
3194     if (sz < sizeof(DEVMODEA))
3195     {
3196         TRACE("corrupted registry for %s ( size %d)\n",debugstr_w(ValueName),sz);
3197         return FALSE;
3198     }
3199     /* ensures that dmSize is not erratically bogus if registry is invalid */
3200     if (ptr && ((DEVMODEA*)ptr)->dmSize < sizeof(DEVMODEA))
3201         ((DEVMODEA*)ptr)->dmSize = sizeof(DEVMODEA);
3202     if(<