1737 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			1737 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
| (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 int attrCtxtPrivIndex;
 | |
| 
 | |
| /*
 | |
|  * 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();
 | |
|         attrCtxtPrivIndex = XpAllocateContextPrivateIndex();
 | |
|         XpAllocateContextPrivate(attrCtxtPrivIndex, 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)pContext->devPrivates[attrCtxtPrivIndex].ptr;
 | |
|     (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)pContext->devPrivates[attrCtxtPrivIndex].ptr;
 | |
| 
 | |
|     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)pContext->devPrivates[attrCtxtPrivIndex].ptr;
 | |
|         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)pContext->devPrivates[attrCtxtPrivIndex].ptr;
 | |
|     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)pContext->devPrivates[attrCtxtPrivIndex].ptr;
 | |
|         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)pContext->devPrivates[attrCtxtPrivIndex].ptr;
 | |
|     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)pContext->devPrivates[attrCtxtPrivIndex].ptr;
 | |
|     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(__UNIXOS2__) || \
 | |
|     defined(ISC) || \
 | |
|     defined(Lynx) || \
 | |
|     defined(__QNX__) || \
 | |
|     defined(__DARWIN__)
 | |
| #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 );
 | |
| }
 |