/*
*
* 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,
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) 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 AUTHORS OR 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "Sunowconfig.h"
/***************************************************************************
Implementation Note: This implementation was done with some generality
in mind. If at some point it becomes necessary to add more
elaborate per class or per instance data, this should be
readily accommodated by this implementation.
It is assumed that the configuration database is relatively
small, say, fewer than 1000 entries. There's no hard limit, but
there's no optimization either.
The "package" mechanism is hokey. It was done to
address the concern that nobody wanted package names specified
on a per instance basis, but there needed to be some mechanism
that identified an instance as belonging to a package.
The previous implementation mentioned that it was not
MTsafe or MThot. The same applies to this implementation.
Error handling is very primitive. If a more sophisticated
means of reporting and acting upon error conditions can be
developed..., great.
It would be nice if we could use alloca, but since this
is also an Xsun server library, we can't.
***************************************************************************/
/***************************************************************************
OWconfig implementation private data structures...
***************************************************************************/
struct databaseStruct {
int dummy;
};
struct classStruct {
int dummy;
};
struct instanceStruct {
char *pkg;
int lineNumber;
};
struct attrStruct {
char *value;
};
/*
* A class list is a child of the database.
* An instance list is a child of a class.
* An attribute list is a child of an instance.
*/
struct nodeStruct {
char *name;
unsigned int hashValue;
void (*nodeFree)();
union {
} nodeValue;
};
/*
* Store comments as a singly linked list...
*/
struct commentStruct {
char *comment;
int lineNumber;
};
/*** Max pathname length... ***/
/*** Unit of allocation for input line buffer... ***/
/*** Max length of statically allocated strings... ***/
/***************************************************************************
Data Declarations...
***************************************************************************/
static int databaseValid = 0;
/*** If ever there are multiple databases, this could be moved into
a database nodeStruct.
***/
static int OWconfigFlags = 0;
static void *(*allocMem)();
static void (*freeMem)();
static int lineNumber;
static int lastLineNumber;
/***************************************************************************
****************************************************************************
Implementation private functions...
****************************************************************************
***************************************************************************/
/***************************************************************************
freeNodeList: The generic node list destructor...
***************************************************************************/
static void
{
while (np) {
}
}
/***************************************************************************
freeClass: The class node destructor...
***************************************************************************/
static void
{
return;
}
/***************************************************************************
freeInstance: The instance node destructor...
***************************************************************************/
static void
{
char *tmp;
if (tmp)
if (OWconfigFlags & OWFLAG_RETAIN) {
if (nextInstance)
if (prevInstance)
if (instanceListHead == ip)
if (instanceListTail == ip)
}
return;
}
/***************************************************************************
freeAttr: The attribute node destructor...
***************************************************************************/
static void
{
char *value;
if (value)
return;
}
/***************************************************************************
hashValue: Generates the arithmetic sum of the bytes of the string
specified by "str".
***************************************************************************/
static unsigned int
{
unsigned int ret = 0;
while (*str) {
str++;
}
return(ret);
}
/***************************************************************************
dupString: Given a non-null pointer to a string, allocates memory
sufficient to contain a copy of the string pointed to by
"str", copies the string to the newly allocated memory, and
returns a pointer to the new string.
***************************************************************************/
static char *
{
int len;
char *ptr;
if (str) {
if (ptr)
return(ptr);
} else
return(NULL);
}
/***************************************************************************
searchNodeList: Searches next-wise along a list, starting at the node
pointed to by "node", until either the end of the list is
found, or a node whose name matches the string pointed to by
"name".
***************************************************************************/
static struct nodeStruct *
{
unsigned int nameHash;
while (node) {
break;
}
return(node);
}
/***************************************************************************
searchClass: Searches for the class node whose name matches the string
pointed to by "class".
***************************************************************************/
static struct nodeStruct *
{
if (OWconfigDatabase.head)
return((struct nodeStruct *)NULL);
}
/***************************************************************************
searchInstance: Searches for the instance node identified by "name",
parented by the class node identified by "class".
***************************************************************************/
static struct nodeStruct *
{
return((struct nodeStruct *)NULL);
}
/***************************************************************************
searchAttr: Searches for the attribute node identified by "attr",
parented by the instance node identified by "name",
which in turn is parented by the class node identified by "class".
***************************************************************************/
static struct nodeStruct *
{
return((struct nodeStruct *)NULL);
}
/***************************************************************************
createClass: Creates a new class node within the OWconfig database.
***************************************************************************/
static struct nodeStruct *
{
if (!cp)
return(NULL);
return(NULL);
}
if (!OWconfigDatabase.head) {
}
if (OWconfigDatabase.tail)
return(cp);
}
/***************************************************************************
createInstance: Creates a new instance node parented by the class node
identified by "class". The instance node will be identified
by "name". If the class node does not already exist, it will
be created.
***************************************************************************/
static struct nodeStruct *
{
if (!cp) {
if (!cp) {
return(NULL);
}
}
if (!ip)
return(NULL);
return(NULL);
}
if (OWconfigFlags & OWFLAG_RETAIN) {
if (instanceListTail) {
lineNumber++;
} else {
}
}
}
return(ip);
}
/***************************************************************************
unlockOWconfig: Remove the read or write lock from an OWconfig file.
***************************************************************************/
static void
unlockOWconfig(int f)
{
/* 4007038
flock_t lockStruct;
lockStruct.l_type = F_UNLCK;
lockStruct.l_whence = 0;
lockStruct.l_start = 0;
lockStruct.l_len = 0;
(void)fcntl(f, F_SETLK, &lockStruct);
*/
return;
}
/***************************************************************************
readLockOWconfig: Place a read lock on an OWconfig file. Should only
fail if the file is write locked.
***************************************************************************/
static int
readLockOWconfig(int f)
{
/* 4007038
Do not do any read locking.
int result;
flock_t lockStruct;
lockStruct.l_type = F_RDLCK;
lockStruct.l_whence = 0;
lockStruct.l_start = 0;
lockStruct.l_len = 0;
result = fcntl(f, F_SETLK, &lockStruct);
if (result != -1)
return(OWCFG_OK);
else
return(OWCFG_LOCK1FAIL);
*/
return(OWCFG_OK);
}
/***************************************************************************
writeLockOWconfig: Place a write lock on an OWconfig file. Will fail
if any other locks, read or write, are already on the file.
***************************************************************************/
static int
writeLockOWconfig(int f)
{
/* 4007038
Do not do any write locking.
int result;
flock_t lockStruct;
lockStruct.l_type = F_WRLCK;
lockStruct.l_whence = 0;
lockStruct.l_start = 0;
lockStruct.l_len = 0;
result = fcntl(f, F_SETLK, &lockStruct);
if (result != -1)
return(OWCFG_OK);
else
return(OWCFG_LOCK1FAIL);
*/
return(OWCFG_OK);
}
/***************************************************************************
accumulateComment:
***************************************************************************/
static void
{
int len;
if (len == 0)
return;
if (*comment =='\n')
return;
newComment = (struct commentStruct *)
sizeof(struct commentStruct);
if (lastComment != NULL) {
} else {
}
return;
}
/***************************************************************************
freeComments:
***************************************************************************/
static void
freeComments(void)
{
while (commentP) {
}
commentList = NULL;
return;
}
/***************************************************************************
initDatabase: If a database already exists, free it, and set the
OWconfigDatabase structure to a known state.
***************************************************************************/
static void
initDatabase(void)
{
if (databaseValid) {
freeComments();
}
databaseValid = 1;
OWconfigDatabase.nodeFree = (void (*)())0;
return;
}
/***************************************************************************
reAllocMem: Because we don't really want to add another parameter to
OWconfigInit..., we implement a realloc routine that uses
the allocMem and freeMem routines specified by OWconfigInit.
***************************************************************************/
static char *
{
char *newPtr;
return(ptr);
if (newPtr) {
}
return(newPtr);
}
/***************************************************************************
readLine: Read the file, line by line, until an "interesting" line
is encountered. Maintain a line number counter along the
way. When a line is found, fill "lineBuf" with the data.
It is the responsibility of the caller to free the dynamically
allocated line buffer.
Note: Need to find proper way to use ctype functions...
***************************************************************************/
static int
{
char *result;
(*lineNumber)++;
if (!*lineBuf)
return(EOF);
if (result) {
char *newBuf;
int newBufSize;
if (!newBuf) {
return(EOF);
}
LINEALLOCUNIT, fp);
if (!result) {
return(EOF);
}
}
result++;
if (OWconfigFlags & OWFLAG_RETAIN) {
}
} else
return(1);
} else {
return(EOF);
}
}
/***************************************************************************
readPair: Starting at character position "charPos" in "lineBuf",
search for an expression of appx. the following syntax:
<alnum>{<whitespace>}<equal>{<whitespace>}\
(<quote><anything goes, even \ stuff><quote>) | \
<alnum>
If such an expression is found, "nameBuf" is filled with the
value to the left of the = and "valBuf" is filled with the
value to the right of the =. "charPos" will be updated to
point at the next candidate expression.
***************************************************************************/
static int
{
/*** Skip whitespace preceding name... ***/
ptr++;
return(!OWCFG_OK);
else
return(OWCFG_SYNTAX1);
}
/*** Get name... ***/
nameBuf++;
ptr++;
}
*nameBuf = (char)0;
/*** Skip "=" and whitespace preceding open quote or string...***/
ptr++;
if (*ptr == (char)0) {
return(OWCFG_SYNTAX1);
}
if (*ptr == '"') {
/*** Fetch quote delimited string... ***/
ptr++;
if (*ptr == '\\') {
if (!*(++ptr))
break;
}
valBuf++;
ptr++;
}
} else {
/*** Fetch white space terminated string... ***/
if (*ptr == '\\') {
if (!*(++ptr))
break;
}
valBuf++;
ptr++;
}
}
*valBuf = (char)0;
/*** Skip past terminating character, if any. ***/
if (*ptr)
ptr++;
return(OWCFG_OK);
}
/***************************************************************************
readOWconfig: Read the OWconfig database file named by "fileName".
The internal database is built and the file's last modified time
is returned in "timeStamp". If anything goes wrong, it is
the caller's responsibility to clean up the database.
***************************************************************************/
static int
{
int skipping = 0;
int result;
if (!fp)
return(OWCFG_OPEN1FAIL);
return(OWCFG_LOCK1FAIL);
}
classVal[0] = (char)0;
nameVal[0] = (char)0;
pkgVal[0] = (char)0;
int charPos;
if (!valBuf) {
return(OWCFG_ALLOC);
}
charPos = 0;
{
/*** class=<value> ??? ***/
skipping = 0;
/*** name=<value> ??? ***/
skipping = 0;
/*** package=<value> ??? ***/
skipping = 0;
/*** <attr>=<value> ??? ***/
if (lineBuf)
if (valBuf)
return(result);
}
skipping = 0;
/*** BLAMMO (TM)... ***/
} else {
if (!skipping) {
skipping = 1;
}
classVal[0] = (char)0;
nameVal[0] = (char)0;
}
} /* while readPair... */
if (result == OWCFG_SYNTAX1)
classVal[0] = (char)0;
nameVal[0] = (char)0;
}
if (lineBuf) {
}
if (valBuf) {
}
} /* while readLine... */
if (lineBuf)
if (valBuf)
return(syntaxResult);
}
/***************************************************************************
writeString: Writes to "fp" a quoted string specified by "str". Does
the necessary \ stuff.
***************************************************************************/
static void
{
if (str) {
while (*str) {
str++;
}
} else {
}
return;
}
/***************************************************************************
writeDatabase:
***************************************************************************/
static void
{
/*** Need to do something about Copyright notices... ***/
currentPackage[0] = (char)0;
if (OWconfigFlags && OWFLAG_RETAIN) {
/*** Write database in something resembling
original source order...
***/
while (ip) {
{
}
/*** Start new package? ***/
}
ap;
{
else
}
}
while (commentP) {
}
} else {
/*** Write database, class by class... ***/
cp;
{
ip;
{
/*** Start new package? ***/
}
ap;
{
else
}
} /* for */
} /* for */
}
}
/***************************************************************************
writeOWconfig: Writes the internal database, in OWconfig syntax, to
the file specified by fileName. As a precaution, the original
file is preserved, renamed as <filename>.BAK.
Note: I'm concerned that a more sophisticated locking mechanism might be
more trouble prone than this one.
***************************************************************************/
static int
{
int f;
if (!fp)
return(OWCFG_OPENTMPFAIL);
/*** Try write locking current OWconfig file... ***/
if (f == -1) {
(void)unlink(tmpFileName);
return(OWCFG_OPENWFAIL);
}
} else {
if (writeLockOWconfig(f) != OWCFG_OK) {
(void)close(f);
(void)unlink(tmpFileName);
return(OWCFG_LOCKWFAIL);
}
unlockOWconfig(f);
(void)close(f);
(void)unlink(tmpFileName);
return(OWCFG_RENAMEFAIL);
}
}
if (f != -1) {
unlockOWconfig(f);
(void)close(f);
}
(void)unlink(tmpFileName);
return(OWCFG_RENAMEFAIL);
}
unlockOWconfig(f);
(void)close(f);
return(OWCFG_OK);
}
/***************************************************************************
****************************************************************************
Exported functions...
****************************************************************************
***************************************************************************/
/***************************************************************************
OWconfigSetPackage:
This function establishes the name of the package to associate with
database resources created by subsequent calls to OWconfigSetAttribute or
OWconfigCreateClass.
Return: None.
***************************************************************************/
void
{
if (package)
return;
}
/***************************************************************************
OWconfigRemovePackage:
This function removes any resources from the database that were
associated with the named package.
Return: OWCFG_OK or !OWCFG_OK.
Note: The data structure is not really optimal for searching by package,
but performance isn't really critical here.
***************************************************************************/
int
{
if (!package)
return(OWCFG_ARGS);
while (cp) {
while(ip) {
continue;
}
}
}
return(result);
}
/***************************************************************************
OWconfigSetInstance:
"OWconfigSetInstance" is a convenient front end to "OWconfigSetAttribute".
It takes a list of attributes and adds them to an instance of a class.
If that class and/or instance does not already exist, they are or it is created.If that instance does exist, replacements, when necessary, occur on a per
Return: OWCFG_OK or !OWCFG_OK(could occur during an out of memory condition).
***************************************************************************/
int
int numberInattr)
{
return(OWCFG_ARGS);
break;
}
return(result);
}
/***************************************************************************
OWconfigRemoveInstance:
"OWconfigRemoveInstance" removes an instance of a class, identified by
"class" and "name".
Return: OWCFG_OK or !OWCFG_OK
***************************************************************************/
int
{
return(OWCFG_ARGS);
if (!ip)
return(OWCFG_INSTANCENAME);
return(OWCFG_OK);
}
/***************************************************************************
OWconfigSetAttribute:
exist, they are or it is created. If the named attribute already exists
within the instance, it is replaced.
Return: OWCFG_OK or !OWCFG_OK(could occur during with out of memory condition).
***************************************************************************/
int
{
char *tmpPkg;
return(OWCFG_ARGS);
if (!cp) {
if (!cp)
return(OWCFG_ALLOC);
}
if (!ip) {
if (!ip)
return(OWCFG_ALLOC);
}
if (ap) {
} else {
ap = (struct nodeStruct *)
allocMem(sizeof(struct nodeStruct));
if (!ap)
return(OWCFG_ALLOC);
return(OWCFG_ALLOC);
}
}
return(OWCFG_ALLOC);
}
if (!tmpPkg)
return(OWCFG_ALLOC);
}
return(OWCFG_OK);
}
/***************************************************************************
OWconfigGetClassNames:
"OWconfigGetClassNames" returns a list of the names of all the
instances of the named class. The end of the list is indicated by
a NULL pointer. All users of this function may call "OWconfigFreeClassNames"
to free the list and the strings it points to.
Return: (char **) to list of class instance names or NULL if class did
not exist.
***************************************************************************/
char **
{
int instanceCount = 0;
if (!class)
return(NULL);
if (!cp)
return(NULL);
/*** Count the number of instances in the class... ***/
if (listOfString) {
ip;
{
/*** If dupString fails, free previously allocated
strings and return NULL...
***/
if (!(*ptr)) {
for (ptr = listOfString;
ptr++)
{
}
return(NULL);
}
}
}
return(listOfString);
}
/***************************************************************************
OWconfigFreeClassNames:
"OWconfigFreeClassNames" frees the list of class names returned by
"OWconfigGetClassNames".
***************************************************************************/
void
{
char **ptr;
if (!list)
return;
return;
}
/***************************************************************************
OWconfigGetInstance:
"OWconfigGetInstance" returns a list of attribute definitions belonging
to the instance identified by "class" and "name". Use "OWconfigFreeInstance"
to free the memory allocated to the information returned by
"OWconfigGetInstance".
Return:
OWconfigAttributePtr or NULL (if instance doesn't exist).
***************************************************************************/
{
int attrCount = 0;
return(NULL);
if (!ip)
return(NULL);
/*** Count the number of attributes in the instance... ***/
attrCount++;
if (replyPtr) {
ap;
{
else
ptr++)
{
}
return(NULL);
}
}
}
return(replyPtr);
}
/***************************************************************************
OWconfigFreeInstance:
"OWconfigFreeInstance" frees the data returned by OWconfigGetInstance.
***************************************************************************/
void
{
if ((!attr) || (!numberInAttr))
return;
ptr++)
{
}
return;
}
/***************************************************************************
OWconfigGetAttribute:
"OWconfigGetAttribute" returns the string value of the requested attribute.
Return: (char *) to value of attribute or NULL if attribute could not
be found.
***************************************************************************/
char *
{
return(NULL);
if (ap)
else
return(NULL);
}
/***************************************************************************
OWconfigFreeAttribute:
"OWconfigFreeAttribute" frees the string returned by OWconfigGetAttribute.
***************************************************************************/
void
{
if (attribute)
return;
}
/***************************************************************************
OWconfigClose:
If writefile was specified, the existing target file, if any, is write locked
using fcntl. If the lock succeeds, the new OWconfig file is written to the
same directory, with a temporary name. The old file is removed and the new
file is renamed to match the old file.
The internal database is freed.
Return: OWCFG_OK or !OWCFG_OK(could occur if write or write lock failed).
Note: When a file is written, all comments are lost from the
original file(s). When written, the OWconfig entries will be grouped by class.
***************************************************************************/
int
{
initDatabase();
return(result);
}
/***************************************************************************
OWconfigValidate: Verifies that the internal database is up to date
with respect to the database file indicated by the global "readFlags".
If it isn't, the files are re-read and a new internal database is
created.
Return:
OWCFG_OK or !OWCFG_OK (if re-read fails).
***************************************************************************/
int
OWconfigValidate(void)
{
int f;
int validate = 0;
if (readFile1) {
if (f == -1)
return(OWCFG_OPEN1FAIL); /*Leave well enough alone*/
(void)close(f);
return(OWCFG_FSTAT1FAIL); /*Leave well enough alone*/
}
validate = 1;
(void)close(f);
}
if (f == -1)
return(OWCFG_OPEN2FAIL); /*Leave well enough alone*/
(void)close(f);
return(OWCFG_FSTAT2FAIL); /*Leave well enough alone*/
}
validate = 1;
(void)close(f);
}
if (validate) {
/*** Setting databaseValid to zero causes initDatabase()
to NOT free existing database...
***/
databaseValid = 0;
databaseValid = 1;
} else {
}
}
return(result);
}
/***************************************************************************
OWconfigInit:
"OWconfigInit" will read the OWconfig files named by readfile1 and
readfile2.
those read from readfile1. The replacement will occur on a per
read locked using the fcntl facility. Immediately after the file
is read, it is unlocked.
Return: OWCFG_OK or !OWCFG_OK. Bogus filepaths can return !OWCFG_OK.
If readfile1 and readfile2 are both non-NULL, and the read of
readfile1 succeeds but the read of readfile2 fails, OWCFG_OK
will be returned. Lock test failures will return !OWCFG_OK.
***************************************************************************/
int
{
if (allocmem)
else
if (freemem)
else
initDatabase();
lineNumber = 0;
lastLineNumber = 0;
/*** Check for specific corner cases... ***/
return(OWCFG_OK);
/*** Read file 1, if specified... ***/
if (readFile1) {
initDatabase();
return(result1);
}
}
/*** Read file 2, if specified... ***/
if (readFile2) {
initDatabase();
if (result2 == OWCFG_LOCK1FAIL)
if (result2 == OWCFG_SYNTAX1)
return(result2);
}
}
return(OWCFG_OK);
else
return(OWCFG_OPENBOTHFAIL);
} else {
if (result1 == OWCFG_OPEN1FAIL)
return(OWCFG_OPEN1FAIL);
else
return(OWCFG_OPEN2FAIL);
}
}