LameScript 2.0 

README.txt

(c)2006-2007 by Carlos H. P. da Silva 
<carloshp dot silva at gmail dot com>

===============================================================================

Index

1. About
2. The LameScript file format
3. The LameScript path
4. The LameScript API
  4.1 LS_init
  4.2 LS_getStatus
  4.3 LS_setPath
  4.4 LS_getPath
  4.5 LS_setScript
  4.6 LS_delScript
  4.7 LS_getScriptCount
  4.8 LS_setSection
  4.9 LS_delSection
  4.10 LS_getSectionCount
  4.11 LS_setKey
  4.12 LS_delKey
  4.13 LS_getKeyCount
  4.14 LS_addValue
  4.15 LS_setValue
  4.16 LS_getValue
  4.17 LS_getIterator
  4.18 LS_hasNext
  4.19 LS_nextValue
  4.20 LS_getValueCount
  4.21 LS_hasValue
  4.22 LS_saveScript
  4.23 LS_saveScriptFile
  4.24 LS_loadScript
  4.25 LS_loadScriptFile
5. LameScript error codes
6. LameScript limits
7. Adding LameScript to a project 
8. Usage tips
9. Licensing
10. Package contents
11. Known bugs
12. Feedback
13. History
  
===============================================================================

1. About

LameScript is a general purpose library to handle simple configuration/script
files. It allows your applications to easily create, save, load, search, modify
these files without caring with annoying details. Originally LameScript was a
quick and dirt side project to my GLQuake engine port (Q2K4) but later evolved
to a fairly complete, general purpose and stand alone library.

2. The LameScript file format

LameScript files are plain text files organized in sections, composed by a
unique name into the file and delimited by "{" and "}". Sections are in 
turn subdivided in key-value pairs and lastly, a pair can hold one or more
values separated by double quotes and commas and terminated by a semicolon. 
Here is an example of key-value pair:

meals="breakfast","lunch","dinner";

And here we have the section example:

car {
name="Diablo";
manufacturer="Lamborghini";
colors="black","red","yellow";
price="US$1,000,000.00";
}

LameScript files also support comment lines. Just start the line with a "#",
like this example:

# this is a comment line. It's ignored by LameScript.
# If you want to write very long comments,
# you must add the "#" at every line.

Some simple rules when composing a LameScript manually:

1) dont forget the "#" at the start of every comment line;
2) you must have at least one section;
3) you must have at least one key-value pair into a section;
4) you must have at least one value into a pair;
4) sections names are unique inside a file (however, you can have the same 
section name repeated across several script files loaded into memory);
5) key-pair names are unique inside a section (however, you can have the
same key-pair name repeated across several sections into the same script
file or as many files you want).

3. The LameScript path

LameScript 2.0 allows the use of several script files at the same time, 
each one holding hundreds of sections, and each one of these in turn can
hold another thousand of key-value pairs. So, to quickly refer to any
element in the script hierarchy we use a symbolic path convention, very
similar to a file system path (like in Unix, DOS or Windows):

$script:section/key

For example, suppose we have a script called "myscript", and this script 
has a section called "mysection", which in turn has a key called "mykey". 
The full path string would be:

"$myscript:mysection/mykey"

You can also use only partial paths, like:

"$myscript" -> defines only the current script

or

"$myscript:mysection" -> defines both script and section

or
":mysection" -> assumes it's a section into the current script

or

"/mykey" -> assumes it's a key into the current section and script

or even

"" or <null> -> uses the current path, as long it points to a valid pair

If you supply a malformed path string, you'll receive a LS_BADPATH error.
If you supply an insufficient path string and try to access data, you'll 
receive a LS_BADPATH error. If you supply an inexistent path, you'll receive 
one of the following errors: LS_NOSCRIPT, LS_NOSECTION or LS_NOKEY error.

4. The LameScript API

There are several functions to handle the scripts into memory as well to save
and load them from files. Also, there are helper functions to quickly retrieve
stored data, and finally functions to set or return the current script, section
and/or pair. 

For test purposes, a small test program

Here are all LameScript API available functions:

4.1 LS_init

int     LS_init (void)

You must invoke LS_init once in your program and before any call to other 
function. As the name implies, this function sets up all internal structures 
used by LameScript.

4.2 LS_getStatus

int     LS_getStatus (void)

Returns the resulting error code from the last called function. See the error
codes at the end of this file for possible values.

4.3 LS_setPath

int     LS_setPath (char *fmt, ...)

Sets the script path string. You can either use a literal string like in the
prior examples or use a formatting string plus any parameter combination, in
the same fashion you would use with the standard printf(). Heres a code
snippet as example:

