[sword-devel] SWConfig Changes

David White sword-devel@crosswire.org
05 Sep 2002 23:37:38 +1000

On Thu, 2002-09-05 at 14:08, Troy A. Griffitts wrote:
> > - An additional constructor for SWConfig should be provided which takes
> > a sequence of configuration files, which are all loaded and combined
> > - A method should be provided which allows loading of a configuration
> > file and merging its contents with the current configuration information
> I personally would like the constructor to take an SWConfig *, and the 
> augment method to take SWConfig *

Well the way I see it, a constructor taking an SWConfig* is essentially
a copy constructor. But I understand about the augment method taking a
SWConfig * (although I'd use a reference since it can't be null).

I assume you mean however, that the information is extracted out of the
passed SWConfig, and copied, rather than a reference to the SWConfig
object be maintained.

> This way, if anyone decides to subclass SWConfig and make something 
> silly, like SWRegistryConfig, they could still use your MultiConfig dealeo.

sure, good point.

> > - When the Save method is called, each section is serialized to the file
> > it originally came from
> > - If a section exists in multiple files, then only those names which
> > came from that file are serialized to the file.
> > - If a name/value pair has been added since the files were loaded, then
> > that name/value pair is serialized to all files which contain the
> > relevant section. [1]
> I think I prefer the [1] option.  Probably the LAST added SWConfig that 
> contained the section.  What will probably happen, is that something 
> like a systemwide module set will be loaded, then a 
> ~/.sword/userprefs.conf will be added to the config, which may override 
> any entries in the systemwide file.  Actually, it would be cool to 
> designate somehow that an SWConfig is readonly, or that an SWConfig is 
> the default write, in case a section doesn't exist, it would be created 
> in the default.

- the function to add a module takes a boolean flag which can set the
module to be read-only. The flag is false by default.
- if a module is read-only, then it will not be considered when changes
are to take place.

> > - When loading multiple files, if different files have the same name but
> > different values, for the same section, then both name/value pairs will
> > be loaded. If they have the same name and the same value, then only one
> > of the copies will be loaded.
> Actually, there is another ambiguous situation that the current augment 
> code deals with.
> consider:
> file1.conf
> [MHC]
> GlobalOptionFilter=ThMLStrongs
> GlobalOptionFilter=ThMLMorph
> file2.conf
> [MHC]
> GlobalOptionFilter=ThMLFootnotes
> In this case we want 3 entries in the final map
> scenerio 2:
> file1.conf
> [MHC]
> CipherKey=
> file2.conf
> [MHC]
> CipherKey=ABCD1234EFGH5678IJKL
> In this case we only want 1 entry.
> Current code handles this the best it can guess.


- a value is considered to be a "null" value if it is empty or consists
only of whitespace characters.
- if a key/value pair contains a null value, it is entered into the map
only if there are no other key/value pairs with the same key
- if a key/value pair is added with the same key as a key/value pair
that has a null value, then the existing key/value pair is removed.

> > - remove use of strtok(). strtok() is a non-reentrant function (not to
> > mention not thread safe), which is potentially dangerous, especially for
> > a library to use. Could be replaced by strtok_r(),
> As long as whatever you use is fast and portable, I'm game, BUT know 
> that strtok is used all over the rest of the code, as well.

Well personally I think that using strtok() is an automatic bug, at
least if it's not documented. That's because strtok() has side effects,
and undocumented side effects can usually be considered bugs.

I have written a concrete example of misbehavior due to this. I must
admit I'm not familiar enough with the Sword API yet to comment on
whether my example seems contrived, but I hope it gets the point across.

Suppose you are a user, and you are storing a list of sword
configuration files, which you want to load in. You are storing them as
a comma-separated list, and now you want to parse the list, and load the
modules, putting them into a vector. So, you write code that looks like

vector<SWConfig*> load_config(char* str)
	vector<SWConfig*> config;
	const char* p = strtok(str,",");
	do {
		config.push_back(new SWConfig(p));
		p = strtok(NULL,",");		
	} while(p);

	return config;

this code should work, but will fail, because the SWConfig constructor
will call SWConfig::Load, which will call strtok, which will clobber the
stored global pointer that strtok relies upon.

I can appreciate that this might not be considered a major or high
priority bug, but I do think it is a bug, and I would be happy to look
into replacing strtok() with an alternative (like my own tokenizer, that
I would make sure is just as fast as strtok()).

> > or hand-coded parsing
> > (since the parsing is not complicated)
> > - get rid of using namespace std; in the header file. Just because
> > people want to use Sword doesn't necessarily mean they want 1000+
> > symbols imported into the global namespace :)
> great.  only add the using std::whatever you use.

well, I usually explicitly qualify names in header files, and then
either do the same in source files, or use using statements at the top
of the source file, below the headers.

> > - make multimapwithdefault more efficient by caching iterators
> Just be sure it's understandable fast and portable.

naturally :)

> > - remove non-private member variables from the SWConfig
> Please don't change the current interface.

ok. If it's ok though, I will add public accessor functions that will
allow users to use it as if the members were private.

> > - modify the code to handle arbitrary line-lengths.
> > 
> > Any comments on these suggested changes are welcome.
> I'm excited!  This functionality will be very coooool and useful!

I hope so!


> 	-Troy.