Table of Contents

ChangeLog

14 November 2014

  • amended the script such that it support keys and values within quotes.

About

A very easy way to write a configuration file is to use a key-value syntax where values are indexed by a key. A sample configuration file could look something like this:

#### START CONFIGURATION ####

# this is the goal you want to attain in L$
goal = 500

# this is the color of the overhead text
color = 0.5, 1, 1 # cyan

# this is a list of pay buttons displayed 
# when an avatar click the donation jar
buttons = 10 ,100, 500, 1000

# if any of your keys or values have spaces
# you can quote them like this:
name = "Evan Saskit"

# no worries, even if the values or keys
# contain additional quotes, the configuration
# reader will still work
greeting = "Hello "Evan!", how are you?"

##### END CONFIGURATON ######

In this configuration file, we set certain values to keys, such as goal = 500. Furthermore, everything after the hash sign # is considered a comment and should not be considered.

Expanding on the notecard reader, we can write the following code that will read such a file and store the key-value data in a list where values are preceded by keys:

Code: Configuration File Reader

///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2014 - License: CC BY 2.0      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////
 
// for notecard reading
integer line = 0;
 
// key-value data will be read into this list
list tuples = [];
 
default {
    state_entry() {
        if(llGetInventoryType("configuration") != INVENTORY_NOTECARD) {
            llSay(DEBUG_CHANNEL, "Sorry, could not find an inventory notecard.");
            return;
        }
        llGetNotecardLine("configuration", line);
    }
    dataserver(key id, string data) {
        if(data == EOF) state done; // invariant, length(tuples) % 2 == 0
        if(data == "") jump continue;
        integer i = llSubStringIndex(data, "#");
        if(i != -1) data = llDeleteSubString(data, i, -1);
        list o = llParseString2List(data, ["="], []);
        // get rid of starting and ending quotes
        string k = llDumpList2String(
            llParseString2List(
                llStringTrim(
                    llList2String(
                        o, 
                        0
                    ), 
                STRING_TRIM), 
            ["\""], []
        ), "\"");
        string v = llDumpList2String(
            llParseString2List(
                llStringTrim(
                    llList2String(
                        o, 
                        1
                    ), 
                STRING_TRIM), 
            ["\""], []
        ), "\"");
        if(k == "" || v == "") jump continue;
        tuples += k;
        tuples += v;
@continue;
        llGetNotecardLine("configuration", ++line);
    }
    changed(integer change) {
        if(change & CHANGED_INVENTORY) {
            llResetScript();
        }
    }
}
 
state done {
    state_entry() {
        llOwnerSay(llDumpList2String(tuples, ","));
    }
    changed(integer change) {
        if(change & CHANGED_INVENTORY) {
            llResetScript();
        }
    }
}

Extracting Tuples

After we have the data loaded into the tuples list, we can extract the keys and values using key-value encode and key-value get:

llOwnerSay(wasKeyValueGet("color", wasKeyValueEncode(tuples)));

The first call is to wasKeyValueEncode that flattens the list and the second call wasKeyValueGet then extracts the value of the key color. It is also possible to store the keys and values in a string type instead of the list type tuples, thereby eliminating one call to wasKeyValueEncode.

Code: Extracting Tuples

///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2014 - License: CC BY 2.0      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2013 Wizardry and Steamworks - License: CC BY 2.0    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueEncode(list data) {
    list k = llList2ListStrided(data, 0, -1, 2);
    list v = llList2ListStrided(llDeleteSubList(data, 0, 0), 0, -1, 2);
    data = [];
    do {
        data += llList2String(k, 0) + "=" + llList2String(v, 0);
        k = llDeleteSubList(k, 0, 0);
        v = llDeleteSubList(v, 0, 0);
    } while(llGetListLength(k) != 0);
    return llDumpList2String(data, "&");
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueGet(string k, string data) {
    if(llStringLength(data) == 0) return "";
    if(llStringLength(k) == 0) return "";
    list a = llParseStringKeepNulls(data, ["&", "="], []);
    integer i = llListFindList(llList2ListStrided(a, 0, -1, 2), [ k ]);
    if(i != -1) return llList2String(a, 2*i+1);
    return "";
}
 
// for notecard reading
integer line = 0;
 
// key-value data will be read into this list
list tuples = [];
 
default {
    state_entry() {
        if(llGetInventoryType("configuration") != INVENTORY_NOTECARD) {
            llSay(DEBUG_CHANNEL, "Sorry, could not find an inventory notecard.");
            return;
        }
        llGetNotecardLine("configuration", line);
    }
    dataserver(key id, string data) {
        if(data == EOF) state done; // invariant, length(tuples) % 2 == 0
        if(data == "") jump continue;
        integer i = llSubStringIndex(data, "#");
        if(i != -1) data = llDeleteSubString(data, i, -1);
        list o = llParseString2List(data, ["="], []);
        string k = llStringTrim(llList2String(o, 0), STRING_TRIM);
        string v = llStringTrim(llList2String(o, 1), STRING_TRIM);
        if(k == "" || v == "") jump continue;
        tuples += k;
        tuples += v;
@continue;
        llGetNotecardLine("configuration", ++line);
    }
}
 
state done {
    state_entry() {
        llOwnerSay(wasKeyValueGet("color", wasKeyValueEncode(tuples)));
    }
}