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

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

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

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