mirror of
https://github.com/gnustep/libs-base.git
synced 2025-05-31 16:50:58 +00:00
Rewrite config file parsing to match conventions of shell
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@21821 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
6ffb338fcf
commit
b8c539c9bc
2 changed files with 233 additions and 134 deletions
|
@ -1,3 +1,9 @@
|
||||||
|
2005-10-14 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
|
* Source/NSPathUtilities.m: Complete rewrite of config file parsing
|
||||||
|
to try to make it consistent with the escaping and quoting conventions
|
||||||
|
of shells, so the same file can be source'ed by /bin/sh
|
||||||
|
|
||||||
2005-10-13 Richard Frith-Macdonald <rfm@gnu.org>
|
2005-10-13 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
* Source/NSPathUtilities.m: Remove some useless functions to clarify
|
* Source/NSPathUtilities.m: Remove some useless functions to clarify
|
||||||
|
|
|
@ -207,25 +207,32 @@ static NSString *setUserGNUstepPath(NSString *userName,
|
||||||
NSString **defaultsPath,
|
NSString **defaultsPath,
|
||||||
NSString **userPath);
|
NSString **userPath);
|
||||||
|
|
||||||
static NSDictionary *GSReadStepConfFile(NSString *name);
|
static NSDictionary *ParseConfigurationFile(NSString *name);
|
||||||
|
|
||||||
static void InitialisePathUtilities(void);
|
static void InitialisePathUtilities(void);
|
||||||
static void ShutdownPathUtilities(void);
|
static void ShutdownPathUtilities(void);
|
||||||
|
|
||||||
/* Convenience MACRO to ease legibility and coding */
|
/* Conditionally assign an object from a dictionary to var
|
||||||
/* Conditionally assign lval to var */
|
* We don't need to retain val before releasing var, because we
|
||||||
|
* can be sure that if var is val it is retained by the dictionary
|
||||||
|
* as well as being retained when it was first placed in var.
|
||||||
|
*/
|
||||||
#define ASSIGN_IF_SET(var, dictionary, key) ({\
|
#define ASSIGN_IF_SET(var, dictionary, key) ({\
|
||||||
id val = [dictionary objectForKey: key];\
|
id val = [dictionary objectForKey: key];\
|
||||||
if (val != nil) { RELEASE(var); var = RETAIN(val); }\
|
if (val != nil)\
|
||||||
})
|
{\
|
||||||
|
RELEASE(var);\
|
||||||
|
var = RETAIN(val);\
|
||||||
|
}\
|
||||||
|
})
|
||||||
|
|
||||||
/* Convenience MACRO to ease legibility and coding */
|
/* Conditionally assign lval to var only if var is nil */
|
||||||
/* Conditionally assign lval to var */
|
#define TEST_ASSIGN(var, lval) ({\
|
||||||
#define TEST_ASSIGN(var, lval) \
|
if ((var == nil)&&(lval != nil))\
|
||||||
if ((var == nil)&&(lval != nil)) \
|
{\
|
||||||
{ \
|
var = RETAIN(lval);\
|
||||||
var = lval; \
|
}\
|
||||||
}
|
})
|
||||||
|
|
||||||
/* Get a path string from a dictionary */
|
/* Get a path string from a dictionary */
|
||||||
static inline NSString *
|
static inline NSString *
|
||||||
|
@ -269,7 +276,7 @@ static NSString *setUserGNUstepPath(NSString *userName,
|
||||||
{
|
{
|
||||||
steprcFile = [home stringByAppendingPathComponent: gnustepRcFileName];
|
steprcFile = [home stringByAppendingPathComponent: gnustepRcFileName];
|
||||||
|
|
||||||
dict = GSReadStepConfFile(steprcFile);
|
dict = ParseConfigurationFile(steprcFile);
|
||||||
if (dict != nil)
|
if (dict != nil)
|
||||||
{
|
{
|
||||||
path = [dict objectForKey: @"GNUSTEP_DEFAULTS_ROOT"];
|
path = [dict objectForKey: @"GNUSTEP_DEFAULTS_ROOT"];
|
||||||
|
@ -385,7 +392,7 @@ static void InitialisePathUtilities(void)
|
||||||
configFile = RETAIN([configFile stringByStandardizingPath]);
|
configFile = RETAIN([configFile stringByStandardizingPath]);
|
||||||
if ([MGR() fileExistsAtPath: configFile])
|
if ([MGR() fileExistsAtPath: configFile])
|
||||||
{
|
{
|
||||||
NSDictionary *d = GSReadStepConfFile(configFile);
|
NSDictionary *d = ParseConfigurationFile(configFile);
|
||||||
|
|
||||||
if (d != nil)
|
if (d != nil)
|
||||||
{
|
{
|
||||||
|
@ -502,33 +509,38 @@ static void ShutdownPathUtilities(void)
|
||||||
* Reads a file and expects it to be in basic unix "conf" style format with
|
* Reads a file and expects it to be in basic unix "conf" style format with
|
||||||
* one key = value per line (the format a unix shell can 'source' in order
|
* one key = value per line (the format a unix shell can 'source' in order
|
||||||
* to define shell variables).<br />
|
* to define shell variables).<br />
|
||||||
* The key must be an unquoted string containing only alphanumerics and
|
* Attempts to mimic the escape sequence and quoting conventions of standard
|
||||||
* the underscore character. It may not begin with a digit, though it may
|
* shells, so that a config file sourced by the make package will produce
|
||||||
* be preceeded by whitespace (which is ignored).<br />
|
* the same results as one parsed by this function.<br />
|
||||||
* The '=' must appear <em>immediately after the key.<br />
|
* The value may be any quoted string (or an unquoted string containing no
|
||||||
* The value may be any quoted string ... any leading or trailing whitespace
|
* white space).<br />
|
||||||
* (except inside a quoted string) is removed.<br />
|
|
||||||
* Lines beginning with a hash '#' are deemed comment lines and ignored.<br/ >
|
* Lines beginning with a hash '#' are deemed comment lines and ignored.<br/ >
|
||||||
* The backslash character may be used as an escape character anywhere
|
* The backslash character may be used as an escape character anywhere
|
||||||
* in the file except within a singly quoted string
|
* in the file except within a singly quoted string
|
||||||
* (where it is taken literally) ... in particular it may be used at
|
* (where it is taken literally).<<br />
|
||||||
* the end of a line to join two lines together.
|
* A backslash followed immediately by a newline (except in a singly
|
||||||
* However, in contrast to normal shell usage,
|
* quoted string) is removed completely along with the newline ... it
|
||||||
* we do not allow newline characters within a quoted string.<br />
|
* thus serves to join lines so that they are treated as a single line.<br />
|
||||||
* NB. Since windows uses backslash characters in paths, it is a good
|
* NB. Since windows uses backslash characters in paths, it is a good
|
||||||
* idea to specify path values in the config file as singly quoted
|
* idea to specify path values in the config file as singly quoted
|
||||||
* strings to avoid having to double all occurrances of the backslash.<br />
|
* strings to avoid having to double all occurrances of the backslash.<br />
|
||||||
* Creates a dictionary of the (key,value) pairs.<br/ >
|
* Returns a dictionary of the (key,value) pairs.<br/ >
|
||||||
*/
|
*/
|
||||||
static NSDictionary *
|
static NSDictionary *
|
||||||
GSReadStepConfFile(NSString *fileName)
|
ParseConfigurationFile(NSString *fileName)
|
||||||
{
|
{
|
||||||
NSMutableDictionary *dict;
|
NSMutableDictionary *dict;
|
||||||
NSDictionary *attributes;
|
NSDictionary *attributes;
|
||||||
NSString *file;
|
NSString *file;
|
||||||
NSArray *lines;
|
unsigned l;
|
||||||
NSRange r;
|
unichar *src;
|
||||||
unsigned count;
|
unichar *dst;
|
||||||
|
unichar *end;
|
||||||
|
unichar *spos;
|
||||||
|
unichar *dpos;
|
||||||
|
BOOL newLine = YES;
|
||||||
|
NSString *key = nil;
|
||||||
|
NSString *lastToken = nil;
|
||||||
|
|
||||||
if ([MGR() isReadableFileAtPath: fileName] == NO)
|
if ([MGR() isReadableFileAtPath: fileName] == NO)
|
||||||
{
|
{
|
||||||
|
@ -556,121 +568,202 @@ GSReadStepConfFile(NSString *fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
file = [NSString stringWithContentsOfFile: fileName];
|
file = [NSString stringWithContentsOfFile: fileName];
|
||||||
|
l = [file length];
|
||||||
|
src = (unichar*)NSZoneMalloc(NSDefaultMallocZone(), sizeof(unichar) * l);
|
||||||
|
spos = src;
|
||||||
|
end = src + l;
|
||||||
|
dst = (unichar*)NSZoneMalloc(NSDefaultMallocZone(), sizeof(unichar) * l);
|
||||||
|
dpos = dst;
|
||||||
|
[file getCharacters: src];
|
||||||
|
|
||||||
/*
|
while (spos < end)
|
||||||
* Allow DOS (CRLF) and Mac (CR) line termination as well as the normal LF
|
|
||||||
*/
|
|
||||||
r = [file rangeOfString: @"\r\n"];
|
|
||||||
if (r.length > 0)
|
|
||||||
{
|
{
|
||||||
file = [file stringByReplacingString: @"\r\n" withString: @"\n"];
|
/*
|
||||||
}
|
* Step past any whitespace ... including blank lines
|
||||||
r = [file rangeOfString: @"\r"];
|
*/
|
||||||
if (r.length > 0)
|
while (spos < end)
|
||||||
{
|
|
||||||
file = [file stringByReplacingString: @"\r" withString: @"\n"];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Remove any escaped newline characters.
|
|
||||||
*/
|
|
||||||
r = [file rangeOfString: @"\\\n"];
|
|
||||||
if (r.length > 0)
|
|
||||||
{
|
|
||||||
file = [file stringByReplacingString: @"\\\n" withString: @""];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Split files into lines
|
|
||||||
*/
|
|
||||||
lines = [file componentsSeparatedByString: @"\n"];
|
|
||||||
count = [lines count];
|
|
||||||
|
|
||||||
while (count-- > 0)
|
|
||||||
{
|
|
||||||
NSString *line;
|
|
||||||
NSString *key;
|
|
||||||
NSString *val;
|
|
||||||
|
|
||||||
line = [[lines objectAtIndex: count] stringByTrimmingSpaces];
|
|
||||||
|
|
||||||
if (([line length] > 0) && ([line characterAtIndex: 0] != '#'))
|
|
||||||
{
|
{
|
||||||
r = [line rangeOfString: @"="];
|
if (*spos == '\\')
|
||||||
if (r.length == 1)
|
|
||||||
{
|
{
|
||||||
unsigned length;
|
spos++;
|
||||||
|
if (spos >= end)
|
||||||
key = [line substringToIndex: r.location];
|
|
||||||
val = [line substringFromIndex: NSMaxRange(r)];
|
|
||||||
|
|
||||||
key = [key stringByTrimmingSpaces];
|
|
||||||
val = [val stringByTrimmingSpaces];
|
|
||||||
|
|
||||||
if ((length = [val length]) > 0)
|
|
||||||
{
|
{
|
||||||
unichar c = [val characterAtIndex: 0];
|
break; // At end of file ... odd but not fatal
|
||||||
|
|
||||||
/*
|
|
||||||
* Strip quotes from the value if necessary.
|
|
||||||
*/
|
|
||||||
if (c == '\'' || c == '"')
|
|
||||||
{
|
|
||||||
if (length > 1 && [val characterAtIndex: length-1] == c)
|
|
||||||
{
|
|
||||||
r = NSMakeRange(1, length-2);
|
|
||||||
val = [val substringWithRange: r];
|
|
||||||
length -= 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
val = [val substringFromIndex: 1];
|
|
||||||
length -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Handle backslash quotes (except in a singly quoted string).
|
|
||||||
*/
|
|
||||||
if (c != '\'')
|
|
||||||
{
|
|
||||||
r = [val rangeOfString: @"\\"];
|
|
||||||
if (r.length > 0)
|
|
||||||
{
|
|
||||||
unichar buf[length];
|
|
||||||
unsigned pos;
|
|
||||||
|
|
||||||
[val getCharacters: buf];
|
|
||||||
for (pos = 0; pos < length; pos++)
|
|
||||||
{
|
|
||||||
if (buf[pos] == '\\')
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
for (i = pos + 1; i < length; i++)
|
|
||||||
{
|
|
||||||
buf[i-1] = buf[i];
|
|
||||||
}
|
|
||||||
length--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val = [NSString stringWithCharacters: buf
|
|
||||||
length: length];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ([key length] > 0)
|
|
||||||
{
|
|
||||||
[dict setObject: val forKey: key];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
if (*spos > ' ')
|
||||||
{
|
{
|
||||||
key = [line stringByTrimmingSpaces];
|
break; // OK ... found a non space character.
|
||||||
val = nil;
|
|
||||||
}
|
}
|
||||||
|
if (*spos == '\r' || *spos == '\n')
|
||||||
|
{
|
||||||
|
newLine = YES;
|
||||||
|
}
|
||||||
|
spos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle any comments .. hash on a new line.
|
||||||
|
*/
|
||||||
|
if (newLine == YES)
|
||||||
|
{
|
||||||
|
if (key != nil)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* On a newline ...so the last key had no value set.
|
||||||
|
* Put an empty cvalue in the dictionary.
|
||||||
|
*/
|
||||||
|
[dict setObject: @"" forKey: key];
|
||||||
|
DESTROY(key);
|
||||||
|
}
|
||||||
|
if (spos < end && *spos == '#')
|
||||||
|
{
|
||||||
|
// Got a comment ... ignore remainder of line.
|
||||||
|
while (spos < end && *spos != '\n' && *spos != '\r')
|
||||||
|
{
|
||||||
|
spos++;
|
||||||
|
}
|
||||||
|
continue; // restart loop ... skip space at start of line
|
||||||
|
}
|
||||||
|
newLine = NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*spos == '=')
|
||||||
|
{
|
||||||
|
if (key != nil)
|
||||||
|
{
|
||||||
|
[dict setObject: @"" forKey: key];
|
||||||
|
DESTROY(key);
|
||||||
|
}
|
||||||
|
key = lastToken;
|
||||||
|
lastToken = nil;
|
||||||
|
spos++;
|
||||||
|
}
|
||||||
|
else if (*spos == '\'')
|
||||||
|
{
|
||||||
|
spos++;
|
||||||
|
while (spos < end)
|
||||||
|
{
|
||||||
|
if (*spos == '\'')
|
||||||
|
{
|
||||||
|
spos++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*dpos++ = *spos++;
|
||||||
|
}
|
||||||
|
DESTROY(lastToken);
|
||||||
|
lastToken = [NSString alloc];
|
||||||
|
lastToken = [lastToken initWithCharacters: dst length: dpos - dst];
|
||||||
|
if (key != nil)
|
||||||
|
{
|
||||||
|
[dict setObject: lastToken forKey: key];
|
||||||
|
DESTROY(key);
|
||||||
|
DESTROY(lastToken);
|
||||||
|
}
|
||||||
|
dpos = dst; // reset output buffer
|
||||||
|
}
|
||||||
|
else if (*spos == '"')
|
||||||
|
{
|
||||||
|
spos++;
|
||||||
|
while (spos < end)
|
||||||
|
{
|
||||||
|
BOOL escaped = NO;
|
||||||
|
|
||||||
|
if (*spos == '\\')
|
||||||
|
{
|
||||||
|
spos++;
|
||||||
|
if (spos >= end)
|
||||||
|
{
|
||||||
|
break; // Unexpected end of file
|
||||||
|
}
|
||||||
|
if (*spos == '\n')
|
||||||
|
{
|
||||||
|
spos++;
|
||||||
|
continue; // escaped newline is removed.
|
||||||
|
}
|
||||||
|
if (*spos == '\r')
|
||||||
|
{
|
||||||
|
spos++;
|
||||||
|
if (spos < end && *spos == '\n')
|
||||||
|
{
|
||||||
|
spos++;
|
||||||
|
}
|
||||||
|
continue; // escaped newline is removed.
|
||||||
|
}
|
||||||
|
escaped = YES;
|
||||||
|
}
|
||||||
|
if (*spos == '"' && escaped == NO)
|
||||||
|
{
|
||||||
|
spos++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*dpos++ = *spos++;
|
||||||
|
}
|
||||||
|
DESTROY(lastToken);
|
||||||
|
lastToken = [NSString alloc];
|
||||||
|
lastToken = [lastToken initWithCharacters: dst length: dpos - dst];
|
||||||
|
if (key != nil)
|
||||||
|
{
|
||||||
|
[dict setObject: lastToken forKey: key];
|
||||||
|
DESTROY(key);
|
||||||
|
DESTROY(lastToken);
|
||||||
|
}
|
||||||
|
dpos = dst; // reset output buffer
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (spos < end)
|
||||||
|
{
|
||||||
|
if (*spos == '\\')
|
||||||
|
{
|
||||||
|
spos++;
|
||||||
|
if (spos >= end)
|
||||||
|
{
|
||||||
|
break; // Unexpected end of file
|
||||||
|
}
|
||||||
|
if (*spos == '\n')
|
||||||
|
{
|
||||||
|
spos++;
|
||||||
|
continue; // escaped newline is removed.
|
||||||
|
}
|
||||||
|
if (*spos == '\r')
|
||||||
|
{
|
||||||
|
spos++;
|
||||||
|
if (spos < end && *spos == '\n')
|
||||||
|
{
|
||||||
|
spos++;
|
||||||
|
}
|
||||||
|
continue; // escaped newline is removed.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (*spos <= ' ' || *spos == '=')
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*dpos++ = *spos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
DESTROY(lastToken);
|
||||||
|
lastToken = [NSString alloc];
|
||||||
|
lastToken = [lastToken initWithCharacters: dst length: dpos - dst];
|
||||||
|
if (key != nil)
|
||||||
|
{
|
||||||
|
[dict setObject: lastToken forKey: key];
|
||||||
|
DESTROY(key);
|
||||||
|
DESTROY(lastToken);
|
||||||
|
}
|
||||||
|
dpos = dst; // reset output buffer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (key != nil)
|
||||||
|
{
|
||||||
|
[dict setObject: @"" forKey: key];
|
||||||
|
DESTROY(key);
|
||||||
|
}
|
||||||
|
DESTROY(lastToken);
|
||||||
|
NSZoneFree(NSDefaultMallocZone(), src);
|
||||||
|
NSZoneFree(NSDefaultMallocZone(), dst);
|
||||||
|
|
||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue