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