summaryrefslogtreecommitdiff
path: root/Xprint/Init.c
diff options
context:
space:
mode:
Diffstat (limited to 'Xprint/Init.c')
-rw-r--r--Xprint/Init.c387
1 files changed, 298 insertions, 89 deletions
diff --git a/Xprint/Init.c b/Xprint/Init.c
index 0deafda7c..cd16847ee 100644
--- a/Xprint/Init.c
+++ b/Xprint/Init.c
@@ -2,11 +2,12 @@
/*
(c) Copyright 1996 Hewlett-Packard Company
(c) Copyright 1996 International Business Machines Corp.
-(c) Copyright 1996 Sun Microsystems, Inc.
+(c) Copyright 1996-2004 Sun Microsystems, Inc.
(c) Copyright 1996 Novell, Inc.
(c) Copyright 1996 Digital Equipment Corp.
(c) Copyright 1996 Fujitsu Limited
(c) Copyright 1996 Hitachi, Ltd.
+(c) Copyright 2003-2004 Roland Mainz <roland.mainz@nrubsig.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -50,7 +51,6 @@ copyright holders.
** *********************************************************
**
********************************************************************/
-/* $XFree86: xc/programs/Xserver/Xprint/Init.c,v 1.14tsi Exp $ */
#include <unistd.h>
#include <stdlib.h>
@@ -91,6 +91,7 @@ typedef char *XPointer;
#include "attributes.h"
#include "os.h"
+#include "spooler.h"
static void GenericScreenInit(
int index,
@@ -116,57 +117,6 @@ static Bool InitPrintDrivers(
#define MODELDIRNAME "/models"
#define FONTDIRNAME "/fonts"
-/*
- * The string LIST_QUEUES is fed to a shell to generate an ordered
- * list of available printers on the system. These string definitions
- * are taken from the file PrintSubSys.C within the code for the
- * dtprintinfo program.
- */
-#ifdef AIXV4
-const char *LIST_QUEUES = "lsallq | grep -v '^bsh$' | sort";
-#else
-#ifdef hpux
-const char *LIST_QUEUES = "LANG=C lpstat -v | "
- "awk '"
- " $2 == \"for\" "
- " { "
- " x = match($3, /:/); "
- " print substr($3, 1, x-1)"
- " }' | sort";
-#else
-#ifdef __osf__
- const char *LIST_QUEUES = "LANG=C lpstat -v | "
- "nawk '"
- " $2 == \"for\" "
- " { print $4 }' "
- " | sort";
-#else
-#ifdef __uxp__
-const char *LIST_QUEUES = "LANG=C lpstat -v | "
- "nawk '"
- " $4 == \"for\" "
- " { "
- " x = match($5, /:/); "
- " print substr($5, 1, x-1)"
- " }' | sort";
-#else
-#if defined(CSRG_BASED) || defined(linux) || defined(ISC) || defined(__GNUC__)
-const char *LIST_QUEUES = "LANG=C lpc status | grep -v '^\t' | "
- "sed -e /:/s/// | sort";
-#else
-const char *LIST_QUEUES = "LANG=C lpstat -v | "
- "nawk '"
- " $2 == \"for\" "
- " { "
- " x = match($3, /:/); "
- " print substr($3, 1, x-1)"
- " }' | sort";
-#endif
-#endif
-#endif
-#endif
-#endif
-
#ifdef XPRASTERDDX
static
@@ -183,9 +133,9 @@ PixmapFormatRec RasterPixmapFormats[] = {
static
PixmapFormatRec ColorPclPixmapFormats[] = {
- { 1, 1, BITMAP_SCANLINE_PAD },
- { 8, 8, BITMAP_SCANLINE_PAD },
- { 24,32, BITMAP_SCANLINE_PAD }
+ { 1, 1, BITMAP_SCANLINE_PAD },
+ { 8, 8, BITMAP_SCANLINE_PAD },
+ { 24, 32, BITMAP_SCANLINE_PAD }
};
#define NUMCPCLFORMATS (sizeof ColorPclPixmapFormats)/(sizeof ColorPclPixmapFormats[0])
@@ -211,9 +161,9 @@ PixmapFormatRec MonoPclPixmapFormats[] = {
static
PixmapFormatRec PSPixmapFormats[] = {
- { 1, 1, BITMAP_SCANLINE_PAD },
- { 8, 8, BITMAP_SCANLINE_PAD },
- { 24,32, BITMAP_SCANLINE_PAD }
+ { 1, 1, BITMAP_SCANLINE_PAD },
+ { 8, 8, BITMAP_SCANLINE_PAD },
+ { 24, 32, BITMAP_SCANLINE_PAD }
};
#define NUMPSFORMATS (sizeof PSPixmapFormats)/(sizeof PSPixmapFormats[0])
@@ -296,6 +246,7 @@ typedef struct _printerDbEntry {
char *qualifier;
int screenNum;
char *driverName;
+ char *desc;
} PrinterDbEntry, *PrinterDbPtr;
static PrinterDbPtr printerDb = (PrinterDbPtr)NULL;
@@ -325,10 +276,82 @@ typedef struct _driverMapping {
int screenNum;
} DriverMapEntry, *DriverMapPtr;
+static const char configFilePath[] =
+"/etc/dt/config/print:/usr/dt/config/print";
+
+static const char printServerConfigDir[] = "XPSERVERCONFIGDIR";
+
+static int printScreenPrivIndex,
+ printWindowPrivIndex,
+ printGCPrivIndex;
+static unsigned long printGeneration = 0;
static char *configFileName = (char *)NULL;
static Bool freeDefaultFontPath = FALSE;
static char *origFontPath = (char *)NULL;
+static Bool xprintInitGlobalsCalled = FALSE;
+/*
+ * This function is responsible for doing initalisation of any global
+ * variables at an very early point of server startup (even before
+ * |ProcessCommandLine()|.
+ */
+void XprintInitGlobals(void)
+{
+ extern char dispatchExceptionAtReset; /* defined in Xserver/dix/dispatch.c */
+
+ xprintInitGlobalsCalled = TRUE;
+
+#ifdef SMART_SCHEDULE
+ /* Somehow the XF86 "smart scheduler" completely kills the Xprint DDX
+ * (see http://xprint.freedesktop.org/cgi-bin/bugzilla/show_bug.cgi?id=467
+ * ("Xfree86's "smart scheduler" breaks Xprt") */
+ SmartScheduleDisable = TRUE;
+#endif /* SMART_SCHEDULE */
+
+ /* Disable internal screensaver for Xprint (workaround for
+ * http://pdx.freedesktop.org/cgi-bin/bugzilla/show_bug.cgi?id=567 ("Xorg
+ * Xprt starts to consume 100% CPU when being idle for some time")) */
+ defaultScreenSaverTime = 0;
+
+ /* Ensure that the maximum request size for the BIGREQUESTS extension
+ * is at least 8MB (see
+ * http://xprint.freedesktop.org/cgi-bin/bugzilla/show_bug.cgi?id=622 - "RFE:
+ * Xprt's default BIGREQUESTS extension buffer size should be 8MB")
+ */
+ maxBigRequestSize = (8*1048576)-1;
+
+ /* Xprt should not reset by default when the last client exists
+ * (default for Xprt is |0|, video Xservers use |DE_RESET|) */
+ dispatchExceptionAtReset = 0;
+}
+
+/*
+ * XprintUseMsg() prints usage for the Xprint-specific options
+ */
+void XprintUseMsg()
+{
+ XpSpoolerTypePtr curr = xpstm;
+
+ /* Option '-XpFile' */
+ ErrorF("-XpFile file specifies an alternate `Xprinters' file, rather\n");
+ ErrorF(" than the default one (e.g.\n");
+ ErrorF(" `${XPCONFIGDIR}/${LANG}/print/Xprinters') or\n");
+ ErrorF(" `${XPCONFIGDIR}/C/print/Xprinters'.\n");
+
+ /* Option '-XpSpoolerType' */
+ ErrorF("-XpSpoolerType string specifies a spooler type.\n");
+ ErrorF(" Supported values are:\n");
+
+ while( curr->name != NULL )
+ {
+ ErrorF(" - '%s'\n", curr->name);
+ curr++;
+ }
+ ErrorF(" (multiple values can be specified, seperated by ':',\n");
+ ErrorF(" the first active spooler will be chosen).\n");
+ ErrorF(" default is '%s'.\n", XPDEFAULTSPOOLERNAMELIST);
+}
+
/*
* XprintOptions checks argv[i] to see if it is our command line
* option specifying a configuration file name. It returns the index
@@ -340,6 +363,7 @@ XprintOptions(
char **argv,
int i)
{
+ extern void ddxUseMsg();
if(strcmp(argv[i], "-XpFile") == 0)
{
if ((i + 1) >= argc) {
@@ -349,8 +373,19 @@ XprintOptions(
configFileName = argv[i + 1];
return i + 2;
}
+ else if(strcmp(argv[i], "-XpSpoolerType") == 0)
+ {
+ if ((i + 1) >= argc) {
+ ddxUseMsg ();
+ return i + 2;
+ }
+ XpSetSpoolerTypeNameList(argv[i + 1]);
+ return i + 2;
+ }
else
+ {
return i;
+ }
}
/************************************************************
@@ -417,19 +452,21 @@ FreePrinterDb(void)
{
PrinterDbPtr pCurEntry, pNextEntry;
- for(pCurEntry = printerDb, pNextEntry = 0;
+ for(pCurEntry = printerDb, pNextEntry = (PrinterDbPtr)NULL;
pCurEntry != (PrinterDbPtr)NULL; pCurEntry = pNextEntry)
{
pNextEntry = pCurEntry->next;
if(pCurEntry->name != (char *)NULL)
xfree(pCurEntry->name);
+ if(pCurEntry->desc != (char *)NULL)
+ xfree(pCurEntry->desc);
/*
* We don't free the driver name, because it's expected to simply
* be a pointer into the xrm database.
*/
xfree(pCurEntry);
}
- printerDb = 0;
+ printerDb = (PrinterDbPtr)NULL;
}
/*
@@ -439,12 +476,13 @@ FreePrinterDb(void)
* XXX AddPrinterDbName needs to check for (and not add) duplicate names.
*/
static Bool
-AddPrinterDbName(char *name)
+AddPrinterDbName(char *name, char *desc)
{
PrinterDbPtr pEntry = (PrinterDbPtr)xalloc(sizeof(PrinterDbEntry));
if(pEntry == (PrinterDbPtr)NULL) return FALSE;
- pEntry->name = strdup(name);
+ pEntry->name = (name != NULL) ? strdup(name) : NULL;
+ pEntry->desc = (desc != NULL) ? strdup(desc) : NULL;
pEntry->qualifier = (char *)NULL;
if(printerDb == (PrinterDbPtr)NULL)
@@ -460,20 +498,45 @@ AddPrinterDbName(char *name)
return TRUE;
}
-static void
+static int
AugmentPrinterDb(const char *command)
{
FILE *fp;
char name[256];
+ int num_printers = 0; /* Number of printers we found */
+ size_t namelen;
+ char *desc = NULL;
fp = popen(command, "r");
/* XXX is a 256 character limit overly restrictive for printer names? */
- while(fgets(name, 256, fp) != (char *)NULL && strlen(name))
+ while(fgets(name, 256, fp) != (char *)NULL && (namelen=strlen(name)))
{
- name[strlen(name) - 1] = (char)'\0'; /* strip the \n */
- AddPrinterDbName(name);
+ char *option = name;
+
+ name[namelen-1] = (char)'\0'; /* strip the \n */
+
+#define XP_DESCRIPTOR "xp-printerattr.descriptor="
+#define XP_DESCRIPTOR_LEN (sizeof(XP_DESCRIPTOR)-1)
+ while (option = strchr(option, '\t')) {
+ option++; /* Skip the '\t' */
+ if (!strncmp(option, XP_DESCRIPTOR, XP_DESCRIPTOR_LEN)) {
+ *(option-1) = '\0'; /* Kill the '\t' (only if we found a valid option) */
+ option += XP_DESCRIPTOR_LEN;
+ if (*option != '\0') {
+ desc = option;
+ }
+ }
+ else
+ {
+ /* Unknown option */
+ ErrorF("AugmentPrinterDb: Unknown option '%s'\n", option);
+ }
+ }
+ AddPrinterDbName(name, desc);
+ num_printers++;
}
pclose(fp);
+ return num_printers;
}
/*
@@ -592,7 +655,7 @@ StoreDriverNames(void)
"xp-ddx-identifier");
if(pEntry->driverName == (char *)NULL ||
strlen(pEntry->driverName) == 0 ||
- GetInitFunc(pEntry->driverName) == 0)
+ GetInitFunc(pEntry->driverName) == (Bool(*)())NULL)
{
if (pEntry->driverName && (strlen(pEntry->driverName) != 0)) {
ErrorF("Xp Extension: Can't load driver %s\n",
@@ -609,6 +672,29 @@ StoreDriverNames(void)
}
}
+/*
+ * StoreDescriptors - queries the attribute store for the descriptor.
+ * if the descriptor is not in the attribute database, then the descriptor
+ * from the printerDb is store in the attribute store for the printer.
+ */
+static void
+StoreDescriptors()
+{
+ PrinterDbPtr pEntry;
+
+ for(pEntry = printerDb; pEntry != (PrinterDbPtr)NULL;
+ pEntry = pEntry->next)
+ {
+ if (pEntry->desc != NULL)
+ {
+ XpAddPrinterAttribute(pEntry->name,
+ (pEntry->qualifier != (char *)NULL)?
+ pEntry->qualifier : pEntry->name,
+ "*descriptor", pEntry->desc);
+ }
+ }
+}
+
static char *
MbStrchr(
char *str,
@@ -650,7 +736,7 @@ GetConfigFileName(void)
/*
* Check for a LANG-specific file.
*/
- if ((dirName = XpGetConfigDir(TRUE)) != 0)
+ if(dirName = XpGetConfigDir(TRUE))
{
filePath = (char *)xalloc(strlen(dirName) +
strlen(XPRINTERSFILENAME) + 2);
@@ -669,7 +755,7 @@ GetConfigFileName(void)
xfree(filePath);
}
- if ((dirName = XpGetConfigDir(FALSE)) != 0)
+ if(dirName = XpGetConfigDir(FALSE))
{
filePath = (char *)xalloc(strlen(dirName) +
strlen(XPRINTERSFILENAME) + 2);
@@ -701,6 +787,7 @@ GetConfigFileName(void)
static PrinterDbPtr
BuildPrinterDb(void)
{
+ char *printerList, *augmentCmd = (char *)NULL;
Bool defaultAugment = TRUE, freeConfigFileName;
if(configFileName && access(configFileName, R_OK) != 0)
@@ -727,9 +814,9 @@ BuildPrinterDb(void)
{
while((tok = strtok((char *)NULL, " \t")) != (char *)NULL)
{
- if ((ptr = MbStrchr(tok, '\012')) != 0)
+ if(ptr = MbStrchr(tok, '\012'))
*ptr = (char)'\0';
- AddPrinterDbName(tok);
+ AddPrinterDbName(tok, NULL);
}
}
else if(strcmp(tok, "Map") == 0)
@@ -768,7 +855,43 @@ BuildPrinterDb(void)
if(defaultAugment == TRUE)
{
- AugmentPrinterDb(LIST_QUEUES);
+ XpSpoolerTypePtr curr_spooler_type; /* spooler we are currently probing for queues */
+ int num_printers_found; /* number of printers found by |AugmentPrinterDb()| */
+ char *tok_lasts; /* strtok_r() position token */
+ char *spnamelist; /* list of spooler names, seperated by ":" */
+ char *spname; /* spooler name */
+
+ spnamelist = strdup(XpGetSpoolerTypeNameList()); /* strtok_r() modifies string so dup' it first */
+
+ for( spname = strtok_r(spnamelist, ":", &tok_lasts) ;
+ spname != NULL ;
+ spname = strtok_r(NULL, ":", &tok_lasts) )
+ {
+ curr_spooler_type = XpSpoolerNameToXpSpoolerType(spname);
+ if(!curr_spooler_type)
+ {
+ FatalError("BuildPrinterDb: No spooler type entry found for '%s'.\n", spname);
+ }
+
+ if(curr_spooler_type->list_queues_command == NULL ||
+ strlen(curr_spooler_type->list_queues_command) == 0)
+ {
+ continue;
+ }
+
+ num_printers_found = AugmentPrinterDb(curr_spooler_type->list_queues_command);
+ /* Did we found a spooler which works ? */
+ if(num_printers_found > 0)
+ {
+ spooler_type = curr_spooler_type;
+#ifdef DEBUG_gisburn
+ fprintf(stderr, "BuildPrinterDb: using '%s'.\n", spooler_type->name);
+#endif /* DEBUG_gisburn */
+ break;
+ }
+ }
+
+ free(spnamelist);
}
MergeNameMap();
@@ -782,6 +905,7 @@ BuildPrinterDb(void)
* in the printerDb
*/
StoreDriverNames();
+ StoreDescriptors();
if(freeConfigFileName)
{
@@ -951,7 +1075,7 @@ FindFontDir(
return (char *)NULL;
configDir = XpGetConfigDir(TRUE);
- if ((fontDir = ValidateFontDir(configDir, modelName)) != 0)
+ if(fontDir = ValidateFontDir(configDir, modelName))
{
xfree(configDir);
return fontDir;
@@ -1013,8 +1137,8 @@ AddToFontPath(
static void
AugmentFontPath(void)
{
- char *modelID, **allIDs = (char **)NULL;
- PrinterDbPtr pDbEntry;
+ char *newPath, *modelID, **allIDs = (char **)NULL;
+ PrinterDbPtr pDb, pDbEntry;
int numModels, i;
if(!origFontPath)
@@ -1072,7 +1196,7 @@ AugmentFontPath(void)
for(i = 0; allIDs != (char **)NULL && allIDs[i] != (char *)NULL; i ++)
{
char *fontDir;
- if ((fontDir = FindFontDir(allIDs[i])) != 0)
+ if(fontDir = FindFontDir(allIDs[i]))
{
AddToFontPath(fontDir);
xfree(fontDir);
@@ -1162,7 +1286,7 @@ XpClientIsPrintClient(
* fpe->name.
*/
if(fpe->name_length < PATH_PREFIX_LEN ||
- (strlen(fontDir) != (unsigned)(fpe->name_length - PATH_PREFIX_LEN)) ||
+ (strlen(fontDir) != (fpe->name_length - PATH_PREFIX_LEN)) ||
strncmp(fontDir, fpe->name + PATH_PREFIX_LEN,
fpe->name_length - PATH_PREFIX_LEN))
{
@@ -1235,6 +1359,38 @@ PrinterInitOutput(
char **driverNames;
char *configDir;
+ /* This should NEVER happen, but... */
+ if( !xprintInitGlobalsCalled )
+ {
+ FatalError("Internal error: XprintInitGlobals() not called.");
+ }
+#ifdef SMART_SCHEDULE
+ /* |XprintInitGlobals| should have set |SmartScheduleDisable| to
+ * |TRUE| - if not we will trigger this safeguard. */
+ if( SmartScheduleDisable != TRUE )
+ {
+ FatalError("Internal error: XF86 smart scheduler incompatible to Xprint DDX.");
+ }
+#endif /* SMART_SCHEDULE */
+ /* Safeguard for
+ * http://pdx.freedesktop.org/cgi-bin/bugzilla/show_bug.cgi?id=567 ("Xorg
+ * Xprt starts to consume 100% CPU when being idle for some time")
+ * |XprintInitGlobals| should have set |defaultScreenSaverTime| to
+ * |0| - if not we will trigger this trap. */
+ if( defaultScreenSaverTime != 0 )
+ {
+ FatalError("Internal screen saver must be OFF for printing.");
+ }
+
+ /* Print a warnung when the maximum request size of the BIGREQUESTS
+ * extension is smaller than 8MB (see
+ * http://xprint.freedesktop.org/cgi-bin/bugzilla/show_bug.cgi?id=622)
+ */
+ if (maxBigRequestSize < (8*1048576)-1) {
+ ErrorF("Xp Extension: BIGREQUESTS max. request is currently %ld bytes "
+ ", recommemded minimum for Xprint is 8MB.\n", (long)maxBigRequestSize);
+ }
+
/*
* this little test is just a warning at startup to make sure
* that the config directory exists.
@@ -1254,10 +1410,9 @@ PrinterInitOutput(
xfree(configDir);
}
else {
- ErrorF("Xp Extension: could not find config dir %s\n",
- configDir ? configDir : XPRINTDIR);
-
- if (configDir) xfree(configDir);
+ /* Refuse to start when we do not have our config dir... */
+ FatalError("Xp Extension: could not find config dir %s\n",
+ configDir ? configDir : XPRINTDIR);
}
if(printerDb != (PrinterDbPtr)NULL)
@@ -1414,7 +1569,7 @@ InitPrintDrivers(
}
if(callInit == TRUE)
{
- pBFunc initFunc;
+ Bool (*initFunc)();
initFunc = GetInitFunc(pDb->driverName);
if(initFunc(index, pScreen, argc, argv) == FALSE)
{
@@ -1451,9 +1606,10 @@ GenericScreenInit(
int argc,
char **argv)
{
+ int i;
float fWidth, fHeight, maxWidth, maxHeight;
unsigned short width, height;
- PrinterDbPtr pDb;
+ PrinterDbPtr pDb, pDb2;
int res, maxRes;
/*
@@ -1477,6 +1633,7 @@ GenericScreenInit(
{
if(pDb->screenNum == index)
{
+
XpValidatePoolsRec *pValRec;
pVFunc dimensionsFunc;
@@ -1506,6 +1663,57 @@ GenericScreenInit(
}
/*
+ * QualifyName - takes an unqualified file name such as X6printers and
+ * a colon-separated list of directory path names such as
+ * /etc/opt/dt:/opt/dt/config.
+ *
+ * Returns a fully qualified file path name such as /etc/opt/dt/X6printers.
+ * The returned value is malloc'd, and the caller is responsible for
+ * freeing the associated memory.
+ */
+static char *
+QualifyName(fileName, searchPath)
+ char *fileName;
+ char *searchPath;
+{
+ char * curPath = searchPath;
+ char * nextPath;
+ char * chance;
+ FILE *pFile;
+
+ if (fileName == NULL || searchPath == NULL)
+ return NULL;
+
+ while (1) {
+ if ((nextPath = strchr(curPath, ':')) != NULL)
+ *nextPath = 0;
+
+ chance = (char *)xalloc(strlen(curPath) + strlen(fileName) + 2);
+ sprintf(chance,"%s/%s",curPath,fileName);
+
+ /* see if we can read from the file */
+ if((pFile = fopen(chance, "r")) != (FILE *)NULL)
+ {
+ fclose(pFile);
+ /* ... restore the colon, .... */
+ if (nextPath)
+ *nextPath = ':';
+
+ return chance;
+ }
+
+ xfree(chance);
+
+ if (nextPath == NULL) /* End of path list? */
+ break;
+
+ /* try the next path */
+ curPath = nextPath + 1;
+ }
+ return NULL;
+}
+
+/*
* FillPrinterListEntry fills in a single XpDiListEntry element with data
* derived from the supplied PrinterDbPtr element.
*
@@ -1548,12 +1756,11 @@ GetPrinterListInfo(
int localeLen,
char *locale)
{
- PrinterDbPtr pDb;
+ PrinterDbPtr pDb, pDb2;
for(pDb = printerDb; pDb != (PrinterDbPtr)NULL; pDb = pDb->next)
{
- if (strlen(pDb->name) == (unsigned)nameLen
- && !strncmp(pDb->name, name, nameLen))
+ if(strlen(pDb->name) == nameLen && !strncmp(pDb->name, name, nameLen))
{
FillPrinterListEntry(pEntry, pDb, localeLen, locale);
return TRUE;
@@ -1598,7 +1805,7 @@ XpDiGetPrinterList(
if(!nameLen || name == (char *)NULL)
{
int i;
- PrinterDbPtr pDb;
+ PrinterDbPtr pDb, pDb2;
for(pDb = printerDb, i = 0; pDb != (PrinterDbPtr)NULL;
pDb = pDb->next, i++)
@@ -1648,11 +1855,12 @@ WindowPtr
XpDiValidatePrinter(char *printerName, int printerNameLen)
{
PrinterDbPtr pCurEntry;
+ WindowPtr pWin;
for(pCurEntry = printerDb;
pCurEntry != (PrinterDbPtr)NULL; pCurEntry = pCurEntry->next)
{
- if(strlen(pCurEntry->name) == (unsigned)printerNameLen &&
+ if(strlen(pCurEntry->name) == printerNameLen &&
!strncmp(pCurEntry->name, printerName, printerNameLen))
return WindowTable[pCurEntry->screenNum];
}
@@ -1680,3 +1888,4 @@ XpDiGetDriverName(int index, char *printerName)
return (char *)NULL; /* XXX Should we supply a default driverName? */
}
+