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