for (int i=0; i < 100; r++) {
  for (int j= 0; j < 100; j++) {
    LS_setPath("$myscript:section%02d/key%02d", i, j);
    if (LS_getStatus == LSOK) {
      // tells how many values are associated to the current key
      printf ("section #%02d, key #%02d has %d value(s)\n", 
        i, j, LS_getValueCount());
    }
    else {
      printf ("Oops, bad path...\n");
    }
  }
}

4.4 LS_getPath

char*   LS_getPath (void)

Returns the current script path string.

4.5 LS_setScript

int     LS_setScript (char *name)

Sets only the script portion of the path. Current section and pair names are
cleared from the path.

4.6 LS_delScript

int     LS_delScript (char *name)

Deletes the referred script with all its sections and keys from memory. This 
does not affect the script file. The current script path is completely cleared 
after this operation.

4.7 LS_getScriptCount

int     LS_getScriptCount (void)

Tells how many scripts are loaded into memory.

4.8 LS_setSection

int     LS_setSection (char *name)

Sets only the section portion of the path, assuming the current script. Current
pair name is cleared from the path.

4.9 LS_delSection

int     LS_delSection (char *name)

Deletes the referred section and all their pairs from the current script. This
does not affect the script file. Only the current script name is preserved in 
the current path.

4.10 LS_getSectionCount

int     LS_getSectionCount (void)

Tells how many sections are into the current script.

4.11 LS_setKey

int     LS_setKey (char *name)

Sets only the key-value pair name of the path, assuming the current script and
section. 

4.12 LS_delKey

int     LS_delKey (char *name)

Deletes the referred key and all their values from the current script.This
does not affect the script file. The current key-value pair name is cleared.

4.13 LS_getKeyCount

int     LS_getKeyCount (void)

Tells how many keys are into the current section.

4.14 LS_addValue

void    LS_addValue (char *value)

Adds a new item to the end of the current key value list. 

4.15 LS_setValue

void    LS_setValue (char *value, int position)

Adds a new item in the given position in the key value list, regardless the
current number of items in this list. This means you can create a key
and just after that sets the value of the 100th position of its value list.
Setting a value in this way destroys any values stored in the informed
position previously.

4.16 LS_getValue

char*   LS_getValue (int position)

Returns the value stored in the given position for the current key-value pair.
For example, for the given key-value pair:

numbers="one","two","three","four","five","six","seven","eight","nine","zero";

LS_getValue(3) would return "four", and so on.

4.17 LS_getIterator

int     LS_getIterator (int position)

Sets the internal interator to point the current value list, starting from
the informed position. Doing so allows the use of the LS_hasNext() and
LS_nextValue() functions to quickly scan and return all the values list. The
following code shows how to:

// assumes the current path is correctly set
if (LS_getIterator (0) == LSOK) {
  while (LS_hasNext()) {
    printf ("%s\n", LS_nextValue());
  }
}

4.18 LS_hasNext

int     LS_hasNext (void)

Checks if there are more items to retrieve from the current values list. See
the LS_getIterator example.

4.19 LS_nextValue

char*   LS_nextValue (void)

Gets the current item from the values list and increments the iterator pointer.
See the LS_getIterator example.

4.20 LS_getValueCount

int     LS_getValueCount (void)

Tells how many items are in the values list of the current key-value pair.

4.21 LS_hasValue

int     LS_hasValue (char *value)

Checks if the informed value exists in the values list on the current key.
Returns its position or -1 if not found or the informed value is null. Calling
LS_hasValue() will reset the iterator internal pointer, so avoid it when
you're inside a loop controlled by LS_hasNext().

4.22 LS_saveScript

int     LS_saveScript (char *filename)

Saves the current script to a text file. 

4.23 LS_saveScriptFile

int     LS_saveScriptFile (FILE *fout)

Pretty much the same as LS_saveScript(), but receives a FILE pointer previously
opened for write in text mode (same as using the "wt" string with fopen()).

4.24 LS_loadScript

int     LS_loadScript (char *filename, char *alias)

Loads a script file into memory. 

4.25 LS_loadScriptFile

int     LS_loadScriptFile (FILE *fin, char *alias)

Again the same as LS_loadScript() but receiving the FILE pointer previously
opened using the "rt" mode string (text read mode).

5. LameScript error codes

These are the error codes, as declared in lsdefs.h and returned by the
LS_getStatus() function. This list can grow in future releases, but the 
current values won't change.

Value   Code symbol     Error explanation
===============================================================================
0x0000  LSOK            Last called function executed sucessfully.
0x0001  LS_NOINIT       You tried to use LameScript without a previous call to
                        LS_init().
