winspool: better /etc/printcap parsing

Huw D M Davies h.davies1 at physics.ox.ac.uk
Mon Nov 10 09:40:51 CST 2003


        Huw Davies <huw at codeweavers.com>
 	Much better parsing of /etc/printcap.
	Don't even try to use heuristics to guess whether the printer is
	PostScript or not - we're going to get it wrong anyway.
	Don't list printer entries that begin with ispunct() - these are used
	for 'tc' aliases.
-- 
Huw Davies
huw at codeweavers.com
Index: dlls/winspool/info.c
===================================================================
RCS file: /home/wine/wine/dlls/winspool/info.c,v
retrieving revision 1.85
diff -u -r1.85 info.c
--- dlls/winspool/info.c	5 Nov 2003 00:36:47 -0000	1.85
+++ dlls/winspool/info.c	10 Nov 2003 15:38:00 -0000
@@ -176,7 +176,6 @@
     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
        ERROR_SUCCESS) {
         ERR("Can't create Printers key\n");
-	SetLastError(ERROR_FILE_NOT_FOUND); /* ?? */
 	return FALSE;
     }
 
@@ -230,130 +229,160 @@
 static BOOL
 PRINTCAP_ParseEntry(char *pent,BOOL isfirst) {
     PRINTER_INFO_2A	pinfo2a;
-    char		*s,*name,*prettyname,*devname;
-    BOOL		isps = FALSE;
-    char		*port,*devline;
+    char		*e,*s,*name,*prettyname,*devname;
+    BOOL		ret = FALSE, set_default = FALSE;
+    char                *port,*devline,*env_default;
+    HKEY                hkeyPrinter, hkeyPrinters;
 
     while (isspace(*pent)) pent++;
     s = strchr(pent,':');
-    if (!s) return FALSE;
-    *s='\0';
-    name = pent;
-    pent = s+1;
-    TRACE("%s\n",name);
-
-    /* Determine whether this is a postscript printer. */
-
-    /* 1. Check if name or aliases contain trigger phrases like 'ps' */
-    if (strstr(name,"ps")		||
-	strstr(name,"pd")		||	/* postscript double page */
-	strstr(name,"postscript")	||
-	strstr(name,"PostScript")
-    ) {
-	TRACE("%s has 'ps' style name, assuming postscript.\n",name);
-	isps = TRUE;
-    }
-    /* 2. Check if this is a remote printer. These usually are postscript
-     *    capable
-     */
-    if (strstr(pent,":rm")) {
-	isps = TRUE;
-	TRACE("%s is remote, assuming postscript.\n",name);
-    }
-    /* 3. Check if we have an input filter program. If we have one, it
-     *    most likely is one capable of converting postscript.
-     *    (Could probably check for occurrence of 'gs' or 'ghostscript'
-     *     in the if file itself.)
-     */
-    if (strstr(pent,":if=/")) {
-	isps = TRUE;
-	TRACE("%s has inputfilter program, assuming postscript.\n",name);
+    if(s) *s='\0';
+    name = HeapAlloc(GetProcessHeap(), 0, strlen(pent) + 1);
+    strcpy(name,pent);
+    if(s) {
+        *s=':';
+        pent = s;
+    } else
+        pent = "";
+
+    TRACE("name=%s entry=%s\n",name, pent);
+
+    if(ispunct(*name)) { /* a tc entry, not a real printer */
+        TRACE("skipping tc entry\n");
+        goto end;
+    }
+
+    if(strstr(pent,":server")) { /* server only version so skip */
+        TRACE("skipping server entry\n");
+        goto end;
     }
 
-    /* If it is not a postscript printer, we cannot use it. */
-    if (!isps)
-	return FALSE;
+    /* Determine whether this is a postscript printer. */
 
+    ret = TRUE;
+    env_default = getenv("PRINTER");
     prettyname = name;
     /* Get longest name, usually the one at the right for later display. */
-    while ((s=strchr(prettyname,'|'))) prettyname = s+1;
-    s=strchr(name,'|');if (s) *s='\0';
+    while((s=strchr(prettyname,'|'))) {
+        *s = '\0';
+        e = s;
+        while(isspace(*--e)) *e = '\0';
+        TRACE("\t%s\n", debugstr_a(prettyname));
+        if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
+        for(prettyname = s+1; isspace(*prettyname); prettyname++)
+            ;
+    }
+    e = prettyname + strlen(prettyname);
+    while(isspace(*--e)) *e = '\0';
+    TRACE("\t%s\n", debugstr_a(prettyname));
+    if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
 
     /* prettyname must fit into the dmDeviceName member of DEVMODE struct,
      * if it is too long, we use it as comment below. */
     devname = prettyname;
     if (strlen(devname)>=CCHDEVICENAME-1)
 	 devname = name;
-    if (strlen(devname)>=CCHDEVICENAME-1)
-	return FALSE;
-
-    if (isfirst) /* set first entry as default */
-	    WINSPOOL_SetDefaultPrinter(devname,name,TRUE);
+    if (strlen(devname)>=CCHDEVICENAME-1) {
+        ret = FALSE;
+        goto end;
+    }
 
-    memset(&pinfo2a,0,sizeof(pinfo2a));
-    pinfo2a.pPrinterName	= devname;
-    pinfo2a.pDatatype		= "RAW";
-    pinfo2a.pPrintProcessor	= "WinPrint";
-    pinfo2a.pDriverName		= "PS Driver";
-    pinfo2a.pComment		= "WINEPS Printer using LPR";
-    pinfo2a.pLocation		= prettyname;
     port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(name)+1);
     sprintf(port,"LPR:%s",name);
-    pinfo2a.pPortName		= port;
-    pinfo2a.pParameters		= "<parameters?>";
-    pinfo2a.pShareName		= "<share name?>";
-    pinfo2a.pSepFile		= "<sep file?>";
 
     devline=HeapAlloc(GetProcessHeap(),0,strlen("WINEPS,")+strlen(port)+1);
     sprintf(devline,"WINEPS,%s",port);
     WriteProfileStringA("devices",devname,devline);
     HeapFree(GetProcessHeap(),0,devline);
-
-    if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
-	if (GetLastError()!=ERROR_PRINTER_ALREADY_EXISTS)
-	    ERR("%s not added by AddPrinterA (%ld)\n",name,GetLastError());
+    
+    if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
+       ERROR_SUCCESS) {
+        ERR("Can't create Printers key\n");
+	ret = FALSE;
+        goto end;
+    }
+    if(RegOpenKeyA(hkeyPrinters, devname, &hkeyPrinter) == ERROR_SUCCESS) {
+        /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
+           and continue */
+        TRACE("Printer already exists\n");
+        RegDeleteValueW(hkeyPrinter, May_Delete_Value);
+        RegCloseKey(hkeyPrinter);
+    } else {
+        memset(&pinfo2a,0,sizeof(pinfo2a));
+        pinfo2a.pPrinterName            = devname;
+        pinfo2a.pDatatype               = "RAW";
+        pinfo2a.pPrintProcessor	        = "WinPrint";
+        pinfo2a.pDriverName             = "PS Driver";
+        pinfo2a.pComment                = "WINEPS Printer using LPR";
+        pinfo2a.pLocation               = prettyname;
+        pinfo2a.pPortName               = port;
+        pinfo2a.pParameters             = "<parameters?>";
+        pinfo2a.pShareName              = "<share name?>";
+        pinfo2a.pSepFile                = "<sep file?>";
+
+        if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
+            if (GetLastError()!=ERROR_PRINTER_ALREADY_EXISTS)
+                ERR("%s not added by AddPrinterA (%ld)\n",name,GetLastError());
+        }
     }
