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