0x0002  LS_NOSCRIPT     You referred to an inexistent or not loaded script. 
0x0003  LS_NOSECTION    You referred to an inexistent section into the current
                        script.
0x0004  LS_NOKEY        You referred to an inexistent key into the current
                        section.
0x0005  LS_BADPATH      You will receive this error if:
                        - you informed a malformed path string;
                        - you supplied an incomplete path, ex: informing
                        only the script and/or section names and then tried to
                        retrieve a value.

0x0006  LS_FULLKEY      You tried to add more items to the current value list
                        than allowed. See the "LameScript limits" section for
                        more details.
                                            
0x0007  LS_BADKEYINDEX  You used an out fo range index to refer data in the
                        values list. See the "LameScript limits" section.
                        
0x0008  LS_BADSCRIPT    You tried to load a malformed LameScript file.

0x0009  LS_BADHASHCODE  LameScript failed to generate a hash code. This is
                        a very unlikely error. 

6. LameScript limits

These are some internal limits in LameScript library. Most of these can be
changed without harm, for either extended support (for example, bigger
item strings) or memory saving, but the current values are already a good 
compromise between both.

Constant    Current value   Used for
===============================================================================
MAXVALNUM   65536           Tells how many items can be added to the value
                            list of a key-value pair. This is the only constant
                            you cannot mess with because the internal iterator 
                            makes some hard coded assumptions about this. In
                            other words, change that and KABOOM.
MAXVALSIZE  1024            The maximum size for a item. You can change that
                            as you please, provided you have enough memory.
MAXPATHSIZE 1024            Defines the maximum length of the path string. You
                            can freely change that if desired.

7. Adding LameScript to a project

To add LameScript to your project its a pretty straightforward task: put all 
source files together your sources, or just use the precompiled library. In 
both cases, make sure you added lscript.h, lsdefs.h and lshash.h to your makefile. 
Finally, you will need to add to your source files:

#include "lscript.h"

And LameScript is ready to use.
                            
8. Usage tips

Here are some things you should keep in mind when using LameScript:

1) LameScript does not replace XML (at least not always).
    There are situations when using XML parsing can be overkill because
    the overhead that can be added (both in code size and speed), and a
    more simple solution like LameScript fits well. BUT, there are times
    when XML is better because it offers things like built-in validation
    through the use of DTD files, Unicode support, etc. and LameScript
    cannot compete with that, simply put.

2) LameScript is not a complete database solution.
    If you need things like referential integrity, indexation and more 
    sofisticated resources, LameScript is not for you.
    
3) LameScript is fast, but NOT that fast.
    Be careful with the choosed points in your application to add calls
    to LameScript. Avoid to make direct calls to LameScript from within
    of time critical loops. This may look like a silly advice, but you
    can be surprised of how often people actually does that. Don't
    forget that I made my best to make a god compromise between speed
    and flexibility, but in the end we are working, most of the time,
    with lots of strings manipulations and this is often expensive in
    terms of time. So, instead of doing silly things like:   
    
    float f;
    
    (...)
    
    // a time critical loop 
    for (...) {
      LS_setPath("$myscript:mysection/mykey");
      f = stof (LS_getValue(0));
      
      // do stuff with f
    }
    
    
    You should do that:
    
    float f;
    
    LS_setPath("$myscript:mysection/mykey");
    f = stof (LS_getValue(0));
    
    // a time critical loop 
    for (...) {
      
      // do stuff with f
    }


9. Licensing

LameScript 2.0 is licensed under the GNU Lesser General Public License v3.0. 
For more information about this license, please refer to:

http://www.gnu.org/licenses/

10. Package contents

The LameScript zip you downloaded contains the following files:

LameScript sources:
    lscript2.c
    lshash.c
    lscript.h
    lsdefs.h
    lshash.h
    
Small test program:
    lstest.c

MSVC 6.0 workspace and project files:
    lamescript.dsw
    lib/lscript.dsp
    test/lstest.dsp

binary library file:
    lscript2.lib

This help file:
    README.txt
    
License files in plain text format:
    gpl-2.0.txt
    lgpl-3.0.txt

11. Known bugs

None at this moment. But they're there, I can smell them. Oh yeah.

12. Feedback

If you have a bug report or improvement suggestion:

carloshp dot silva at gmail dot com

LameScript official site:

http://fragmachine.quakedev.com

13. History

2.0 (2007-08-04)
    Complete rewrite from scratch. Only the original LameScript
    file format (and name :D ) was preserved.
    
1.01 (2005-04-27)
    Initial public release, too messy, incomplete and ugly. R.I.P.
    