-    HeapFree(GetProcessHeap(),0,port);
-    return TRUE;
+    RegCloseKey(hkeyPrinters);
+
+    if (isfirst || set_default)
+        WINSPOOL_SetDefaultPrinter(devname,name,TRUE);
+
+    HeapFree(GetProcessHeap(), 0, port);
+ end:
+    HeapFree(GetProcessHeap(), 0, name);
+    return ret;
 }
 
 static BOOL
 PRINTCAP_LoadPrinters(void) {
-    BOOL		hadprinter = FALSE, isfirst = TRUE;
+    BOOL		hadprinter = FALSE;
     char		buf[200];
     FILE		*f;
+    char *pent = NULL;
+    BOOL had_bash = FALSE;
 
     f = fopen("/etc/printcap","r");
     if (!f)
 	return FALSE;
 
-    while (fgets(buf,sizeof(buf),f)) {
-	char	*pent = NULL;
-	do {
-	    char	*s;
-	    s=strchr(buf,'\n'); if (s) *s='\0';
-	    if ((buf[0]=='#') || (buf[0]=='\0'))
-		continue;
-
-	    if (pent) {
-		pent=HeapReAlloc(GetProcessHeap(),0,pent,strlen(pent)+strlen(buf)+2);
-		strcat(pent,buf);
-	    } else {
-		pent=HeapAlloc(GetProcessHeap(),0,strlen(buf)+1);
-		strcpy(pent,buf);
-	    }
-
-	    if (strlen(pent) && (pent[strlen(pent)-1] == '\\'))
-		pent[strlen(pent)-1] = '\0';
-	    else
-		break;
-	} while (fgets(buf,sizeof(buf),f));
-	if (pent)
-	    hadprinter |= PRINTCAP_ParseEntry(pent,isfirst);
-	isfirst = FALSE;
-	if (pent) HeapFree(GetProcessHeap(),0,pent);
-	pent = NULL;
-	if (feof(f)) break;
+    while(fgets(buf,sizeof(buf),f)) {
+        char *start, *end;
+
+        end=strchr(buf,'\n');
+        if (end) *end='\0';
+    
+        start = buf;
+        while(isspace(*start)) start++;
+        if(*start == '#' || *start == '\0')
+            continue;
+
+        if(pent && !had_bash && *start != ':' && *start != '|') { /* start of new entry, parse the previous one */
+	    hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
+            HeapFree(GetProcessHeap(),0,pent);
+            pent = NULL;
+        }
+
+        if (end && *--end == '\\') {
+            *end = '\0';
+            had_bash = TRUE;
+        } else
+            had_bash = FALSE;
+
+        if (pent) {
+            pent=HeapReAlloc(GetProcessHeap(),0,pent,strlen(pent)+strlen(start)+1);
+            strcat(pent,start);
+        } else {
+            pent=HeapAlloc(GetProcessHeap(),0,strlen(start)+1);
+            strcpy(pent,start);
+        }
+
+    }
+    if(pent) {
+        hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
+        HeapFree(GetProcessHeap(),0,pent);
     }
     fclose(f);
     return hadprinter;
@@ -413,9 +442,10 @@
         RegCloseKey(hkeyPrinters);
     }
 
-    /* We want to avoid calling AddPrinter on cups printers as much as
-       possible, because this will (eventually) lead to a call to
-       cupsGetPPD which takes forever.  So we'll tag all printers that
+    /* We want to avoid calling AddPrinter on printers as much as
+       possible, because on cups printers this will (eventually) lead
+       to a call to cupsGetPPD which takes forever, even with non-cups
+       printers AddPrinter takes a while.  So we'll tag all printers that
        were automatically added last time around, if they still exist
        we'll leave them be otherwise we'll delete them. */
     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
@@ -440,11 +470,18 @@
 
 
 #ifdef HAVE_CUPS_CUPS_H
-    /* If we have any CUPS based printers, skip looking for printcap printers */
     done = CUPS_LoadPrinters();
-
 #endif
 
+    if(!done) { /* If we have any CUPS based printers, skip looking for printcap printers */
+        /* Check for [ppd] section in config file before parsing /etc/printcap */
+        if (RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\ppd",
+                        &hkey) == ERROR_SUCCESS) {
+            RegCloseKey(hkey);
+            PRINTCAP_LoadPrinters();
+        }
+    }
+
     /* Now enumerate the list again and delete any printers that a still tagged */
     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
     if(needed) {
@@ -469,18 +506,8 @@
         HeapFree(GetProcessHeap(), 0, pi);
     }
 
+    return;
 
-    if(done)
-        return;
-
-    /* Check for [ppd] section in config file before parsing /etc/printcap */
-
-    if (RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\ppd",
-    	    &hkey) == ERROR_SUCCESS)
-    {
-    	RegCloseKey(hkey);
-    	PRINTCAP_LoadPrinters();
-    }
 }
 
 



More information about the wine-patches mailing list