diff options
Diffstat (limited to 'xorg-server/hw/xprint/attributes.c')
-rw-r--r-- | xorg-server/hw/xprint/attributes.c | 1740 |
1 files changed, 1740 insertions, 0 deletions
diff --git a/xorg-server/hw/xprint/attributes.c b/xorg-server/hw/xprint/attributes.c new file mode 100644 index 000000000..e79360496 --- /dev/null +++ b/xorg-server/hw/xprint/attributes.c @@ -0,0 +1,1740 @@ +/* +(c) Copyright 1996 Hewlett-Packard Company +(c) Copyright 1996 International Business Machines Corp. +(c) Copyright 1996 Sun Microsystems, Inc. +(c) Copyright 1996 Novell, Inc. +(c) Copyright 1996 Digital Equipment Corp. +(c) Copyright 1996 Fujitsu Limited +(c) Copyright 1996 Hitachi, Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the names of the copyright holders shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in this Software without prior written authorization from said +copyright holders. +*/ +/******************************************************************* +** +** ********************************************************* +** * +** * File: attributes.c +** * +** * Contents: +** * Implementation of the attribute store for Xp. +** * +** * Copyright: Copyright 1995 Hewlett-Packard Company +** * +** ********************************************************* +** +********************************************************************/ + +#ifdef HAVE_DIX_CONFIG_H +#include <dix-config.h> +#endif + +#include <X11/Xproto.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <pwd.h> +#include <grp.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#if (defined(sun) && defined(SVR4)) || defined(__SCO__) || defined(__UNIXWARE__) +#include <wchar.h> +#endif +#include "scrnintstr.h" + +#include <X11/extensions/Printstr.h> + +#include "attributes.h" + +#include <X11/Xlib.h> +#include <X11/Xresource.h> + +#include "spooler.h" + +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif +#ifndef MAX +#define MAX(a,b) (((a)>(b))?(a):(b)) +#endif + + +static XrmDatabase CopyDb(XrmDatabase inDb); + +extern XrmDatabase XpSpoolerGetServerAttributes(void); + +static int attrGeneration = 0; + +typedef struct { + XrmDatabase *pDb; + char *qualifier; + char *modelId; +} DbEnumStruct; + +typedef struct { + char *stringDb; + int nextPos; + int space; +} StringDbStruct; + +typedef struct _printerAttrs { + struct _printerAttrs *next; + char *name; + char *qualifier; + XrmDatabase printerAttrs; + XrmDatabase docAttrs; + XrmDatabase jobAttrs; +} PrAttrs, *PrAttrPtr; + +static PrAttrPtr attrList = (PrAttrPtr)NULL; + +typedef struct _systemAttrs { + XrmDatabase doc; + XrmDatabase job; + XrmDatabase printers; + XrmDatabase server; +} SysAttrs, *SysAttrsPtr; + +SysAttrs systemAttributes; + +/* + * attrCtxtPrivIndex hold the attribute store's context private index. + * This index is allocated at the time the attribute store is initialized. + */ +static DevPrivateKey attrCtxtPrivKey = &attrCtxtPrivKey; + +/* + * The ContextAttrs structure descibes the context private space reserved + * by the attribute store. + */ +typedef struct _contextAttrs { + XrmDatabase printerAttrs; + XrmDatabase docAttrs; + XrmDatabase jobAttrs; + XrmDatabase pageAttrs; +} ContextAttrs, *ContextAttrPtr; + +/* + * XPDIR is relative to (i.e. is a subdir of) XPRINTDIR/$LANG. + */ +static const char XPDIR[] = "/print"; +/* + * The following files/directories define or are within subdirectories of the + * above-defined XPDIR. + */ +static const char XPPRINTERATTRFILE[] = "/attributes/printer"; +static const char XPJOBATTRFILE[] = "/attributes/job"; +static const char XPDOCATTRFILE[] = "/attributes/document"; +static const char XPMODELDIR[] = "/models"; + +static char NULL_STRING[] = "\0"; + +/* + * XpGetConfigDirBase returns a string containing the path name of the base + * where the print server configuration directory is localed. + */ +static +char *XpGetConfigDirBase(void) +{ + char *configDir; + + /* + * If the XPCONFIGDIR environment variable is not set, then use the + * compile-time constant XPRINTDIR. XPRINTDIR is passed in on the + * compile command line, and is defined in $(TOP)/config/cf/Project.tmpl. + */ + if((configDir = getenv("XPCONFIGDIR")) == (char *)NULL) + configDir = XPRINTDIR; + + return configDir; +} + +/* + * XpGetConfigDir returns a string containing the path name of the print + * server configuration directory. If the useLocale parameter is False + * the it returns the path to the "/C" directory. If the useLocale + * parameter is True it returns the path to the directory associated with + * $LANG. It makes no attempt to ensure that the directory actually exists. + */ +char * +XpGetConfigDir(Bool useLocale) +{ + char *dirName, *langName, *langDir, *configDir; + Bool freeLangDir = False; + + if(useLocale == False) langDir = "/C"; + else + { + langName = getenv("LC_ALL"); + if (langName == NULL) { + langName = getenv("LANG"); + } + + if(langName == (char *)NULL) + return (char *)NULL; + else + { + if(strcmp(langName, "C") == 0) + return (char *)NULL; + langDir = (char *)xalloc(strlen(langName) + 2); + sprintf(langDir, "/%s", langName); + freeLangDir = True; + } + } + + configDir = XpGetConfigDirBase(); + + dirName = (char *)xalloc(strlen(configDir) + strlen(XPDIR) + + strlen(langDir) + 1); + sprintf(dirName, "%s%s%s", configDir, langDir, XPDIR); + + if(freeLangDir == True) + xfree(langDir); + + return dirName; +} + +/* + * GetMergedDatabase reads and merges xrmdb files from the top-level printer + * config directory, and from the directory associated with the current + * locale (if other than the top-level). + */ +static XrmDatabase +GetMergedDatabase(const char *attrName) +{ + char *dirName, *fileName; + XrmDatabase db; + + if((dirName = XpGetConfigDir(False)) == (char *)NULL) + return (XrmDatabase)NULL; + if((fileName = (char *)xalloc(strlen(dirName) + strlen(attrName) + 1)) == + (char *)NULL) + return (XrmDatabase)NULL; + sprintf(fileName, "%s%s", dirName, attrName); + db = XrmGetFileDatabase(fileName); + xfree(fileName); + xfree(dirName); + + if((dirName = XpGetConfigDir(True)) == (char *)NULL) + return db; + if((fileName = (char *)xalloc(strlen(dirName) + strlen(attrName) + 1)) == + (char *)NULL) + return db; + sprintf(fileName, "%s%s", dirName, attrName); + (void)XrmCombineFileDatabase(fileName, &db, True); + xfree(fileName); + xfree(dirName); + + return db; +} + +/* + * BuildSystemAttributes reads the on-disk configuration files for printers, + * initial job, and initial document attributes. The resulting xrm + * databases are then dissected as needed for each printer. + * It also allocates a contextPrivate space for the attributes, + * reserving space to store pointers to the attribute stores for + * the context. + */ +static void +BuildSystemAttributes(void) +{ + if(systemAttributes.printers != (XrmDatabase)NULL) + XrmDestroyDatabase(systemAttributes.printers); + systemAttributes.printers = GetMergedDatabase(XPPRINTERATTRFILE); + if(systemAttributes.job != (XrmDatabase)NULL) + XrmDestroyDatabase(systemAttributes.job); + systemAttributes.job = GetMergedDatabase(XPJOBATTRFILE); + if(systemAttributes.doc != (XrmDatabase)NULL) + XrmDestroyDatabase(systemAttributes.doc); + systemAttributes.doc = GetMergedDatabase(XPDOCATTRFILE); + if(systemAttributes.server != (XrmDatabase)NULL) + XrmDestroyDatabase(systemAttributes.server); + systemAttributes.server = XpSpoolerGetServerAttributes(); + return; +} + +/* + * AddDbEntry is called by XrmEnumerateDatabase, and adds the supplied + * database entry to the database pointed to within the "DbEnumStruct" + * passed as the client_data (aka "closure"). + */ +static Bool +AddDbEntry( + XrmDatabase *sourceDB, + XrmBindingList bindings, + XrmQuarkList quarks, + XrmRepresentation *type, + XrmValue *value, + XPointer client_data) +{ + DbEnumStruct *pEnumStruct = (DbEnumStruct *)client_data; + XrmName xrm_name[5]; + XrmClass xrm_class[5]; + XrmBinding xrm_bind[3]; + XrmValue realVal; + XrmRepresentation rep_type; + + xrm_name[0] = XrmStringToQuark (pEnumStruct->qualifier); + xrm_class[0] = XrmStringToQuark (pEnumStruct->modelId); + + for(;*quarks; quarks++) + xrm_name[1] = xrm_class[1] = *quarks; + + xrm_name[2] = (XrmQuark)NULL; + xrm_class[2] = (XrmQuark)NULL; + + if(XrmQGetResource (*sourceDB, xrm_name, xrm_class, &rep_type, &realVal)) + { + xrm_bind[0] = XrmBindLoosely; + + xrm_name[0] = xrm_name[1]; + xrm_name[1] = NULLQUARK; + + XrmQPutStringResource(pEnumStruct->pDb, xrm_bind, xrm_name, + (char *)realVal.addr); + } + + return FALSE; +} + +/* + * BuildPrinterAttrs - builds and returns an XrmDatabase for the printer + * of the specified name/qualifier, if we have enough information. + * If we don't have a model-config + * file, then just enumerate the systemAttributes->printers database, + * otherwise read in the model-config database and merge into it the + * systemAttributes->printers database. This database is then enumerated + * with the printer qualifier (and the model name as class if we have it), and + * the resulting elements are stored into the database for this particular + * printer. + */ +static XrmDatabase +BuildPrinterAttrs( + char *printerName, + char *qualifierName) +{ + XrmDatabase printerDB = (XrmDatabase)NULL; + + if(systemAttributes.printers != (XrmDatabase)NULL) + { + char *fileName; + XrmDatabase modelDB = (XrmDatabase)NULL; + XrmName xrm_name[5], xrm_class[2]; + XrmRepresentation rep_type; + XrmValue value; + DbEnumStruct enumStruct; + Bool freeModelDB = False; + /* + * Build the initial db based on the model-config files + */ + xrm_name[0] = XrmStringToQuark (qualifierName); + xrm_name[1] = XrmStringToQuark ("xp-model-identifier"); + xrm_name[2] = (XrmQuark)NULL; + XrmQGetResource (systemAttributes.printers, xrm_name, xrm_name, + &rep_type, &value); + + if(value.addr != (XPointer)NULL) + { + fileName = (char *)xalloc(strlen(XPMODELDIR) + + strlen((char *)value.addr) + + strlen("model-config") + 3); + sprintf(fileName, "%s/%s/%s", XPMODELDIR, value.addr, + "model-config"); + modelDB = GetMergedDatabase(fileName); + xfree(fileName); + if(modelDB != (XrmDatabase)NULL) + { + XrmDatabase tempDB = (XrmDatabase)NULL; + /* + * have to make a temp copy because MergeDatabase destroys + * the "source" database. Merge in the printers DB + */ + tempDB = CopyDb(systemAttributes.printers); + XrmMergeDatabases(tempDB, &modelDB); + freeModelDB = True; + } + } + + /* + * Check to see if we knew the name AND found a database file + */ + if(modelDB == (XrmDatabase)NULL) + modelDB = systemAttributes.printers; + + xrm_name[0] = XrmStringToQuark (qualifierName); + xrm_name[1] = (XrmQuark)NULL; + xrm_class[0] = XrmStringToQuark((char *)value.addr); + xrm_class[1] = (XrmQuark)NULL; + enumStruct.pDb = &printerDB; + enumStruct.qualifier = (char *)qualifierName; + enumStruct.modelId = (char *)value.addr; + XrmEnumerateDatabase(modelDB, xrm_name, xrm_class, XrmEnumAllLevels, + AddDbEntry, (XPointer) &enumStruct); + + if(freeModelDB == True) XrmDestroyDatabase(modelDB); + } + XrmPutStringResource(&printerDB, "*printer-name", printerName); + XrmPutStringResource(&printerDB, "*qualifier", qualifierName); + return printerDB; +} + +/* + * BuildABase - builds an XrmDatabase by enumerating the supplied sourceBase + * database for elements relevant for the printer named by printerName, + * and deriving a class for printerName from the model declared in the + * systemAttributes.printers database. If no model is defined for this + * printer then the printerName is used as the class as well. + * + * This is used to build the initial value document and initial value + * job attribute databases for each printer by searching the system + * level doc and job databases. + */ +static XrmDatabase +BuildABase( + char *printerName, + char *qualifierName, + XrmDatabase sourceBase) +{ + XrmDatabase builtDB = (XrmDatabase)NULL; + + if(sourceBase != (XrmDatabase)NULL) + { + XrmName xrm_name[5], xrm_class[2]; + XrmRepresentation rep_type; + XrmValue value; + DbEnumStruct enumStruct; + + /* + * Retrieve the model name for use as the class. + */ + xrm_name[0] = XrmStringToQuark (printerName); + xrm_name[1] = XrmStringToQuark ("xp-model-identifier"); + xrm_name[2] = (XrmQuark)NULL; + XrmQGetResource (systemAttributes.printers, xrm_name, xrm_name, + &rep_type, &value); + /* + * if we have a model name then use it as the class, otherwise + * just use the printer name as the class as well as the name. + */ + if(value.addr != (XPointer)NULL) + xrm_class[0] = XrmStringToQuark((char *)value.addr); + else + xrm_class[0] = xrm_name[0]; + xrm_class[1] = (XrmQuark)NULL; + + xrm_name[1] = (XrmQuark)NULL; + + enumStruct.pDb = &builtDB; + enumStruct.qualifier = (char *)qualifierName; + enumStruct.modelId = (char *)value.addr; + XrmEnumerateDatabase(sourceBase, xrm_name, xrm_class, XrmEnumAllLevels, + AddDbEntry, (XPointer) &enumStruct); + } + + XrmPutStringResource(&builtDB, "*qualifier", qualifierName); + + return builtDB; +} + +/* + * FreeAttrList is called upon server recycle, and frees the printer + * databases stored in the global attrList. + */ +static void +FreeAttrList(void) +{ + PrAttrPtr pAttr, pNext; + + for(pAttr = attrList, pNext = attrList; + pAttr != (PrAttrPtr)NULL; + pAttr = pNext) + { + pNext = pAttr->next; + if(pAttr->printerAttrs != (XrmDatabase)NULL) + XrmDestroyDatabase(pAttr->printerAttrs); + if(pAttr->docAttrs != (XrmDatabase)NULL) + XrmDestroyDatabase(pAttr->docAttrs); + if(pAttr->jobAttrs != (XrmDatabase)NULL) + XrmDestroyDatabase(pAttr->jobAttrs); + xfree(pAttr->name); + xfree(pAttr->qualifier); + xfree(pAttr); + } + attrList = (PrAttrPtr)NULL; +} + +/* + * XpRehashAttributes - frees the per-printer attribute list and + * calls BuildSystemAttributes to rebuild the overall attribute + * store. It is expected that a caller of this will follow it + * by calling XpBuildAttributeStore for a new list of printers. + */ +int +XpRehashAttributes(void) +{ + if(attrList != (PrAttrPtr)NULL) + FreeAttrList(); + BuildSystemAttributes(); + return Success; +} + +/* + * XpBuildAttributeStore - creates the attribute database associated + * with the specified printer. The first time this is called it + * calls BuildSystemAttributes to create the system-level databases. + */ +void +XpBuildAttributeStore( + char *printerName, + char *qualifierName) +{ + PrAttrPtr pAttr; + + if((pAttr = (PrAttrPtr)xalloc(sizeof(PrAttrs))) == (PrAttrPtr)NULL) + return; + + if(attrGeneration != serverGeneration) + { + if(attrList != (PrAttrPtr)NULL) + FreeAttrList(); + dixRequestPrivate(attrCtxtPrivKey, sizeof(ContextAttrs)); + BuildSystemAttributes(); + + attrGeneration = serverGeneration; + } + + if(attrList == (PrAttrPtr)NULL) + { + pAttr->next = (PrAttrPtr)NULL; + attrList = pAttr; + } + else + { + pAttr->next = attrList; + attrList = pAttr; + } + + pAttr->name = strdup(printerName); + pAttr->qualifier = strdup(qualifierName); + pAttr->printerAttrs = BuildPrinterAttrs(printerName, qualifierName); + pAttr->docAttrs = BuildABase(printerName, qualifierName, + systemAttributes.doc); + pAttr->jobAttrs = BuildABase(printerName, qualifierName, + systemAttributes.job); +} + + +static Bool +StoreEntry( + XrmDatabase *sourceDB, + XrmBindingList bindings, + XrmQuarkList quarks, + XrmRepresentation *type, + XrmValue *value, + XPointer client_data) +{ + XrmDatabase *outDb = (XrmDatabase *)client_data; + + XrmQPutStringResource(outDb, bindings, quarks, (char *)value->addr); + + return FALSE; +} + +/* + * XpCopyDb - makes a copy of the specified XrmDatabase and returns + * the copy. + */ +static XrmDatabase +CopyDb(XrmDatabase inDb) +{ + XrmDatabase outDb = (XrmDatabase)NULL; + XrmQuark empty = NULLQUARK; + + (void)XrmEnumerateDatabase(inDb, &empty, &empty, XrmEnumAllLevels, + StoreEntry, (XPointer) &outDb); + return outDb; +} + +/* + * XpInitAttributes - initializes the attribute store for the specified + * context. It does this by making copies of the printer, doc, and job + * attributes databases for the printer associated with the context. + */ +void +XpInitAttributes(XpContextPtr pContext) +{ + ContextAttrPtr pCtxtAttrs; + PrAttrPtr pPrAttr = attrList; + + /* Initialize all the pointers to NULL */ + pCtxtAttrs = (ContextAttrPtr)dixLookupPrivate(&pContext->devPrivates, + attrCtxtPrivKey); + (void)memset((void *)pCtxtAttrs, 0, (size_t) sizeof(ContextAttrs)); + + for(pPrAttr = attrList; pPrAttr != (PrAttrPtr)NULL; pPrAttr = pPrAttr->next) + if(!strcmp(pPrAttr->name, pContext->printerName)) break; + + if(pPrAttr != (PrAttrPtr)NULL) + { + pCtxtAttrs->printerAttrs = CopyDb(pPrAttr->printerAttrs); + pCtxtAttrs->docAttrs = CopyDb(pPrAttr->docAttrs); + pCtxtAttrs->jobAttrs = CopyDb(pPrAttr->jobAttrs); + } +} + +void +XpDestroyAttributes( + XpContextPtr pContext) +{ + ContextAttrPtr pCtxtAttrs; + + pCtxtAttrs = (ContextAttrPtr)dixLookupPrivate(&pContext->devPrivates, + attrCtxtPrivKey); + if(pCtxtAttrs->printerAttrs != (XrmDatabase)NULL) + XrmDestroyDatabase(pCtxtAttrs->printerAttrs); + if(pCtxtAttrs->docAttrs != (XrmDatabase)NULL) + XrmDestroyDatabase(pCtxtAttrs->docAttrs); + if(pCtxtAttrs->jobAttrs != (XrmDatabase)NULL) + XrmDestroyDatabase(pCtxtAttrs->jobAttrs); + if(pCtxtAttrs->pageAttrs != (XrmDatabase)NULL) + XrmDestroyDatabase(pCtxtAttrs->pageAttrs); +} + +/* + * XpGetOneAttribute returns the string value of the specified attribute + * in the specified class for the specified print context. If the attribute + * doesn't exist in the database for this context, or if the class database + * doesn't exist for this context, then NULL is returned. The caller must + * not free the returned string, as the returned pointer points into the + * database. This function can also return a value from the server attributes, + * in which case the pContext parameter is ignored. + */ +char * +XpGetOneAttribute( + XpContextPtr pContext, + XPAttributes class, + char *attributeName) +{ + ContextAttrPtr pCtxtAttrs; + XrmDatabase db = (XrmDatabase)NULL; + XrmName xrm_name[3]; + XrmRepresentation rep_type; + XrmValue value; + + if(class == XPServerAttr) + { + if(systemAttributes.server == (XrmDatabase)NULL) + return NULL_STRING; + + xrm_name[0] = XrmStringToQuark (attributeName); + xrm_name[1] = (XrmQuark)NULL; + XrmQGetResource(systemAttributes.server, xrm_name, xrm_name, + &rep_type, &value); + + if(value.addr == (char *)NULL) + return NULL_STRING; + return (char *)value.addr; + } + else + { + pCtxtAttrs=(ContextAttrPtr)dixLookupPrivate(&pContext->devPrivates, + attrCtxtPrivKey); + switch(class) + { + case XPPrinterAttr: + db = pCtxtAttrs->printerAttrs; + break; + case XPDocAttr: + db = pCtxtAttrs->docAttrs; + break; + case XPJobAttr: + db = pCtxtAttrs->jobAttrs; + break; + case XPPageAttr: + db = pCtxtAttrs->pageAttrs; + break; + default: + break; + } + } + if(db == (XrmDatabase)NULL) + return NULL_STRING; + + xrm_name[0] = XrmStringToQuark ("qualifier"); + xrm_name[1] = (XrmQuark)NULL; + XrmQGetResource(db, xrm_name, xrm_name, &rep_type, &value); + + xrm_name[0] = XrmStringToQuark (value.addr); + xrm_name[1] = XrmStringToQuark (attributeName); + xrm_name[2] = (XrmQuark)NULL; + if(XrmQGetResource(db, xrm_name, xrm_name, &rep_type, &value)) + return (char *)value.addr; + else + return NULL_STRING; +} + +/* + * XpPutOneAttribute updates one attribute for the specified + * context and class. This function is intended for use by the attribute + * validation module which updates the XrmDatabases directly. This + * function does not recognize XPServerAttr. + */ +void +XpPutOneAttribute( + XpContextPtr pContext, + XPAttributes class, + const char* attributeName, + const char* value) +{ + ContextAttrPtr pCtxtAttrs; + XrmDatabase db; + XrmBinding bindings[1]; + XrmQuark quarks[2]; + + pCtxtAttrs = (ContextAttrPtr)dixLookupPrivate(&pContext->devPrivates, + attrCtxtPrivKey); + switch(class) + { + case XPPrinterAttr: + db = pCtxtAttrs->printerAttrs; + break; + case XPDocAttr: + db = pCtxtAttrs->docAttrs; + break; + case XPJobAttr: + db = pCtxtAttrs->jobAttrs; + break; + case XPPageAttr: + db = pCtxtAttrs->pageAttrs; + break; + default: + return; + } + bindings[0] = XrmBindLoosely; + quarks[0] = XrmStringToQuark(attributeName); + quarks[1] = (XrmQuark)NULL; + XrmQPutStringResource(&db, bindings, quarks, value ? value : ""); +} + + + +/******************************************************************************* + * + * The following routines: ExpandSpace, PutString, PutByte, and AppendEntry + * form the functional core of the GetAttributes routine. Xrm does not + * supply a routine to form a string database from an XrmDatabase, except + * by writing the database to a file. This code avoids the file system + * overhead, but is a bit clunky in its memory management. + * + ******************************************************************************/ + +/* + * ExpandSpace expands the memory allocated for the string database in + * the StringDbStruct passed in, and updates the "space" field of the + * struct to indicate the new amount of space available. + */ +static Bool +ExpandSpace( + StringDbStruct *pStr) +{ + char *newSpace; + + if((newSpace = (char *)xrealloc(pStr->stringDb, pStr->nextPos + pStr->space + + 1024)) == (char *)NULL) + return False; + pStr->space += 1024; + pStr->stringDb = newSpace; + return True; +} + +/* + * PutString puts the contents of a null-terminated string into the string + * database in the StringDbStruct passed in. If there is insufficient room + * for the string, ExpandSpace is called, and the nextPos and space fields + * are updated. + */ +static void +PutString( + StringDbStruct *pStr, + char *pString) +{ + int len = strlen(pString); + + if(len >= pStr->space) + if(!ExpandSpace(pStr)) + return; + strcpy(&pStr->stringDb[pStr->nextPos], pString); + pStr->nextPos += len; + pStr->space -= len; +} + +/* + * PutByte puts a single byte value in to the string database in the passed-in + * StringDbStruct. ExpandSpace is called if there is insufficient room for + * the byte, and the nextPos and space fields are updated. + */ +static void +PutByte( + StringDbStruct *pStr, + char byte) +{ + if(pStr->space <= 1) + if(!ExpandSpace(pStr)) + return; + pStr->stringDb[pStr->nextPos] = byte; + pStr->nextPos++; + pStr->space--; +} + +#define XrmQString XrmPermStringToQuark("String") + +/* + * AppendEntry is called by XrmEnumerateDatabase, and serves to append + * a database entry onto a string database. The passed-in "closure" + * struct contains a pointer to the string, and a count of the remaining + * bytes. If there are insufficient remaining bytes then the struct + * is realloced, and the count of the space remaining is updated. + * Database elements of types other than String are ignored! + * This code is based directly on that in "DumpEntry" in Xrm.c. + */ +static Bool +AppendEntry( + XrmDatabase *db, + XrmBindingList bindings, + XrmQuarkList quarks, + XrmRepresentation *type, + XrmValuePtr value, + XPointer data) +{ + StringDbStruct *pEnumStr = (StringDbStruct *)data; + Bool firstNameSeen; + unsigned int i; + char *s, c; + + if (*type != XrmQString) + return False; + + for (firstNameSeen = False; *quarks; bindings++, quarks++) { + if (*bindings == XrmBindLoosely) { + PutString(pEnumStr, "*"); + } else if (firstNameSeen) { + PutString(pEnumStr, "."); + } + firstNameSeen = True; + PutString(pEnumStr, XrmQuarkToString(*quarks)); + } + s = value->addr; + i = value->size; + PutString(pEnumStr, ":\t"); + if(i) i--; + + if (i && (*s == ' ' || *s == '\t')) + PutByte(pEnumStr, '\\'); /* preserve leading whitespace */ + + while (i--) { + c = *s++; + if (c == '\n') { + if (i) + PutString(pEnumStr, "\\n\\\n"); + else + PutString(pEnumStr, "\\n"); + } else if (c == '\\') + PutString(pEnumStr, "\\\\"); + else if ((c < ' ' && c != '\t') || + ((unsigned char)c >= 0x7f && (unsigned char)c < 0xa0)) + { + char temp[4]; + (void) sprintf(temp, "\\%03o", (unsigned char)c); + PutString(pEnumStr, temp); + } + else + PutByte(pEnumStr, c); + } + PutByte(pEnumStr, '\n'); + pEnumStr->stringDb[pEnumStr->nextPos] = (char)'\0'; + return False; +} + +/* + * XpGetAttributes returns a string database version of the Xrm database + * for the specified context and class. This function can also return the + * contents of the server attributes, in which case the pContext parameter + * is ignored. + * + * The caller is responsible for freeing the returned string, + * unlike XpGetOneAttribute, where the caller must not free the string. + */ +char * +XpGetAttributes( + XpContextPtr pContext, + XPAttributes class) +{ + ContextAttrPtr pCtxtAttrs; + XrmDatabase db = (XrmDatabase)NULL; + StringDbStruct enumStruct; + XrmQuark empty = NULLQUARK; + + if(class == XPServerAttr) + db = systemAttributes.server; + else + { + pCtxtAttrs=(ContextAttrPtr)dixLookupPrivate(&pContext->devPrivates, + attrCtxtPrivKey); + switch(class) + { + case XPServerAttr: + db = systemAttributes.server; + break; + case XPPrinterAttr: + db = pCtxtAttrs->printerAttrs; + break; + case XPDocAttr: + db = pCtxtAttrs->docAttrs; + break; + case XPJobAttr: + db = pCtxtAttrs->jobAttrs; + break; + case XPPageAttr: + db = pCtxtAttrs->pageAttrs; + break; + default: + break; + } + } + if(db == (XrmDatabase)NULL) + { + char *retval = (char *)xalloc(1); + retval[0] = (char)'\0'; + return retval; + } + + if((enumStruct.stringDb = (char *)xalloc(1024)) == (char *)NULL) + return (char *)NULL; + enumStruct.stringDb[0] = (char)'\0'; + enumStruct.nextPos = 0; + enumStruct.space = 1024; + (void)XrmEnumerateDatabase(db, &empty, &empty, XrmEnumAllLevels, + AppendEntry, (XPointer) &enumStruct); + + return enumStruct.stringDb; +} + +int +XpAugmentAttributes( + XpContextPtr pContext, + XPAttributes class, + char *attributes) +{ + XrmDatabase db; + ContextAttrPtr pCtxtAttrs; + + db = XrmGetStringDatabase(attributes); + if(db == (XrmDatabase)NULL) return BadAlloc; + + pCtxtAttrs = (ContextAttrPtr)dixLookupPrivate(&pContext->devPrivates, + attrCtxtPrivKey); + switch(class) + { + case XPPrinterAttr: + XrmMergeDatabases(db, &pCtxtAttrs->printerAttrs); + break; + case XPDocAttr: + XrmMergeDatabases(db, &pCtxtAttrs->docAttrs); + break; + case XPJobAttr: + XrmMergeDatabases(db, &pCtxtAttrs->jobAttrs); + break; + case XPPageAttr: + XrmMergeDatabases(db, &pCtxtAttrs->pageAttrs); + break; + default: + break; + } + return Success; +} + +/* + * XpSetAttributes - sets the attribute stores for a specified context. + */ +int +XpSetAttributes( + XpContextPtr pContext, + XPAttributes class, + char *attributes) +{ + XrmDatabase db; + ContextAttrPtr pCtxtAttrs; + + db = XrmGetStringDatabase(attributes); + if(db == (XrmDatabase)NULL) return BadAlloc; + + pCtxtAttrs=(ContextAttrPtr)dixLookupPrivate(&pContext->devPrivates, + attrCtxtPrivKey); + switch(class) + { + case XPPrinterAttr: + if(pCtxtAttrs->printerAttrs != (XrmDatabase)NULL) + XrmDestroyDatabase(pCtxtAttrs->printerAttrs); + pCtxtAttrs->printerAttrs = db; + break; + case XPDocAttr: + if(pCtxtAttrs->docAttrs != (XrmDatabase)NULL) + XrmDestroyDatabase(pCtxtAttrs->docAttrs); + pCtxtAttrs->docAttrs = db; + break; + case XPJobAttr: + if(pCtxtAttrs->jobAttrs != (XrmDatabase)NULL) + XrmDestroyDatabase(pCtxtAttrs->jobAttrs); + pCtxtAttrs->jobAttrs = db; + break; + case XPPageAttr: + if(pCtxtAttrs->pageAttrs != (XrmDatabase)NULL) + XrmDestroyDatabase(pCtxtAttrs->pageAttrs); + pCtxtAttrs->pageAttrs = db; + break; + default: + break; + } + return Success; +} + +void +XpAddPrinterAttribute( + char *printerName, + char *printerQualifier, + char *attributeName, + char *attributeValue) +{ + PrAttrPtr pAttr; + + for(pAttr = attrList; pAttr != (PrAttrPtr)NULL; pAttr = pAttr->next) + { + if(!strcmp(printerQualifier, pAttr->qualifier)) + { + XrmPutStringResource(&pAttr->printerAttrs, attributeName, + attributeValue); + break; + } + } +} + +const char * +XpGetPrinterAttribute(const char *printerName, + const char *attribute) +{ + PrAttrPtr pAttr; + XrmValue value; + char *type; + + for(pAttr = attrList; pAttr != (PrAttrPtr)NULL; pAttr = pAttr->next) + { + if(!strcmp(printerName, pAttr->qualifier)) + { + char *attrStr; + + attrStr = (char *)xalloc(strlen(printerName) + strlen(attribute) + + 2); + sprintf(attrStr, "%s.%s", printerName, attribute); + XrmGetResource(pAttr->printerAttrs, attrStr, attrStr, + &type, &value); + xfree(attrStr); + break; + } + } + if(value.addr != (XPointer)NULL && strlen(value.addr) != 0) + return value.addr; + else + return ""; +} + +/******************************************************************************* + * + * The following routines are not attribute routines, but are rather + * spooler interface functions. They should presumably move to + * a SpoolerIf.c of some similarly named file. + * + ******************************************************************************/ +#include <locale.h> + +static char serverAttrStr[] = "*document-attributes-supported: copy-count\n\ +*job-attributes-supported: job-name job-owner\ + notification-profile xp-spooler-command-options\n\ +*multiple-documents-supported: False"; + +XrmDatabase +XpSpoolerGetServerAttributes(void) +{ + char *totalAttrs, *localeName; + XrmDatabase db; + + localeName = setlocale(LC_CTYPE, (char *)NULL); + if(!localeName || strlen(localeName) == 0) + localeName = "C"; + + if((totalAttrs = (char *)xalloc(strlen(serverAttrStr) + strlen(localeName) + + 11)) == (char *)NULL) + return (XrmDatabase)NULL; + sprintf(totalAttrs, "%s\n%s\t%s", serverAttrStr, "*locale:", localeName); + + db = XrmGetStringDatabase(totalAttrs); + xfree(totalAttrs); + return db; +} + +/* + * Tailf() works similar to "/bin/tail -f fd_in >fd_out" until + * the process |child| terminates (the child status is + * returned in |child_status|). + * This function is used to copy the stdout/stderr output of a + * child to fd_out until the child terminates. + */ +static +void Tailf(int fd_in, int fd_out, pid_t child, int *child_status) +{ + char b[256]; + ssize_t sz; + Bool childDone = FALSE; + struct timeval timeout; + long fpos = 0; /* XXX: this is not correct for largefile support */ + + timeout.tv_sec = 0; + timeout.tv_usec = 100000; + + for(;;) + { + /* Check whether the child is still alive or not */ + if (waitpid(child, child_status, WNOHANG) == child) + childDone = TRUE; + + /* Copy traffic from |fd_in| to |fd_out| + * (Note we have to use |pread()| here to avoid race conditions + * between a child process writing to the same file using the + * same file pointer (|dup(2)| and |fork(2)| just duplicate the + * file handle but not the pointer)). + */ + while ((sz = pread(fd_in, b, sizeof(b), fpos)) > 0) + { + fpos += sz; + write(fd_out, b, sz); + } + + if (childDone) + break; + + (void)select(0, NULL, NULL, NULL, &timeout); + } +} + +/* + * SendFileToCommand takes three character pointers - the file name, + * the command to execute, + * and the "argv" style NULL-terminated vector of arguments for the command. + * The command is exec'd, and the file contents are sent to the command + * via stdin. + * + * WARNING: This function will try to adopt the userId of the supplied + * user name prior to exec'ing the supplied command. + */ +static void +SendFileToCommand( + XpContextPtr pContext, + char *fileName, + char *pCommand, + char **argVector, + char *userName) +{ + pid_t childPid; + int pipefd[2]; + int status; + struct stat statBuf; + FILE *fp, *outPipe; + FILE *resFp; /* output from launched command */ + int resfd; + + resFp = tmpfile(); + if (resFp == NULL) + { + ErrorF("SendFileToCommand: Cannot open temporary file for command output\n"); + return; + } + resfd = fileno(resFp); + + if(pipe(pipefd)) + { + ErrorF("SendFileToCommand: Cannot open pipe\n"); + fclose(resFp); + return; + } + + if(stat(fileName, &statBuf) < 0 || (int)statBuf.st_size == 0) + { + close(pipefd[0]); + close(pipefd[1]); + fclose(resFp); + return; + } + + fp = fopen(fileName, "r"); + if(fp == (FILE *)NULL) + { + ErrorF("SendFileToCommand: Cannot open scratch spool file '%s'\n", fileName); + close(pipefd[0]); + close(pipefd[1]); + fclose(resFp); + return; + } + + if((childPid = fork()) == 0) + { + close(pipefd[1]); + + /* Replace current stdin with input from the pipe */ + close(STDIN_FILENO); + dup(pipefd[0]); + close(pipefd[0]); + + /* Close current stdout and redirect it to resfd */ + close(STDOUT_FILENO); + dup(resfd); + + /* Close current stderr and redirect it to resfd + * (valgrind may not like that, in this case simply start it using + * % valgrind 50>/dev/tty --logfile-fd=50 <more-options> ./Xprt ... #) + */ + close(STDERR_FILENO); + dup(resfd); + + fclose(resFp); + + /* + * If a user name is specified, try to set our uid to match that + * user name. This is to allow e.g. a banner page to show the + * name of the printing user rather than the user who started + * the print server. + */ + if(userName) + { + uid_t myUid; + + if((myUid = geteuid()) == (uid_t)0) + { + struct passwd *pPasswd; + + if((pPasswd = getpwnam(userName))) + { + if (setgid((gid_t)pPasswd->pw_gid) != 0) + perror("SendFileToCommand: setgid() failure."); + + if (initgroups(userName, (gid_t)pPasswd->pw_gid) != 0) + perror("SendFileToCommand: initgroups() failure."); + + if (setuid((uid_t)pPasswd->pw_uid) != 0) + perror("SendFileToCommand: setuid() failure."); + } + } + } + /* return BadAlloc? */ + if (execv(pCommand, argVector) == -1) { + FatalError("unable to exec '%s'", pCommand); + } + } + else + { + (void) close(pipefd[0]); + + outPipe = fdopen(pipefd[1], "w"); + (void) TransferBytes(fp, outPipe, (int)statBuf.st_size); + + (void) fclose(outPipe); + (void) fclose(fp); + + /* Wait for spooler child (and send all it's output to stderr) */ + Tailf(resfd, STDERR_FILENO, childPid, &status); + + if (status != EXIT_SUCCESS) + { + ErrorF("SendFileToCommand: spooler command returned non-zero status %d.\n", status); + } + + /* Store "xp-spooler-command-results" XPJobAttr that the + * client can fetch it on demand */ + if ((fstat(resfd, &statBuf) >= 0) && (statBuf.st_size >= 0)) + { + long bufSize; + char *buf; + + bufSize = statBuf.st_size; + + /* Clamp buffer size to 4MB to prevent that we allocate giant + * buffers if the spooler goes mad and spams it's stdout/stderr + * channel. */ + bufSize = MIN(bufSize, 4*1024*1024); + + buf = xalloc(bufSize+1); + if (buf != NULL) + { + bufSize = pread(resfd, buf, bufSize, 0); + buf[bufSize]='\0'; + + /* XXX: This should be converted from local multibyte encoding to + * Compound Text encoding first */ + XpPutOneAttribute(pContext, XPJobAttr, "xp-spooler-command-results", buf); + + xfree(buf); + } + } + else + { + ErrorF("SendFileToCommand: fstat() failed.\n"); + } + + fclose(resFp); + } + return; +} + +/* + * ReplaceAllKeywords causes all the predefined keywords (e.g. %options%) + * to be replaced with the appropriate values derived from the attribute + * store for the supplied print context. The ReplaceAnyString utility + * routine is used to perform the actual replacements. + */ + +static char * +ReplaceAllKeywords( + XpContextPtr pContext, + char *command) +{ + char *cmdOpt; + + cmdOpt = XpGetOneAttribute(pContext, XPPrinterAttr, + "xp-spooler-printer-name"); + if(cmdOpt != (char *)NULL && strlen(cmdOpt) != 0) + command = ReplaceAnyString(command, "%printer-name%", cmdOpt); + else + command = ReplaceAnyString(command, "%printer-name%", + pContext->printerName); + + cmdOpt = XpGetOneAttribute(pContext, XPDocAttr, "copy-count"); + if(cmdOpt != (char *)NULL && strlen(cmdOpt) != 0) + command = ReplaceAnyString(command, "%copy-count%", cmdOpt); + else + command = ReplaceAnyString(command, "%copy-count%", "1"); + + cmdOpt = XpGetOneAttribute(pContext, XPJobAttr, "job-name"); + if(cmdOpt != (char *)NULL && strlen(cmdOpt) != 0) + command = ReplaceAnyString(command, "%job-name%", cmdOpt); + else + command = ReplaceAnyString(command, "%job-name%", ""); + + cmdOpt = XpGetOneAttribute(pContext, XPJobAttr, "job-owner"); + if(cmdOpt != (char *)NULL && strlen(cmdOpt) != 0) + command = ReplaceAnyString(command, "%job-owner%", cmdOpt); + else + command = ReplaceAnyString(command, "%job-owner%", ""); + + cmdOpt = XpGetOneAttribute(pContext, XPJobAttr, + "xp-spooler-command-options"); + if(cmdOpt != (char *)NULL && strlen(cmdOpt) != 0) + command = ReplaceAnyString(command, "%options%", cmdOpt); + else + command = ReplaceAnyString(command, "%options%", ""); + + /* New in xprint.mozdev.org release 007 - replace "%xpconfigdir%" with + * location of $XPCONFIGDIR */ + command = ReplaceAnyString(command, "%xpconfigdir%", XpGetConfigDirBase()); + + return command; +} + +#ifdef __QNX__ +#define toascii( c ) ((unsigned)(c) & 0x007f) +#endif + +#if defined(CSRG_BASED) || \ + defined(linux) || \ + defined(__CYGWIN__) || \ + (defined(sun) && !defined(SVR4)) || \ + (defined(SVR4) && !defined(sun) && !defined(__UNIXWARE__)) || \ + defined(ISC) || \ + defined(Lynx) || \ + defined(__QNX__) || \ + defined(__APPLE__) +#define iswspace(c) (isascii(c) && isspace(toascii(c))) +#endif + +/* + * GetToken - takes in a string and returns a malloc'd copy of the + * first non-white-space sequence of characters in the string. + * It returns the number of _bytes_ (NOT characters) parsed through + * the inStr to get to the end of the returned token. + */ +static int +GetToken( + char *inStr, + char **outStr) +{ + size_t mbCurMax = MB_CUR_MAX; + wchar_t curChar; + int i, numBytes, byteLen = strlen(inStr); + char *tok; + + /* + * read through any leading white space. + */ + for(i = 0, numBytes = 0; i < byteLen; i += numBytes) + { + numBytes = mbtowc(&curChar, &inStr[i], mbCurMax); + if(!iswspace(curChar)) + break; + } + tok = inStr + i; + + /* + * find the end of the token. + */ + byteLen = strlen(tok); + for(i = 0, numBytes = 0; i < byteLen; i += numBytes) + { + numBytes = mbtowc(&curChar, &tok[i], mbCurMax); + if(iswspace(curChar)) + break; + } + + if((*outStr = (char *)xalloc(i + 1)) == (char *)NULL) + return 0; + strncpy(*outStr, tok, i); + (*outStr)[i] = (char)'\0'; + return (tok + i) - inStr; +} + +static void +FreeVector( + char **vector) +{ + int i; + + if(vector == (char **)NULL) return; + + for(i = 0; vector[i] != (char *)NULL; i++) + xfree(vector[i]); + xfree(vector); +} + + +/* + * AddVector appends the pAddition arg vector to the pTarget arg vector. + * If the pTarget cannot be realloc'd, then pTarget is set to NULL. + */ +static void +AddVector( + char ***pTarget, + char **pAddition) +{ + int numTarget, numAdd, i; + + for(numTarget = 0; (*pTarget)[numTarget] != (char *)NULL; numTarget++) + ; + for(numAdd = 0; pAddition[numAdd] != (char *)NULL; numAdd++) + ; + + *pTarget = (char **)xrealloc((void *)*pTarget, (numTarget + numAdd + 1) * + sizeof(char *)); + if(*pTarget == (char **)NULL) + return; + for(i = 0; i < numAdd; i++) + (*pTarget)[numTarget + i] = pAddition[i]; + + (*pTarget)[numTarget + numAdd] = (char *)NULL; +} + +static char ** +BuildArgVector( + char *argString, + XpContextPtr pContext) +{ + char **pVector; + char *curTok; + int numChars, i; + static int beenHere = 0; /* prevent recursion on embedded %options% + */ + + pVector = (char **)xalloc(sizeof(char *)); + pVector[0] = (char *)NULL; + for(i = 0; (numChars = GetToken(argString, &curTok)) != 0; + i++, argString += numChars) + { + if(beenHere || strcmp(curTok, "%options%")) + { + if(curTok[0] == (char)'\0') + { + xfree(curTok); + } + else + { + pVector = (char **)xrealloc((void *)pVector, + (i + 2)*sizeof(char *)); + if(pVector == (char **)NULL) + return (char **)NULL; + pVector[i] = curTok; + pVector[i + 1] = (char *)NULL; + } + } + else if(!beenHere) + { + char **optionsVec; + + curTok = ReplaceAllKeywords(pContext, curTok); + beenHere = 1; + optionsVec = BuildArgVector(curTok, pContext); + xfree(curTok); + beenHere = 0; + AddVector(&pVector, optionsVec); + xfree(optionsVec); + } + } + if(numChars == 0 && curTok != (char *)NULL) + xfree(curTok); + return pVector; +} + +/* + * VectorizeCommand takes a string and breaks it into a command name and + * an array of character pointers suitable for handing to execv. The + * array is NULL-terminated. + * The returned char * is the command name, and should be freed when no + * longer needed. The array elements returned in the pVector parameter + * should be individually freed, and the array itself should also be + * freed when no longer needed. + */ +static char * +VectorizeCommand( + char *command, + char ***pVector, + XpContextPtr pContext) +{ + char *cmdName; + int numChars; + + if(command == (char *)NULL) + return (char *)NULL; + + numChars = GetToken(command, &cmdName); + + if(cmdName == (char *)NULL) + return (char *)NULL; + + /* Mangle the command name, too... */ + cmdName = ReplaceAllKeywords(pContext, cmdName); + + if(cmdName == (char *)NULL) + return (char *)NULL; + + *pVector = BuildArgVector(command, pContext); + + return cmdName; +} + +int +XpSubmitJob(fileName, pContext) + char *fileName; + XpContextPtr pContext; +{ + char **vector, *cmdNam, *command, *userName; + int i; + + command = XpGetOneAttribute(pContext, XPPrinterAttr, "xp-spooler-command"); + if(command == (char *)NULL || strlen(command) == 0) + { + if( spooler_type ) + { + command = strdup(spooler_type->spool_command); + } + else + { + ErrorF("XpSubmitJob: No default spool command defined.\n"); + } + } + else + { + command = strdup(command); + } + if(command == (char *)NULL) + { + ErrorF("XpSubmitJob: No spooler command found, cannot submit job.\n"); + return BadAlloc; + } + + cmdNam = VectorizeCommand(command, &vector, pContext); + xfree(command); + + if(cmdNam == (char *)NULL) + return BadAlloc; + + for(i = 0; vector[i] != (char *)NULL; i++) + { + vector[i] = ReplaceAllKeywords(pContext, vector[i]); + if(vector[i] == (char *)NULL) + { + xfree(cmdNam); + for(i = 0; vector[i] != (char *)NULL; i++) + xfree(vector[i]); + xfree(vector); + return BadAlloc; + } + } + + userName = XpGetOneAttribute(pContext, XPJobAttr, "job-owner"); + if(userName != (char *)NULL && strlen(userName) == 0) + userName = (char *)NULL; + + SendFileToCommand(pContext, fileName, cmdNam, vector, userName); + + FreeVector(vector); + xfree(cmdNam); + + return Success; +} + +/* + * SearchInputTrays() + * + * Given a tray, return the medium in the tray. Conversely, given a + * medium, return a tray in which it can be found. In either case, + * return NULL if the given tray or medium cannot be found. + */ +#define TRAY 0 +#define MEDIUM 1 + +static char * +SearchInputTrays(XpContextPtr pCon, + int which, + char *val) +{ + char *inputTraysMedium, tray[80], medium[80], *copy; + char *pS, *pE, *pLast; + + inputTraysMedium = XpGetOneAttribute( pCon, XPPrinterAttr, + "input-trays-medium" ); + + copy = strdup( inputTraysMedium ); + pS = copy; + pLast = copy + strlen( copy ); + + while( pS < pLast ) + { + while( *pS && *pS != '{' ) + pS++; + + pE = ++pS; + while( *pE && *pE != '}' ) + pE++; + *pE = '\0'; + + sscanf( pS, "%s %s", tray, medium ); + + if( which == MEDIUM && !strcmp( val, medium ) ) + { + xfree( copy ); + return strdup( tray ); + } + + if( which == TRAY && !strcmp( val, tray ) ) + { + xfree( copy ); + return strdup( medium ); + } + + pS = pE + 1; + } + + xfree( copy ); + return strdup( NULL_STRING ); +} + +/* + * XpGetTrayMediumFromContext() + * + * Given a print context, hit the input-trays-medium, + * default-input-tray and default-medium attributes to find the + * appropriate tray to use, and the medium in that tray. + */ +void +XpGetTrayMediumFromContext(XpContextPtr pCon, + char **medium, + char **tray) +{ + char *defMedium, *defTray; + char *t, *m; + + defMedium = XpGetOneAttribute( pCon, XPPageAttr, + "default-medium" ); + if( *defMedium == '\0' ) + defMedium = XpGetOneAttribute( pCon, XPDocAttr, + "default-medium" ); + + defTray = XpGetOneAttribute( pCon, XPPageAttr, + "default-input-tray" ); + if( *defTray == '\0' ) + defTray = XpGetOneAttribute( pCon, XPDocAttr, + "default-input-tray" ); + + /* + * First, check to see if the default tray has the default medium + * installed. This is the ideal case. + */ + m = SearchInputTrays( pCon, TRAY, defTray ); + if( !strcmp( m, defMedium ) ) + { + xfree( m ); + *tray = strdup( defTray ); + *medium = strdup( defMedium ); + return; + } + + /* + * If the default tray doesn't have the default medium, search for + * a tray which has the default medium. + */ + t = SearchInputTrays( pCon, MEDIUM, defMedium ); + if( t ) + { + *tray = t; + *medium = strdup( defMedium ); + return; + } + + /* + * If all else fails, just return the default tray, and whatever + * medium happens to be there. Note that we simply return + * whatever is in the attribute store. Any further correction is + * left up to the DDX driver. + */ + *tray = strdup( defTray ); + *medium = m; + xfree( t ); +} |