first big deposit of LSL source (for opensim, osgrid, second life, etc), including the
authorChris Koeritz <fred@gruntose.com>
Mon, 30 Apr 2012 14:01:10 +0000 (10:01 -0400)
committerChris Koeritz <fred@gruntose.com>
Mon, 30 Apr 2012 14:01:10 +0000 (10:01 -0400)
huffotronic updater and most of our free library scripts.

31 files changed:
huffware/huffotronic_updater_freebie_v5.3/FPS_color_changer_v2.4.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/FreeView_v1.8.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/JukeBox_v1.6.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/TL_Door_fredmod_v4.4.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/a_huffotronic_update_server_v23.2.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/auto-retire_v2.8.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/card_configurator_v8.1.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/clear_text_and_effects_v2.2.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/comfortable_sitting_v7.5.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/create_objects_v1.3.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/data_cow_v3.3.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/fade_opacity_v3.7.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/fredboxmux_no_rot_v3.0.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/fredboxmux_v3.0.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/huff-update_client_v20.1.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/hufflets_v6.3.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/huffware_id_registry_v3.2.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/inventory_exchanger_v3.7.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/license__free_in_osgrid_only_v1.8.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/menu_list_manager_v12.4.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/menutini_library_v5.9.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/non-script_giver_v2.8.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/noteworthy_library_v12.4.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/particle_projector_v3.1.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/party_culiar_v5.7.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/rotanium_rotato_v2.7.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/take_touches_v0.2.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/text_label_v3.9.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/texture_mover_v3.0.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/texture_shower_v2.7.txt [new file with mode: 0755]
huffware/huffotronic_updater_freebie_v5.3/zenmondos_mailbox_v0.4.txt [new file with mode: 0755]

diff --git a/huffware/huffotronic_updater_freebie_v5.3/FPS_color_changer_v2.4.txt b/huffware/huffotronic_updater_freebie_v5.3/FPS_color_changer_v2.4.txt
new file mode 100755 (executable)
index 0000000..b7be286
--- /dev/null
@@ -0,0 +1,156 @@
+
+// huffware script: FPS color changer, by fred huffhines
+//
+// a freebie script by eltee statosky supplied inspiration for this script.
+// we have re-used his color scheme, but rewritten this for our needs.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+integer SHOW_TITLE = FALSE;
+    // if this is true, a title is set above the object.
+
+//hmmm: this max fps value used to compute a percentage may be totally bogus.
+//  highest i've seen is 55 or so in opensim.  used to be highest in second life
+//  was 45 or something.  seems unknowable so far.
+float MAX_SIM_FPS = 60.0;
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.5.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+//log_it("invpo=" + (string)posn);
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+//log_it("pos=" + (string)space_v_posn);
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+//log_it("space v@" + (string)space_v_posn);
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+//log_it("indy=" + (string)space_v_posn);
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+//log_it("saw case of previously redundant item, aieee.  flattened: " + to_chop_up);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+default
+{
+    state_entry() { if (llSubStringIndex(llGetObjectName(),  "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry() {
+        auto_retire();  // make sure newest addition is only version of script.
+        llSetTimerEvent(4.0);
+    }
+    
+    timer() {
+        float fps = llGetRegionFPS();
+        vector sim_color;
+        if (fps > 50) sim_color = <0.5, 1.0, 0.5>;   
+        else if (fps > 40) sim_color = <0.75, 0.75, 0>;
+        else if (fps > 30) sim_color = <1.0, 0.5, 0.5>;
+        else sim_color = <0.5, 0.0, 0.0>;
+        llSetColor(sim_color, ALL_SIDES);
+        if (SHOW_TITLE) {
+            integer left_part = (integer)fps;  // left of decimal point.
+            integer right_part = (integer)(llRound((fps - (integer)fps) * 10.0));
+            float simplified_fps = (float)left_part + ((float)right_part / 10.0);
+            llSetText(llGetRegionName() + " "
+                + (string)left_part + "." + (string)right_part
+                + " FPS ("
+                + (string)llRound(simplified_fps / MAX_SIM_FPS * 100.0)
+                + "%)",
+                sim_color, 1.0);
+        } else {
+            llSetText("--", <0.0, 0.0, 0.0>, 0.0);
+        }
+    }
+}
diff --git a/huffware/huffotronic_updater_freebie_v5.3/FreeView_v1.8.txt b/huffware/huffotronic_updater_freebie_v5.3/FreeView_v1.8.txt
new file mode 100755 (executable)
index 0000000..bb809fd
--- /dev/null
@@ -0,0 +1,834 @@
+
+// huffware script: freeview modified by fred huffhines (see original license below).
+//
+// my changes are licensed this way:
+//   this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+//   do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+// fred's changes include:
+// + assorted tweaks that i have since forgotten the details about.
+// + set the freeview to startup as a picture viewer, since that's my most common usage.
+
+//////////////
+// original author info and licensing:
+//FreeView 1.2 WebGuide (revision 4) - By CrystalShard Foo
+//Multifunctional Picture viewer and Video control script with webguide support
+//This script is distributed for free and must stay that way. 
+//      *** If you wish to give/sell a product using this script,  ***
+//      ***     THEN THE SCRIPT MUST REMAIN FULL-PERM AND FREE.    ***
+//      ***   Failure to do so will REVOKE your right to use it!   ***
+//Help for using this script can be obtained at: http://www.slguide.com/help
+//Feel free to modify this script and post your improvement. Leave the credits intact but feel free to add your name at its bottom.
+//Whats new:
+//- Now using FULL_BRIGHT instead of PRIM_MATERIAL_LIGHT for the screen display
+//- Added an ownership-change code to handle cases where FreeView gets deeded to group post Video Init.
+//- Renamed WebGuide to TV-Guide to reflect what this thing does better.
+//- Added a 'Fix Scale' button to Picture mode to help against user texture-scale changes.
+//- Additional minor help-tips and code improvements
+//Enjoy!
+//////////////
+
+
+//Constants
+integer PICTURE_ROTATION_TIMER = 30;   //In whole seconds
+
+integer DISPLAY_ON_SIDE = ALL_SIDES; //Change this to change where the image will be displayed
+
+key VIDEO_DEFAULT = "71b8ff26-087d-5f44-285b-d38df2e11a81";  //Test pattern - Used as default video texture when one is missing in parcel media
+key BLANK = "5748decc-f629-461c-9a36-a35a221fe21f"; //Blank texture - Used when there are no textures to display in Picture mode
+string NOTECARD = "bookmarks";  //Used to host URL bookmarks for video streams
+
+integer VIDEO_BRIGHT = TRUE;    //FULL_BRIGHT status for Video
+integer PICTURE_BRIGHT = TRUE;  //FULL_BRIGHT status for Picture
+
+integer REMOTE_CHANNEL = 9238742;
+
+integer EXTERNAL_TOUCH_CHANNEL = 1327;
+    // used by other prims to tell the viewer prim that the avatar has clicked on them.
+
+integer mode = 0;           //Freeview mode.
+                            //Mode 0 - Power off
+                            //Mode 1 - Picture viewer
+                            //Mode 2 - Video
+
+integer listenHandle = -1;      //Dialog menu listen handler
+integer listenUrl = -1;         //listen handler for channel 1 for when a URL is being added
+integer listenTimer = -1;       //Timer variable for removing all listeners after 2 minutes of listener inactivity
+integer listenRemote = -1;      //listen handler for the remote during initial setup
+integer encryption = 0;
+integer numberofnotecardlines = 0;  //Stores the current number of detected notecard lines.
+integer notecardline = 0;       //Current notecard line
+
+integer loop_image = FALSE;     //Are we looping pictures with a timer? (picture mode)
+integer current_texture = 0;    //Current texture number in inventory being displayed (picture mode)
+integer chan;                   //llDialog listen channel
+integer notecardcheck = 0;
+key video_texture;              //Currently used video display texture for parcel media stream
+
+string moviename;
+string tempmoviename;
+key notecardkey = NULL_KEY;
+key tempuser;                   //Temp key storge variable
+string tempurl;                 //Temp string storge variable
+
+integer isGroup = TRUE;
+key groupcheck = NULL_KEY;
+key last_owner;
+key XML_channel;
+
+pictures()      //Change mode to Picture Viewer
+{
+    //Initilize variables
+    
+    //Change prim to Light material while coloring face 0 black to prevent light-lag generation.
+    llSetPrimitiveParams([PRIM_BUMP_SHINY, DISPLAY_ON_SIDE, PRIM_SHINY_NONE, PRIM_BUMP_NONE, PRIM_COLOR, DISPLAY_ON_SIDE, <1,1,1>, 1.0, PRIM_MATERIAL, PRIM_MATERIAL_PLASTIC, PRIM_FULLBRIGHT, DISPLAY_ON_SIDE, PICTURE_BRIGHT]);
+
+    integer check = llGetInventoryNumber(INVENTORY_TEXTURE);
+     
+    if(check == 0)
+    {
+        report("No pictures found.");
+        llSetTexture(BLANK,DISPLAY_ON_SIDE);
+        return;
+    }
+    else    
+        if(current_texture > check)
+            //Set to first texture if available
+            current_texture = 0;
+            
+    display_texture(current_texture);
+}
+
+video()         //Change mode to Video
+{
+    //Change prim to Light material while coloring face 0 black to prevent light-lag generation.
+    llSetPrimitiveParams([PRIM_BUMP_SHINY, DISPLAY_ON_SIDE, PRIM_SHINY_NONE, PRIM_BUMP_NONE, PRIM_COLOR, DISPLAY_ON_SIDE, <1,1,1>, 1.0, PRIM_MATERIAL, PRIM_MATERIAL_PLASTIC, PRIM_FULLBRIGHT, DISPLAY_ON_SIDE, VIDEO_BRIGHT, PRIM_TEXTURE, DISPLAY_ON_SIDE, "62dc73ca-265f-7ca0-0453-e2a6aa60bb6f", llGetTextureScale(DISPLAY_ON_SIDE), llGetTextureOffset(DISPLAY_ON_SIDE), llGetTextureRot(DISPLAY_ON_SIDE)]);
+    
+    report("Video mode"+moviename+": Stopped");
+    if(finditem(NOTECARD) != -1)
+        tempuser = llGetNumberOfNotecardLines(NOTECARD);
+    video_texture = llList2Key(llParcelMediaQuery([PARCEL_MEDIA_COMMAND_TEXTURE]),0);
+    if(video_texture == NULL_KEY)
+    {
+        video_texture = VIDEO_DEFAULT;
+        llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_TEXTURE,VIDEO_DEFAULT]);
+        llSay(0,"No parcel media texture found. Setting texture to default: "+(string)VIDEO_DEFAULT);
+        if(llGetLandOwnerAt(llGetPos()) != llGetOwner())
+            llSay(0,"Error: Cannot modify parcel media settings. "+llGetObjectName()+" is not owned by parcel owner.");
+    }
+    
+    llSetTexture(video_texture,DISPLAY_ON_SIDE);
+}
+
+off()
+{
+    report("Click to power on.");
+    llSetPrimitiveParams([PRIM_BUMP_SHINY, DISPLAY_ON_SIDE, PRIM_SHINY_LOW, PRIM_BUMP_NONE, PRIM_COLOR, DISPLAY_ON_SIDE, <0.1,0.1,0.1>, 1.0,PRIM_MATERIAL, PRIM_MATERIAL_PLASTIC, PRIM_FULLBRIGHT, DISPLAY_ON_SIDE, FALSE, PRIM_TEXTURE, DISPLAY_ON_SIDE, BLANK, llGetTextureScale(DISPLAY_ON_SIDE), llGetTextureOffset(DISPLAY_ON_SIDE), llGetTextureRot(DISPLAY_ON_SIDE)]);
+}
+
+integer finditem(string name)   //Finds and returns an item's inventory number
+{
+    integer i;
+    for(i=0;i<llGetInventoryNumber(INVENTORY_NOTECARD);i++)
+        if(llGetInventoryName(INVENTORY_NOTECARD,i) == NOTECARD)
+            return i;
+    return -1;
+}
+
+seturl(string url, key id)  //Set parcel media URL
+{
+    if(mode != 2)
+    {
+        video();
+        mode = 2;
+    }
+    moviename = tempmoviename;
+    if(moviename)
+        moviename = " ["+moviename+"]";
+    tempmoviename = "";
+    string oldurl = llList2String(llParcelMediaQuery([PARCEL_MEDIA_COMMAND_URL]),0);
+    if(oldurl != "")
+        llOwnerSay("Setting new media URL. The old URL was: "+oldurl);
+
+    llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_URL,url]);
+    if(id!=NULL_KEY)
+        menu(id);
+    else
+    {
+        report("Video mode"+moviename+": Playing");
+        llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_PLAY]);
+    }
+       
+    if(isGroup)
+        llSay(0,"New media URL set.");
+    else
+        llOwnerSay("New media URL set: "+url);
+}
+
+string mediatype(string ext)    //Returns a string stating the filetype of a file based on file extension
+{
+    ext = llToLower(ext);
+    if(ext == "swf")
+        return "Flash";
+    if(ext == "mov" || ext == "avi" || ext == "mpg" || ext == "mpeg" || ext == "smil")
+        return "Video";
+    if(ext == "jpg" || ext == "mpeg" || ext == "gif" || ext == "png" || ext == "pict" || ext == "tga" || ext == "tiff" || ext == "sgi" || ext == "bmp")
+        return "Image";
+    if(ext == "txt")
+        return "Text";
+    if(ext == "mp3" || ext == "wav")
+        return "Audio";
+    return "Unknown";
+}
+
+browse(key id)      //Image browser function for picture viewer mode
+{
+    integer check = llGetInventoryNumber(INVENTORY_TEXTURE);
+    string header;
+    if(check > 0)
+        header = "("+(string)(current_texture+1)+"/"+(string)check+") "+llGetInventoryName(INVENTORY_TEXTURE,current_texture);
+    else
+        header = "No pictures found.";
+    llDialog(id,"** Monitor Control **\n Picture Viewer mode\n- Image browser\n- "+header,["Back","Next","Menu"],chan);
+    extendtimer();
+}
+
+report(string str)
+{
+    llSetObjectDesc(str);
+}
+
+extendtimer()       //Add another 2 minute to the Listen Removal timer (use when a Listen event is triggered)
+{
+    if(listenHandle == -1)
+        listenHandle = llListen(chan,"","","");
+    listenTimer = (integer)llGetTime() + 120;
+    if(loop_image == FALSE)
+        llSetTimerEvent(45);
+}
+
+config(key id)      //Configuration menu
+{
+    extendtimer();
+    llDialog(id,"Current media URL:\n"+llList2String(llParcelMediaQuery([PARCEL_MEDIA_COMMAND_URL]),0)+"\nTip: If the picture is abit off, try 'Align ON'",["Set URL","Align ON","Align OFF","Menu","Set Remote"],chan);
+}
+
+tell_remote(string str)
+{
+    llShout(REMOTE_CHANNEL,llXorBase64Strings(llStringToBase64((string)encryption + str), llStringToBase64((string)encryption)));
+}
+
+menu(key id)        //Dialog menus for all 3 modes
+{
+    list buttons = [];
+    string title = "** Monitor control **";
+    
+    extendtimer();
+
+    if(mode != 0)
+    {
+        if(mode == 1)       //Pictures menu
+        {
+            title+="\n  Picture Viewer mode";
+            buttons+=["Browse"];
+            if(loop_image == FALSE)
+                buttons+=["Loop"];
+            else
+                buttons+=["Unloop"];
+            buttons+=["Video","Power off","Help","Fix scale"];
+        }
+        else                //Video menu
+        {
+            title+="\n Video display mode\n"+moviename+"\nTip:\nClick 'TV Guide' to view the Online bookmarks.";
+            buttons+=["Pictures","Configure","Power off","Loop","Unload","Help","Play","Stop","Pause","TV Guide","Bookmarks","Set URL"];
+        }
+    }
+    else
+        buttons += ["Pictures","Video","Help"];
+    
+    llDialog(id,title,buttons,chan);
+}
+
+display_texture(integer check)  //Display texture and set name in description (picture mode)
+{                               //"Check" holds the number of textures in contents. The function uses "current_texture" to display.
+    string name = llGetInventoryName(INVENTORY_TEXTURE,current_texture);
+    llSetTexture(name,DISPLAY_ON_SIDE);
+    report("Showing picture: "+name+" ("+(string)(current_texture+1)+"/"+(string)check+")");
+}
+    
+
+next()  //Change to next texture (picture mode)
+{       //This function is used twice - by the menu and timer. Therefor, it is a dedicated function.
+    current_texture++;
+    integer check = llGetInventoryNumber(INVENTORY_TEXTURE);
+    if(check == 0)
+    {
+        llSetTexture(BLANK,DISPLAY_ON_SIDE);
+        current_texture = 0;
+        report("No pictures found.");
+        return;
+    }
+    if(check == current_texture)
+        current_texture = 0;
+    
+    display_texture(check);
+    return;
+}
+
+//////////////
+// from hufflets...
+
+// returns the index of the first occurrence of "pattern" inside
+// the "full_string".  if it is not found, then a negative number is returned.
+integer find_substring(string full_string, string pattern)
+{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); }
+
+// returns TRUE if the "prefix" string is the first part of "compare_with".
+integer is_prefix(string compare_with, string prefix)
+{ return find_substring(compare_with, prefix) == 0; }
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.8.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+default {
+    state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry()
+    {
+        auto_retire();
+        llListen(EXTERNAL_TOUCH_CHANNEL, "", NULL_KEY, "");
+            // we listen on our touch channel in all cases and in all states.  this allows us
+            // to always pass along the user's touch from other prims to run the menus.
+        chan = (integer)llFrand(1000) + 1000;   //Pick a random listen channel for the listener
+        if(PICTURE_ROTATION_TIMER <= 0)         //Ensure the value is no less or equal 0
+            PICTURE_ROTATION_TIMER = 1;
+        llListenRemove(listenHandle);
+        listenHandle = -1;
+        last_owner = llGetOwner();
+        groupcheck = llRequestAgentData(llGetOwner(),DATA_NAME);
+        off();
+        llOpenRemoteDataChannel();
+        // fred's changes to start up in picture viewing looper.
+        mode = 1;  // picture viewing.
+        pictures();  // show the pictures.
+        loop_image = TRUE;
+        llSetTimerEvent(PICTURE_ROTATION_TIMER);  // keep showing new pics.
+    }
+    
+    on_rez(integer i)
+    {
+        llSay(0,"Welcome to FreeView - your free, open-source television!");
+        llResetScript();
+    }
+
+    touch_start(integer total_number)
+    {
+        //-------------------------------------------------------------------------------
+        //Listen only to owner or group member. Edit this code to change access controls.
+        if(llDetectedKey(0) != llGetOwner() && llDetectedGroup(0) == FALSE)
+            return;
+        //-------------------------------------------------------------------------------
+
+        menu(llDetectedKey(0));
+    }
+    
+    changed(integer change)
+    {
+        if(change == CHANGED_INVENTORY) //If inventory change
+        {
+            if(mode == 1)   //If picture mode
+            {
+                integer check = llGetInventoryNumber(INVENTORY_TEXTURE);
+                if(check != 0)
+                {
+                    current_texture = 0;
+                    display_texture(check);
+                }
+                else
+                {
+                    llSetTexture(BLANK,DISPLAY_ON_SIDE);
+                    report("No pictures found.");
+                }
+            }
+            else
+                if(mode == 2)   //If video mode
+                    if(finditem(NOTECARD) != -1)    //And bookmarks notecard present
+                        if(notecardkey != llGetInventoryKey(NOTECARD))
+                            tempuser = llGetNumberOfNotecardLines(NOTECARD);    //Reload number of lines
+        }
+        else if(change == CHANGED_OWNER)
+        {
+            isGroup = TRUE;
+            last_owner = llGetOwner();
+            groupcheck = llRequestAgentData(llGetOwner(),DATA_NAME);
+            
+            if(mode == 2)
+            {
+                llSay(0,"Detected change in ownership. Attempting to obtain current parcel media texture...");
+                video();
+            }
+        }
+    }
+    
+    listen(integer channel, string name, key id, string message)
+    {
+        if ( (channel == EXTERNAL_TOUCH_CHANNEL) && is_prefix(message, "touched")) {
+            // pretend we got touched by the av.
+            message = llDeleteSubString(message, 0, 6);
+            menu((key)message);
+            return;
+        }        
+        if(message == "Pictures")
+        {
+            if(mode == 2)
+                llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_STOP]);
+            pictures();
+            mode = 1;
+            menu(id);
+            return;
+        }
+        if(message == "Video")
+        {
+            video();
+            mode = 2;
+            menu(id);
+            return;
+        }
+        if(message == "Power off")
+        {
+            if(mode == 2)
+                llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_UNLOAD]);
+            off();
+            mode = 0;
+            return;
+        }
+        if(message == "Help")
+        {
+            llSay(0,"Help documentation is available at: http://www.slguide.com/help");
+            if(isGroup)
+            {
+                if(id == NULL_KEY)
+                {
+                    llSay(0,"FreeView cannot load help pages while set to group without the remote.");
+                    llSay(0,"For further assistance, please consult: http://slguide.com/help");
+                }
+                else
+                    tell_remote("HELP"+(string)id+(string)XML_channel);
+            }
+            else
+                llLoadURL(id,"Help pages for FreeView","http://www.slguide.com?c="+(string)XML_channel+"&help=1");
+        }
+        if(mode == 1)
+        {
+            if(message == "Browse")
+            {
+                loop_image = FALSE;
+                browse(id);
+                return;
+            }
+            if(message == "Next")
+            {
+                extendtimer();
+                next();
+                browse(id);
+            }
+            if(message == "Back")
+            {
+                extendtimer();
+                current_texture--;
+                integer check = llGetInventoryNumber(INVENTORY_TEXTURE);
+                if(check == 0)
+                {
+                    llSetTexture(BLANK,DISPLAY_ON_SIDE);
+                    current_texture = 0;
+                    report("No pictures found.");
+                    return;
+                }
+                if(current_texture < 0)
+                    current_texture = check - 1;
+                
+                display_texture(check);
+                
+                browse(id);
+                return;
+            }
+            if(message == "Menu")
+            {
+                menu(id);
+                return;
+            }
+            if(message == "Loop")
+            {
+                llSetTimerEvent(PICTURE_ROTATION_TIMER);
+                loop_image = TRUE;
+                llOwnerSay("Picture will change every "+(string)PICTURE_ROTATION_TIMER+" seconds.");
+                return;
+            }
+            if(message == "Unloop")
+            {
+                loop_image = FALSE;
+                llOwnerSay("Picture loop disabled.");
+                return;
+            }
+            if(message == "Fix scale")
+            {
+                llSay(0,"Setting display texture to 1,1 repeats and 0,0 offset.");
+                llScaleTexture(1, 1, DISPLAY_ON_SIDE);
+                llOffsetTexture(0, 0, DISPLAY_ON_SIDE);
+                return;
+            }
+        }
+        if(mode == 2)
+        {
+            if(channel == REMOTE_CHANNEL)
+            {
+                if(encryption == 0)
+                    encryption = (integer)message;
+                llListenRemove(listenRemote);
+                listenRemote = -1;
+                llSay(0,"Remote configured ("+(string)id+")");
+            }
+                
+            if(message == "TV Guide")
+            {
+                if(isGroup)
+                {
+                    if(!encryption)
+                    {
+                        llSay(0,"** Error - This FreeView object has been deeded to group. You must use a Remote control to open the TV Guide.");
+                        llSay(0,"You can set up the remote control from the Video -> Configuration menu. Please refer to the notecard for further assistance.");
+                        return;
+                    }
+                    tell_remote((string)id+(string)XML_channel+(string)llGetOwner());
+                }
+                else
+                    llLoadURL(id, "Come to the Guide to Start Your Viewer Playing!", "http://slguide.com/index.php?v=" + (string)llGetKey() + "&c=" + (string)XML_channel + "&o=" + (string)llGetOwner() + "&");
+                return;
+            }
+
+            string header = "Video mode"+moviename+": ";
+            
+            if(message == "<< Prev")
+            {
+                notecardline--;
+                if(notecardline < 0)
+                    notecardline = numberofnotecardlines - 1;
+                tempuser = id;
+                llGetNotecardLine(NOTECARD,notecardline);
+                return;
+            }
+            if(message == "Next >>")
+            {
+                notecardline++;
+                if(notecardline >= numberofnotecardlines)
+                    notecardline = 0;
+                tempuser = id;
+                llGetNotecardLine(NOTECARD,notecardline);
+                return;
+            }
+            if(message == "Use")
+            {
+                if(tempurl == "** No URL specified! **")
+                    tempurl = "";
+                seturl(tempurl,id);
+                return;
+            }
+                    
+            if(message == "Menu")
+            {
+                menu(id);
+                return;
+            }
+            if(message == "Configure")
+            {
+                config(id);
+                return;
+            }
+            if(message == "Bookmarks")
+            {
+                if(notecardcheck != -1)
+                {
+                    llDialog(id,"Error: No valid bookmark data found in notecard '"+NOTECARD+"'.",["Menu"],chan);
+                    return;
+                }
+                if(finditem(NOTECARD) != -1)                
+                {
+                    tempuser = id;
+                    if(numberofnotecardlines < notecardline)
+                        notecardline = 0;
+                    llGetNotecardLine(NOTECARD,notecardline);
+                }
+                else
+                    llDialog(id,"Error: No notecard named "+NOTECARD+" found in contents.",["Menu"],chan);
+                return;
+            }
+            
+            if(llGetLandOwnerAt(llGetPos()) != llGetOwner())    //If we do not have permissions to actually do the following functions
+            {
+                llSay(0,"Error: Cannot modify parcel media settings. "+llGetObjectName()+" is not owned by parcel owner.");
+                menu(id);
+                return; //Abort
+            }
+            
+            if(listenUrl != -1 && channel == 1) //Incoming data from "Set URL" command (user spoke on channel 1)
+            {
+                llListenRemove(listenUrl);
+                listenUrl = -1;
+                tempmoviename = "";
+                seturl(message,id);
+            }
+            if(message == "Play")
+            {
+                report(header+"Playing");
+                llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_PLAY]);
+                return;
+            }
+            if(message == "Stop")
+            {
+                report(header+"Stopped");
+                llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_STOP]);
+                return;
+            }
+            if(message == "Pause")
+            {
+                report(header+"Paused");
+                llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_PAUSE]);
+                return;
+            }
+            if(message == "Unload")
+            {
+                report(header+"Stopped");
+                llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_UNLOAD]);
+                return;
+            }
+            if(message == "Loop")
+            {
+                llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_LOOP]);
+                return;
+            }
+            //URL , Auto-Scale, 
+            if(message == "Set URL")
+            {
+                report(header+"Stopped");
+                listenUrl = llListen(1,"",id,"");
+                llDialog(id,"Please type the URL of your choice with /1 in thebegining. For example, /1 www.google.com",["Ok"],938);
+                return;
+            }
+            if(message == "Align ON")
+            {
+                report(header+"Stopped");
+                llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_AUTO_ALIGN,TRUE]);
+                menu(id);
+                return;
+            }
+            if(message == "Align OFF")
+            {
+                report(header+"Stopped");
+                llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_AUTO_ALIGN,FALSE]);
+                menu(id);
+                return;
+            }
+            if(message == "Set Remote")
+            {
+                llSay(0,"Configuring remote...");
+                encryption = 0;
+                llListenRemove(listenRemote);
+                listenRemote = llListen(REMOTE_CHANNEL,"","","");
+                llSay(REMOTE_CHANNEL,"SETUP");
+            }
+        }
+    }
+    
+    dataserver(key queryid, string data)
+    {
+        if(queryid == groupcheck)       //Test if object is deeded to group
+        {
+            groupcheck = NULL_KEY;
+            isGroup = FALSE;
+            return;
+        }
+        
+        if(queryid == tempuser) //If just checking number of notecard lines
+        {
+            numberofnotecardlines = (integer)data;
+            notecardkey = llGetInventoryKey(NOTECARD);
+            notecardcheck = 0;
+            llGetNotecardLine(NOTECARD,notecardcheck);
+            return;
+        }
+        if(notecardcheck != -1)
+        {
+            if(data != EOF)
+            {
+                if(data == "")
+                {
+                    notecardcheck++;
+                    llGetNotecardLine(NOTECARD,notecardcheck);
+                }
+                else
+                {
+                    notecardcheck = -1;
+                    return;
+                }
+            }
+            else
+                return;
+        }
+
+        if(data == "" && notecardline < numberofnotecardlines)    //If user just pressed "enter" in bookmarks, skip
+        {
+            notecardline++;
+            llGetNotecardLine(NOTECARD,notecardline);
+            return;
+        }
+        
+        if(data == EOF)
+        {
+            notecardline = 0;
+            llGetNotecardLine(NOTECARD,notecardline);
+            return;
+        }
+        list parsed = llParseString2List(data,["|","| "," |"," | "],[]);    //Ensure no blank spaces before "http://".
+        string name = llList2String(parsed,0);
+        tempurl = llList2String(parsed,1);
+        if(tempurl == "")
+            tempurl = "** No URL specified! **";
+            
+        tempmoviename = name;
+                
+        llDialog(tempuser,"Bookmarks notecard ("+(string)(notecardline+1)+"/"+(string)numberofnotecardlines+")\n"+name+" ("+mediatype(llList2String(llParseString2List(tempurl,["."],[]),-1))+")\n"+tempurl,["<< Prev","Use","Next >>","Menu"],chan);
+    }
+    
+    remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval)
+    {
+        if (type == REMOTE_DATA_CHANNEL)
+        {
+            XML_channel = channel;
+        } 
+        else if(type == REMOTE_DATA_REQUEST)
+        {
+            list media_info = llParseString2List(sval, ["|"], []);
+            tempmoviename = llList2String(media_info,0);
+            seturl(llList2String(media_info,1),NULL_KEY);
+            llRemoteDataReply(channel, message_id, sval, 1);
+        }
+    }
+    
+    timer()
+    {
+        if(llGetTime() > listenTimer)       //If listener time expired...
+        {
+            llListenRemove(listenHandle);   //Remove listeneres.
+            llListenRemove(listenUrl);
+            llListenRemove(listenRemote);
+            listenHandle = -1;
+            listenUrl = -1;
+            listenRemote = -1;
+            listenTimer = -1;
+            if(loop_image == FALSE || mode != 1) //If we're not looping pictures or are in picture mode at all
+                llSetTimerEvent(0.0);   //Remove timer
+        }
+        
+        if(loop_image == TRUE && mode == 1) //If we're looping pictures and and we're in picture mode...
+            next(); //Next picture
+    }
+}
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/JukeBox_v1.6.txt b/huffware/huffotronic_updater_freebie_v5.3/JukeBox_v1.6.txt
new file mode 100755 (executable)
index 0000000..21b4842
--- /dev/null
@@ -0,0 +1,367 @@
+
+// jukebox script.  unknown provenance.
+// modified for use in opensim by fred huffhines.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+float INTERVAL = 9.00;
+
+integer IS_OPENSIM = TRUE;
+    // this must be set to true to turn off the sound queuing.  opensim does not currently implement it.
+
+float SOUND_OFFSET_PERIOD = 1.1;  // number of seconds to start the sound in advance.
+
+integer LISTEN_CHAN = 2000;
+integer SEND_CHAN = 2001;
+float VOLUME = 1.0;
+
+integer g_iSound;
+integer total_tracks;
+integer g_iListenCtrl = -1;
+integer g_iPlaying;
+integer g_iLinked;
+integer g_iStop;
+integer g_iPod;
+string g_sLink;
+
+// DEBUG
+integer g_iWasLinked;
+integer g_iFinished;
+
+list song_names;
+    // hold onto the names of all the songs so we don't need to keep
+    // going back to inventory.
+
+Initialize()
+{
+    llSetText(llGetObjectName(), <0, 100, 100>, 10);
+    
+    // reset listeners
+    if ( g_iListenCtrl != -1 )
+    {
+        llListenRemove(g_iListenCtrl);
+    }
+    g_iListenCtrl = llListen(LISTEN_CHAN,"","","");
+    g_iPlaying = 0;
+    g_iLinked = 0;
+
+    integer i;
+    total_tracks = llGetInventoryNumber(INVENTORY_SOUND);
+    for ( i = 0; i < total_tracks; i++ ) {
+        string name = llGetInventoryName(INVENTORY_SOUND, i);
+        song_names += [ name ];
+        llPreloadSound(name);
+    }
+//llOwnerSay("got song names list: " + (string)song_names);
+}
+
+
+
+PlaySong()
+{
+    integer i;
+
+    g_iPlaying = 1;    
+    llWhisper(0, "Playing...");
+    if (!IS_OPENSIM) {
+        llSetSoundQueueing(TRUE);
+    }
+    llAdjustSoundVolume(1.0);
+    llPlaySound(llList2String(song_names, 0), VOLUME);
+    llPreloadSound(llList2String(song_names, 1));
+    // set the timer interval for just before the track chunk ends.
+    llSetTimerEvent(INTERVAL - SOUND_OFFSET_PERIOD);
+    g_iSound = 1;
+}
+
+
+StopSong()
+{
+    g_iPlaying = 0;
+    llWhisper(0, "Stopping...");
+    llStopSound();
+    llAdjustSoundVolume(0);
+    llSetTimerEvent(0.0);
+}
+
+
+integer CheckLink()
+{
+    string sLink;
+    
+    sLink = llGetLinkName(1);
+    g_sLink = sLink;
+    if ( llGetSubString(sLink,0,6) == "Jukebox" )
+    {
+        return TRUE;
+    }
+    return FALSE;
+}
+
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.8.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+default {
+    state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry()
+    {
+        auto_retire();
+        Initialize();
+    }
+    
+    on_rez(integer start_param)
+    {
+        Initialize();
+        if ( start_param )
+        {
+            g_iPod = start_param - 1;
+            if ( g_iPod )
+            {
+                llRequestPermissions(llGetOwner(),PERMISSION_ATTACH);
+            } else {
+                // Tell the controller what the CD key is so it can link
+                llWhisper(SEND_CHAN,"LINK " + (string)llGetKey());
+            }
+        }
+    }
+    
+    changed(integer change)
+    {
+        if ( change == CHANGED_LINK )
+        {
+            if ( llGetLinkNumber() == 0 )
+            {
+                StopSong();
+                llDie();
+            } else {
+                if ( g_iStop )
+                {
+                    llMessageLinked(1,llGetLinkNumber(),"UNLINK","");
+                } else {
+                    llMessageLinked(1,llGetLinkNumber(),"LINKID","");
+                    g_iWasLinked = 1;
+                }
+            }
+        }
+    }
+    
+    attach(key id)
+    {
+        if ( id == NULL_KEY )
+        {
+            llDie();
+        } else {
+            PlaySong();
+        }
+    }
+    
+    run_time_permissions(integer perm)
+    {
+        if ( perm == PERMISSION_ATTACH )
+        {
+            llAttachToAvatar(ATTACH_LSHOULDER);
+            llSetTexture("clear",ALL_SIDES);
+        }
+    }
+    
+    touch_start(integer total_number)
+    {
+        integer i;
+        
+        for ( i = 0; i < total_number; i++ )
+        {
+            if ( llDetectedKey(i) == llGetOwner() )
+            {
+//llWhisper(0,"DEBUG: g_iPlaying=" + (string)g_iPlaying);
+//llWhisper(0,"DEBUG: Link=" + (string)llGetLinkNumber());
+//llWhisper(0,"DEBUG: g_iWasLinked=" + (string)g_iWasLinked);
+//llWhisper(0,"DEBUG: g_iFinished=" + (string)g_iFinished);
+                if ( g_iPlaying ) StopSong();
+                else PlaySong();
+            }
+        }
+    }
+    
+    listen(integer channel, string name, key id, string message)
+    {
+        if ( message == "RESET" )
+        {
+            if ( llGetLinkNumber() == 0 )
+            {
+                llDie();
+            } else {
+                llMessageLinked(1,llGetLinkNumber(),"UNLINK","");
+            }
+        }
+        
+        if ( message == "STOP" )
+        {
+            if ( g_iPod )
+            {
+                StopSong();
+                llDetachFromAvatar();
+            }
+        }
+    }
+
+    link_message(integer sender_num, integer num, string str, key id)
+    {
+        if ( str == "PLAY" )
+        {
+            if ( !g_iPlaying )
+            {
+                PlaySong();
+            }
+            return;
+        }
+        
+        if ( str == "STOP" )
+        {
+            g_iStop = 1;
+            StopSong();
+            llMessageLinked(1,llGetLinkNumber(),"UNLINK","");
+        }
+        
+        if ( str == "VOLUME" )
+        {
+            VOLUME = (float)num / 10.0;
+            llAdjustSoundVolume(VOLUME);
+        }
+    }
+    
+    timer()
+    {
+        if ( g_iPlaying )
+        {
+//llWhisper(0, "playing song #" + (string)(g_iSound+1) + ": " +  llList2String(song_names, g_iSound));
+            llPlaySound(llList2String(song_names, g_iSound),VOLUME);
+            if ( g_iSound < (total_tracks - 1) )
+            {
+                llPreloadSound(llList2String(song_names, g_iSound + 1));
+            }
+            g_iSound++;
+            if ( g_iSound >= total_tracks )
+            {
+                llSetTimerEvent(INTERVAL + 5.0);
+                g_iPlaying = 0;
+            } else {
+                llSetTimerEvent(INTERVAL - SOUND_OFFSET_PERIOD);
+            }                
+        } else {
+            if ( llGetLinkNumber() != 0 )
+            {
+                llSetTimerEvent(0.0);
+                if ( g_iPod )
+                {
+                    llWhisper(SEND_CHAN,"FINISH");
+                    llDetachFromAvatar();
+                } else {
+                    llMessageLinked(1,0,"FINISH","");
+                    g_iFinished = 1;
+                }
+            }
+        }
+    }
+}
diff --git a/huffware/huffotronic_updater_freebie_v5.3/TL_Door_fredmod_v4.4.txt b/huffware/huffotronic_updater_freebie_v5.3/TL_Door_fredmod_v4.4.txt
new file mode 100755 (executable)
index 0000000..0255c6c
--- /dev/null
@@ -0,0 +1,697 @@
+
+// fred huffhines mods:
+//
+// took away the universal skeleton key that was lodged in this script.
+//
+// stopped considering door's scale; this is not usually needed, plus we were blowing
+//      past the SL limit on object names.
+//
+// moved to only storing a couple digits after the decimal point; this is another
+//      crucial thing to limit the size of the object name.
+//
+// added a "toggle" command that behaves like touch, in that the door will be opened
+//      or closed based on current state.
+//
+// made the sensor distance required before the door will listen to someone into a
+//      configurable parameter, instead of the woefully tiny, hard-coded 5 meters.
+//
+// added debugging flag and switchable logging for debugging mode.
+//
+// *original license and author info below...*
+//
+// plus, timeless prototype said this about using the script in osgrid and elsewhere:
+//      "hi, thanks for asking, yes you may use the door script in other grids."
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+// more fred huffhines mods: (circa march 2012)
+//   added PASS_COMMANDS flag, which can be used to pass along any commands we
+// hear to anyone else on our same channel within the object.
+//   added stifling of commands heard so they don't get re-sent, causing endless
+// loops of door openings.
+
+
+//------------------------------------------------------
+// Timeless Linked Door Script by Timeless Prototype
+//------------------------------------------------------
+// The latest version of this script can always be found
+// in the Library section of the wiki:
+// http://www.secondlife.com/badgeo/
+// This script is free to use, but whereever it is used
+// the SCRIPT's permissions MUST be set to:
+// [x] Next owner can modify
+// [x] Next owner can copy
+// [x] Next owner can transfer
+// [x] Allow anyone to copy
+// [x] Share with group
+
+//------------------------------------------------------
+// USAGE INSTRUCTIONS FOR EVERYDAY USE:
+//------------------------------------------------------
+// Say the following commands on channel 0:
+// 'unlock'     - Unlocks all doors in range.
+// 'lock'       - Locks all doors in range and allows
+//                only the permitted users to open it.
+// To open the door, either Touch it, Walk into it or
+// say 'open' or say 'close'.
+
+//------------------------------------------------------
+// USAGE INSTRUCTIONS FOR BUILDERS:
+//------------------------------------------------------
+// 1. Copy and paste this script into the door prim and
+//    change the settings (see further down).
+// 2. The door prim must be linked to at least one other
+//    prim (could be linked to the house for example).
+// 3. The door prim MUST NOT be the root prim.
+// 4. Use Edit Linked Parts to move, rotate and size the
+//    door prim for the closed state.
+// 5. When ready, stand close to the door and say
+//    '/door closed' (this records the closed door
+//    position, rotation and size to the object's
+//    name and description).
+// 6. Use the Edit Linked parts to move, rotate and size
+//    the door prim for the opened state.
+// 7. When ready, stand close to the door and say
+//    '/door opened' (this records the opened door
+//    position, rotation and size).
+// 8. Once recorded it will not accept these commands
+//    again. If you do need to redo the settings then
+//    delete the Name and Description of the door prim
+//    (these are where the position information is
+//    stored), and then follow the steps above again.
+//    Note: deleting the object name won't save, so set
+//    the object name to 'Object' to reset the object
+//    name.
+
+//------------------------------------------------------
+// Change these settings to suit your needs.
+//------------------------------------------------------
+// To mute any/all of the sounds set the sound string(s)
+// to "" (empty string).
+// To get the UUID of a sound, right click on the sound
+// in your inventory and choose "Copy Asset UUID", then
+// paste the UUID in here.
+string      doorOpenSound       = "Door open";
+string      doorCloseSound      = "Door close";
+string      confirmedSound      = "69743cb2-e509-ed4d-4e52-e697dc13d7ac";
+string      accessDeniedSound   = "58da0f9f-42e5-8a8f-ee51-4fac6c247c98";
+string      doorBellSound       = ""; // Setting to empty stops door announcements too.
+float       autoCloseTime       = 120.0; // 0 seconds to disable auto close.
+integer     allowGroupToo       = TRUE; // Set to FALSE to disallow same group access to door.
+list        allowedAgentUUIDs   = []; // Comma-separated, quoted list of avatar UUIDs who are allowed access to this door.
+// was allowing this sneaky guy: "8efecbac-35de-4f40-89c1-2c772b83cafa"
+integer     listenChannel       = 10106;
+float       RESPONSE_DISTANCE   = 120.0;  // how far to allow a command from users with permission.
+integer     DEBUGGING           = FALSE;
+integer     PASS_COMMANDS       = TRUE;
+    // if true, then we will order other doors to open when we do.
+    // we use the listenChannel as the id to pass commands on, so that only the doors listening to
+    // the same place will hear our commands.
+integer     command_is_a_response  = FALSE;
+    // if this is true, then the door open and close must not re-echo their actions.
+
+//------------------------------------------------------
+// Leave the rest of the settings alone, these are
+// handled by the script itself.
+//------------------------------------------------------
+integer     isLocked            = FALSE; // Only when the door is locked do the permissions apply.
+integer     isOpen              = TRUE;
+vector      openPos             = ZERO_VECTOR;
+rotation    openRot             = ZERO_ROTATION;
+vector      closedPos           = ZERO_VECTOR;
+rotation    closedRot           = ZERO_ROTATION;
+key         openerKey           = NULL_KEY;
+key         closerKey           = NULL_KEY;
+integer     isSetup             = FALSE;
+integer     listenHandle        = 0;
+string      avatarName          = "";
+
+// zooms the sub-prim to a new rotation and position.
+jump_to_position(vector position, rotation new_rot)
+{
+    list config_blast = [ 
+        PRIM_SIZE, llGetScale(), ///????
+        // first jump away from where we started, trying to get past an opensim bug.
+//        PRIM_POSITION, ZERO_VECTOR,
+//        PRIM_POSITION, ZERO_VECTOR,
+//        PRIM_POSITION, ZERO_VECTOR,
+//        PRIM_POSITION, ZERO_VECTOR,
+//        PRIM_POSITION, ZERO_VECTOR,
+        PRIM_ROTATION, ZERO_ROTATION * new_rot / llGetRootRotation(),
+//        PRIM_POSITION, position,
+//        PRIM_POSITION, position,
+//        PRIM_POSITION, position,
+//        PRIM_POSITION, position,
+//        PRIM_POSITION, position,
+        PRIM_POSITION, position
+        ];
+    llSetLinkPrimitiveParams(llGetLinkNumber(), config_blast);
+if (DEBUGGING) llOwnerSay("want pos=" + (string)position + ", got=" + (string)llGetLocalPos()
++ ", and want rot=" + (string)new_rot + ", got=" + (string)llGetLocalRot());
+}
+
+mySayName(integer channel, string objectName, string message)
+{
+    string name = llGetObjectName();
+    llSetObjectName(objectName);
+    llSay(0, "/me " + message);
+    llSetObjectName(name);
+}
+
+mySay(integer channel, string message)
+{
+    string name = llGetObjectName();
+    llSetObjectName("Door");
+    llSay(0, message);
+    llSetObjectName(name);
+}
+
+myOwnerSay(string message)
+{
+    string name = llGetObjectName();
+    llSetObjectName("Door");
+    llOwnerSay(message);
+    llSetObjectName(name);
+}
+
+mySoundConfirmed()
+{
+    if (confirmedSound != "")
+    {
+        llTriggerSound(confirmedSound, 1.0);
+    }
+}
+
+mySoundAccessDenied()
+{
+    if (accessDeniedSound != "")
+    {
+        llTriggerSound(accessDeniedSound, 1.0);
+    }
+}
+
+myGetDoorParams()
+{
+    isSetup = FALSE;
+    if (llSubStringIndex(llGetObjectDesc(), "D;") == 0 && llSubStringIndex(llGetObjectName(), "D;") == 0)
+    {
+        list nameWords = llParseString2List(llGetObjectName(), [";"], []);
+        list descWords = llParseString2List(llGetObjectDesc(), [";"], []);
+        if (llGetListLength(nameWords) != 3 || llGetListLength(descWords) != 3)
+        {
+            myOwnerSay("The door prim's name and/or description has invalid syntax and/or number of parameters. Delete the door prim's name and description and setup the door prim again.");
+        }
+        else
+        {
+            openPos = (vector)llList2String(nameWords, 1);
+            openRot = (rotation)llList2String(nameWords, 2);
+            closedPos = (vector)llList2String(descWords, 1);
+            closedRot = (rotation)llList2String(descWords, 2);
+            isSetup = TRUE;
+        }
+//llSay(0, "got open pos=" + (string)(openPos) + " rot=" + (string)(openRot));
+//llSay(0, "got close pos=" + (string)(closedPos) + " rot=" + (string)(closedRot));
+        
+    }
+}
+
+// if open_state is true, the parms are for an open door.
+mySetDoorParams(integer open_state, vector Pos, rotation Rot)
+{
+    if (open_state) {
+        // parms for open state.
+        llSetObjectName("D;" + vector_chop(Pos) + ";" + rotation_chop(Rot));
+    } else {
+        // parms for closed state.
+        llSetObjectDesc("D;" + vector_chop(Pos) + ";" + rotation_chop(Rot));
+    }
+    isSetup = TRUE;
+}
+
+integer myPermissionCheck(key id)
+{
+    integer hasPermission = FALSE;
+    if (isLocked == FALSE) {
+        if (DEBUGGING) llOwnerSay("perm--unlocked: okay");
+        hasPermission = TRUE;
+    } else if (llGetOwnerKey(id) == llGetOwner()) {
+        if (DEBUGGING) llOwnerSay("perm--is owner: okay");
+        hasPermission = TRUE;
+    } else if (allowGroupToo == TRUE && llSameGroup(id)) {
+        if (DEBUGGING) llOwnerSay("perm--same group: okay");
+        hasPermission = TRUE;
+    } else if (llListFindList(allowedAgentUUIDs, [(string)id]) != -1) {
+        if (DEBUGGING) llOwnerSay("perm--in list: okay");
+        hasPermission = TRUE;
+    } else {
+        if (DEBUGGING) llOwnerSay("perm--not found anywhere: bad perms");
+    }
+    return hasPermission;
+}
+
+myOpenDoor()
+{
+    isOpen = FALSE;
+    myToggleDoor();
+}
+
+myCloseDoor()
+{
+    isOpen = TRUE;
+    myToggleDoor();
+}
+
+myToggleDoor()
+{
+    if (isSetup == FALSE)
+    {
+        myOwnerSay("The door prim has not been configured yet. Please read the usage instructions in the door script.");
+    }
+    else if (llGetLinkNumber() == 0 || llGetLinkNumber() == 1)
+    {
+        myOwnerSay("The door prim must be linked to at least one other prim and the door prim must not be the root prim");
+    }
+    else
+    {
+        isOpen = !isOpen;
+if (DEBUGGING) llOwnerSay("door open state is now=" + (string)isOpen);
+        if (isOpen)
+        {
+if (DEBUGGING) llOwnerSay("opening the door.");
+            if (doorBellSound != "")
+            {
+                llTriggerSound(doorBellSound, 1.0);
+                if (avatarName != "")
+                {
+                    mySayName(0, avatarName, "is at the door.");
+                    avatarName = "";
+                }
+            }
+            if (doorOpenSound != "")
+            {
+                llTriggerSound(doorOpenSound, 1.0);
+            }
+            jump_to_position(openPos, openRot);
+//            list config_blast = [ PRIM_POSITION, llGetLocalPos() + <4, 4, 4>,
+//                PRIM_ROTATION, ZERO_ROTATION * openRot / llGetRootRotation(),
+//                PRIM_POSITION, openPos
+////                PRIM_SIZE, llGetScale() 
+//                ];
+//            llSetPrimitiveParams(config_blast);
+//if (DEBUGGING) llOwnerSay("want pos=" + (string)openPos + ", got=" + (string)llGetLocalPos()
+//+ ", and want rot=" + (string)openRot + ", got=" + (string)llGetLocalRot());
+
+            if (PASS_COMMANDS && !command_is_a_response) {
+                // Door API.
+                llMessageLinked(LINK_SET, listenChannel, "cmd|door|open", llGetKey());
+            }
+            command_is_a_response = FALSE;  // took care of that one.
+        }
+        else
+        {
+if (DEBUGGING) llOwnerSay("closing the door.");
+            if (doorCloseSound != "")
+            {
+                llTriggerSound(doorCloseSound, 1.0);
+            }
+            jump_to_position(closedPos, closedRot);
+//            list config_blast = [ 
+//                PRIM_ROTATION, ZERO_ROTATION * closedRot / llGetRootRotation(),
+//                PRIM_POSITION, closedPos
+////                PRIM_SIZE, llGetScale()
+//                ];
+//            llSetPrimitiveParams(config_blast);
+//if (DEBUGGING) llOwnerSay("want pos=" + (string)closedPos + ", got=" + (string)llGetLocalPos()
+//+ ", and want rot=" + (string)closedRot + ", got=" + (string)llGetLocalRot());
+            if (PASS_COMMANDS && !command_is_a_response) {
+                // Door API.
+                llMessageLinked(LINK_SET, listenChannel, "cmd|door|close", llGetKey());
+            }
+            command_is_a_response = FALSE;  // took care of that one.
+        }
+        
+        llSetTimerEvent(0.0);
+        if (isOpen == TRUE && autoCloseTime != 0.0)
+        {
+            llSetTimerEvent(autoCloseTime);
+        }
+    }
+}
+
+//////////////
+// from hufflets...
+
+// returns the index of the first occurrence of "pattern" inside
+// the "full_string".  if it is not found, then a negative number is returned.
+integer find_substring(string full_string, string pattern)
+{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); }
+
+// returns text for a floating point number, but includes only
+// three digits after the decimal point.
+string float_chop(float to_show)
+{
+    integer mant = llAbs(llRound(to_show * 1000.0) / 1000);
+    string neg_sign;
+    if (to_show < 0.0) neg_sign = "-";
+    string dec_s = (string)((llRound(to_show * 1000.0) - mant * 1000) / 1000.0);
+    dec_s = llGetSubString(llGetSubString(dec_s, find_substring(dec_s, ".") + 1, -1), 0, 2);
+    // strip off all trailing zeros.
+    while (llGetSubString(dec_s, -1, -1) == "0")
+        dec_s = llDeleteSubString(dec_s, -1, -1);
+    string to_return = neg_sign + (string)mant;
+    if (llStringLength(dec_s)) to_return += "." + dec_s;
+    return to_return;
+}
+
+// returns a prettier form for vector text, with chopped floats.
+string vector_chop(vector to_show)
+{
+    return "<" + float_chop(to_show.x) + ","
+        + float_chop(to_show.y) + ","
+        + float_chop(to_show.z) + ">";
+}
+
+// similarly, for a rotation.
+string rotation_chop(rotation to_show)
+{
+    return "<" + float_chop(to_show.x) + ","
+        + float_chop(to_show.y) + ","
+        + float_chop(to_show.z) + ","
+        + float_chop(to_show.s) + ">";
+}
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.5.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+//log_it("invpo=" + (string)posn);
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+//log_it("pos=" + (string)space_v_posn);
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+//log_it("space v@" + (string)space_v_posn);
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+//log_it("indy=" + (string)space_v_posn);
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+//log_it("saw case of previously redundant item, aieee.  flattened: " + to_chop_up);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+default
+{
+    state_entry() { if (llSubStringIndex(llGetObjectName(),  "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry()
+    {
+        auto_retire();
+        listenHandle = llListen(listenChannel, "", NULL_KEY, "");
+        myGetDoorParams();
+        myCloseDoor();
+    }
+    
+    on_rez(integer parm) { llResetScript(); }
+
+    touch_start(integer total_number)
+    {
+        command_is_a_response = FALSE;
+        if (myPermissionCheck(llDetectedKey(0)) == TRUE)
+        {
+            avatarName = llDetectedName(0);
+            myToggleDoor();
+        }
+        else
+        {
+            mySoundAccessDenied();
+        }
+    }
+    
+    timer()
+    {
+        myCloseDoor();
+    }
+    
+    link_message(integer sender_num, integer num, string str, key id)
+    {
+        // Door API. The API is here in case you want to create PIN entry keypads or whatever.
+        if (num == listenChannel) {
+            if (id == llGetKey()) return;  // don't listen to our own commands.
+            command_is_a_response = TRUE;
+            if (str == "cmd|door|open") myOpenDoor();
+            else if (str == "cmd|door|close") myCloseDoor();
+            else if (str == "cmd|door|discover")
+                llMessageLinked(LINK_SET, listenChannel, "cmd|door|discovered|" + (string)llGetKey(),
+                    llGetKey());
+            else command_is_a_response = FALSE;
+//hmmm: above protocol seems redundant, but sending back the original id (like this did before)
+//      in the id field does not fit in with our usual schemes very well.
+        }
+    }
+    
+    listen(integer channel, string name, key id, string message)
+    {
+//        if (DEBUGGING) llOwnerSay("heard: " + message);
+        command_is_a_response = FALSE;  // don't get involved with the link message checking.
+
+        // Performance note: it's quicker to compare the strings than to compare permissions each time anyone says anything on this channel.
+        if (message == "open")
+        {
+            if (myPermissionCheck(id) == TRUE)
+            {
+                // Only open the door if the person is quite close to this door.
+                openerKey = id;
+                closerKey = NULL_KEY;
+                avatarName = name;
+                llSensor(name, id, AGENT, RESPONSE_DISTANCE, TWO_PI);
+            }
+            else
+            {
+                mySoundAccessDenied();
+            }
+        }
+        else if (message == "close")
+        {
+            if (myPermissionCheck(id) == TRUE)
+            {
+                openerKey = NULL_KEY;
+                closerKey = id;
+                avatarName = name;
+                // Only close the door if the person is quite close to this door.
+                llSensor(name, id, AGENT, RESPONSE_DISTANCE, TWO_PI);
+            }
+            else
+            {
+                mySoundAccessDenied();
+            }
+        }
+        else if (message == "lock")
+        {
+            if (myPermissionCheck(id) == TRUE)
+            {
+                isLocked = TRUE;
+                mySoundConfirmed();
+            }
+            else
+            {
+                mySoundAccessDenied();
+            }
+        }
+        else if (message == "unlock")
+        {
+            if (myPermissionCheck(id) == TRUE)
+            {
+                isLocked = FALSE;
+                mySoundConfirmed();
+            }
+            else
+            {
+                mySoundAccessDenied();
+            }
+        }
+        else if (message == "toggle")
+        {
+            if (myPermissionCheck(id) == TRUE)
+            {
+                avatarName = name;
+                myToggleDoor();
+            }
+            else
+            {
+                mySoundAccessDenied();
+            }
+        }
+        else if (message == "/door opened" && llSubStringIndex(llGetObjectName(), "D;") == -1)
+        {
+            if (llGetOwnerKey(id) == llGetOwner())
+            {
+                mySoundConfirmed();
+                openPos = llGetLocalPos();
+                openRot = llGetLocalRot();
+                isOpen = TRUE;
+                mySetDoorParams(TRUE, openPos, openRot);
+//llSay(0, "set open pos=" + (string)(openPos) + " rot=" + (string)(openRot));
+            }
+            else
+            {
+                mySoundAccessDenied();
+            }
+        }
+        else if (message == "/door closed" && llSubStringIndex(llGetObjectDesc(), "D;") == -1)
+        {
+            if (llGetOwnerKey(id) == llGetOwner())
+            {
+                mySoundConfirmed();
+                closedPos = llGetLocalPos();
+                closedRot = llGetLocalRot();
+                isOpen = FALSE;
+                mySetDoorParams(FALSE, closedPos, closedRot);
+//llSay(0, "set close pos=" + (string)(closedPos) + " rot=" + (string)(closedRot));
+            }
+            else
+            {
+                mySoundAccessDenied();
+            }
+        }
+    }
+    
+    sensor(integer num_detected)
+    {
+        if (openerKey != NULL_KEY)
+        {
+            integer i;
+            for (i = 0; i < num_detected; i++)
+            {
+                if (llDetectedKey(i) == openerKey && myPermissionCheck(llDetectedKey(i)) == TRUE)
+                {
+                    myOpenDoor();
+                }
+            }
+            openerKey = NULL_KEY;
+        }
+        else
+        {
+            integer i;
+            for (i = 0; i < num_detected; i++)
+            {
+                if (llDetectedKey(i) == closerKey && myPermissionCheck(llDetectedKey(i)) == TRUE)
+                {
+                    myCloseDoor();
+                }
+            }
+            closerKey = NULL_KEY;
+        }
+    }
+
+//------------------------------------------------------
+// Uncomment the following code if you particularly want
+// collisions to affect the door state.    
+//------------------------------------------------------
+
+//    collision_start(integer num_detected)
+//    {
+//        integer i;
+//        for (i = 0; i < num_detected; i++)
+//        {
+//            if (myPermissionCheck(llDetectedKey(i)) == TRUE)
+//            {
+//                avatarName = llDetectedName(i);
+//                myOpenDoor();
+//            }
+//            else if (llDetectedType(i) & AGENT)
+//            {
+//                mySoundAccessDenied();
+//            }
+//        }
+//    }
+
+}
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/a_huffotronic_update_server_v23.2.txt b/huffware/huffotronic_updater_freebie_v5.3/a_huffotronic_update_server_v23.2.txt
new file mode 100755 (executable)
index 0000000..ef201cf
--- /dev/null
@@ -0,0 +1,940 @@
+
+// huffware script: huff-update server, by fred huffhines.
+//
+// this script is the server side of the update process.  it should reside in an object that
+// has all the newest versions of scripts and objects that will be updated.  when rezzed, and
+// at periodic intervals, it announces on a private chat channel that updates are available.
+// when objects respond that they might like an update, it tells them the scripts that it has
+// stored inside of it.  if any of those scripts are an older version inside the client
+// (update requesting) object, then the client will request the newer versions.  the server
+// object will stuff them into it and tell them to start running.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+integer IS_OPENSIM = TRUE;  // must be set to true for opensim, false for second life.
+
+integer DEBUGGING = FALSE;  // set this to true for noisier diagnostics.
+
+
+
+// updater dependency section:
+// should be moved to a notecard!!!
+//
+// format is a list of strings, where each string has a pair wrapped by the
+// huffware item separators.  the pair contains (1) the basename of a script
+// that has a new dependency and (2) the basename of that new dependency.
+list known_script_dependencies;
+
+
+
+
+load_script_deps()
+{
+    known_script_dependencies = [
+        llDumpList2String(["jaunt wik rez", "jaunt config funcs"], ITEM_LIST_SEPARATOR),
+        llDumpList2String(["jaunt wik rez", "data cow"], ITEM_LIST_SEPARATOR),
+        llDumpList2String(["jaunt wik rez", "jaunt rezolator"], ITEM_LIST_SEPARATOR),
+        llDumpList2String(["remotely personable", "inventory exchanger"], ITEM_LIST_SEPARATOR),
+        llDumpList2String(["animote main driver", "exchange driver hudimation"], ITEM_LIST_SEPARATOR),
+        llDumpList2String(["animote main driver", "avatar timer manager"], ITEM_LIST_SEPARATOR),
+        llDumpList2String(["animote main driver", "avatar choice memory"], ITEM_LIST_SEPARATOR),
+        llDumpList2String(["giftorse", "particle projector"], ITEM_LIST_SEPARATOR),
+        llDumpList2String(["huff-search brainiac", "searchbert armature"], ITEM_LIST_SEPARATOR),
+
+        
+//special purpose -- removes the updater from the elevator buttons...
+//llDumpList2String(["comfortable sitting", "zap updater from elevators"], ITEM_LIST_SEPARATOR),
+// => do not uncomment unless you want your elevators to shed their ability to update.
+
+
+        // this allows us to add or remove items above at will without complaints about comma.
+        llDumpList2String(["xyzzy", "hopefully-never-matches"], ITEM_LIST_SEPARATOR)
+    ];
+}
+
+
+
+
+// global constants...
+
+integer USER_COMMAND_CHANNEL = 4;  // channel where we listen to user commands.
+
+float ANNOUNCEMENT_PERIOD = 14.0;  // how often we tell other objects that we have tasty treats.
+
+integer UPDATE_ANNOUNCEMENT_CHANNEL   = -420108;  // used by server to brag about itself.
+integer OLD_REQUEST_INVENTORY_CHANNEL = -421008;  // used by clients to request an update list.
+
+string UPDATE_ANNOUNCEMENT_PREFIX   = "#huff-update#";  // first part of any announcement.
+string REQUEST_INVENTORY_PREFIX     = "#huff-reqinv#";  // first part of request for inventory list.
+string REPORT_AVAILABLE_SCRIPTS     = "#scripts#";  // server's keyword to let client know script inventory.
+string REQUEST_SCRIPT_UPDATE        = "#updatego#";  // keyword used by client to request some updates.
+string SHUT_THEM_DOWN               = "#huffdown#";  // server tells client to stop any non-updater scripts.
+string READY_TO_UPDATE              = "#listoneeds#";  // the client tells the server the scripts it wants.
+string SCRIPTS_ARE_CURRENT          = "#gottemthx#";  // client says this when all new scripts are in place.
+string START_THEM_UP                = "#huffup#";  // server tells client to start up other scripts again.
+string DONE_UPDATING                = "#finito#";  // the client is done updating.
+string BUSY_BUSY                    = "#busymuch#";  // a signal that the server is too busy to update us.
+
+integer UPDATER_SCRIPT_PIN          = -1231008;  // the hook for our scripts to be modified.
+
+string RESTART_UPDATER_COMMAND      = "#restart";  // said in open chat to recrank the updater.
+string SHOW_SCRIPTS_COMMAND         = "#show";  // said in open chat to list out the scripts.
+string SHUTDOWN_COMMAND             = "#destroy";  // shuts down object and destroys it.
+
+string UPDATER_PARM_SEPARATOR = "~~~";
+    // three tildes is an uncommon thing to have otherwise, so we use it to separate
+    // our commands in linked messages.
+
+string SCRIPT_DEPENDENCY_MARK = "DEP";  // signals that a dependency is coming.
+
+integer ENTRIES_PER_LINE = 4;  // number of items from a list shown on one line of text.
+
+integer MAXIMUM_ACTIVE_CLIENTS = 5;  // number of conversations we will allow at a time.
+
+float LONGEST_SLACK_PER_CLIENT = 84.0;
+    // we allow a client to be out of touch with us for this many seconds.  after that,
+    // we decide it's deceased and remove it from our list.
+
+integer MESSAGE_SIZE_LIMIT = 800;  // longest thing that can be safely said without clipping (guess).
+
+float SCRIPT_LIST_PAUSE_INTERVAL = 1.4;  // pause between large chunks of update text.
+
+string ITEM_LIST_SEPARATOR = "``";  // separates dependencies.
+
+float CHANGED_INVENTORY_SNOOZER = 7.0;
+    // the number of seconds we sleep once we see an inventory change.  we don't want to
+    // react to this immediately.  this overrides the normal announcement cycle until it's
+    // dealt with by the timer.
+
+string CONTINUANCE_MARKER = "...";
+    // a string sent when the update list is too long and needs to be continued in another chat.
+
+string UPDATER_BASE_NAME = "huff-update client";
+    // the name of the updater script that keeps everything in sync.
+
+//////////////
+
+// global variables...
+
+list scripts_available;  // the list of scripts we have in our inventory for distribution.
+list objects_available;  // list of objects for handing out.
+
+list active_clients;  // list of keys for clients that are updating currently.
+list active_update_channels;  // active conversations on client chosen channels.
+list active_listen_ids;  // the ids for the listener on those channels.
+list active_timestamps;  // tracks when the client was last active.
+
+integer inventory_request_channel;  // our personal channel that the update client should talk with.
+
+integer dealing_with_change;  // has the inventory changed?  we will deal with this in the timer.
+
+// displays the status of the update server.
+show_status(key who_says)
+{
+    string title = "[mem free=" + (string)llGetFreeMemory() + "]";
+    title = "Listening for requests on channel " + (string)inventory_request_channel;
+    if (llGetListLength(active_update_channels))
+        title += "\nactive channels="
+            + dump_list(active_update_channels, FALSE, ENTRIES_PER_LINE);
+    else
+        title += "\nNo channels active.";
+        
+    if (llGetListLength(active_clients))
+        title += "\nactive clients=" + dump_keyed_list(active_clients, TRUE, 2);
+    else
+        title += "\nNo clients active.";
+    
+    if (llGetOwner() == who_says) {
+        string addition = " ";
+        if (USER_COMMAND_CHANNEL != 0) addition = "/" + (string)USER_COMMAND_CHANNEL + " ";
+        title += "\n[ \"" + addition + SHOW_SCRIPTS_COMMAND + "\" lists all the scripts, "
+            + "\"" + addition + SHUTDOWN_COMMAND + "\" zaps the updater, "
+            + "\"" + addition + RESTART_UPDATER_COMMAND + "\" refreshes the updater ]";
+    }
+
+    llWhisper(0, title);
+}
+
+// plink a quietous string of resonance...
+squonk(integer indy)
+{
+    string snd = llGetInventoryName(INVENTORY_SOUND, indy);
+    if (snd != "") llTriggerSound(snd, 1.0);
+}
+
+// list out the scripts that the server's object contains.
+show_scripts()
+{
+    string title = (string)llGetListLength(scripts_available) + " scripts available:";
+    show_list(title, scripts_available);
+    title = (string)llGetListLength(objects_available) + " objects available:";
+    show_list(title, objects_available);
+}
+
+// lets the client know that we're here and have some scripts available.
+announce_presence()
+{
+    if (llGetListLength(active_clients) < MAXIMUM_ACTIVE_CLIENTS) {
+        // only announce if we're not already booked up.
+        llWhisper(UPDATE_ANNOUNCEMENT_CHANNEL, UPDATE_ANNOUNCEMENT_PREFIX + (string)inventory_request_channel);
+    }
+}
+
+// lifted from "clear text and effects" script; should be nearly identical
+// to that, except that we set the texture animation.
+reset_all_effects()
+{
+    llSetText("", <0,0,0>, 0);  // clear any text above object.
+    llSetSitText("Sit Here");  // reset sit text to default state.
+    llSetTouchText("Touch");  // similarly for touch.
+    llSitTarget(ZERO_VECTOR, ZERO_ROTATION);  // reset sit target position.
+    llParticleSystem([]);  // turn off all particles.
+    llSensorRemove();  // stop any running sensors.
+    llTargetOmega(<0.0, 0.0, 0.0>, 0, 1.0);  // stop rotations of object.
+    llSetLinkAlpha(LINK_SET, 1.0, ALL_SIDES);  // turn off any transparency.
+    // keep it from being physical and from disapparating.
+    llSetPrimitiveParams([PRIM_TEMP_ON_REZ, FALSE, PRIM_PHYSICS, FALSE,
+        PRIM_PHANTOM, TRUE]);
+    llSetLinkColor(LINK_SET, <1.0, 1.0, 1.0>, ALL_SIDES);  // reset color to white.
+
+    //
+    // the following are specific to the huffotronic update server.
+    //
+
+    // we re-assert our special texture set here, in case some wayward scripts have
+    // messed with us.
+    integer textures_held = llGetInventoryNumber(INVENTORY_TEXTURE);
+    integer indy;
+    for (indy = 0; indy < textures_held; indy++) {
+        string curr_tex = llGetInventoryName(INVENTORY_TEXTURE, indy);
+        // we have a simple scheme for putting textures on the updater.
+        // we have an inside, an outside and the ends.
+        if (is_prefix(curr_tex, "~~s0")) {
+            llSetTexture(curr_tex, ALL_SIDES);
+        } else if (is_prefix(curr_tex, "~~s1")) {
+            llSetTexture(curr_tex, 1);
+        } else if (is_prefix(curr_tex, "~~s2")) {
+            llSetTexture(curr_tex, 0);
+            llSetTexture(curr_tex, 3);
+        }
+    }
+    
+    // re-assert our texture animation also.
+    llSetTextureAnim(ANIM_ON | LOOP | SMOOTH | ROTATE,
+        ALL_SIDES, 0, 0, 0, TWO_PI, 1.0 / 36.0);
+}
+
+// set up the update server object.
+initialize_root()
+{
+    // set up our particular "look".
+    reset_all_effects();
+
+    // shut down any competing scripts in this object.
+    // we try to swat any other script that's been added before we let them do weird
+    // stuff.  for example, a pet script might start acting like one.  this
+    // function is not guaranteed to run before that bad stuff can happen,
+    // and that's maybe the one major issue with this approach.  as long as
+    // the contained scripts aren't evil (like if they jump someplace else
+    // as soon as they start), then there shouldn't be any serious problems.
+    knock_down_other_scripts();
+
+    // reset our variables.
+    active_update_channels = [];
+    active_listen_ids = [];
+    active_clients = [];
+    active_timestamps = [];
+    scripts_available = [];
+    objects_available = [];
+    dealing_with_change = FALSE;  // not handling any inventory changes.
+
+    // make sure we know about any scripts that have new dependencies.
+    load_script_deps();
+
+    // clean out any older versions of the scripts before we make our
+    // inventory.
+    destroy_older_versions();
+
+    // now accumulate the list of scripts in our inventory.    
+    integer items_held = llGetInventoryNumber(INVENTORY_SCRIPT);
+    integer indy;
+    for (indy = 0; indy < items_held; indy++) {
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, indy);
+        // we don't provide our own script for updating; it must be kept from
+        // floating around, like into other objects that are not updaters.
+////        if (curr_script != llGetScriptName())
+            scripts_available += curr_script;
+    }
+    items_held = llGetInventoryNumber(INVENTORY_OBJECT);
+    for (indy = 0; indy < items_held; indy++) {
+        objects_available += llGetInventoryName(INVENTORY_OBJECT, indy);
+//log_it("added obj: " + llGetInventoryName(INVENTORY_OBJECT, indy));
+    }
+    items_held = llGetInventoryNumber(INVENTORY_NOTECARD);
+    for (indy = 0; indy < items_held; indy++) {
+        objects_available += llGetInventoryName(INVENTORY_NOTECARD, indy);
+//log_it("added note: " + llGetInventoryName(INVENTORY_NOTECARD, indy));
+    }
+
+    // listen to the owner.
+    llListen(USER_COMMAND_CHANNEL, "", llGetOwner(), "");
+    // listen for any requests from our loyal clients.
+    inventory_request_channel = random_channel();
+    llListen(inventory_request_channel, "", NULL_KEY, "");
+    // listen for older clients too.
+    llListen(OLD_REQUEST_INVENTORY_CHANNEL, "", NULL_KEY, "");
+
+    // set up the periodic announcements.
+    llSetTimerEvent(ANNOUNCEMENT_PERIOD);
+}
+
+handle_timer()
+{
+    if (dealing_with_change) {
+        dealing_with_change = FALSE;
+        state rerun;  // zoom back to the starting point.
+    }
+    integer indy;
+    integer timecheck = llGetUnixTime();  // use for whole loop.
+    for (indy = llGetListLength(active_timestamps) - 1; indy >= 0; indy--) {
+        integer last_time = llList2Integer(active_timestamps, indy);
+        if (llAbs(last_time - timecheck) > LONGEST_SLACK_PER_CLIENT) {
+            // we need to clear out this item.  we know we can whack the client
+            // at the same index and that will take care of this slacker.
+            key curr_key = llList2Key(active_clients, indy);
+            log_it("timed-out client: " + llKey2Name(curr_key) + " [" + (string)curr_key + "]");
+            remove_client(curr_key);
+        }
+    }
+
+    // let the objects nearby know that we are open for business by
+    // announcing the script inventory.
+    announce_presence();
+}
+
+// turns a list into a nicely formatted string.
+string dump_list(list to_show, integer initial_line_break, integer entries_per_line)
+{
+    string msg;
+    integer indy;
+    for (indy = 0; indy < llGetListLength(to_show); indy++) {
+        // we break every Nth entry, but not if it's the first line and
+        // they said to not have the initial line break.
+        if ( !(indy % entries_per_line) && (indy || initial_line_break) )
+            msg += "\n";
+        string cursc = llList2String(to_show, indy);
+        msg += cursc;
+        // add commas where needed.
+        if (indy < llGetListLength(to_show) - 1)
+            msg += ", ";
+    }
+    return msg;
+}
+
+// similar to dump_keyed_list, but only shows the names, each on their own line.
+string dump_names_for_keys(list to_show)
+{
+    string msg;
+    integer indy;
+    for (indy = 0; indy < llGetListLength(to_show); indy++) {
+        // we only line break after the first entry.
+        if (indy > 0) msg += "\n";
+        string keystr = llList2String(to_show, indy);
+        msg += llKey2Name(keystr);
+    }
+    return msg;
+}
+
+// similar to dump_list
+string dump_keyed_list(list to_show, integer initial_line_break, integer entries_per_line)
+{
+    string msg;
+    integer indy;
+    for (indy = 0; indy < llGetListLength(to_show); indy++) {
+        // we break every Nth entry, but not if it's the first line and
+        // they said to not have the initial line break.
+        if ( !(indy % entries_per_line) && (indy || initial_line_break) )
+            msg += "\n";
+        string keystr = llList2String(to_show, indy);
+        msg += llKey2Name(keystr) + " (" + keystr + ")";
+        // add commas where needed.
+        if (indy < llGetListLength(to_show) - 1)
+            msg += ", ";
+    }
+    return msg;
+}
+
+// shows the list specified in a compact manner.
+show_list(string title, list to_show)
+{
+    string to_say = title + dump_list(to_show, TRUE, ENTRIES_PER_LINE);
+    // flush some memory.
+    title = "";
+    to_show = [];
+    integer indy;
+    // say the output in pieces to avoid over-clogging chat.
+    for (indy = 0; indy < llStringLength(to_say); indy += MESSAGE_SIZE_LIMIT) {
+        integer last_indy = indy + MESSAGE_SIZE_LIMIT - 1;
+        string addition;
+        if (last_indy < llStringLength(to_say)) addition = CONTINUANCE_MARKER;
+        llWhisper(0, llGetSubString(to_say, indy, last_indy) + addition);
+    }
+}
+
+// stops all the scripts besides this one.
+knock_down_other_scripts()
+{
+    // set all scripts but this to not be running.
+    integer indy;
+    string self_script = llGetScriptName();
+    list split = compute_basename_and_version(self_script);
+    string self_base = llList2String(split, 0);
+    self_script = ""; split = [];  // free memory.
+    integer count = llGetInventoryNumber(INVENTORY_SCRIPT);
+    // we set all other scripts that are not versions of this script to not be running.
+    for (indy = 0; indy < count; indy++) {
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, indy);
+        if (!is_prefix(curr_script, self_base)
+            && !is_prefix(curr_script, UPDATER_BASE_NAME) ) {
+            llSetScriptState(curr_script, FALSE);
+        }
+    }
+}
+
+// set a text label on the updater with the list of clients.
+set_our_label()
+{
+    string label = "";
+    if (llGetListLength(active_clients) > 0) label = "[updating]\n";
+    llSetText(label + dump_names_for_keys(active_clients), <0.8, 0.95, 0.92>, 1.0);
+}
+
+// clean out a client that we should be done with.
+remove_client(key id)
+{
+    // locate said client of deceased nature...
+    integer indy = find_in_list(active_clients, id);
+    if (indy < 0) {
+//        if (DEBUGGING) log_it("failure to find client to remove: " + (string)id);
+        return;
+    }
+    active_clients = llDeleteSubList(active_clients, indy, indy);
+    // also clean out the channel and stop listening to it.
+integer act_chan = llList2Integer(active_update_channels, indy);
+    active_update_channels = llDeleteSubList(active_update_channels, indy, indy);
+    integer listen_to_remove = llList2Integer(active_listen_ids, indy);
+//log_it("remove listen " + (string)listen_to_remove + " on chan " + (string)act_chan);
+    llListenRemove(listen_to_remove);
+    active_listen_ids = llDeleteSubList(active_listen_ids, indy, indy);
+    active_timestamps = llDeleteSubList(active_timestamps, indy, indy);
+    set_our_label();
+}
+
+// fix a partial match to a script name if we can't find the exact item.
+string backpatch_script_name(string partial)
+{
+    if (llGetInventoryType(partial) == INVENTORY_SCRIPT) return partial;  // all set.
+    integer dep_indy;
+    for (dep_indy = 0; dep_indy < llGetInventoryNumber(INVENTORY_SCRIPT); dep_indy++) {
+        string curr_name = llGetInventoryName(INVENTORY_SCRIPT, dep_indy);
+        if (is_prefix(curr_name, partial)) {
+//            log_it("found real name " + curr_name + " for part: " + partial);
+            return curr_name;
+        }
+    }
+//    log_it("no matches for partial script name!");
+    return "";  // no matches!
+}
+
+// moves the upgrade process with "id" along to the next step given the request in
+// the message.
+propel_upgrade_process(integer channel, key id, string message)
+{
+    if (DEBUGGING) log_it("got upgrade note from " + (string)id + " with msg=" + message);
+    if (message == REQUEST_SCRIPT_UPDATE) {
+        // begins the update process with the client.
+        llSay(channel, SHUT_THEM_DOWN);
+    } else if (is_prefix(message, READY_TO_UPDATE)) {
+        // whack the prefix so we can get the list they want.
+        message = llDeleteSubString(message, 0, llStringLength(READY_TO_UPDATE) - 1);
+        list requests = llParseString2List(message, [UPDATER_PARM_SEPARATOR], []);
+        message = "";
+        // send over the scripts the client asked for, since it seems to be ready for them.
+        if (llGetListLength(requests)) {
+            show_list("updating " + llKey2Name(id) + " (key " + (string)id + ") with", requests);
+            integer indy;
+            for (indy = 0; indy < llGetListLength(requests); indy++) {
+                string curr = llList2String(requests, indy);
+                if (find_in_list(objects_available, curr) >= 0) {
+//log_it("handing object over: " + curr);
+                    // it's an object, so treat it that way.
+                    llGiveInventory(id, curr);
+                } else {
+//log_it("handing script over: " + curr);
+                    // assume it's a script, and use script pin to stuff it.
+                    curr = backpatch_script_name(curr);
+                    if (curr != "") {
+//                        integer starting_state = FALSE;
+// second life was okay with scripts being plugged in unstarted.  opensim is not.
+// and second life appears to be unhappy with scripts plugged in as started.  so we
+// have an impasse.
+// this should be: true for opensim, and false for second life.
+                        integer starting_state = IS_OPENSIM;
+//                        if (DEBUGGING) log_it("installing script using updater pin.");
+                        llRemoteLoadScriptPin(id, curr, UPDATER_SCRIPT_PIN, starting_state, 0);
+                    }
+                }
+            }
+        }
+    } else if (message == SCRIPTS_ARE_CURRENT) {
+        // the client thinks it's ready to get back up and running.
+//log_it("heard client is ready!");
+        llSay(channel, START_THEM_UP);
+        // kludge for older clients (pre 10.4 version) to try to help them start up.
+//old and not useful.        llSleep(0.2); llSay(channel, START_THEM_UP); llSleep(0.2); llSay(channel, START_THEM_UP);
+        remove_client(id);
+    } else if (message == DONE_UPDATING) {
+        // this client has nothing to do for now.
+//log_it("heard client is done: " + (string)id);
+        remove_client(id);
+    } else {
+//log_it("weird note from client: " + message);
+        return;  // not used.
+    }
+    
+}
+
+// blasts out the inventory list to a curious client.
+spew_inventory_list(integer channel, string message, key id)
+{
+    if (!is_prefix(message, REQUEST_INVENTORY_PREFIX)) {
+
+        // this is an old style update alert that we still use at startup of the client
+        // to ensure that finishing replacement of the updater script is never unnoticed.
+        if (is_prefix(message, DONE_UPDATING)) {
+//            if (DEBUGGING) log_it("found very special message from startup of updater.");
+            propel_upgrade_process(channel, id, message);
+        }
+        
+        // argh, this is not the right kind of message on our channel.
+        return;
+    }
+    string chan_str = llDeleteSubString(message, 0, llStringLength(REQUEST_INVENTORY_PREFIX) - 1);
+    integer new_update_channel = (integer)chan_str;    
+    if (llGetListLength(active_clients) >= MAXIMUM_ACTIVE_CLIENTS) {
+        // got to tell them "not right now".  we'll pretend we have no
+        // scripts; they'll know what we mean if the update client is
+        // recent enough.  really old clients will just go to sleep until later.
+        if (DEBUGGING) log_it("having to disallow new client '" + llKey2Name(id) + "', too many now.");
+        llSay(new_update_channel, REPORT_AVAILABLE_SCRIPTS + BUSY_BUSY);
+        return;
+    }
+
+    // looks like we're going to try to handle the request for them.    
+    if (DEBUGGING) log_it("server heard update req on chan " + (string)channel + " from: " + llKey2Name(id));
+    
+//log_it("add client convo chan " + (string)new_update_channel);
+    integer existing_indy = find_in_list(active_clients, id);
+    if (existing_indy < 0) {
+        active_clients += id;
+        active_update_channels += new_update_channel;
+        integer new_listen_id = llListen(new_update_channel, "", id, "");
+//log_it("add listen " + (string)new_listen_id + " on chan " + (string)new_update_channel);
+        active_listen_ids += new_listen_id;
+        active_timestamps += llGetUnixTime();
+        set_our_label();
+    } else {
+//        if (DEBUGGING) log_it("same client came back before finishing previous upgrade, rolling with it.");
+        // delete old listener so we don't leave it dangling.
+        integer old_listen_id = llList2Integer(active_listen_ids, existing_indy);
+//log_it("remove old listen " + (string)old_listen_id);
+        llListenRemove(old_listen_id);
+        // update the channel and listener id for the new registration.
+        active_update_channels = chop_list(active_update_channels, 0, existing_indy - 1)
+            + [ new_update_channel ]
+            + chop_list(active_update_channels, existing_indy + 1,
+                llGetListLength(active_update_channels) - 1);
+        integer new_listen_id = llListen(new_update_channel, "", id, "");
+//log_it("add new listen " + (string)new_listen_id);
+        active_listen_ids = chop_list(active_listen_ids, 0, existing_indy - 1)
+            + [ new_listen_id ]
+            + chop_list(active_listen_ids, existing_indy + 1,
+                llGetListLength(active_listen_ids) - 1);
+        active_timestamps = chop_list(active_timestamps, 0, existing_indy - 1)
+            + [ new_listen_id ]
+            + chop_list(active_timestamps, existing_indy + 1,
+                llGetListLength(active_timestamps) - 1);
+    }
+
+    // our report name is always called available scripts, but it can actually have
+    // script dependency definitions, script names and object names.
+    string msg = REPORT_AVAILABLE_SCRIPTS;
+    string curr;  // temp.
+
+    // add in the huff updater, since that's the most crucial that they know we have.
+    integer posn;
+    string UPDATE_CLIENT_SCRIPT = "huff-update client";
+    for (posn = 0; posn < llGetListLength(scripts_available); posn++) {
+        curr = llList2String(scripts_available, posn);
+        if (llDeleteSubString(curr, llStringLength(UPDATE_CLIENT_SCRIPT), -1)
+                == UPDATE_CLIENT_SCRIPT) {
+//log_it("found "  + curr);
+            msg += curr + UPDATER_PARM_SEPARATOR;
+            posn = 99999;  // jump out.
+        }
+        if (DEBUGGING && (posn == llGetListLength(scripts_available) - 1) ) {
+            log_it("epic fail, found no updater client script.");
+        }
+    }
+
+    // speak about the dependencies that we know.
+    for (posn = 0; posn < llGetListLength(known_script_dependencies); posn++) {
+        msg += SCRIPT_DEPENDENCY_MARK
+            + llList2String(known_script_dependencies, posn) + UPDATER_PARM_SEPARATOR;
+        if (llStringLength(msg) > MESSAGE_SIZE_LIMIT - 50) {
+            llSay(new_update_channel, msg + CONTINUANCE_MARKER);
+//log_it(msg + CONTINUANCE_MARKER);
+            llSleep(SCRIPT_LIST_PAUSE_INTERVAL);
+            msg = REPORT_AVAILABLE_SCRIPTS;
+        }
+    }        
+    // tell this new client what scripts we have.
+    for (posn = 0; posn < llGetListLength(scripts_available); posn++) {
+        curr = llList2String(scripts_available, posn);
+        if (llDeleteSubString(curr, llStringLength(UPDATE_CLIENT_SCRIPT), -1)
+                != UPDATE_CLIENT_SCRIPT) {            
+            // add in the next item, along with the parameter separator.
+            msg += curr + UPDATER_PARM_SEPARATOR;
+//log_it("adding " + curr);
+            if (llStringLength(msg) > MESSAGE_SIZE_LIMIT - 50) {
+                llSay(new_update_channel, msg + CONTINUANCE_MARKER);
+//log_it(msg + CONTINUANCE_MARKER);
+                if (channel == OLD_REQUEST_INVENTORY_CHANNEL) {
+                    // stop sending the list to them since they may not know how
+                    // to interpret a multiple part update list.
+                    return;
+                }
+                llSleep(SCRIPT_LIST_PAUSE_INTERVAL);
+                msg = REPORT_AVAILABLE_SCRIPTS;
+            }
+        }
+    }
+    // mention any objects that are available.
+    for (posn = 0; posn < llGetListLength(objects_available); posn++) {
+        // add in the next item, along with the parameter separator.            
+        msg += llList2String(objects_available, posn)
+            + UPDATER_PARM_SEPARATOR;
+        if (llStringLength(msg) > MESSAGE_SIZE_LIMIT - 50) {
+            llSay(new_update_channel, msg + CONTINUANCE_MARKER);
+//log_it(msg + CONTINUANCE_MARKER);
+            llSleep(SCRIPT_LIST_PAUSE_INTERVAL);
+            msg = REPORT_AVAILABLE_SCRIPTS;
+        }
+    }
+    // say final bit, even if it's mostly blank.  we need to let them know
+    // that we're done and not adding that continuation flag string.
+    llSay(new_update_channel, msg);
+}
+
+// handles verbal commands from objects that want updates.
+process_verbal_requests(integer channel, string name, key id, string message)
+{
+    if ( (channel == OLD_REQUEST_INVENTORY_CHANNEL) || (channel == inventory_request_channel) ) {
+        spew_inventory_list(channel, message, id);
+        return;
+    } else if (channel == USER_COMMAND_CHANNEL) {
+if (DEBUGGING) log_it("heard orders: " + message);
+        // simple verbal commands.
+        if (message == RESTART_UPDATER_COMMAND) {
+            log_it("Restarting now.");
+            llResetScript();
+        } else if (message == SHOW_SCRIPTS_COMMAND) {
+            show_scripts();
+        } else if (message == SHUTDOWN_COMMAND) {
+            // we will de-rez now (i.e., die) if we are not one of the special names that is undying.
+            if (!matches_substring(llGetObjectName(), "keeper")) {
+//                log_it("server " + (string)inventory_request_channel + " now disintegrating.");
+                squonk(1);
+                llDie();
+            }
+        }
+        return;
+    }
+
+    integer indy;
+    // see if the channel is for one of our valid updates that's in progress.
+    for (indy = 0; indy < llGetListLength(active_update_channels); indy++) {
+        integer cur_chan = llList2Integer(active_update_channels, indy);
+        if (cur_chan == channel) {
+            // yes, this is really for that guy.
+            propel_upgrade_process(channel, id, message);
+            return;
+        }
+    }
+}
+
+//////////////
+// from hufflets...
+
+integer debug_num = 0;
+
+// a debugging output method.  can be disabled entirely in one place.
+log_it(string to_say)
+{
+    debug_num++;
+    // tell this to the owner.    
+//    llOwnerSay(llGetDate() + ": " + llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+llWhisper(0, llGetDate() + ": " + llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on an unusual channel for chat if it's not intended for general public.
+//    llSay(108, llGetDate() + ": " + llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on open chat that anyone can hear.  we take off the bling for this one.
+//    llSay(0, to_say);
+}
+
+// returns TRUE if the "pattern" is found in the "full_string".
+integer matches_substring(string full_string, string pattern)
+{ return (find_substring(full_string, pattern) >= 0); }
+
+// returns the index of the first occurrence of "pattern" inside
+// the "full_string".  if it is not found, then a negative number is returned.
+integer find_substring(string full_string, string pattern)
+{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); }
+
+// returns TRUE if the "prefix" string is the first part of "compare_with".
+integer is_prefix(string compare_with, string prefix)
+{ return find_substring(compare_with, prefix) == 0; }
+
+// joins a list of parameters using the parameter sentinel for the library.
+string wrap_parameters(list to_flatten)
+{ return llDumpList2String(to_flatten, UPDATER_PARM_SEPARATOR); }
+
+// locates the string "text" in the list to "search_in".
+integer find_in_list(list search_in, string text)
+{ 
+    integer len = llGetListLength(search_in);
+    integer i; 
+    for (i = 0; i < len; i++) { 
+        if (llList2String(search_in, i) == text) 
+            return i; 
+    } 
+    return -1;
+}
+
+// returns the portion of the list between start and end, but only if they are
+// valid compared with the list length.  an attempt to use negative start or
+// end values also returns a blank list.
+list chop_list(list to_chop, integer start, integer end)
+{
+    integer last_len = llGetListLength(to_chop) - 1;
+    if ( (start < 0) || (end < 0) || (start > last_len) || (end > last_len) ) return [];
+    return llList2List(to_chop, start, end);
+}
+
+// a random channel for the first interaction with the client.
+integer random_channel() { return -(integer)(llFrand(800000) + 20000); }
+
+// note that this new, lower memory version, depends on the inventory functions returning
+// items in alphabetical order.
+scrub_items_by_type(string this_guy, integer inventory_type)
+{
+    list removal_list;
+    integer outer;
+    for (outer = 0; outer < llGetInventoryNumber(inventory_type); outer++) {
+        string curr = llGetInventoryName(inventory_type, outer);
+        list split = compute_basename_and_version(curr);
+        // make sure there was a comparable version number in this name.
+        if ( (curr != this_guy) && llGetListLength(split)) {
+            string curr_base = llList2String(split, 0);
+            float curr_ver = (float)llList2String(split, 1);
+//log_it("outer: " + curr_base + " / " + (string)curr_ver);
+            integer inner;
+            for (inner = outer + 1; inner < llGetInventoryNumber(inventory_type); inner++) {
+                string next_guy = llGetInventoryName(inventory_type, inner);
+                list comp_split = compute_basename_and_version(next_guy);
+                if (llGetListLength(comp_split)) {
+                    string comp_base = llList2String(comp_split, 0);
+                    float comp_ver = (float)llList2String(comp_split, 1);
+                    // okay, now we can actually compare.
+                    if (curr_base != comp_base) {
+                        // break out of inner loop.  we are past where the names can matter.
+                        inner = 2 * llGetInventoryNumber(inventory_type);
+                    } else {
+//log_it("inner: " + comp_base + " / " + (string)comp_ver);
+                        if (curr_ver <= comp_ver) {
+                            // the script at inner index is comparable or better than
+                            // the script at the outer index.
+                            removal_list += curr;
+                        } else {
+                            // this inner script must be inferior to the outer one,
+                            // somehow, which defies our expectation of alphabetical ordering.
+                            removal_list += next_guy;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    // now actually do the deletions.
+    for (outer = 0; outer < llGetListLength(removal_list); outer++) {
+        string to_whack = llList2String(removal_list, outer);
+        log_it("removing older asset: " + to_whack);
+        llRemoveInventory(to_whack);
+    }
+    
+    if (IS_OPENSIM && (llGetListLength(removal_list) > 0) ) {
+        log_it("now restarting to avoid opensim late updating change event.");
+        llResetScript();
+    }
+}
+
+// ensures that only the latest version of any script or object is kept in our inventory.
+destroy_older_versions()
+{
+    // firstly, iterate across scripts to clean out older versions.
+    scrub_items_by_type(llGetScriptName(), INVENTORY_SCRIPT);
+    // secondly, try to clean out the objects.
+    scrub_items_by_type(llGetScriptName(), INVENTORY_OBJECT);
+    // thirdly, try to clean out the notecards.
+    scrub_items_by_type(llGetScriptName(), INVENTORY_NOTECARD);
+}
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.4.
+// distributed under BSD-like license.
+//   partly based on the self-upgrading scripts from markov brodsky and jippen faddoul.
+// the function auto_retire() should be added *inside* a version numbered script that
+// you wish to give the capability of self-upgrading.
+//   this script supports a notation for versions embedded in script names where a 'v'
+// is followed by a number in the form "major.minor", e.g. "grunkle script by ted v8.2".
+// when the containing script is dropped into an object with a different version, the
+// most recent version eats any existing ones.
+//   keep in mind that this code must be *copied* into your script you wish to add
+// auto-retirement capability to.
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+// end hufflets.
+//////////////
+
+default
+{
+    state_entry()
+    {
+        auto_retire();
+        initialize_root();  // get set up to start answering requests.
+        llWhisper(0, llGetScriptName() + " started...  touch for more info.");
+        squonk(0);
+    }
+    
+    state_exit() { llSetTimerEvent(0); }
+
+    on_rez(integer parm) {
+        state rerun;
+    }
+
+    timer() { handle_timer(); }
+
+    touch_start(integer count) {
+        show_status(llDetectedKey(0));
+    }
+    
+    listen(integer channel, string name, key id, string message) {
+        // make sure that the object is something we should even talk to.
+        if (llGetOwnerKey(id) != llGetOwner()) {
+            return;
+        }
+        // looks okay, let's see if this is useful communication.
+        process_verbal_requests(channel, name, id, message); 
+    }
+
+    changed(integer change) {
+        if (change & CHANGED_INVENTORY) {
+            log_it("inventory changed, scheduled a restart.");
+            // sleep a little bit; otherwise we get SL noise about scripts not being
+            // there when it told us they were there.  this can lead to some scripts
+            // doing bizarre things if they are running when added.
+            dealing_with_change = TRUE;
+            llSetTimerEvent(0.0);  // kludge to get around second life bug.
+            llSetTimerEvent(CHANGED_INVENTORY_SNOOZER);
+        }
+    }
+}
+
+state rerun { state_entry() { state default; } }
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/auto-retire_v2.8.txt b/huffware/huffotronic_updater_freebie_v5.3/auto-retire_v2.8.txt
new file mode 100755 (executable)
index 0000000..9c96028
--- /dev/null
@@ -0,0 +1,111 @@
+
+//////////////[copy starting here...]//////////////
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.8.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+//////////////[end of area to copy]//////////////
+
+//////////////
+// this is how a user of the auto_retire script gets the retirement process
+// to work.  whenever the script goes into its default state, such as when dropped
+// into an object, this function invocation makes sure that no earlier versions
+// of our script are left in inventory.
+default
+{
+    state_entry() {
+        auto_retire();
+    }
+}
+//////////////
diff --git a/huffware/huffotronic_updater_freebie_v5.3/card_configurator_v8.1.txt b/huffware/huffotronic_updater_freebie_v5.3/card_configurator_v8.1.txt
new file mode 100755 (executable)
index 0000000..9653ec0
--- /dev/null
@@ -0,0 +1,499 @@
+
+// huffware script: card configurator, by fred huffhines.
+//
+// processes a notecard with configuration info, then sends the information as packages of
+// configuration nuggest to a consuming script.  this is one level above the noteworthy script,
+// which simply reads the notecard.  this script keeps the config parsing out of higher-level
+// scripts.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+// global constants...
+
+integer MAXIMUM_ITEMS_PER_BURST = 6;
+    // try this out as a good limit for the size of the sends.
+
+// global variables...
+
+string signature_required;
+    // sent to us when the request for configuration info is made.
+
+list good_prefixes;
+    // similarly, a set of prefixes for notecard lines that the calling script
+    // cares about.  if this is empty, then anything will match.
+
+string global_notecard_name;  // name of our notecard in the object's inventory.
+integer response_code;  // set to uniquely identify the notecard read in progress.
+list global_config_list;  // a collection of configuration parameters from our notecard.
+list global_responses;  // the items we want to send back to the requestor.
+
+// card configurator link message API:
+//////////////
+// do not redefine these constants.
+integer CARD_CONFIGURATOR_HUFFWARE_ID = 10042;
+    // the unique id within the huffware system for the card configurator script to
+    // accept commands on.  this is used in llMessageLinked as the num parameter.
+string HUFFWARE_PARM_SEPARATOR = "{~~~}";
+    // this pattern is an uncommon thing to see in text, so we use it to separate
+    // our commands in link messages.
+string HUFFWARE_ITEM_SEPARATOR = "{|||}";
+    // used to separate lists of items from each other when stored inside a parameter.
+    // this allows lists to be passed as single string parameters if needed.
+integer REPLY_DISTANCE = 100008;  // offset added to service's huffware id in reply IDs.
+//////////////
+string BAD_NOTECARD_TEXT = "*badcrd*";
+    // the sign that we hated the notecards we found, or there were none.
+string FINISHED_READING_NOTECARDS = "**finished**";
+    // the sign that we are done plowing through the card we found.
+string BEGIN_READING_NOTECARD_COMMAND = "#read-cfg#";
+    // requests that the configurator find a good notecard and read its contents.
+    // it should send the contents via the alert below.  first parm is the signature and
+    // second is the wrapped list of valid item prefixes.
+string READ_PARTICULAR_NOTECARD_COMMAND = "#read-note#";
+    // requests that the configurator find a good notecard and read its contents.
+    // it should send the contents via the alert below.  first two parms are the same as
+    // begin reading notecard, and the third parameter is the name of the specific notecard.
+string CARD_CONFIG_RECEIVED_ALERT = "#cfg-event-upd#";
+    // this message is sent when the configurator has found some data updates or has finished
+    // reading the configuration file.
+//////////////
+
+// imported interfaces below...
+
+// requires noteworthy library v8.5 or better.
+//////////////
+// do not redefine these constants.
+integer NOTEWORTHY_HUFFWARE_ID = 10010;
+    // the unique id within the huffware system for the noteworthy script to
+    // accept commands on.  this is used in llMessageLinked as the num parameter.
+// commands available via the noteworthy library:
+string BUSY_READING_INDICATOR = "busy_already";
+    // this return value indicates that the script is already in use by some other script.
+    // the calling script should try again later.
+string NOTECARD_READ_CONTINUATION = "continue!";
+    // returned as first parameter if there is still more data to handle.
+// commands available via the noteworthy library:
+string READ_NOTECARD_COMMAND = "#read_note#";
+    // command used to tell the script to read notecards.  needs a signature to find
+    // in the card as the first parameter, and a randomly generated response code for
+    // the second parameter.  the response code is used to uniquely identify a set of
+    // pending notecard readings (hopefully).  the signature can be blank.
+    // the results will be fired back as the string value returned, which will have
+    // as first element the notecard's name (or BAD_NOTECARD_INDICATOR if none was
+    // found) and as subsequent elements an embedded list that was read from the
+    // notecard.  this necessarily limits the size of the notecards that we can read
+    // and return.
+string READ_SPECIFIC_NOTECARD_COMMAND = "#read_thisun#";
+    // like the read notecard command, but specifies the notecard name to use.  only that
+    // specific notecard file will be consulted.  first and second parm are still signature
+    // and response code, third parm is the notecard name.
+//
+//////////////
+// joins a list of parameters using the parameter sentinel for the library.
+string wrap_parameters(list to_flatten)
+{ return llDumpList2String(to_flatten, HUFFWARE_PARM_SEPARATOR); }
+//////////////
+
+// this function fires off a request to the noteworthy library via a link message.
+// noteworthy will look for a notecard with our particular signature in it and
+// if it finds one, it will read the configuration therein.  an empty string is
+// returned if noteworthy couldn't find anything.
+request_configuration()
+{
+    global_notecard_name = "";  // reset any previous card.
+    // try to find a notecard with our configuration.
+    response_code = -1 * (integer)randomize_within_range(23, 80000, FALSE);
+    string parms_sent = wrap_parameters([signature_required, response_code]);
+    llMessageLinked(LINK_THIS, NOTEWORTHY_HUFFWARE_ID, READ_NOTECARD_COMMAND,
+         parms_sent);
+}
+
+request_specific_configuration()
+{
+    // try to find a notecard with our configuration.
+    response_code = -1 * (integer)randomize_within_range(23, 80000, FALSE);
+    string parms_sent = wrap_parameters([signature_required, response_code, global_notecard_name]);
+    llMessageLinked(LINK_THIS, NOTEWORTHY_HUFFWARE_ID, READ_SPECIFIC_NOTECARD_COMMAND,
+         parms_sent);
+}
+
+// processes link messages received from the noteworthy library.
+process_notecard(integer which, integer num, string msg, key id)
+{
+    if (msg != READ_NOTECARD_COMMAND) return;  // not for us.
+    // process the result of reading the notecard.
+    list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
+    string notecard_name = llList2String(parms, 0);
+    integer response_for = llList2Integer(parms, 1);
+    if (response_for != response_code) return;  // oops, this isn't for us.
+    if (notecard_name != BAD_NOTECARD_TEXT) {
+        // a valid notecard has been found.
+        global_notecard_name = notecard_name;  // record its name for later use.
+        // snag all but the first two elements for our config now.
+        global_config_list = llList2List(parms, 2, -1);
+        parms = [];  // toss the old copy.
+        // and process the file as a set of definitions.
+        process_ini_config();
+        send_data_burst();  // send any pending configs out.
+        if (notecard_name != NOTECARD_READ_CONTINUATION) {
+//log_it("sending final sentinel at end of card.");
+            // blast out a fake data burst that means we're done reading.
+            global_notecard_name = FINISHED_READING_NOTECARDS;
+            global_responses += [ FINISHED_READING_NOTECARDS ];
+            send_data_burst();
+        }
+//log_it("after config read, memory left=" + (string)llGetFreeMemory());
+    } else {
+        // we hated the notecards we found, or there were none.
+        // send a failure response.
+        llMessageLinked(LINK_THIS, CARD_CONFIGURATOR_HUFFWARE_ID + REPLY_DISTANCE,
+            CARD_CONFIG_RECEIVED_ALERT, wrap_parameters([BAD_NOTECARD_TEXT]));
+    }
+}
+
+///////////////
+
+// sends the currently held data out to whoever requested it.
+send_data_burst()
+{
+    if (!llGetListLength(global_responses)) return;  // nothing to send.
+//log_it("sending " + (string)llGetListLength(global_responses) + " items");
+    llMessageLinked(LINK_THIS, CARD_CONFIGURATOR_HUFFWARE_ID + REPLY_DISTANCE, CARD_CONFIG_RECEIVED_ALERT,
+        wrap_parameters([global_notecard_name] + global_responses));
+    global_responses = [];  // reset any items held.
+}
+
+// consumes the notecard in a very application specific way to retrieve our configuration items.
+// the example script only looks for two variables: name and description.  if those are found in
+// the sample card, then they are proudly shown.
+parse_variable_definition(string to_parse)
+{
+    string content;  // filled after finding a variable name.
+    list x_y = separate_variable_definition(to_parse);
+    string x = llList2String(x_y, 0);
+    if (!llGetListLength(good_prefixes)) {
+        global_responses += x_y;
+    } else {
+        integer indy;
+        for (indy = 0; indy < llGetListLength(good_prefixes); indy++) {
+            // if it's one of our desired prefixes, then we add it.
+            if (is_prefix(x, llList2String(good_prefixes, indy))) {
+                global_responses += x_y;
+                indy = llGetListLength(good_prefixes) + 5;  // skip rest of loop.
+            }
+        }
+    }
+    
+    if (llGetListLength(global_responses) > MAXIMUM_ITEMS_PER_BURST) {
+        send_data_burst();
+    }
+}
+
+// examines all entries that we got from the notecard to see if any contain definitions.
+// this is basically an INI file reader, but it uses a list instead of a file.
+// ini files provide a format with multiple sections of config information, like so:
+//    [section_1]
+//    name1=value1
+//    name2=value2 ...etc...
+//    [section_2]
+//    name1=value1 ...etc...
+process_ini_config()
+{
+//    log_it("scanning notecard for variable definitions...");
+    integer indy;
+    integer count = llGetListLength(global_config_list);
+    // iterate across the items in our configuration to look for ones that are not done yet.
+    for (indy = 0; indy < count; indy++) {
+        string line = llList2String(global_config_list, indy);
+//log_it("ini proc: " + line);
+        // search for a section beginning.
+        if (llGetSubString(line, 0, 0) == "[") {
+            // we found the start of a section name.  now read the contents.
+//            log_it("reading section: " + llGetSubString(line, 1, -2));
+//            global_responses += [ line, "e" ];
+            indy++;  // skip the section line.
+        }
+        integer sec_indy;
+        for (sec_indy = indy; sec_indy < count; sec_indy++) {
+            // read the lines in the section.
+            line = llList2String(global_config_list, sec_indy);
+            if (llGetSubString(line, 0, 0) != "[") {
+                // try to interpret this line as a variable setting.  this is just
+                // one example of a way to handle the config file; one might instead
+                // want to do something below once a whole section is read.
+                parse_variable_definition(line);
+            } else {
+                // we're at the beginning of a new section now, so start processing its
+                // configuration in the outer loop.
+                indy = sec_indy - 1;  // set indy to proper beginning of section.
+                sec_indy = count + 3;  // skip remainder of inner loop.
+            }
+        }
+        if (sec_indy == count) indy = count + 3;  // skip out of loop now.
+    }
+    global_config_list = [];  // we ate it!
+}
+
+initialize_specific(string specific_card)
+{
+    // reset our relevant variables.
+    global_notecard_name = specific_card;
+    global_config_list = [];
+    global_responses = [];
+
+    // request that the noteworthy library start looking for our notecard.
+    request_specific_configuration();
+}
+
+initialize()
+{
+    // reset our relevant variables.
+    global_notecard_name = "";
+    global_config_list = [];
+    global_responses = [];
+
+    // request that the noteworthy library start looking for our notecard.
+    request_configuration();
+}
+
+//////////////
+// from hufflets...
+//
+integer debug_num = 0;
+
+// a debugging output method.  can be disabled entirely in one place.
+log_it(string to_say)
+{
+    debug_num++;
+    // tell this to the owner.    
+    llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on open chat, but use an unusual channel.
+//    llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+}
+
+integer find_substring(string full_string, string pattern)
+{
+    string full_lower = llToLower(full_string);
+    return llSubStringIndex(full_lower, pattern);
+}
+
+//////////////
+
+// locates the string "text" in the list to "search_in".
+integer find_in_list(list search_in, string text)
+{ 
+    integer len = llGetListLength(search_in);
+    integer i; 
+    for (i = 0; i < len; i++) { 
+        if (llList2String(search_in, i) == text) 
+            return i; 
+    } 
+    return -1;
+}
+
+// returns TRUE if the "prefix" string is the first part of "compare_with".
+integer is_prefix(string compare_with, string prefix)
+{ return (llSubStringIndex(compare_with, prefix) == 0); }
+
+// returns a number at most "maximum" and at least "minimum".
+// if "allow_negative" is TRUE, then the return may be positive or negative.
+float randomize_within_range(float minimum, float maximum, integer allow_negative)
+{
+    if (minimum > maximum) {
+        // flip the two if they are reversed.
+        float temp = minimum; minimum = maximum; maximum = temp;
+    }
+    float to_return = minimum + llFrand(maximum - minimum);
+    if (allow_negative) {
+        if (llFrand(1.0) < 0.5) to_return *= -1.0;
+    }
+    return to_return;
+}
+
+// strips the spaces off of the beginning and end of a string.
+string strip_spaces(string to_strip)
+{
+    // clean out initial spaces.
+    while (llGetSubString(to_strip, 0, 0) == " ")
+        to_strip = llDeleteSubString(to_strip, 0, 0);
+    // clean out ending spaces.
+    while (llGetSubString(to_strip, -1, -1) == " ")
+        to_strip = llDeleteSubString(to_strip, -1, -1);
+    return to_strip;
+}
+
+// parses a variable definition to find the name of the variable and its value.
+// this returns two strings [X, Y], if "to_split" is in the form X=Y.
+list separate_variable_definition(string to_split)
+{
+    integer equals_indy = llSubStringIndex(to_split, "=");
+    // we don't support missing an equals sign, and we don't support it as the first character.
+    if (equals_indy <= 0) return [];  // no match.
+    string x = llGetSubString(to_split, 0, equals_indy - 1);
+    string y = llGetSubString(to_split, equals_indy + 1, -1);
+    to_split = "";  // save space.
+///log_it("got x = " + x + " and y = " + y);
+    return [ strip_spaces(x), strip_spaces(y) ];
+}
+//
+// end hufflets
+//////////////
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.5.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+//log_it("invpo=" + (string)posn);
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+//log_it("pos=" + (string)space_v_posn);
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+//log_it("space v@" + (string)space_v_posn);
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+//log_it("indy=" + (string)space_v_posn);
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+//log_it("saw case of previously redundant item, aieee.  flattened: " + to_chop_up);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+default
+{
+    state_entry() { if (llSubStringIndex(llGetObjectName(),  "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry()
+    {
+        auto_retire();  // make sure newest addition is only version of script.
+    }
+
+    on_rez(integer parm) { state rerun; }
+
+    link_message(integer sender, integer num, string msg, key id) {
+        if (num == NOTEWORTHY_HUFFWARE_ID + REPLY_DISTANCE) {
+            if (msg == READ_NOTECARD_COMMAND) {
+                process_notecard(sender, num, msg, id);
+            }
+            return;
+        }
+        
+        if (num != CARD_CONFIGURATOR_HUFFWARE_ID) return;  // not even slightly for us.
+        
+        if (msg == BEGIN_READING_NOTECARD_COMMAND) {
+            list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
+            signature_required = llList2String(parms, 0);
+            string prefixes_wrap = llList2String(parms, 1);
+//log_it("pref raw is: " + prefixes_wrap);
+            good_prefixes = llParseString2List(prefixes_wrap, [HUFFWARE_ITEM_SEPARATOR], []);
+//log_it("signature to find: " + signature_required);
+            initialize();
+//log_it("prefixes are: " + (string)good_prefixes);
+//log_it("began reading, memory left=" + (string)llGetFreeMemory());
+            return;
+        }
+        if (msg == READ_PARTICULAR_NOTECARD_COMMAND) {
+            list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
+            signature_required = llList2String(parms, 0);
+            string prefixes_wrap = llList2String(parms, 1);
+            string specific_card = llList2String(parms, 2);
+//log_it("pref raw is: " + prefixes_wrap);
+            good_prefixes = llParseString2List(prefixes_wrap, [HUFFWARE_ITEM_SEPARATOR], []);
+//log_it("signature to find: " + signature_required);
+            initialize_specific(specific_card);
+//log_it("prefixes are: " + (string)good_prefixes);
+//log_it("notecard is: " + specific_card);
+//log_it("began reading, memory left=" + (string)llGetFreeMemory());
+            return;
+        }
+    }
+}
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/clear_text_and_effects_v2.2.txt b/huffware/huffotronic_updater_freebie_v5.3/clear_text_and_effects_v2.2.txt
new file mode 100755 (executable)
index 0000000..d550a27
--- /dev/null
@@ -0,0 +1,150 @@
+
+// huffware script: clear text and effects, by fred huffhines
+//
+// a simple helper for cleaning the text off of objects after a script has
+// set the sit text or main text.  it also stops any particle systems that
+// had been running previously and stops any sensors.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.5.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+//log_it("invpo=" + (string)posn);
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+//log_it("pos=" + (string)space_v_posn);
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+//log_it("space v@" + (string)space_v_posn);
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+//log_it("indy=" + (string)space_v_posn);
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+//log_it("saw case of previously redundant item, aieee.  flattened: " + to_chop_up);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+default
+{
+    state_entry() { if (llSubStringIndex(llGetObjectName(),  "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry()
+    {
+        auto_retire();
+
+        llSetText("", <0,0,0>, 0);  // clear any text above object.
+        llSetSitText("Sit Here");  // reset sit text to default state.
+        llSetTouchText("Touch");  // similarly for touch.
+        llSitTarget(ZERO_VECTOR, ZERO_ROTATION);  // reset sit target position.
+
+        llParticleSystem([]);  // turn off all particles.
+
+        llSensorRemove();  // stop any running sensors.
+
+        llTargetOmega(<0.0, 0.0, 0.0>, 0, 1.0);  // stop rotations of object.
+
+        llSetTextureAnim(0, ALL_SIDES, 0, 0, 0, 0, 0);  // stop texture animations.
+
+        llSetLinkAlpha(LINK_SET, 1.0, ALL_SIDES);  // turn off any transparency.
+        llSetLinkColor(LINK_SET, <1.0, 1.0, 1.0>, ALL_SIDES);  // reset color to white.
+
+        // keep it from being physical and from disapparating.
+        llSetPrimitiveParams([PRIM_TEMP_ON_REZ, FALSE, PRIM_PHYSICS, FALSE]);
+    }
+
+    // make sure that we apply these settings when the object is rezzed, if it
+    // had been stored with annoying text or whatever and needs to be cleared.
+    on_rez(integer parm) { llResetScript(); }
+}
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/comfortable_sitting_v7.5.txt b/huffware/huffotronic_updater_freebie_v5.3/comfortable_sitting_v7.5.txt
new file mode 100755 (executable)
index 0000000..202c58f
--- /dev/null
@@ -0,0 +1,882 @@
+
+// huffware script: comfortable sitting, by fred huffhines.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+//////////////
+
+// global constants...
+
+string NOTEWORTHY_SIGNATURE = "#sitting";
+    // the notecard must begin with this as its first line for it to be
+    // recognized as our configuration card.
+
+//////////////
+
+// global variables that are loaded from a notecard.
+
+vector AVATAR_ROTATION = <0, 0, -90>;  // star chair.
+//vector AVATAR_ROTATION = <0, 0, 0>;
+    // the euler vector for rotation of the avatar after sitting, in degrees.
+    // the rotation vector should be tailored to the object.
+
+vector AVATAR_POSITION = <-0.1, -0.28, -0.1>;  // star chair.
+//vector AVATAR_POSITION = <0.34, 0, 0>;
+    // the position of the sitting offset from the object center.   also needs to be
+    // tailored to the particular object.
+
+integer GOTO_WHICH_FLOOR = 1;
+    // when serving as an elevator, this is a way to cause the teleport offset
+    // to go to a particular floor.  the real z position is calculated from
+    // this times the floor size in meters.  note that floors are numbered
+    // starting at 1 (one).
+    
+float FLOOR_SIZE_IN_METERS = 0.0;
+    // when the script is used in an elevator, this specifies the height of the
+    // floors.  our current scheme will only work if that is constant between
+    // the floors.
+
+float BASE_FLOOR_HEIGHT = 0.0;
+    // the position of the first floor in meters.  this will not affect the
+    // position calculations unless floor size is non-zero.
+
+integer UNSEAT_AFTERWARDS = FALSE;
+    // if this is true, then the avatar is unseated just after sitting down.
+    
+float PAUSE_BEFORE_EVICTION = 0.28;
+    // the number of seconds that the avatars get to sit before we pop them
+    // out of the chair/teleporter/whatever.
+
+vector CAMERA_EYE_OFFSET = <3, 2, 1.5>;  // star chair.
+    // the offset for the camera after the avatar sits down.
+//relative to the avatar?
+
+vector CAMERA_TARGET = <-3, 0, 1>;  // star chair.
+    // the location where the camera is looking at once the avatar sits down.
+//relative to the avatar?
+
+//////////////
+
+// global variables used in processing notecards...
+
+integer pending_response_id;  // set to uniquely identify the notecard read in progress.
+list global_config_list;  // a collection of configuration parameters from our notecard.
+integer global_config_index;  // allows wrap-around feature, which we don't use here.
+
+//////////////
+
+// interfaces for library scripts we rely on...
+
+// requires noteworthy library.
+// in this odd case, where we are trying to shrink script count, the noteworthy library
+// is embedded inside here.
+//////////////
+// do not redefine these constants.
+integer NOTEWORTHY_HUFFWARE_ID = 10010;
+    // the unique id within the huffware system for the noteworthy script to
+    // accept commands on.  this is used in llMessageLinked as the num parameter.
+string HUFFWARE_PARM_SEPARATOR = "{~~~}";
+    // this pattern is an uncommon thing to see in text, so we use it to separate
+    // our commands in link messages.
+string HUFFWARE_ITEM_SEPARATOR = "{|||}";
+    // used to separate lists of items from each other when stored inside a parameter.
+    // this allows lists to be passed as single string parameters if needed.
+integer REPLY_DISTANCE = 100008;  // offset added to service's huffware id in reply IDs.
+string BAD_NOTECARD_INDICATOR = "bad_notecard";
+    // indicates that the notecard reading process has failed to find an appropriate one.
+string NOTECARD_READ_CONTINUATION = "continue!";
+    // returned as first parameter if there is still more data to handle.
+// commands available via the noteworthy library:
+string READ_NOTECARD_COMMAND = "#read_note#";
+    // command used to tell the script to read notecards.  needs a signature to find
+    // in the card as the first parameter, and a randomly generated response code for
+    // the second parameter.  the response code is used to uniquely identify a set of
+    // pending notecard readings (hopefully).  the signature can be blank.
+    // the results will be fired back as the string value returned, which will have
+    // as first element the notecard's name (or BAD_NOTECARD_INDICATOR if none was
+    // found) and as subsequent elements an embedded list that was read from the
+    // notecard.  this necessarily limits the size of the notecards that we can read
+    // and return.
+string READ_SPECIFIC_NOTECARD_COMMAND = "#read_thisun#";
+    // like the read notecard command, but specifies the notecard name to use.  only that
+    // specific notecard file will be consulted.  first and second parm are still signature
+    // and response code, third parm is the notecard name.
+//
+//////////////
+// joins a list of parameters using the parameter sentinel for the library.
+string wrap_parameters(list to_flatten)
+{ return llDumpList2String(to_flatten, HUFFWARE_PARM_SEPARATOR); }
+//////////////
+
+// establishes the sitting parameters including camera offsets.
+setup_seating_arrangements()
+{
+    llUnSit(llAvatarOnSitTarget());  // no one gets to ignore a script change.        
+    vector new_rot = AVATAR_ROTATION;
+    new_rot *= DEG_TO_RAD;  // convert to radians.
+    rotation quat = llEuler2Rot(new_rot);  // convert to quaternion.
+    // get our position set up and take into account the elevator position.
+    vector position = AVATAR_POSITION;
+//rename that variable to be "which floor to go to"
+    if (FLOOR_SIZE_IN_METERS != 0) {
+        vector temp = llGetPos();
+        integer my_floor = (integer) ((temp.z - BASE_FLOOR_HEIGHT ) / FLOOR_SIZE_IN_METERS) + 1;
+//log_it("my floor is " + (string)my_floor);
+        float add_in = (float)(GOTO_WHICH_FLOOR - my_floor) * FLOOR_SIZE_IN_METERS;
+//log_it("decided to add in z: " + (string)add_in);
+        position += <0, 0, add_in>;        
+    }
+    // also we make this absolute by taking out the object's own rotation.
+    // it's hard enough in life for z components to not mean z axis.
+    position /= llGetRot();
+    llSitTarget(position, quat / llGetRot());
+//hmmm: do we need same rot treatment on camera things?    
+//hmmm: trying it.
+    // now set the camera position to avoid having random viewpoint.
+    llSetCameraEyeOffset(CAMERA_EYE_OFFSET / llGetRot());
+    llSetCameraAtOffset(CAMERA_TARGET / llGetRot());
+}
+
+// this function fires off a request to the noteworthy library via a link message.
+// noteworthy will look for a notecard with our particular signature in it and
+// if it finds one, it will read the configuration therein.  an empty string is
+// returned if noteworthy couldn't find anything.
+request_configuration()
+{
+    // try to find a notecard with our configuration.
+    pending_response_id = -1 * (integer)randomize_within_range(23, 80000, FALSE);
+    string parms_sent = wrap_parameters([NOTEWORTHY_SIGNATURE, pending_response_id]);
+//call direct into noteworthy.
+noteworthy_handle_link_message(LINK_THIS, NOTEWORTHY_HUFFWARE_ID, READ_NOTECARD_COMMAND, parms_sent);
+        
+//    llMessageLinked(LINK_THIS, NOTEWORTHY_HUFFWARE_ID, READ_NOTECARD_COMMAND,
+//         parms_sent);
+}
+
+// processes link messages received from the noteworthy library.
+handle_link_message(integer which, integer num, string msg, key id)
+{
+    if ( (num != NOTEWORTHY_HUFFWARE_ID + REPLY_DISTANCE)
+            || (msg != READ_NOTECARD_COMMAND) )
+        return;  // not for us.
+//log_it("handl: msg=" + msg + " id=" + id);
+    // process the result of reading the notecard.
+    list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
+    string notecard_name = llList2String(parms, 0);
+//log_it("raw is " + llList2String(parms, 1));
+    integer response_for = llList2Integer(parms, 1);
+//log_it("resp cod=" + pending_response_id + " but this for=" + response_for);
+//if (response_for != pending_response_id) log_it("bad response code???");    
+    if (response_for != pending_response_id) return;  // oops, this isn't for us.
+    if (notecard_name == BAD_NOTECARD_INDICATOR) {
+        // we hated the notecards we found, or there were none.
+        log_it("We apologize; there seem to be no notecards with a first line of '"
+            + NOTEWORTHY_SIGNATURE
+            + "'.  We can't read any configuration until that situation improves.");
+    } else {
+//log_it("got to handle link");
+        // snag all but the first two elements for our config now.
+        global_config_list += llList2List(parms, 2, -1);
+        // make sure we shouldn't keep going.
+        if (notecard_name != NOTECARD_READ_CONTINUATION) {
+            // a valid notecard has been found.
+            global_config_index = 0;  // we are starting over in the config list.
+//            log_it("read notecard \"" + notecard_name + "\":");
+            // and process the file as a set of definitions.
+            process_ini_config();
+            // now that we have a new set of parameters, use them.
+            setup_seating_arrangements();
+        }
+    }
+}
+
+///////////////
+
+// consumes the notecard in a very application specific way to retrieve our configuration items.
+// the example script only looks for two variables: name and description.  if those are found in
+// the sample card, then they are proudly shown.
+parse_variable_definition(string to_parse)
+{
+    string content;  // filled after finding a variable name.
+    if ( (content = get_variable_value(to_parse, "avatar_rotation")) != "") {
+        AVATAR_ROTATION = (vector)content;
+//        log_it("** got avatar_rotation of '" + content + "'");
+    } else if ( (content = get_variable_value(to_parse, "avatar_position")) != "") {
+        AVATAR_POSITION = (vector)content;
+//        log_it("** got avatar_position of '" + content + "'");
+    } else if ( (content = get_variable_value(to_parse, "goto_which_floor")) != "") {
+        GOTO_WHICH_FLOOR = (integer)content;
+//        log_it("** got GOTO_WHICH_FLOOR of '" + content + "'");
+    } else if ( (content = get_variable_value(to_parse, "floor_size_in_meters")) != "") {
+        FLOOR_SIZE_IN_METERS = (float)content;
+//        log_it("** got FLOOR_SIZE_IN_METERS of '" + content + "'");
+    } else if ( (content = get_variable_value(to_parse, "base_floor_height")) != "") {
+        BASE_FLOOR_HEIGHT = (float)content;
+//        log_it("** got BASE_FLOOR_HEIGHT of '" + content + "'");
+    } else if ( (content = get_variable_value(to_parse, "unseat_afterwards")) != "") {
+        UNSEAT_AFTERWARDS = (integer)content;
+//        log_it("** got unseat_afterwards of '" + content + "'");
+    } else if ( (content = get_variable_value(to_parse, "pause_before_eviction")) != "") {
+        PAUSE_BEFORE_EVICTION = (float)content;
+//        log_it("** got pause_before_eviction of '" + content + "'");
+    } else if ( (content = get_variable_value(to_parse, "camera_eye_offset")) != "") {
+        CAMERA_EYE_OFFSET = (vector)content;
+//        log_it("** got camera_eye_offset of '" + content + "'");
+    } else if ( (content = get_variable_value(to_parse, "camera_target")) != "") {
+        CAMERA_TARGET = (vector)content;
+//        log_it("** got camera_target of '" + content + "'");
+//    } else {
+//        log_it("unknown variable seen: " + to_parse);
+    }
+}
+
+initialize()
+{
+    // reset our relevant variables.
+    global_config_list = [];
+    global_config_index = 0;
+
+    // announce that we're open for business.
+///    log_it("started, free mem=" + (string)llGetFreeMemory());
+
+    // request that the noteworthy library start looking for our notecard.
+    request_configuration();
+    
+    integer indy = 32;
+    while (indy >= 0) {
+        indy = find_in_inventory_partial("noteworthy", INVENTORY_SCRIPT);
+        if (indy >= 0) {
+            llRemoveInventory(llGetInventoryName(INVENTORY_SCRIPT, indy));
+        }
+    }
+    
+}
+
+//////////////
+// from hufflets...
+//
+
+integer debug_num = 0;
+
+// a debugging output method.  can be disabled entirely in one place.
+log_it(string to_say)
+{
+    debug_num++;
+    // tell this to the owner.    
+    llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on an unusual channel for chat if it's not intended for general public.
+//    llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on open chat that anyone can hear.  we take off the bling for this one.
+//    llSay(0, to_say);
+}
+
+// locates the string "text" in the list to "search_in".
+integer find_in_list(list search_in, string text)
+{ 
+    integer len = llGetListLength(search_in);
+    integer i; 
+    for (i = 0; i < len; i++) { 
+        if (llList2String(search_in, i) == text) 
+            return i; 
+    } 
+    return -1;
+}
+
+// returns TRUE if the "prefix" string is the first part of "compare_with".
+integer is_prefix(string compare_with, string prefix)
+{ return (llSubStringIndex(compare_with, prefix) == 0); }
+
+// returns a number at most "maximum" and at least "minimum".
+// if "allow_negative" is TRUE, then the return may be positive or negative.
+float randomize_within_range(float minimum, float maximum, integer allow_negative)
+{
+    if (minimum > maximum) {
+        // flip the two if they are reversed.
+        float temp = minimum; minimum = maximum; maximum = temp;
+    }
+    float to_return = minimum + llFrand(maximum - minimum);
+    if (allow_negative) {
+        if (llFrand(1.0) < 0.5) to_return *= -1.0;
+    }
+    return to_return;
+}
+
+// strips the spaces off of the beginning and end of a string.
+string strip_spaces(string to_strip)
+{
+    // clean out initial spaces.
+    while (llGetSubString(to_strip, 0, 0) == " ")
+        to_strip = llDeleteSubString(to_strip, 0, 0);
+    // clean out ending spaces.
+    while (llGetSubString(to_strip, -1, -1) == " ")
+        to_strip = llDeleteSubString(to_strip, -1, -1);
+    return to_strip;
+}
+
+// parses a variable definition to find the name of the variable and its value.
+// this returns two strings [X, Y], if "to_split" is in the form X=Y.
+list separate_variable_definition(string to_split)
+{
+    integer equals_indy = llSubStringIndex(to_split, "=");
+    // we don't support missing an equals sign, and we don't support it as the first character.
+    if (equals_indy <= 0) return [];  // no match.
+    string x = llGetSubString(to_split, 0, equals_indy - 1);
+    string y = llGetSubString(to_split, equals_indy + 1, -1);
+    to_split = "";  // save space.
+    return [ strip_spaces(x), strip_spaces(y) ];
+}
+
+// returns a non-empty string if "to_check" defines a value for "variable_name".
+// this must be in the form "X=Y", where X is the variable_name and Y is the value.
+string get_variable_value(string to_check, string variable_name)
+{
+    list x_y = separate_variable_definition(to_check);
+    if (llGetListLength(x_y) != 2) return "";  // failure to parse a variable def at all.
+    if (!is_prefix(llList2String(x_y, 0), variable_name)) return "";  // no match.
+    return llList2String(x_y, 1);  // a match!
+}
+
+// examines all entries that we got from the notecard to see if any contain definitions.
+// this is basically an INI file reader, but it uses a list instead of a file.
+// ini files provide a format with multiple sections of config information, like so:
+//    [section_1]
+//    name1=value1
+//    name2=value2 ...etc...
+//    [section_2]
+//    name1=value1 ...etc...
+process_ini_config()
+{
+//    log_it("scanning notecard for variable definitions...");
+    integer indy;
+    integer count = llGetListLength(global_config_list);
+    string section_name;  // set later if we see one.
+
+    // iterate across the items in our configuration to look for ones that are not done yet.            
+    for (indy = global_config_index; indy < count; indy++) {
+        string line = llList2String(global_config_list, indy);
+        // search for a section beginning.
+        if (llGetSubString(line, 0, 0) == "[") {
+            // we found the start of a section name.  now read the contents.
+            indy++;  // skip section line.
+            section_name = llGetSubString(line, 1, -2);
+            log_it("reading section: " + section_name);
+        }
+        integer sec_indy;
+        for (sec_indy = indy; sec_indy < count; sec_indy++) {
+            // read the lines in the section.
+            line = llList2String(global_config_list, sec_indy);
+            if (llGetSubString(line, 0, 0) != "[") {
+                // try to interpret this line as a variable setting.  this is just
+                // one example of a way to handle the config file; one might instead
+                // want to do something below once a whole section is read.
+                parse_variable_definition(line);
+                indy = sec_indy;  // track that we've passed this line.
+            } else {
+                // we're at the beginning of a new section now, so start processing its
+                // configuration in the outer loop.
+                indy = sec_indy - 1;  // set indy to proper beginning of section.
+                global_config_index = indy;  // remember where we had read to.
+                sec_indy = count + 3;  // skip remainder of inner loop.
+            }
+        }
+    }
+
+    global_config_index = 0;  // reset outer position if want to re-read.
+}
+
+// locates the item with "name_to_find" in the inventory items with the "type".
+// a value from 0 to N-1 is returned if it's found, where N is the number of
+// items in the inventory.
+integer find_in_inventory_partial(string name_to_find, integer inv_type)
+{
+    integer num_inv = llGetInventoryNumber(inv_type);
+    if (num_inv == 0) return -1;  // nothing there!
+    integer inv;
+    for (inv = 0; inv < num_inv; inv++) {
+        if (is_prefix(llGetInventoryName(inv_type, inv), name_to_find))
+            return inv;
+    }
+    return -2;  // failed to find it.
+}
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 1.9.
+// distributed under BSD-like license.
+//   partly based on the self-upgrading scripts from markov brodsky and jippen faddoul.
+// the function auto_retire() should be added *inside* a version numbered script that
+// you wish to give the capability of self-upgrading.
+//   this script supports a notation for versions embedded in script names where a 'v'
+// is followed by a number in the form "major.minor", e.g. "grunkle script by ted v8.2".
+// when the containing script is dropped into an object with a different version, the
+// most recent version eats any existing ones.
+//   keep in mind that this code must be *copied* into your script you wish to add
+// auto-retirement capability to.
+//
+// example usage of the auto-retirement script:
+//
+// default {
+//    state_entry() {
+//        auto_retire();  // make sure newest addition is only version of script.
+//    }
+// }
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                if ((float)inv_version_string < (float)version_string) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    if (llSubStringIndex(to_chop_up, " ") < 0) return [];  // no space found, not a valid name to work on.
+        
+    string basename = to_chop_up;  // script name with no version attached.
+    
+    integer posn;
+    // minimum script name is 2 characters plus version.
+    for (posn = llStringLength(to_chop_up) - 1;
+        (posn >= 2) && (llGetSubString(to_chop_up, posn, posn) != " ");
+        posn--) {
+        // find the space.  do nothing else.
+    }
+    if (posn < 2) return [];  // no space found.
+    string full_suffix = llGetSubString(to_chop_up, posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+// end hufflets
+//////////////
+
+//////////////
+// from noteworthy:
+
+// huffware script: noteworthy library, by fred huffhines.
+//
+// a handy approach to reading a notecard.  this version supports requiring
+// a 'signature' in the notecard's first line, so that multiple notecards can
+// exist in an object without being misinterpreted.  the script is accessed via
+// its link message API, so it can be used in an object without all this code
+// needing to be embedded in another script.  it also supports queuing up requests
+// to read notecards, so multiple scripts can use it to read their specific
+// notecards without any special handling (besides waiting a bit longer).
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+// constants that can be adapted to your needs...
+
+integer DEBUGGING = FALSE;
+    // if this is true, then a lot of extra noise is generated when notecards are read.
+
+float TIME_OUT_ON_ONE_NOTECARD = 120.0;
+    // we allow one line of a notecard this long to be read before we decide it's hosed.
+    // some sims are very very slow, and a time-out of one minute has been found lacking;
+    // we saw at least one case where the first notecard line to be read took longer than
+    // 60 seconds.  that's why we keep cranking this time-out up...
+
+// constants that should not be changed...
+
+// outcomes from handling a line in a notecard.
+integer STILL_READING = -8;  // the notecard seems good, but isn't finished.
+integer BAD_NOTECARD = -9;  // this notecard doesn't have our signature.
+integer DONE_READING = -10;  // the notecard is finished being read.
+
+integer LIST_ELEMENT_GUESS = 200;  // guess at how many bytes average list element uses.
+
+integer MAXIMUM_LINES_USED = 4;
+    // we will read this many lines at a time from the appropriate notecard.
+// global variables...
+
+string requested_signature = "";
+    // if this is non-empty, then it must be found in the first line of the script.
+
+integer only_read_one_notecard = FALSE;  // true if just one specific notecard should be used.
+
+string global_notecard_name;  // the name of the card we're reading now.
+key global_query_id = NULL_KEY;  // the identifier of our data query.
+integer current_response_code = 0;  // the code that our client uses for its reading.
+list global_query_contents;  // the lines we have read from the notecard.
+
+integer line_number = 0;  // which line are we at in notecard?
+
+integer found_signature_line = FALSE;  // did we find our first line in a notecard yet?
+
+integer trying_notecard_at = -1;  // where we are currently looking for a good notecard.
+
+list pending_signatures;  // signatures from queued requests for reading.
+list pending_response_codes;  // response codes for the queued requests.
+list pending_notecard_names;  // card names if it's a specific request.
+
+//////////////
+
+startup_initialize()
+{
+    llSetTimerEvent(0.0);
+    pending_signatures = [];
+    pending_response_codes = [];
+    pending_notecard_names = [];
+    current_response_code = 0;
+}
+
+reset_for_reading(string signature, integer response_code_in)
+{
+    requested_signature = signature;
+    current_response_code = response_code_in;
+    llSetTimerEvent(TIME_OUT_ON_ONE_NOTECARD);  // don't allow a read to happen forever.
+    global_query_contents = [];
+    global_notecard_name = "";
+    line_number = 0;
+    found_signature_line = FALSE;
+    trying_notecard_at = -1;
+    global_query_id = NULL_KEY;
+}
+
+// use the existing global notecard setting to start reading.
+select_specific_notecard()
+{
+    global_query_id = NULL_KEY;  // reset the query id so we don't get bogus answers.
+    line_number = 0;  // reset line number again.
+    global_query_id = llGetNotecardLine(global_notecard_name, 0);
+}
+
+// picks the notecard at the "index" (from 0 to num_notecards - 1) and
+// starts reading it.
+select_notecard(integer index)
+{
+    global_query_id = NULL_KEY;  // reset the query id so we don't get bogus answers.
+    string card_specified = llGetInventoryName(INVENTORY_NOTECARD, index);
+    if (card_specified == "") return;   // not good.  bad index.
+    global_notecard_name = card_specified;
+    line_number = 0;  // reset line number again.
+    // we have a new file name, so load up the destinations, hopefully.
+    global_query_id = llGetNotecardLine(global_notecard_name, 0);
+}
+
+// increments our index in the count of notecards that the object has, and start
+// reading the next notecard (at the index).
+integer try_next_notecard()
+{
+    if (only_read_one_notecard) {
+        return FALSE;  // we were only going to try one.
+    }
+    // reset some values that might have applied before.
+    global_notecard_name = "";
+    // skip to the next card.
+    trying_notecard_at++;
+    // make sure we're not over the count of cards.
+    if (trying_notecard_at >= llGetInventoryNumber(INVENTORY_NOTECARD)) {
+        // this is a problem.  we didn't find anything suitable.
+        return FALSE;
+    }
+    // so, now we'll try the next notecard to look for our signature.
+    select_notecard(trying_notecard_at);
+    return TRUE;
+}
+
+// process a line of text that we received from the current notecard.
+integer handle_notecard_line(key query_id, string data)
+{
+    // if we're not at the end of the notecard we're reading...
+    if (data != EOF) {
+        // there's more to read in the notecard still.
+        if (data != "") {
+            // make sure we even have a signature to look for.
+            if (!found_signature_line && (requested_signature == "")) {
+                // no signature means that we always find it.
+                found_signature_line = TRUE;
+            }
+            // did we already get our signature?  if not, see if this is it.
+            if (!found_signature_line && (data != requested_signature) ) {
+                // this is a bad notecard.  skip it.
+                if (!try_next_notecard()) {
+                    // we have no more to work with.
+                    return BAD_NOTECARD;
+                }
+                return STILL_READING;  // we'll keep going.
+            } else if (!found_signature_line && (data == requested_signature) ) {
+                // this is a good signature line, so record that and then skip it.
+                found_signature_line = TRUE;
+            } else {
+                if (DEBUGGING
+                    && ( ( (requested_signature == "") && (line_number == 0) )
+                        || ( (requested_signature != "") && (line_number == 1) ) ) ) {
+                    log_it("started reading " + global_notecard_name + "...");
+                }
+                // don't record any lines that are comments.
+                if ( (llGetSubString(data, 0, 0) != "#")
+                    && (llGetSubString(data, 0, 0) != ";") ) {
+                    // add the non-blank line to our list.
+                    global_query_contents += data;
+                    // make sure we still have enough space to keep going.
+                    if (llGetListLength(global_query_contents) >= MAXIMUM_LINES_USED) {
+                        // ooh, make sure we pause before blowing our heap&stack.
+                        send_reply(LINK_THIS, [ NOTECARD_READ_CONTINUATION,
+                            current_response_code ], READ_NOTECARD_COMMAND, TRUE);                
+                    }
+                }
+            }
+        }
+        line_number++;  // increase the line count.
+        // reset the timer rather than timing out, if we actually got some data.
+        llSetTimerEvent(TIME_OUT_ON_ONE_NOTECARD);        
+        // request the next line from the notecard.
+        global_query_id = llGetNotecardLine(global_notecard_name, line_number);
+        if (global_query_id == NULL_KEY) {
+//log_it("failed to restart notecard reading.");
+            return DONE_READING;
+//is that the best outcome?
+        }
+        return STILL_READING;
+    } else {
+        // that's the end of the notecard.  we need some minimal verification that it
+        // wasn't full of garbage.
+        if (!found_signature_line) {
+            if (DEBUGGING) log_it("never found signature in " + global_notecard_name);
+            if (!try_next_notecard()) {
+                return BAD_NOTECARD;  // we failed to find a good line?
+            } else {
+                // the next notecard's coming through now.
+                return STILL_READING;
+            }
+        } else {
+//            if (DEBUGGING) log_it("found signature.");
+            // saw the signature line, so this is a good one.
+            return DONE_READING;
+        }
+    }
+}
+
+// only sends reply; does not reset notecard process.
+send_reply(integer destination, list parms, string command,
+    integer include_query)
+{
+//log_it("froyo: curre code=" + current_response_code);
+//integer items = llGetListLength(parms);
+//if (include_query) items += llGetListLength(global_query_contents);
+//log_it("pre-sending " + (string)items + " items, mem=" + (string)llGetFreeMemory());
+
+   if (!include_query) {
+        handle_link_message(destination, NOTEWORTHY_HUFFWARE_ID + REPLY_DISTANCE,
+            command, llDumpList2String(parms, HUFFWARE_PARM_SEPARATOR));
+    } else {
+        handle_link_message(destination, NOTEWORTHY_HUFFWARE_ID + REPLY_DISTANCE,
+            command,
+            llDumpList2String(parms + global_query_contents, HUFFWARE_PARM_SEPARATOR));
+    }
+    global_query_contents = [];
+//log_it("post-sending, mem=" + (string)llGetFreeMemory());
+}
+
+// a simple version of a reply for a command that has been executed.  the parameters
+// might contain an outcome or result of the operation that was requested.
+send_reply_and_reset(integer destination, list parms, string command,
+    integer include_query)
+{
+    llSetTimerEvent(0.0);  // stop the timer, since we're about to reply.
+    send_reply(destination, parms, command, include_query);
+//log_it("about to reset response code");
+    current_response_code = 0;  // reset back to default so we can start another read.
+    global_query_id = NULL_KEY;  // reset so we accept no more data.
+}
+
+// if there are other pending notecard reads, this goes to the next one listed.
+dequeue_next_request()
+{
+    if (llGetListLength(pending_signatures)) {
+        // get the info from the next pending item.
+        string sig = llList2String(pending_signatures, 0);
+        integer response_code_temp = llList2Integer(pending_response_codes, 0);
+        string notecard = llList2String(pending_notecard_names, 0);
+        // whack the head of the queue since we grabbed the info.
+        pending_signatures = llDeleteSubList(pending_signatures, 0, 0);
+        pending_response_codes = llDeleteSubList(pending_response_codes, 0, 0);
+        pending_notecard_names = llDeleteSubList(pending_notecard_names, 0, 0);
+        if (llStringLength(notecard)) {
+            global_notecard_name = notecard;
+            select_specific_notecard();
+        } else {
+            reset_for_reading(sig, response_code_temp);
+        }
+    }
+}
+
+// deals with one data server answer from the notecard.
+process_notecard_line(key query_id, string data)
+{
+    // try to consume a line from the notecard.
+    integer outcome = handle_notecard_line(query_id, data);
+    if (outcome == DONE_READING) {
+        // that was a valid notecard and we read all of it.
+        if (DEBUGGING) log_it("finished reading " + global_notecard_name + ".");
+        // send back the results.
+        send_reply_and_reset(LINK_THIS, [ global_notecard_name, current_response_code ],
+            READ_NOTECARD_COMMAND, TRUE);
+    } else if (outcome == BAD_NOTECARD) {
+        // bail; this problem must be addressed by other means.
+        if (DEBUGGING) log_it("failed to find an appropriate notecard");
+        send_reply_and_reset(LINK_THIS, [ BAD_NOTECARD_INDICATOR, current_response_code ],
+            READ_NOTECARD_COMMAND, FALSE);
+    } else if (outcome == STILL_READING) {
+        // we have a good card and are still processing it.
+        return;
+    } else {
+        if (DEBUGGING) log_it("unknown outcome from handle_notecard_line");
+        // again, bail out.  we have no idea what happened with this.
+        send_reply_and_reset(LINK_THIS, [ BAD_NOTECARD_INDICATOR, current_response_code ],
+            READ_NOTECARD_COMMAND, FALSE);
+    }
+    // if we have reached here, we should crank up the next queued notecard reading.
+    dequeue_next_request();
+}
+
+// processes requests from our users.
+noteworthy_handle_link_message(integer which, integer num, string msg, key id)
+{
+    if (num != NOTEWORTHY_HUFFWARE_ID) return;  // not for us.
+
+    if (msg == READ_NOTECARD_COMMAND) {
+        only_read_one_notecard = FALSE;  // general inquiry for any card.
+        list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
+//log_it("read notecard--parms are: " + (string)parms);
+        string signature = llList2String(parms, 0);
+        integer response_code_temp = llList2Integer(parms, 1);
+//log_it("got signature " + signature + " and respcode " + (string)response_code_temp);
+//holding:        if (!current_response_code) {
+            // go ahead and process this request; we aren't busy.
+            reset_for_reading(signature, response_code_temp);
+            if (!try_next_notecard()) {
+                if (DEBUGGING) log_it("failed to find any appropriate notecards at all.");
+                send_reply_and_reset(LINK_THIS, [ BAD_NOTECARD_INDICATOR, response_code_temp ],
+                    READ_NOTECARD_COMMAND, FALSE);
+                return;
+            }
+//holding:        } else {
+//holding:            // we're already busy.
+//holding://            send_reply_and_reset(LINK_THIS, [ BUSY_READING_INDICATOR, response_code_temp ],
+//holding://                READ_NOTECARD_COMMAND, FALSE);
+//holding:            // stack this request; another is in progress.
+//holding:            pending_signatures += signature;
+//holding:            pending_response_codes += response_code_temp;
+//holding:            pending_notecard_names += "";
+//holding:        }
+    } else if (msg == READ_SPECIFIC_NOTECARD_COMMAND) {
+        only_read_one_notecard = TRUE;  // they want one particular card.
+        list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
+//log_it("read specific--parms are: " + (string)parms);
+        string signature = llList2String(parms, 0);
+        integer response_code_temp = llList2Integer(parms, 1);
+        string notecard_name = llList2String(parms, 2);
+//log_it("got signature " + signature + " and respcode " + (string)response_code_temp);
+//holding:        if (!current_response_code) {
+            // go ahead and process this request; we aren't busy.
+            reset_for_reading(signature, response_code_temp);
+            global_notecard_name = notecard_name;  // set our global.
+            select_specific_notecard();
+//holding:        } else {
+//holding:            // we're already busy.
+//holding://            send_reply_and_reset(LINK_THIS, [ BUSY_READING_INDICATOR, response_code_temp ],
+//holding://                READ_NOTECARD_COMMAND, FALSE);
+//holding:            // stack this request; another is in progress.
+//holding:            pending_signatures += signature;
+//holding:            pending_response_codes += response_code_temp;
+//holding:            pending_notecard_names += notecard_name;
+//holding:        }
+    }
+}
+
+    
+default
+{
+    state_entry() { if (llSubStringIndex(llGetObjectName(),  "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry() {
+        auto_retire();  // make sure newest addition is only version of script.
+/////not needed now        llSleep(1.0);  // snooze just a bit to let noteworthy start up?
+        startup_initialize();
+        initialize();  // start asking about the notecards.
+        setup_seating_arrangements();  // use our current defaults for sitting posn.
+    }
+
+    on_rez(integer parm) { llResetScript(); }
+
+    changed(integer change) {
+        if (change & CHANGED_INVENTORY) {
+//            llSleep(3.14159265358);  // delay to avoid interfering with upgrade.
+            llResetScript(); 
+        }
+        if (!(change & CHANGED_LINK)) return;  // don't care.
+        if (!UNSEAT_AFTERWARDS) return;  // nothing else below is needed.
+        if (llAvatarOnSitTarget() == NULL_KEY) return;  // no one there, so ditto.
+        // now give them a bit of time to rest before dumping them.
+        llSetTimerEvent(PAUSE_BEFORE_EVICTION);
+    }
+    
+    timer() {
+        if (current_response_code != 0) {
+            llSetTimerEvent(0.0);  // stop any timer now.
+            // let the caller know this has failed out.
+            if (DEBUGGING) log_it("time out processing '" + requested_signature + "'");
+            send_reply_and_reset(LINK_THIS, [ BAD_NOTECARD_INDICATOR, current_response_code ],
+                READ_NOTECARD_COMMAND, FALSE);
+            current_response_code = 0;  // we gave up on that one.
+            dequeue_next_request();  // get next reading started if we have anything to read.
+        } else {
+            // perform short range teleport, effectively...
+            llUnSit(llAvatarOnSitTarget());  // ha, got that guy back up.
+            llSetTimerEvent(0.0);  // reset timer.
+        }
+    }
+
+    // process the response from the noteworthy library.
+    link_message(integer which, integer num, string msg, key id)
+    { handle_link_message(which, num, msg, id); }
+
+    dataserver(key query_id, string data) {
+        // make sure this data is for us.
+        if (global_query_id != query_id) return;
+        // yep, seems to be.
+        process_notecard_line(query_id, data);
+    }            
+}
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/create_objects_v1.3.txt b/huffware/huffotronic_updater_freebie_v5.3/create_objects_v1.3.txt
new file mode 100755 (executable)
index 0000000..4311856
--- /dev/null
@@ -0,0 +1,176 @@
+
+// huffware script: create object, by fred huffhines.
+//
+// this script rezzes new objects from inventory every time the user clicks.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+// from hufflets:
+
+// returns a number at most "maximum" and at least "minimum".
+// if "allow_negative" is TRUE, then the return may be positive or negative.
+float randomize_within_range(float minimum, float maximum, integer allow_negative)
+{
+    if (minimum > maximum) {
+        // flip the two if they are reversed.
+        float temp = minimum; minimum = maximum; maximum = temp;
+    }
+    float to_return = minimum + llFrand(maximum - minimum);
+    if (allow_negative) {
+        if (llFrand(1.0) < 0.5) to_return *= -1.0;
+    }
+    return to_return;
+}
+
+// returns a random vector where x,y,z will be between "minimums" and "maximums"
+// x,y,z components.  if "allow_negative" is true, then any component will
+// randomly be negative or positive.
+vector random_bound_vector(vector minimums, vector maximums, integer allow_negative)
+{
+    return <randomize_within_range(minimums.x, maximums.x, allow_negative),
+        randomize_within_range(minimums.y, maximums.y, allow_negative),
+        randomize_within_range(minimums.z, maximums.z, allow_negative)>;
+}
+
+// returns a vector whose components are between minimum and maximum.
+// if allow_negative is true, then they can be either positive or negative.
+vector random_vector(float minimum, float maximum, integer allow_negative)
+{
+    return random_bound_vector(<minimum, minimum, minimum>,
+        <maximum, maximum, maximum>, allow_negative);
+}
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.5.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+//log_it("invpo=" + (string)posn);
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+//log_it("pos=" + (string)space_v_posn);
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+//log_it("space v@" + (string)space_v_posn);
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+//log_it("indy=" + (string)space_v_posn);
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+//log_it("saw case of previously redundant item, aieee.  flattened: " + to_chop_up);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+// end of hufflets.
+
+default
+{
+    state_entry() { if (llSubStringIndex(llGetObjectName(),  "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry() {
+        auto_retire();
+    }
+
+    touch_start(integer total_number) {
+        if (llDetectedKey(0) != llGetOwner()) return;
+        if (llGetInventoryNumber(INVENTORY_OBJECT) < 1) {
+            llSay(0, "This object needs contents to rez; currently it has none.");
+            return;
+        }
+        integer i;
+        for (i = 0; i < llGetInventoryNumber(INVENTORY_OBJECT); i++) {
+            vector addition = random_vector(0.2, 2.0, TRUE);
+            // only allow a new z position that is higher than current value.
+            if (addition.z < 0.0) addition.z *= -1.0;
+            llRezObject(llGetInventoryName(INVENTORY_OBJECT, i), llGetPos() + addition, ZERO_VECTOR, ZERO_ROTATION, 0);
+        }
+    }
+}
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/data_cow_v3.3.txt b/huffware/huffotronic_updater_freebie_v5.3/data_cow_v3.3.txt
new file mode 100755 (executable)
index 0000000..c03cb4c
--- /dev/null
@@ -0,0 +1,227 @@
+
+// huffware script: data cow, by fred huffhines.
+//
+// a data cow is a script that manages a list of text items.  it allows an object
+// to offload the memory burden of managing a list of items, since LSL is very tight
+// on memory, even when compiled to mono.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+// global variables.
+
+list the_list;
+    // the main list that we manage here.  we support adding to it, retrieving it
+    // and modifying it via our API.
+list the_names;
+    // the short names for each item in our list.  we are basically supporting a
+    // symbol table data structure here.
+
+// link message API for the data cow library.
+//////////////
+// do not redefine these constants.
+integer DATA_COW_HUFFWARE_ID = 10017;
+    // the unique id within the huffware system for the jaunt script to
+    // accept commands on.  this is used in llMessageLinked as the num parameter.
+string HUFFWARE_PARM_SEPARATOR = "{~~~}";
+    // this pattern is an uncommon thing to see in text, so we use it to separate
+    // our commands in link messages.
+///string HUFFWARE_ITEM_SEPARATOR = "{|||}";
+    // used to separate lists of items from each other when stored inside a parameter.
+    // this allows lists to be passed as single string parameters if needed.
+integer REPLY_DISTANCE = 100008;  // offset added to service's huffware id in reply IDs.
+//////////////
+//
+string RESET_LIST_COMMAND = "reset_L";
+    // flushes out the currently held list.  does not send a reply.
+string ADD_ITEM_COMMAND = "add_I";
+    // adds items to the list.  this is a list of pairs of name/value, where the name is
+    // how the item will be looked up from the list, and the value is the contents to be stored.
+    // this command has no reply.
+string REMOVE_ITEM_COMMAND = "rm_I";
+    // accepts a list of names for items.  all the mentioned ones will be removed from the list.
+    // this command also has no reply.
+string GET_COW_LENGTH = "get_Lc";
+    // returns a single integer which is the length of the cow's list currently.
+string GET_ITEM_COMMAND = "get_I";
+    // retrieves the item's contents for a given name.  first parm is the name.  if there
+    // are other parameters, then they are taken as other items to return also.
+    // the return data will be pairs of <name, entry> for each of the names in the request
+    // that is not empty.  note that you can use an integer index by prefacing it with a
+    // '#' sign.  for example, asking for item "#32" will return index 32 in the list of items.
+string TAGGED_GET_ITEM_COMMAND = "get_T";
+    // like get item, except the first parameter is an identifier that the caller wants to
+    // use to tag this request.  the other parameters are still taken as names.  the response
+    // will contain the tag as the first item, then the <name, entry> pairs that were found.
+//////////////
+
+// sets up the data cow for business.
+initialize()
+{
+    // flush all contents.
+    the_list = [];
+    the_names = [];
+}
+
+// find a named item in our list, if it exists.
+integer lookup_item(string name)
+{
+    integer indy;
+    if (llGetSubString(name, 0, 0) == "#") {
+        // our special sign for just using an index.
+        indy = (integer)llGetSubString(name, 1, -1);
+        if ( (indy >= 0) && (indy < llGetListLength(the_names)) ) return indy;
+    } else {
+        for (indy = 0; indy < llGetListLength(the_names); indy++) {
+            if (name == llList2String(the_names, indy)) return indy;  // we know where it is now.
+        }
+    }
+    return -1;  // never found it.
+}
+
+// places a named item in our list.  if there's an existing item with
+// that name, then it's replaced.
+integer global_mem_complained = FALSE;
+add_item(string name, string content)
+{
+    if (llGetFreeMemory() < 2000) {
+        if (!global_mem_complained) {
+            global_mem_complained = TRUE;
+            llOwnerSay("out of memory at " + name);
+        }
+        return;
+    }
+    integer current = lookup_item(name);
+    if (current < 0) {
+//llOwnerSay("new item: " + name);
+        // this is a new item.
+        the_names += name;
+        the_list += content;
+    } else {
+//llOwnerSay("reusing slot " + (string)current + " for " + name);
+        // this is an old item to be replaced.
+        the_list = chop_list(the_list, 0, current - 1)
+            + [ content ]
+            + chop_list(the_list, current + 1, llGetListLength(the_list) - 1);
+    }
+//log_it("mem=" + (string)llGetFreeMemory());
+}
+
+// deletes an existing item if possible.  TRUE is returned on success.
+integer remove_item(string name)
+{
+    integer indy = lookup_item(name);
+    if (indy < 0) return FALSE;  // not present.
+    // found our sad whackee.  we know the name and content should be
+    // stored at the exact same index in the two lists.
+    the_names = chop_list(the_names, 0, indy - 1)
+        + chop_list(the_names, indy + 1, llGetListLength(the_names) - 1);
+    the_list = chop_list(the_list, 0, indy - 1)
+        + chop_list(the_list, indy + 1, llGetListLength(the_list) - 1);
+    return TRUE;  // all done here.
+}
+
+// supports our link message API by answering requests from clients.
+handle_link_message(integer sender, integer num, string msg, key id)
+{
+    if (num != DATA_COW_HUFFWARE_ID) return;  // not for us.
+//log_it("hlm--mem free=" + (string)llGetFreeMemory());
+//log_it("rcvd: " + msg + " " + (string)num + " " + (string)id);
+    if (msg == RESET_LIST_COMMAND) {
+        initialize();
+    } else if (msg == ADD_ITEM_COMMAND) {
+        list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
+        integer indy;
+        for (indy = 0; indy < llGetListLength(parms); indy += 2) {
+            add_item(llList2String(parms, indy), llList2String(parms, indy + 1));
+        }
+    } else if (msg == REMOVE_ITEM_COMMAND) {
+        list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
+        integer indy;
+        for (indy = 0; indy < llGetListLength(parms); indy++) {
+            remove_item(llList2String(parms, indy));
+        }
+    } else if (msg == GET_COW_LENGTH) {
+        list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
+        string tag = llList2String(parms, 0);
+        list to_reply;
+        if (tag != "") to_reply = [ tag ];
+        to_reply += [ llGetListLength(the_list) ];
+        send_reply(LINK_THIS, to_reply, msg);
+    } else if ( (msg == GET_ITEM_COMMAND) || (msg == TAGGED_GET_ITEM_COMMAND) ) {
+        list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
+        integer indy;
+        string tag;
+        if (msg == TAGGED_GET_ITEM_COMMAND) {
+            // extract the tag, if they want that type of command handling.
+            tag = llList2String(parms, 0);
+            parms = llDeleteSubList(parms, 0, 0);
+        }
+        list to_reply;
+        // make sure we return the tag if they gave us one.
+        if (tag != "") to_reply = [ tag ];
+        for (indy = 0; indy < llGetListLength(parms); indy++) {
+            // iterate through the parameters and reply about any that we find.
+            integer found_posn = lookup_item(llList2String(parms, indy));
+            if (found_posn >= 0) {
+                // this item did exist.
+                to_reply += [ llList2String(the_names, found_posn), llList2String(the_list, found_posn) ];
+            }
+        }
+        send_reply(LINK_THIS, to_reply, msg);
+    } 
+}
+
+//////////////
+// from hufflets...
+//
+//integer debug_num = 0;
+
+// a debugging output method.  can be disabled entirely in one place.
+//log_it(string to_say)
+//{
+//    debug_num++;
+//    // tell this to the owner.    
+//    llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+//    // say this on open chat, but use an unusual channel.
+////    llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+//}
+
+// returns the portion of the list between start and end, but only if they are
+// valid compared with the list length.  an attempt to use negative start or
+// end values also returns a blank list.
+list chop_list(list to_chop, integer start, integer end)
+{
+    integer last_len = llGetListLength(to_chop) - 1;
+    if ( (start < 0) || (end < 0) || (start > last_len) || (end > last_len) ) return [];
+    return llList2List(to_chop, start, end);
+}
+
+// a simple version of a reply for a command that has been executed.  the parameters
+// might contain an outcome or result of the operation that was requested.
+send_reply(integer destination, list parms, string command)
+{
+    llMessageLinked(destination, DATA_COW_HUFFWARE_ID + REPLY_DISTANCE,
+        command,  llDumpList2String(parms, HUFFWARE_PARM_SEPARATOR));
+}
+
+//
+// end hufflets
+//////////////
+
+default
+{
+    state_entry() { if (llSubStringIndex(llGetObjectName(),  "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry() { initialize(); }
+
+    link_message(integer sender, integer num, string msg, key id)
+    { handle_link_message(sender, num, msg, id); }
+}
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/fade_opacity_v3.7.txt b/huffware/huffotronic_updater_freebie_v5.3/fade_opacity_v3.7.txt
new file mode 100755 (executable)
index 0000000..1671e5a
--- /dev/null
@@ -0,0 +1,201 @@
+
+// huffware script: fade opacity, by fred huffhines, bsd-style license.
+//
+// when the object is touched, the opacity changes towards either
+// totally transparent or totally opaque.  when touch stops, the
+// changes stop.  the next time the objects is touched, the opacity
+// changes in the opposite direction.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+// modifiable parameters for the fading...
+
+integer SWITCH_ONLY = FALSE;  // if true, does not fade, just switches on and off.
+
+float pace_increment = 0.02;  // how much we change transparency in one timer call.
+
+float timer_interval = 0.1;  // how frequently timer is hit.
+
+float highest_opacity = 1.0;  // the most visible an object can be.
+
+float lowest_opacity = 0.0;  // the least visible an object can be.
+
+//integer FADE_TARGETS = LINK_THIS;  // affect only the prim this script is in.
+integer FADE_TARGETS = LINK_SET;  // fade the entire object, all prims.
+//integer FADE_TARGETS = 3;  // fade just the second prim, not the root.
+
+// globals...
+
+float global_direction = -1.0;
+    // tells us whether we are making the object more transparent
+    // (positive numbers) or less transparent (negative numbers).
+
+integer last_update;
+    // unix time of last mention of the opacity.
+
+// displays the current opaqueness of the object.
+show_opacity()
+{
+    float opacity = llGetAlpha(ALL_SIDES) / (float)llGetNumberOfSides() * 100.0;
+    string message = (string)((integer)opacity) + "% opacity";
+    llSetText(message, <.2, .8, .6>, 1.0);
+    // whisper the setting if we haven't in a bit.
+    if (llGetUnixTime() - last_update >= 2) {
+        llWhisper(0, message);
+        last_update = llGetUnixTime();
+    }
+}
+
+// get rid of the text label.
+show_nothing() { llSetText("", <0.0, 0.0, 0.0>, 0.0); }
+
+//////////////
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.5.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+//log_it("invpo=" + (string)posn);
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+//log_it("pos=" + (string)space_v_posn);
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+//log_it("space v@" + (string)space_v_posn);
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+//log_it("indy=" + (string)space_v_posn);
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+//log_it("saw case of previously redundant item, aieee.  flattened: " + to_chop_up);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+default
+{
+    state_entry() { if (llSubStringIndex(llGetObjectName(),  "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry() {
+        auto_retire();
+        show_nothing();  // clean out any existing text.
+        last_update = llGetUnixTime();
+    }
+
+    touch_start(integer total_number) {
+        global_direction *= -1.0;  // change the direction for next time.
+        if (SWITCH_ONLY) {
+            if (global_direction > 0.0)
+                llSetLinkAlpha(FADE_TARGETS, highest_opacity, ALL_SIDES);
+            else
+                llSetLinkAlpha(FADE_TARGETS, lowest_opacity, ALL_SIDES);
+        } else {
+            // do the full fade routine.  start the timer to change the
+            // opacity while the avatar keeps touching the object.
+            llSetTimerEvent(timer_interval);
+        }
+    }
+
+    touch_end(integer total_number) {
+        llSetTimerEvent(0);  // stop the timer.
+        show_nothing();  // clear the text out.
+    }
+
+    timer() {
+        float current = llGetAlpha(ALL_SIDES) / (float)llGetNumberOfSides()
+            + pace_increment * global_direction;
+        if (current > highest_opacity) { current = highest_opacity; }
+        if (current < lowest_opacity) { current = lowest_opacity; }
+        // change transparency on all links, including root prim.
+        llSetLinkAlpha(FADE_TARGETS, current, ALL_SIDES);
+        show_opacity();
+    }
+}
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/fredboxmux_no_rot_v3.0.txt b/huffware/huffotronic_updater_freebie_v5.3/fredboxmux_no_rot_v3.0.txt
new file mode 100755 (executable)
index 0000000..ca48191
--- /dev/null
@@ -0,0 +1,669 @@
+
+// huffware script: fredboxmux (no rotation), by fred huffhines.
+//
+// a memory saving kludge script; it is the combination of the canonical huffware
+// scripts: (1) non-script giver, (2) rotanium rotato, (3) text label.
+// our theory is that by having only two scripts per display object (this script and
+// the updater client) instead of four, we'll save some cpu on our overburdened but
+// beloved server (serene).
+// given that this script is intended to replace those three scripts, it will eat them
+// on startup to avoid having redundant services in the object.
+// update for april 24 2011: added a question menu before doing the copying to user's
+// inventory, since opensim can do a weird thing that sets all folders to "loading...".
+// this now keeps the objects from being super annoying when one didn't mean to click it.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+// non-script giver:
+//
+// gives all objects, notecards, etc contained in an object when that object is touched.
+// does not give out scripts, since these are generally not something that should be handed
+// to the customer.
+//
+// rotanium rotato:
+//
+// causes the object to rotate according to the parameters set below.
+// this can use herky-jerky timed rotation with llSetRot or it can use
+// smooth rotation with llTargetOmega.
+//
+// text label:
+//
+// a super simple script for giving an object readable text.
+
+integer DEBUGGING = FALSE;  // set to true for noisier runs.
+
+integer DO_ROTATION = FALSE;  // is this the rotating version or not?
+
+integer USE_SENSORS = TRUE;  // should we look for avatars so we can display the label?
+
+float SMOOTH_TIMER_FREQUENCY = 7.0;
+    // fastest possible rate of change for the smooth rotater, in seconds.  the smooth
+    // rotater doesn't need to hit the timer all that often, but this is faster than it
+    // needs to be for rotation, since it is also used as the rate at which the avatar sensor
+    // fires.
+
+float TIME_TO_CLEAR_TITLE = 8.0;
+    // how many seconds before the title that was set for an avatar or due to a touch will
+    // disappear again.
+
+vector LABEL_COLOR = <0.3, 0.9, 0.4>;
+    // color of the text above object.
+
+float ACTIVE_LABEL_DISTANCE = 5.0;
+    // how far away can avatars cause the label to light up?
+
+integer ONLY_GIVE_TO_OWNER = TRUE;
+    // if this is true, then only the owner will receive a copy of the items.
+
+integer GIVE_UNCOPYABLES = FALSE;
+    // this flag is dangerous when true, because it means that uncopyable objects will still
+    // be handed to the target.  if that target refuses the non-copyable items, then the items
+    // will be lost forever.  that is not so good if you've sold the person a non-copy item.
+
+string EXCLUDED_NOTECARD = "product description";
+    // a special case; if there is a giftorse configuration card, we won't hand that out.
+
+float SMOOTH_CHANCE_FOR_ADJUSTING = 0.28;
+    // we won't always change the smooth rotation, even though our timer is pretty slow.
+    // this value is the percentage of the time that we do actually change rotation (divided
+    // by 100).
+
+float SMOOTH_ROTATION_GAIN_MAX = 0.0490873852122;
+    // the gain is how fast we will rotate in radians per second.
+    // PI / 2 is about 90 degrees per second, which seems way too fast.
+    // 0.196349540849 is about PI / 16, 0.0981747704244 is about PI / 32,
+    // and 0.0490873852122 is about PI / 64.
+
+string object_label = "default";  // change this if you want a specific name.
+
+string old_name;  // tracks the last known name so we know if we need to update title.
+float old_opacity = 3.14;  // tracks the last opacity setting for the label.
+
+integer label_changed = TRUE;
+    // this remembers if the label has stayed put or not.  we use it to decide
+    // whether we need to check the label for carriage returns.
+
+vector current_add_in = <0.0, 0.0, 0.4>;
+    // randomly assigned to if RANDOMIZE_ROTATION is true.
+
+float current_gain = -0.05;
+    // speed of smooth rotation; will randomly change if RANDOMIZE_ROTATION is true.
+
+float MIN_ADDITION = 0.01;
+    // smallest amount of change we will ever have.
+float MAX_ADDITION = 7.0;
+    // largest amount of change we will ever have.
+
+key first_toucher;  // tracks who clicked on the object to get contents.
+
+float label_opacity = 1.0;  // how opaque should text be?  1.0 is solid, 0.0 transparent.
+
+// takes out the 3 scripts that have been combined into the mux.  otherwise, it's
+// more of a pain to update all the boxes with this thing when it's ready to go.
+remove_redundant_scripts()
+{
+    integer posn;
+    string self = llGetScriptName();
+    // zoom across the scripts to see if we have any in the inventory that
+    // are slated for removal.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if (curr_script != self) {
+            if ( (llSubStringIndex(curr_script, "non-script giver") == 0)
+                || (llSubStringIndex(curr_script, "rotanium rotato") == 0)
+                || (llSubStringIndex(curr_script, "text label") == 0) ) {
+                // this one is a match!  zap it.
+                llRemoveInventory(curr_script);
+            }
+            
+        }
+    }
+}
+
+// the avatar has said it's okay to hand out all the stuff to her/him.
+really_give_contents() { give_out_contents(first_toucher); }
+
+// give out pictures, notecards, objects, etc. that are hiding in the object.
+give_out_contents(key give_to)
+{
+    list all_to_give = [];  // the set we will hand over in a batch.
+    list uncopyables = [];  // the list we have to do individually.
+    // find out how many items there are.
+    integer count = llGetInventoryNumber(INVENTORY_ALL);
+    // iterate across all the items and add them to the gift list if appropriate.
+    integer indy;
+    for (indy = 0; indy < count; indy++) {
+        string item_name = llGetInventoryName(INVENTORY_ALL, indy);
+        integer type = llGetInventoryType(item_name);
+        if ( (type != INVENTORY_SCRIPT) 
+            && ( (type != INVENTORY_NOTECARD) || (item_name != EXCLUDED_NOTECARD) ) ) {
+            // it's okay to add this item; it's not a script and we are not skipping the special notecard.
+            integer mask = MASK_OWNER;
+            if (!ONLY_GIVE_TO_OWNER) mask = MASK_EVERYONE;
+            integer perms = llGetInventoryPermMask(item_name, mask);
+            if (perms & PERM_COPY) {
+                // a normal object that we can hand out.
+                all_to_give += item_name;
+            } else {
+                uncopyables += item_name;
+            }
+        }
+    }
+    // hand the customer the whole set as one big chunk, named after the object.
+    llGiveInventoryList(give_to, llGetObjectName(), all_to_give);
+
+    // handle any problematic items.  we cannot copy these objects into a category folder,
+    // so we can either not try to copy them (a lot safer) or we can try to deliver them
+    // normally as individual items.  the latter choice is more dangerous, because if the
+    // owner discards these items rather than keeping them, the items will be lost forever!
+    if (llGetListLength(uncopyables) > 0) {
+        string plural = " ";
+        string is_verb = "is ";
+        string third_noun_subj = "it ";
+        string third_noun_obj = "it ";
+        if (llGetListLength(uncopyables) > 1) {
+            plural = "s ";
+            is_verb = "are ";
+            third_noun_subj = "they ";
+            third_noun_obj = "them ";
+        }
+        
+        string uncopyable_message = "will be left inside the object.  To get " + third_noun_obj
+            + ", please copy " + third_noun_obj + "\nmanually from this object into your inventory.";
+        if (GIVE_UNCOPYABLES) {
+            uncopyable_message = "will be moved over to your inventory."
+            + "\nPlease look in your main folders for "
+            + third_noun_obj + "(e.g., in Objects or Textures).";
+        }
+        
+        string failure_message = "The item" + plural
+            + "[" + llDumpList2String(uncopyables, "; ") + "]\n"
+            + is_verb + "not copyable; " + third_noun_subj
+            + uncopyable_message;
+            
+        if (llGetOwner() == give_to) {
+            // the object can be moved to inventory, but not with the category method.
+            llOwnerSay(failure_message);
+        } else {
+            // this seems like a weird case; it will probably just fail anyhow?
+            // if the item's not copyable and you're not the owner of this object,
+            // how can we give it to you?
+            llInstantMessage(give_to, failure_message);
+        }
+        
+        // now that we've announced this weird situation, handle it appropriately.
+        if (GIVE_UNCOPYABLES) {
+            for (indy = 0; indy < llGetListLength(uncopyables); indy++) {
+                string item_name = llList2String(uncopyables, indy);
+                llGiveInventory(give_to, item_name);
+            }
+        }  // otherwise leave them be.
+    }
+}
+
+// causes the object to rotate using whatever the current settings are.
+smooth_rotate_using_our_settings()
+{
+    // make sure we are using the rotational values we were asked to.
+    llTargetOmega(current_add_in, current_gain, 1.0);
+}
+
+// sets the gain and add in to random choices.
+randomize_values()
+{
+    current_gain = randomize_within_range(0.001, SMOOTH_ROTATION_GAIN_MAX, TRUE);
+    current_add_in = random_vector(MIN_ADDITION, MAX_ADDITION, TRUE);
+}
+
+// performs the timed rotation that has been configured for us.
+rotate_as_requested()
+{
+    // our slack timer went off, so randomize the rotation if requested.
+    if (llFrand(1.0) >= (1.0 - SMOOTH_CHANCE_FOR_ADJUSTING) ) {
+        randomize_values();
+        smooth_rotate_using_our_settings();
+    }
+}
+
+initialize_fredboxmux()
+{
+    // make sure we pick a good random channel.
+    menu_system_channel = -1 * (integer)randomize_within_range(200, 10000000, FALSE);
+
+    // if needed, we will set our initial random rotation.
+    randomize_values();
+        
+    // do a first rotate, so we move right at startup.  otherwise we won't move
+    // until after our first timer hits.
+    if (DO_ROTATION) rotate_as_requested();
+
+    // now set the timer for our mode.
+    llSetTimerEvent(SMOOTH_TIMER_FREQUENCY);
+    if (DO_ROTATION) smooth_rotate_using_our_settings();
+}
+
+set_text()
+{
+//log_it("old name " + old_name + " -- curr name " + llGetObjectName());
+    if (old_name != llGetObjectName()) {
+        // we're out of synch on the object name.
+        label_changed = TRUE;
+    }
+    if (old_opacity != label_opacity) {
+        // here we're out of synch on last opacity used.
+        label_changed = TRUE;
+    }
+    if (label_changed) {
+        // reset the object title to a decorated version of object name if it says "default".
+        string new_label = object_label;
+        if (new_label == "default") new_label = llGetObjectName();
+    
+        integer indy;
+        integer keep_going = TRUE;
+        while (keep_going) {
+            indy = find_substring(new_label, "\\n");
+            if (indy < 0) {
+                keep_going = FALSE;
+            } else {
+                new_label = llGetSubString(new_label, 0, indy - 1)
+                    + "\n" + llGetSubString(new_label, indy + 2, -1);
+            }
+        }
+        old_name = llGetObjectName();
+        old_opacity = label_opacity;
+        label_changed = FALSE;  // we have dealt with it now.
+//log_it("setting text: " + new_label);
+        llSetText(new_label, LABEL_COLOR, label_opacity);
+    }
+    if (label_opacity != 0) {
+        // if we set a lit-up title, clear it again pretty soon.
+        llSetTimerEvent(TIME_TO_CLEAR_TITLE);
+    }
+}
+
+//////////////
+// code borrowed from menutini to raise a menu asking if they actually meant to get all
+// the contents.  an opensim inventory bug makes all the folders look foolish if we
+// do any inventory giving accidentally.
+//////////////
+
+// global variables...
+
+list _private_global_buttons;  // holds onto the active set of menu options.
+string _private_global_av_key;  // the key for the avatar who clicks the menu.
+string _private_global_title;  // holds onto current title text.
+
+integer _menu_base_start = 0;  // position in the items of the current menu.
+
+integer listening_id = 0;
+    // the current id of our listening for the menu.  it's an id returned by LSL
+    // that we need to track so we can cancel the listen.
+
+integer menu_system_channel = -123;
+    // messages come back to us from this channel when user clicks the dialog.
+    // this is set later and the default is meaningless.
+
+string global_menu_name = "";
+    // hangs onto the current menu's name.
+
+//hmmm: note; to manage multiple concurrent menus on different channels,
+//      we must make these into lists.  then the timeouts should apply
+//      individually to these instead of overall (if we even do timeouts;
+//      it's nicer if menus never stop being active).
+
+string NEXT_MENU_TEXT = "Next >>";
+    // what the next item will say for showing next menu page.
+    
+//integer TIMEOUT_FOR_MENU = 42;
+    // timeout for the menu in seconds.
+
+// displays the menu requested.  it's "menu_name" is an internal name that is
+// not displayed to the user.  the "title" is the content shown in the main area
+// of the menu.  "commands_in" is the list of menu items to show as buttons.
+// the "menu_channel" is where the user's clicked response will be sent.  the
+// "listen_to" key is the avatar expected to click the menu, which is needed to
+// listen to his response.
+show_menu(string menu_name, string title, list buttons,
+    integer menu_channel, key listen_to)
+{
+    // save our new parms.
+    global_menu_name = menu_name;
+    _private_global_title = title;
+    _private_global_buttons = buttons;
+    menu_system_channel = menu_channel;
+    _private_global_av_key = listen_to;
+    if (DEBUGGING) {
+        log_it("menu name: " + global_menu_name);
+        log_it("title: " + _private_global_title);
+        log_it("buttons: " + (string)buttons);
+        log_it("channel: " + (string)menu_system_channel);
+        log_it("listen key: " + (string)listen_to);
+    }
+
+    integer add_next = FALSE;  // true if we should add a next menu item.
+
+    // the math here incorporates current button position.
+    integer current = _menu_base_start;
+    integer max_buttons = llGetListLength(buttons) - current;
+
+    if (max_buttons > 12) {
+        // limitation of SL: menus have a max of 12 buttons.
+        max_buttons = 12;
+        add_next = TRUE;
+    } else if (llGetListLength(buttons) > 12) {
+        // we already have been adding next.  let's make sure this gets
+        // a wrap-around next button.
+        add_next = TRUE;
+    }
+    // chop out what we can use in a menu.
+    list trunc_buttons = llList2List(buttons, current, current + max_buttons - 1);
+    if (add_next) {
+        // we were asked to add a menu item for the next screen.
+        trunc_buttons = llList2List(trunc_buttons, 0, 10) + NEXT_MENU_TEXT;
+    }
+
+    listening_id = llListen(menu_channel, "", listen_to, "");
+    list commands;
+    integer i;
+    // take only the prefix of the string, to avoid getting a length complaint.
+    for (i = 0; i < llGetListLength(trunc_buttons); i++) {
+        string curr = llList2String(trunc_buttons, i);
+        integer last_pos = 23;  // default maximum, highest possible is 24.
+        if (llStringLength(curr) - 1 < last_pos) last_pos = llStringLength(curr) - 1;
+        curr = llGetSubString(curr, 0, last_pos);
+        commands += curr;
+    }
+    llDialog(listen_to, title, commands, menu_channel);
+}
+
+// shuts down any connection we might have had with any active menu.  we will not
+// send any responses after this point (although we might already have responded when
+// the user clicked the menu).
+clear_menu()
+{
+    llListenRemove(listening_id);
+}
+
+// process the response when the user chooses a menu item.
+process_menu_response(integer channel, string name, key id, string message)
+{
+  if (channel != menu_system_channel) return;  // not for us.
+  
+    if (message == NEXT_MENU_TEXT) {
+        // this is the special choice, so we need to go to the next page.
+        _menu_base_start += 11;
+        if (_menu_base_start > llGetListLength(_private_global_buttons)) {
+            // we have wrapped around the list.  go to the start again.
+            _menu_base_start = 0;
+        }
+        show_menu(global_menu_name, _private_global_title,
+            _private_global_buttons, menu_system_channel,
+            _private_global_av_key);
+        return;  // handled by opening a new menu.
+    }
+    
+    string calculated_name;
+    integer indy;
+    // first try for an exact match.
+    for (indy = 0; indy < llGetListLength(_private_global_buttons); indy++) {
+        string curr = llList2String(_private_global_buttons, indy);
+        if (curr == message) {
+            // correct the answer based on the full button string.
+            calculated_name = curr;
+        }
+    }
+    if (calculated_name == "") {
+        // try an imprecise match if the exact matching didn't work.
+        for (indy = 0; indy < llGetListLength(_private_global_buttons); indy++) {
+            string curr = llList2String(_private_global_buttons, indy);
+            if (is_prefix(curr, message)) {
+                // correct the answer based on the full button string.
+                calculated_name = curr;
+            }
+        }
+    }
+    if (calculated_name == "yes") {
+        // only send a response if that menu choice made sense to us.
+        really_give_contents();
+        clear_menu();
+    }
+}
+
+// end from menutini.
+//////////////
+
+//////////////
+// start of hufflets...
+
+// returns a number at most "maximum" and at least "minimum".
+// if "allow_negative" is TRUE, then the return may be positive or negative.
+float randomize_within_range(float minimum, float maximum, integer allow_negative)
+{
+    if (minimum > maximum) {
+        // flip the two if they are reversed.
+        float temp = minimum; minimum = maximum; maximum = temp;
+    }
+    float to_return = minimum + llFrand(maximum - minimum);
+    if (allow_negative) {
+        if (llFrand(1.0) < 0.5) to_return *= -1.0;
+    }
+    return to_return;
+}
+
+// returns a random vector where x,y,z will be between "minimums" and "maximums"
+// x,y,z components.  if "allow_negative" is true, then any component will
+// randomly be negative or positive.
+vector random_bound_vector(vector minimums, vector maximums, integer allow_negative)
+{
+    return <randomize_within_range(minimums.x, maximums.x, allow_negative),
+        randomize_within_range(minimums.y, maximums.y, allow_negative),
+        randomize_within_range(minimums.z, maximums.z, allow_negative)>;
+}
+
+// returns a vector whose components are between minimum and maximum.
+// if allow_negative is true, then they can be either positive or negative.
+vector random_vector(float minimum, float maximum, integer allow_negative)
+{
+    return random_bound_vector(<minimum, minimum, minimum>,
+        <maximum, maximum, maximum>, allow_negative);
+}
+
+//////////////
+
+integer debug_num = 0;
+
+// a debugging output method.  can be disabled entirely in one place.
+log_it(string to_say)
+{
+    debug_num++;
+    // tell this to the owner.    
+    llOwnerSay(llGetDate() + ": " + llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+//llWhisper(0, llGetDate() + ": " + llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on an unusual channel for chat if it's not intended for general public.
+//    llSay(108, llGetDate() + ": " + llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on open chat that anyone can hear.  we take off the bling for this one.
+//    llSay(0, to_say);
+}
+
+// returns the index of the first occurrence of "pattern" inside
+// the "full_string".  if it is not found, then a negative number is returned.
+integer find_substring(string full_string, string pattern)
+{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); }
+
+// returns TRUE if the "prefix" string is the first part of "compare_with".
+integer is_prefix(string compare_with, string prefix)
+{ return (llSubStringIndex(compare_with, prefix) == 0); }
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.8.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+//end hufflets...
+//////////////
+
+default
+{
+    state_entry() { if (llSubStringIndex(llGetObjectName(),  "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry() {
+        auto_retire();
+        remove_redundant_scripts();
+        initialize_fredboxmux();
+        label_opacity = 1;  // start out showing the label.
+        set_text();
+    }
+    
+    on_rez(integer start_parm) { state default; }
+    
+    changed(integer change) {
+        if (change & CHANGED_INVENTORY) {
+            // we show the label at least a bit when the contents change.
+            label_opacity = 1;
+            set_text(); 
+        }
+    }
+    
+    touch_start(integer num) {
+        label_opacity = 1.0;
+        set_text();
+        first_toucher = llDetectedKey(0);
+        // are we only supposed to give stuff to the owner?
+        if (ONLY_GIVE_TO_OWNER && (first_toucher != llGetOwner()) ) {
+            first_toucher = NULL_KEY;
+            return;  // bail out.
+        }
+        show_menu("askreally", "Would you like a copy of this object's contents?",
+            ["yes", "no"], -18264, first_toucher);
+    }
+
+    listen(integer channel, string name, key id, string message)
+    { process_menu_response(channel, name, id, message); }
+
+    timer() {
+        llSetTimerEvent(0);
+        if (DO_ROTATION) rotate_as_requested();
+        if (USE_SENSORS) {
+            llSensor("", NULL_KEY, AGENT, ACTIVE_LABEL_DISTANCE, PI);
+        } else {
+            if (label_opacity != 0.0) {
+                label_opacity = 0.0;
+                set_text();
+            }            
+        }
+        llSetTimerEvent(SMOOTH_TIMER_FREQUENCY);
+    }
+
+    sensor(integer count) {
+        if (label_opacity != 1.0) {
+            label_opacity = 1.0;
+            set_text();
+        }
+    }
+    
+    no_sensor() {
+        if (label_opacity != 0.0) {
+            label_opacity = 0.0;
+            set_text();
+        }
+    }
+}
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/fredboxmux_v3.0.txt b/huffware/huffotronic_updater_freebie_v5.3/fredboxmux_v3.0.txt
new file mode 100755 (executable)
index 0000000..c41662c
--- /dev/null
@@ -0,0 +1,669 @@
+
+// huffware script: fredboxmux (with rotation), by fred huffhines.
+//
+// a memory saving kludge script; it is the combination of the canonical huffware
+// scripts: (1) non-script giver, (2) rotanium rotato, (3) text label.
+// our theory is that by having only two scripts per display object (this script and
+// the updater client) instead of four, we'll save some cpu on our overburdened but
+// beloved server (serene).
+// given that this script is intended to replace those three scripts, it will eat them
+// on startup to avoid having redundant services in the object.
+// update for april 24 2011: added a question menu before doing the copying to user's
+// inventory, since opensim can do a weird thing that sets all folders to "loading...".
+// this now keeps the objects from being super annoying when one didn't mean to click it.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+// non-script giver:
+//
+// gives all objects, notecards, etc contained in an object when that object is touched.
+// does not give out scripts, since these are generally not something that should be handed
+// to the customer.
+//
+// rotanium rotato:
+//
+// causes the object to rotate according to the parameters set below.
+// this can use herky-jerky timed rotation with llSetRot or it can use
+// smooth rotation with llTargetOmega.
+//
+// text label:
+//
+// a super simple script for giving an object readable text.
+
+integer DEBUGGING = FALSE;  // set to true for noisier runs.
+
+integer DO_ROTATION = TRUE;  // is this the rotating version or not?
+
+integer USE_SENSORS = TRUE;  // should we look for avatars so we can display the label?
+
+float SMOOTH_TIMER_FREQUENCY = 7.0;
+    // fastest possible rate of change for the smooth rotater, in seconds.  the smooth
+    // rotater doesn't need to hit the timer all that often, but this is faster than it
+    // needs to be for rotation, since it is also used as the rate at which the avatar sensor
+    // fires.
+
+float TIME_TO_CLEAR_TITLE = 8.0;
+    // how many seconds before the title that was set for an avatar or due to a touch will
+    // disappear again.
+
+vector LABEL_COLOR = <0.3, 0.9, 0.4>;
+    // color of the text above object.
+
+float ACTIVE_LABEL_DISTANCE = 5.0;
+    // how far away can avatars cause the label to light up?
+
+integer ONLY_GIVE_TO_OWNER = TRUE;
+    // if this is true, then only the owner will receive a copy of the items.
+
+integer GIVE_UNCOPYABLES = FALSE;
+    // this flag is dangerous when true, because it means that uncopyable objects will still
+    // be handed to the target.  if that target refuses the non-copyable items, then the items
+    // will be lost forever.  that is not so good if you've sold the person a non-copy item.
+
+string EXCLUDED_NOTECARD = "product description";
+    // a special case; if there is a giftorse configuration card, we won't hand that out.
+
+float SMOOTH_CHANCE_FOR_ADJUSTING = 0.28;
+    // we won't always change the smooth rotation, even though our timer is pretty slow.
+    // this value is the percentage of the time that we do actually change rotation (divided
+    // by 100).
+
+float SMOOTH_ROTATION_GAIN_MAX = 0.0490873852122;
+    // the gain is how fast we will rotate in radians per second.
+    // PI / 2 is about 90 degrees per second, which seems way too fast.
+    // 0.196349540849 is about PI / 16, 0.0981747704244 is about PI / 32,
+    // and 0.0490873852122 is about PI / 64.
+
+string object_label = "default";  // change this if you want a specific name.
+
+string old_name;  // tracks the last known name so we know if we need to update title.
+float old_opacity = 3.14;  // tracks the last opacity setting for the label.
+
+integer label_changed = TRUE;
+    // this remembers if the label has stayed put or not.  we use it to decide
+    // whether we need to check the label for carriage returns.
+
+vector current_add_in = <0.0, 0.0, 0.4>;
+    // randomly assigned to if RANDOMIZE_ROTATION is true.
+
+float current_gain = -0.05;
+    // speed of smooth rotation; will randomly change if RANDOMIZE_ROTATION is true.
+
+float MIN_ADDITION = 0.01;
+    // smallest amount of change we will ever have.
+float MAX_ADDITION = 7.0;
+    // largest amount of change we will ever have.
+
+key first_toucher;  // tracks who clicked on the object to get contents.
+
+float label_opacity = 1.0;  // how opaque should text be?  1.0 is solid, 0.0 transparent.
+
+// takes out the 3 scripts that have been combined into the mux.  otherwise, it's
+// more of a pain to update all the boxes with this thing when it's ready to go.
+remove_redundant_scripts()
+{
+    integer posn;
+    string self = llGetScriptName();
+    // zoom across the scripts to see if we have any in the inventory that
+    // are slated for removal.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if (curr_script != self) {
+            if ( (llSubStringIndex(curr_script, "non-script giver") == 0)
+                || (llSubStringIndex(curr_script, "rotanium rotato") == 0)
+                || (llSubStringIndex(curr_script, "text label") == 0) ) {
+                // this one is a match!  zap it.
+                llRemoveInventory(curr_script);
+            }
+            
+        }
+    }
+}
+
+// the avatar has said it's okay to hand out all the stuff to her/him.
+really_give_contents() { give_out_contents(first_toucher); }
+
+// give out pictures, notecards, objects, etc. that are hiding in the object.
+give_out_contents(key give_to)
+{
+    list all_to_give = [];  // the set we will hand over in a batch.
+    list uncopyables = [];  // the list we have to do individually.
+    // find out how many items there are.
+    integer count = llGetInventoryNumber(INVENTORY_ALL);
+    // iterate across all the items and add them to the gift list if appropriate.
+    integer indy;
+    for (indy = 0; indy < count; indy++) {
+        string item_name = llGetInventoryName(INVENTORY_ALL, indy);
+        integer type = llGetInventoryType(item_name);
+        if ( (type != INVENTORY_SCRIPT) 
+            && ( (type != INVENTORY_NOTECARD) || (item_name != EXCLUDED_NOTECARD) ) ) {
+            // it's okay to add this item; it's not a script and we are not skipping the special notecard.
+            integer mask = MASK_OWNER;
+            if (!ONLY_GIVE_TO_OWNER) mask = MASK_EVERYONE;
+            integer perms = llGetInventoryPermMask(item_name, mask);
+            if (perms & PERM_COPY) {
+                // a normal object that we can hand out.
+                all_to_give += item_name;
+            } else {
+                uncopyables += item_name;
+            }
+        }
+    }
+    // hand the customer the whole set as one big chunk, named after the object.
+    llGiveInventoryList(give_to, llGetObjectName(), all_to_give);
+
+    // handle any problematic items.  we cannot copy these objects into a category folder,
+    // so we can either not try to copy them (a lot safer) or we can try to deliver them
+    // normally as individual items.  the latter choice is more dangerous, because if the
+    // owner discards these items rather than keeping them, the items will be lost forever!
+    if (llGetListLength(uncopyables) > 0) {
+        string plural = " ";
+        string is_verb = "is ";
+        string third_noun_subj = "it ";
+        string third_noun_obj = "it ";
+        if (llGetListLength(uncopyables) > 1) {
+            plural = "s ";
+            is_verb = "are ";
+            third_noun_subj = "they ";
+            third_noun_obj = "them ";
+        }
+        
+        string uncopyable_message = "will be left inside the object.  To get " + third_noun_obj
+            + ", please copy " + third_noun_obj + "\nmanually from this object into your inventory.";
+        if (GIVE_UNCOPYABLES) {
+            uncopyable_message = "will be moved over to your inventory."
+            + "\nPlease look in your main folders for "
+            + third_noun_obj + "(e.g., in Objects or Textures).";
+        }
+        
+        string failure_message = "The item" + plural
+            + "[" + llDumpList2String(uncopyables, "; ") + "]\n"
+            + is_verb + "not copyable; " + third_noun_subj
+            + uncopyable_message;
+            
+        if (llGetOwner() == give_to) {
+            // the object can be moved to inventory, but not with the category method.
+            llOwnerSay(failure_message);
+        } else {
+            // this seems like a weird case; it will probably just fail anyhow?
+            // if the item's not copyable and you're not the owner of this object,
+            // how can we give it to you?
+            llInstantMessage(give_to, failure_message);
+        }
+        
+        // now that we've announced this weird situation, handle it appropriately.
+        if (GIVE_UNCOPYABLES) {
+            for (indy = 0; indy < llGetListLength(uncopyables); indy++) {
+                string item_name = llList2String(uncopyables, indy);
+                llGiveInventory(give_to, item_name);
+            }
+        }  // otherwise leave them be.
+    }
+}
+
+// causes the object to rotate using whatever the current settings are.
+smooth_rotate_using_our_settings()
+{
+    // make sure we are using the rotational values we were asked to.
+    llTargetOmega(current_add_in, current_gain, 1.0);
+}
+
+// sets the gain and add in to random choices.
+randomize_values()
+{
+    current_gain = randomize_within_range(0.001, SMOOTH_ROTATION_GAIN_MAX, TRUE);
+    current_add_in = random_vector(MIN_ADDITION, MAX_ADDITION, TRUE);
+}
+
+// performs the timed rotation that has been configured for us.
+rotate_as_requested()
+{
+    // our slack timer went off, so randomize the rotation if requested.
+    if (llFrand(1.0) >= (1.0 - SMOOTH_CHANCE_FOR_ADJUSTING) ) {
+        randomize_values();
+        smooth_rotate_using_our_settings();
+    }
+}
+
+initialize_fredboxmux()
+{
+    // make sure we pick a good random channel.
+    menu_system_channel = -1 * (integer)randomize_within_range(200, 10000000, FALSE);
+
+    // if needed, we will set our initial random rotation.
+    randomize_values();
+        
+    // do a first rotate, so we move right at startup.  otherwise we won't move
+    // until after our first timer hits.
+    if (DO_ROTATION) rotate_as_requested();
+
+    // now set the timer for our mode.
+    llSetTimerEvent(SMOOTH_TIMER_FREQUENCY);
+    if (DO_ROTATION) smooth_rotate_using_our_settings();
+}
+
+set_text()
+{
+//log_it("old name " + old_name + " -- curr name " + llGetObjectName());
+    if (old_name != llGetObjectName()) {
+        // we're out of synch on the object name.
+        label_changed = TRUE;
+    }
+    if (old_opacity != label_opacity) {
+        // here we're out of synch on last opacity used.
+        label_changed = TRUE;
+    }
+    if (label_changed) {
+        // reset the object title to a decorated version of object name if it says "default".
+        string new_label = object_label;
+        if (new_label == "default") new_label = llGetObjectName();
+    
+        integer indy;
+        integer keep_going = TRUE;
+        while (keep_going) {
+            indy = find_substring(new_label, "\\n");
+            if (indy < 0) {
+                keep_going = FALSE;
+            } else {
+                new_label = llGetSubString(new_label, 0, indy - 1)
+                    + "\n" + llGetSubString(new_label, indy + 2, -1);
+            }
+        }
+        old_name = llGetObjectName();
+        old_opacity = label_opacity;
+        label_changed = FALSE;  // we have dealt with it now.
+//log_it("setting text: " + new_label);
+        llSetText(new_label, LABEL_COLOR, label_opacity);
+    }
+    if (label_opacity != 0) {
+        // if we set a lit-up title, clear it again pretty soon.
+        llSetTimerEvent(TIME_TO_CLEAR_TITLE);
+    }
+}
+
+//////////////
+// code borrowed from menutini to raise a menu asking if they actually meant to get all
+// the contents.  an opensim inventory bug makes all the folders look foolish if we
+// do any inventory giving accidentally.
+//////////////
+
+// global variables...
+
+list _private_global_buttons;  // holds onto the active set of menu options.
+string _private_global_av_key;  // the key for the avatar who clicks the menu.
+string _private_global_title;  // holds onto current title text.
+
+integer _menu_base_start = 0;  // position in the items of the current menu.
+
+integer listening_id = 0;
+    // the current id of our listening for the menu.  it's an id returned by LSL
+    // that we need to track so we can cancel the listen.
+
+integer menu_system_channel = -123;
+    // messages come back to us from this channel when user clicks the dialog.
+    // this is set later and the default is meaningless.
+
+string global_menu_name = "";
+    // hangs onto the current menu's name.
+
+//hmmm: note; to manage multiple concurrent menus on different channels,
+//      we must make these into lists.  then the timeouts should apply
+//      individually to these instead of overall (if we even do timeouts;
+//      it's nicer if menus never stop being active).
+
+string NEXT_MENU_TEXT = "Next >>";
+    // what the next item will say for showing next menu page.
+    
+//integer TIMEOUT_FOR_MENU = 42;
+    // timeout for the menu in seconds.
+
+// displays the menu requested.  it's "menu_name" is an internal name that is
+// not displayed to the user.  the "title" is the content shown in the main area
+// of the menu.  "commands_in" is the list of menu items to show as buttons.
+// the "menu_channel" is where the user's clicked response will be sent.  the
+// "listen_to" key is the avatar expected to click the menu, which is needed to
+// listen to his response.
+show_menu(string menu_name, string title, list buttons,
+    integer menu_channel, key listen_to)
+{
+    // save our new parms.
+    global_menu_name = menu_name;
+    _private_global_title = title;
+    _private_global_buttons = buttons;
+    menu_system_channel = menu_channel;
+    _private_global_av_key = listen_to;
+    if (DEBUGGING) {
+        log_it("menu name: " + global_menu_name);
+        log_it("title: " + _private_global_title);
+        log_it("buttons: " + (string)buttons);
+        log_it("channel: " + (string)menu_system_channel);
+        log_it("listen key: " + (string)listen_to);
+    }
+
+    integer add_next = FALSE;  // true if we should add a next menu item.
+
+    // the math here incorporates current button position.
+    integer current = _menu_base_start;
+    integer max_buttons = llGetListLength(buttons) - current;
+
+    if (max_buttons > 12) {
+        // limitation of SL: menus have a max of 12 buttons.
+        max_buttons = 12;
+        add_next = TRUE;
+    } else if (llGetListLength(buttons) > 12) {
+        // we already have been adding next.  let's make sure this gets
+        // a wrap-around next button.
+        add_next = TRUE;
+    }
+    // chop out what we can use in a menu.
+    list trunc_buttons = llList2List(buttons, current, current + max_buttons - 1);
+    if (add_next) {
+        // we were asked to add a menu item for the next screen.
+        trunc_buttons = llList2List(trunc_buttons, 0, 10) + NEXT_MENU_TEXT;
+    }
+
+    listening_id = llListen(menu_channel, "", listen_to, "");
+    list commands;
+    integer i;
+    // take only the prefix of the string, to avoid getting a length complaint.
+    for (i = 0; i < llGetListLength(trunc_buttons); i++) {
+        string curr = llList2String(trunc_buttons, i);
+        integer last_pos = 23;  // default maximum, highest possible is 24.
+        if (llStringLength(curr) - 1 < last_pos) last_pos = llStringLength(curr) - 1;
+        curr = llGetSubString(curr, 0, last_pos);
+        commands += curr;
+    }
+    llDialog(listen_to, title, commands, menu_channel);
+}
+
+// shuts down any connection we might have had with any active menu.  we will not
+// send any responses after this point (although we might already have responded when
+// the user clicked the menu).
+clear_menu()
+{
+    llListenRemove(listening_id);
+}
+
+// process the response when the user chooses a menu item.
+process_menu_response(integer channel, string name, key id, string message)
+{
+  if (channel != menu_system_channel) return;  // not for us.
+  
+    if (message == NEXT_MENU_TEXT) {
+        // this is the special choice, so we need to go to the next page.
+        _menu_base_start += 11;
+        if (_menu_base_start > llGetListLength(_private_global_buttons)) {
+            // we have wrapped around the list.  go to the start again.
+            _menu_base_start = 0;
+        }
+        show_menu(global_menu_name, _private_global_title,
+            _private_global_buttons, menu_system_channel,
+            _private_global_av_key);
+        return;  // handled by opening a new menu.
+    }
+    
+    string calculated_name;
+    integer indy;
+    // first try for an exact match.
+    for (indy = 0; indy < llGetListLength(_private_global_buttons); indy++) {
+        string curr = llList2String(_private_global_buttons, indy);
+        if (curr == message) {
+            // correct the answer based on the full button string.
+            calculated_name = curr;
+        }
+    }
+    if (calculated_name == "") {
+        // try an imprecise match if the exact matching didn't work.
+        for (indy = 0; indy < llGetListLength(_private_global_buttons); indy++) {
+            string curr = llList2String(_private_global_buttons, indy);
+            if (is_prefix(curr, message)) {
+                // correct the answer based on the full button string.
+                calculated_name = curr;
+            }
+        }
+    }
+    if (calculated_name == "yes") {
+        // only send a response if that menu choice made sense to us.
+        really_give_contents();
+        clear_menu();
+    }
+}
+
+// end from menutini.
+//////////////
+
+//////////////
+// start of hufflets...
+
+// returns a number at most "maximum" and at least "minimum".
+// if "allow_negative" is TRUE, then the return may be positive or negative.
+float randomize_within_range(float minimum, float maximum, integer allow_negative)
+{
+    if (minimum > maximum) {
+        // flip the two if they are reversed.
+        float temp = minimum; minimum = maximum; maximum = temp;
+    }
+    float to_return = minimum + llFrand(maximum - minimum);
+    if (allow_negative) {
+        if (llFrand(1.0) < 0.5) to_return *= -1.0;
+    }
+    return to_return;
+}
+
+// returns a random vector where x,y,z will be between "minimums" and "maximums"
+// x,y,z components.  if "allow_negative" is true, then any component will
+// randomly be negative or positive.
+vector random_bound_vector(vector minimums, vector maximums, integer allow_negative)
+{
+    return <randomize_within_range(minimums.x, maximums.x, allow_negative),
+        randomize_within_range(minimums.y, maximums.y, allow_negative),
+        randomize_within_range(minimums.z, maximums.z, allow_negative)>;
+}
+
+// returns a vector whose components are between minimum and maximum.
+// if allow_negative is true, then they can be either positive or negative.
+vector random_vector(float minimum, float maximum, integer allow_negative)
+{
+    return random_bound_vector(<minimum, minimum, minimum>,
+        <maximum, maximum, maximum>, allow_negative);
+}
+
+//////////////
+
+integer debug_num = 0;
+
+// a debugging output method.  can be disabled entirely in one place.
+log_it(string to_say)
+{
+    debug_num++;
+    // tell this to the owner.    
+    llOwnerSay(llGetDate() + ": " + llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+//llWhisper(0, llGetDate() + ": " + llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on an unusual channel for chat if it's not intended for general public.
+//    llSay(108, llGetDate() + ": " + llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on open chat that anyone can hear.  we take off the bling for this one.
+//    llSay(0, to_say);
+}
+
+// returns the index of the first occurrence of "pattern" inside
+// the "full_string".  if it is not found, then a negative number is returned.
+integer find_substring(string full_string, string pattern)
+{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); }
+
+// returns TRUE if the "prefix" string is the first part of "compare_with".
+integer is_prefix(string compare_with, string prefix)
+{ return (llSubStringIndex(compare_with, prefix) == 0); }
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.8.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+//end hufflets...
+//////////////
+
+default
+{
+    state_entry() { if (llSubStringIndex(llGetObjectName(),  "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry() {
+        auto_retire();
+        remove_redundant_scripts();
+        initialize_fredboxmux();
+        label_opacity = 1;  // start out showing the label.
+        set_text();
+    }
+    
+    on_rez(integer start_parm) { state default; }
+    
+    changed(integer change) {
+        if (change & CHANGED_INVENTORY) {
+            // we show the label at least a bit when the contents change.
+            label_opacity = 1;
+            set_text(); 
+        }
+    }
+    
+    touch_start(integer num) {
+        label_opacity = 1.0;
+        set_text();
+        first_toucher = llDetectedKey(0);
+        // are we only supposed to give stuff to the owner?
+        if (ONLY_GIVE_TO_OWNER && (first_toucher != llGetOwner()) ) {
+            first_toucher = NULL_KEY;
+            return;  // bail out.
+        }
+        show_menu("askreally", "Would you like a copy of this object's contents?",
+            ["yes", "no"], -18264, first_toucher);
+    }
+
+    listen(integer channel, string name, key id, string message)
+    { process_menu_response(channel, name, id, message); }
+
+    timer() {
+        llSetTimerEvent(0);
+        if (DO_ROTATION) rotate_as_requested();
+        if (USE_SENSORS) {
+            llSensor("", NULL_KEY, AGENT, ACTIVE_LABEL_DISTANCE, PI);
+        } else {
+            if (label_opacity != 0.0) {
+                label_opacity = 0.0;
+                set_text();
+            }            
+        }
+        llSetTimerEvent(SMOOTH_TIMER_FREQUENCY);
+    }
+
+    sensor(integer count) {
+        if (label_opacity != 1.0) {
+            label_opacity = 1.0;
+            set_text();
+        }
+    }
+    
+    no_sensor() {
+        if (label_opacity != 0.0) {
+            label_opacity = 0.0;
+            set_text();
+        }
+    }
+}
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/huff-update_client_v20.1.txt b/huffware/huffotronic_updater_freebie_v5.3/huff-update_client_v20.1.txt
new file mode 100755 (executable)
index 0000000..4a458bb
--- /dev/null
@@ -0,0 +1,826 @@
+
+// huffware script: huff-update client, by fred huffhines.
+//
+// this script is the client side of the update process.  it should reside in an object that
+// has scripts which should be automatically updated.  it will listen for announcements by
+// an update server and communicate with the server to ensure that all of its scripts are
+// the most up to date available with the server.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+// global constants...
+
+integer DEBUGGING = FALSE;  // if TRUE, the script will output status information.
+
+integer SERVER_IGNORE_TIME = 1200;  // number of seconds between performing an upgrade with the same server.
+
+integer MAXIMUM_UPDATE_TIME_ALLOWED = 140;  // we allow one upgrade process to take this long overall.
+
+integer UPDATE_ANNOUNCEMENT_CHANNEL   = -420108;  // used by server to brag about itself.
+integer OLD_REQUEST_INVENTORY_CHANNEL = -421008;  // used by clients to request an update list.
+
+string UPDATE_ANNOUNCEMENT_PREFIX   = "#huff-update#";  // first part of any announcement.
+string REQUEST_INVENTORY_PREFIX     = "#huff-reqinv#";  // first part of request for inventory list.
+string REPORT_AVAILABLE_SCRIPTS     = "#scripts#";  // server's keyword to let client know script inventory.
+string REQUEST_SCRIPT_UPDATE        = "#updatego#";  // keyword used by client to request some updates.
+string SHUT_THEM_DOWN               = "#huffdown#";  // server tells client to stop any non-updater scripts.
+string READY_TO_UPDATE              = "#listoneeds#";  // the client tells the server the scripts it wants.
+string SCRIPTS_ARE_CURRENT          = "#gottemthx#";  // client says this when all new scripts are in place.
+string START_THEM_UP                = "#huffup#";  // server tells client to start up other scripts again.
+string DONE_UPDATING                = "#finito#";  // the client is done updating.
+string BUSY_BUSY                    = "#busymuch#";  // a signal that the server is too busy to update us.
+
+float UPDATE_TIMER_INTERVAL = 2.0;  // interval between checks on our update status.
+
+integer UPDATER_SCRIPT_PIN = -1231008;  // the hook for our scripts to be modified.
+
+///float BUSY_SERVER_PAUSE_TIME = 38.0;  // num seconds to delay when server says it's too busy.
+
+string UPDATER_PARM_SEPARATOR = "~~~";
+    // three tildes is an uncommon thing to have otherwise, so we use it to separate
+    // our commands in linked messages.
+
+string SCRIPT_DEPENDENCY_MARK = "DEP";  // signals that a dependency is coming.
+string ITEM_LIST_SEPARATOR = "``";  // separates dependencies.
+
+integer MAXIMUM_SERVERS_TRACKED = 32;
+    // we will listen to this many servers before we decide to remove one.
+
+string CONTINUANCE_MARKER = "...";
+    // a string sent when the update list is too long and needs to be continued in another chat.
+
+string SERVER_SCRIPT = "a huffotronic update server";
+    // the prefix of our server script that hands out updates.
+
+// global variables...
+
+integer inventory_request_channel;  // used for newer version servers to cut down cross chatter.
+list updaters_heard;  // the update servers we've heard from recently.
+list last_interactions;  // times of the last update process engaged with the updater.
+integer update_channel;  // current channel for interaction with specific server.
+key current_server;  // the updater that is active right now, if any.
+integer update_start_time;  // when the last update process began.
+list updates_needed;  // stores the set of scripts that are in need of an update.
+list known_script_dependencies;  // stores the list of dependency info.
+
+careful_crankup()
+{
+    knock_around_other_scripts(TRUE);
+    // clean out the older items and scripts.  we do this after getting everyone running
+    // since we might be whacking ourselves.
+    destroy_older_versions();
+}
+
+// reset our variables.
+initialize()
+{
+    updaters_heard = [];
+    last_interactions = [];
+    inventory_request_channel = 0;
+    update_channel = 0;
+    current_server = NULL_KEY;
+    llSetTimerEvent(0.0);
+    llSetRemoteScriptAccessPin(UPDATER_SCRIPT_PIN);
+    // a new enhancements; tells the server that this guy has finished an update cycle.  this
+    // only comes into play when the updater script itself has just been updated, but it's
+    // nice for the server to avoid claiming erroneous timeouts occurred.
+    llSay(OLD_REQUEST_INVENTORY_CHANNEL, DONE_UPDATING);
+    llSleep(0.4);  // snooze and repeat to overcome occasionally lossy chats.
+    llSay(OLD_REQUEST_INVENTORY_CHANNEL, DONE_UPDATING);
+}
+
+whack_updater_record(key id)
+{
+    integer prev_indy = find_in_list(updaters_heard, id);
+    if (prev_indy < 0) return;  // not there.
+    updaters_heard = chop_list(updaters_heard, 0, prev_indy - 1)
+            + chop_list(updaters_heard, prev_indy + 1, llGetListLength(updaters_heard) - 1);
+    last_interactions = chop_list(last_interactions, 0, prev_indy - 1)
+            + chop_list(last_interactions, prev_indy + 1, llGetListLength(last_interactions) - 1);
+}
+
+// note that this new, lower memory version, depends on the inventory functions returning
+// items in alphabetical order.
+scrub_items_by_type(string this_guy, integer inventory_type)
+{
+    list removal_list;
+    integer outer;
+    for (outer = 0; outer < llGetInventoryNumber(inventory_type); outer++) {
+        string curr = llGetInventoryName(inventory_type, outer);
+        list split = compute_basename_and_version(curr);
+        // make sure there was a comparable version number in this name.
+        if ( (curr != this_guy) && llGetListLength(split)) {
+            string curr_base = llList2String(split, 0);
+            float curr_ver = (float)llList2String(split, 1);
+//log_it("outer: " + curr_base + " / " + (string)curr_ver);
+            integer inner;
+            for (inner = outer + 1; inner < llGetInventoryNumber(inventory_type); inner++) {
+                string next_guy = llGetInventoryName(inventory_type, inner);
+                list comp_split = compute_basename_and_version(next_guy);
+                if (llGetListLength(comp_split)) {
+                    string comp_base = llList2String(comp_split, 0);
+                    float comp_ver = (float)llList2String(comp_split, 1);
+                    // okay, now we can actually compare.
+                    if (curr_base != comp_base) {
+                        // break out of inner loop.  we are past where the names can matter.
+                        inner = 2 * llGetInventoryNumber(inventory_type);
+                    } else {
+//log_it("inner: " + comp_base + " / " + (string)comp_ver);
+                        if (curr_ver <= comp_ver) {
+                            // the script at inner index is comparable or better than
+                            // the script at the outer index.
+                            removal_list += curr;
+                        } else {
+                            // this inner script must be inferior to the outer one,
+                            // somehow, which defies our expectation of alphabetical ordering.
+                            removal_list += next_guy;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    // now actually do the deletions.
+    for (outer = 0; outer < llGetListLength(removal_list); outer++) {
+        string to_whack = llList2String(removal_list, outer);
+        if (DEBUGGING)
+            log_it("removing older asset: " + to_whack);
+        llRemoveInventory(to_whack);
+    }
+}
+
+// ensures that only the latest version of any script or object is kept in our inventory.
+destroy_older_versions()
+{
+    // firstly, iterate across scripts to clean out older versions.
+    scrub_items_by_type(llGetScriptName(), INVENTORY_SCRIPT);
+    // secondly, try to clean out the objects.
+    scrub_items_by_type(llGetScriptName(), INVENTORY_OBJECT);
+    // thirdly, try to clean out the notecards.
+    scrub_items_by_type(llGetScriptName(), INVENTORY_NOTECARD);
+}
+
+// sets the object to be listening for update info.
+// if "just_owner" is true, then we will not listen on the general announcement channel.
+listen_for_orders(integer just_owner)
+{
+    if (!just_owner) {
+        // try to hear an update being announced.
+        llListen(UPDATE_ANNOUNCEMENT_CHANNEL, "", NULL_KEY, "");
+    }
+
+    // super secret owner controls.
+    llListen(0, "", llGetOwner(), "");
+}
+
+// returns true if this object is a huffotronic updater of some sort.
+integer inside_of_updater()
+{
+    return find_substring(llGetObjectName(), "huffotronic") >= 0;
+}
+
+// returns true if a script is a version of our update server.
+integer matches_server_script(string to_check)
+{
+    return is_prefix(to_check, SERVER_SCRIPT);
+}
+
+// stops all the scripts besides this one.
+knock_around_other_scripts(integer running_state)
+{
+    integer insider = inside_of_updater();
+    if (running_state == TRUE) {
+        // make sure we crank up the scripts that are new first.  we want to reset them
+        // as well, which we don't want to do for any existing scripts.
+        integer crank_indy;
+        for (crank_indy = 0; crank_indy < llGetListLength(updates_needed); crank_indy++) {
+            string crankee = llList2String(updates_needed, crank_indy);
+            if (find_in_inventory(crankee, INVENTORY_SCRIPT, TRUE) >= 0) {
+                if (!insider || matches_server_script(crankee)) {
+                    // allow it to run again.
+                    llSetScriptState(crankee, TRUE);
+                    // reset it, to make sure it starts at the top.
+                    llResetOtherScript(crankee);
+                }
+            }
+        }
+    }
+
+    integer indy;
+    string self_script = llGetScriptName();
+    // we set all other scripts to the running state requested.
+    for (indy = 0; indy < llGetInventoryNumber(INVENTORY_SCRIPT); indy++) {
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, indy);
+        if ( (curr_script != self_script)
+            && (!insider || matches_server_script(curr_script)) ) {
+            // this one seems ripe for being set to the state requested.
+            llSetScriptState(curr_script, running_state);
+        }
+    }
+}
+
+// a random channel for the interaction with the server.
+integer random_channel() { return -(integer)(llFrand(800000) + 20000); }
+
+// make sure that any dependencies for the script with "basename" are added to the list
+// of requests we make during an update.
+list add_dependencies(string basename)
+{
+    list to_return;
+    integer indy;
+    for (indy = 0; indy < llGetListLength(known_script_dependencies); indy++) {
+        list deps = llParseString2List(llList2String(known_script_dependencies, indy),
+            [ITEM_LIST_SEPARATOR], []);
+//log_it("base=" + llList2String(dep, 0) + " lastver=" + llList2String(dep, 1) + " newdep=" + llList2String(dep, 2));
+        if (basename == llList2String(deps, 0)) {
+            // first off, is this item with new dependencies actually present?
+            integer where = find_in_inventory(basename, INVENTORY_SCRIPT, FALSE);
+            if (where >= 0) {
+                // we do use the script with deps, but is the dependent item really missing?
+                where = find_in_inventory(llList2String(deps, 1), INVENTORY_SCRIPT, FALSE);
+                if (where < 0) {
+                    // we found a dependency match for this script, so we'll ask for the missing item.
+                    if (DEBUGGING)
+                        log_it("missing dep: " + llList2String(deps, 1));
+                    to_return += [ llList2String(deps, 1) ];
+                }
+            }
+        }
+    }
+    return to_return;
+}
+
+// complains if memory seems to be getting tight.
+test_memory()
+{
+    if (llGetFreeMemory() < 4096)
+        log_it("mem_free = " + (string)llGetFreeMemory());
+}
+
+// starts an update given a list of scripts that the server has available, encoded as
+// a string in the "encoded_list".
+integer initiate_update(string encoded_list)
+{
+    list scripts_avail = llParseString2List(encoded_list, [UPDATER_PARM_SEPARATOR], []);
+    integer continue_listening_for_scripts = FALSE;
+      // if true, we aren't done hearing about available scripts yet.
+    encoded_list = "";
+    // figure out which scripts we need by comparing the list available from the server
+    // against our current inventory.  we only want scripts with newer version numbers.
+    integer sindy;
+    for (sindy = 0; sindy < llGetListLength(scripts_avail); sindy++) {
+        string curr = llList2String(scripts_avail, sindy);
+        if (curr == CONTINUANCE_MARKER) {
+            // this is a special continuation signal.  we need to hear the rest of the list.
+            continue_listening_for_scripts = TRUE;
+        } else if (is_prefix(curr, SCRIPT_DEPENDENCY_MARK)) {
+            // we've found a dependency item.
+            known_script_dependencies += [ llGetSubString(curr, llStringLength(SCRIPT_DEPENDENCY_MARK), -1) ];
+//log_it("script dep: " + llGetSubString(curr, llStringLength(SCRIPT_DEPENDENCY_MARK), -1));
+        } else {
+            list split = compute_basename_and_version(curr);
+            if (llGetListLength(split) == 2) {
+                string basename = llList2String(split, 0);
+                string version = llList2String(split, 1);
+                split = [];
+                integer oy_indy;
+//replace common code with func.
+                for (oy_indy = 0; oy_indy < llGetInventoryNumber(INVENTORY_OBJECT); oy_indy++) {
+                    list srv_split = compute_basename_and_version
+                        (llGetInventoryName(INVENTORY_OBJECT, oy_indy));
+                    if ( (llGetListLength(srv_split) == 2)
+                            && (basename == llList2String(srv_split, 0))
+                            && ((float)version > (float)llList2String(srv_split, 1)) ) {
+//                        if (DEBUGGING) {
+                            log_it("i need '" + curr + "' from server " + (string)inventory_request_channel);
+//                        }
+                        test_memory();
+                        updates_needed += [ curr ];
+                    }
+                }
+                for (oy_indy = 0; oy_indy < llGetInventoryNumber(INVENTORY_NOTECARD); oy_indy++) {
+                    list srv_split = compute_basename_and_version
+                        (llGetInventoryName(INVENTORY_NOTECARD, oy_indy));
+                    if ( (llGetListLength(srv_split) == 2)
+                            && (basename == llList2String(srv_split, 0))
+                            && ((float)version > (float)llList2String(srv_split, 1)) ) {
+                        if (DEBUGGING) {
+                            log_it("i need '" + curr + "' from server " + (string)inventory_request_channel);
+                        }
+                        test_memory();
+                        updates_needed += [ curr ];
+                    }
+                }
+                for (oy_indy = 0; oy_indy < llGetInventoryNumber(INVENTORY_SCRIPT); oy_indy++) {
+                    list srv_split = compute_basename_and_version
+                        (llGetInventoryName(INVENTORY_SCRIPT, oy_indy));
+                    if ( (llGetListLength(srv_split) == 2)
+                            && (basename == llList2String(srv_split, 0))
+                            && ((float)version > (float)llList2String(srv_split, 1)) ) {
+                        if (DEBUGGING) {
+                            log_it("i need '" + curr + "' from server " + (string)inventory_request_channel);
+                        }
+                        test_memory();
+                        updates_needed += [ curr ];
+                    }
+                }
+                updates_needed += add_dependencies(basename);
+            }
+        }
+    }
+    // we skip the next step if we're still waiting to hear about more.
+    if (continue_listening_for_scripts) {
+//log_it("still listening for more updates...");
+        return FALSE;
+    }
+    if (llGetListLength(updates_needed)) {
+//log_it("update chan=" + (string)update_channel);
+        llSay(update_channel, REQUEST_SCRIPT_UPDATE);
+        if (DEBUGGING) {
+            log_it("told server " + (string)inventory_request_channel + " that i need updating.");
+        }
+    } else {
+        if (DEBUGGING) {
+            log_it("told server " + (string)inventory_request_channel + " that i am done updating.");
+        }
+        llSay(update_channel, DONE_UPDATING);
+    }
+    return TRUE;
+}    
+
+// this alerts the server to our most desired scripts.
+tell_server_our_wish_list()
+{
+    llSay(update_channel, READY_TO_UPDATE + wrap_parameters(updates_needed));
+}
+
+// checks whether all of the updates needed are present yet.
+integer check_on_update_presence()
+{
+    integer indy;
+    for (indy = 0; indy < llGetListLength(updates_needed); indy++) {
+        integer found = find_in_inventory(llList2String(updates_needed, indy), INVENTORY_ALL, TRUE);
+        // any single missing guy means they aren't up to date yet.
+        if (found < 0) {
+            if (DEBUGGING) log_it(llList2String(updates_needed, indy) + " not seen as updated yet.");
+            return FALSE;
+        }
+    }
+    // nothing was detected as missing anymore.
+    return TRUE;
+}
+
+// respond to spoken commands from the server.
+integer process_update_news(integer channel, string name, key id, string message)
+{
+    if (!channel) {
+        // this is a command.
+        if (message == "ureset") {
+            llResetScript();  // start over.
+        }
+        if (message == "ushow") {
+            integer sindy;
+            integer script_count = llGetInventoryNumber(INVENTORY_SCRIPT);
+            list script_list = [ "scripts--" ];  // first item is just a header.
+            for (sindy = 0; sindy < script_count; sindy++) {
+                script_list += [ llGetInventoryName(INVENTORY_SCRIPT, sindy) ];
+            }
+            dump_list_to_log(script_list);
+        }
+        return FALSE;  // nothing to do here.
+    }
+    if (!update_channel && (channel == UPDATE_ANNOUNCEMENT_CHANNEL)) {
+/* never seen.        if (id == llGetKey()) {
+if (DEBUGGING) log_it("ignoring update from self.");            
+            return FALSE;  // ack, that's our very object.
+        }
+*/
+        if (llStringLength(message) > llStringLength(UPDATE_ANNOUNCEMENT_PREFIX)) {
+            // this is a new style update message.  we can set a different request channel.
+            string just_chan = llDeleteSubString(message, 0, llStringLength(UPDATE_ANNOUNCEMENT_PREFIX) - 1);
+            inventory_request_channel = (integer)just_chan;
+        }
+        integer prev_indy = find_in_list(updaters_heard, id);
+        // find the talker in our list.
+        if (prev_indy >= 0) {
+            // that guy was already heard from.  check when last interacted.
+            integer last_heard = llList2Integer(last_interactions, prev_indy);
+            if (llAbs(llGetUnixTime() - last_heard) < SERVER_IGNORE_TIME) {
+                return FALSE;  // not time to update with this guy again yet.
+            }
+//            if (DEBUGGING) { log_it("started listening again to server " + (string)id); }
+            // make sure we think of this as a new updater now.
+            whack_updater_record(id);
+        }
+
+        if (DEBUGGING) { log_it("heard server " + (string)inventory_request_channel + "'s announcement."); }
+        // record our new server.
+        current_server = id;
+        // make a random pause so not all updaters try to crank up at same time.
+        llSleep(randomize_within_range(2.8, 18.2, FALSE));
+
+        if (llGetListLength(updaters_heard) > MAXIMUM_SERVERS_TRACKED) {
+            // oops, this is not good.  we have too many servers now.
+//hmmm: room for improvement here by tossing out the server that is oldest.
+            updaters_heard = llDeleteSubList(updaters_heard, 0, 0);
+            last_interactions = llDeleteSubList(last_interactions, 0, 0);
+        }
+        
+        // add the talker to our list.
+        updaters_heard += id;
+        last_interactions += llGetUnixTime();
+    
+        // begin the update interaction with this guy.
+        update_channel = random_channel();
+        return TRUE;
+    }
+    if (update_channel && (channel == update_channel) ) {
+        if (is_prefix(message, REPORT_AVAILABLE_SCRIPTS)) {
+            // tasty, this is a list of scripts that can be had.
+            message = llDeleteSubString(message, 0, llStringLength(REPORT_AVAILABLE_SCRIPTS) - 1);
+            if (message == BUSY_BUSY) {
+                // server has signified that it's too busy (or its owner is a moron) because it is
+                // claiming it has no scripts at all.
+                if (DEBUGGING) {
+                    log_it("server " + (string)inventory_request_channel + " is too busy to update us now.");
+                }
+                // make it seem like we need to do this one again sooner than normal.
+                whack_updater_record(id);
+                // busy server means move no further forward.
+                return FALSE;
+            }
+            return initiate_update(message);
+        } else if (is_prefix(message, SHUT_THEM_DOWN)) {
+            if (DEBUGGING) { log_it("stopping other scripts."); }
+            knock_around_other_scripts(FALSE);
+            // now that we know for sure the server's ready to update us,
+            // we tell it what we need.
+            tell_server_our_wish_list();
+            return FALSE;
+        } else if (is_prefix(message, START_THEM_UP)) {
+            // let the server know that we've finished, for all intents and purposes.
+            llSay(update_channel, DONE_UPDATING);
+            // we pause a random bit first; we want to ensure we aren't swamping
+            // SL with our inventory loading.
+            llSleep(randomize_within_range(2.5, 8.2, FALSE));
+            if (DEBUGGING) { log_it("starting other scripts."); }
+            careful_crankup();
+            return TRUE;  // change state now.
+//        } else {
+//log_it("unknown command on update channel: " + message);
+        }
+    }
+    return FALSE;
+}
+
+//////////////
+// from hufflets...
+
+integer debug_num = 0;
+
+// a debugging output method.  can be disabled entirely in one place.
+log_it(string to_say)
+{
+    debug_num++;
+    llWhisper(0, llGetScriptName() + " [" + (string)debug_num + "] (" + (string)llGetFreeMemory() + ") " + to_say);
+}
+
+// returns a number at most "maximum" and at least "minimum".
+// if "allow_negative" is TRUE, then the return may be positive or negative.
+float randomize_within_range(float minimum, float maximum, integer allow_negative)
+{
+    if (minimum > maximum) {
+        // flip the two if they are reversed.
+        float temp = minimum; minimum = maximum; maximum = temp;
+    }
+    float to_return = minimum + llFrand(maximum - minimum);
+    if (allow_negative) {
+        if (llFrand(1.0) < 0.5) to_return *= -1.0;
+    }
+    return to_return;
+}
+
+// returns TRUE if the "pattern" is found in the "full_string".
+integer matches_substring(string full_string, string pattern)
+{ return (find_substring(full_string, pattern) >= 0); }
+
+// returns the index of the first occurrence of "pattern" inside
+// the "full_string".  if it is not found, then a negative number is returned.
+integer find_substring(string full_string, string pattern)
+{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); }
+
+// returns TRUE if the "prefix" string is the first part of "compare_with".
+integer is_prefix(string compare_with, string prefix)
+{ return find_substring(compare_with, prefix) == 0; }
+
+// locates the string "text" in the list to "search_in".
+integer find_in_list(list search_in, string text)
+{ 
+    integer len = llGetListLength(search_in);
+    integer i; 
+    for (i = 0; i < len; i++) { 
+        if (llList2String(search_in, i) == text) 
+            return i; 
+    } 
+    return -1;
+}
+
+// returns the portion of the list between start and end, but only if they are
+// valid compared with the list length.  an attempt to use negative start or
+// end values also returns a blank list.
+list chop_list(list to_chop, integer start, integer end)
+{
+    integer last_len = llGetListLength(to_chop) - 1;
+    if ( (start < 0) || (end < 0) || (start > last_len) || (end > last_len) ) return [];
+    return llList2List(to_chop, start, end);
+}
+
+// joins a list of parameters using the parameter sentinel for the library.
+string wrap_parameters(list to_flatten)
+{ return llDumpList2String(to_flatten, UPDATER_PARM_SEPARATOR); }
+
+// locates the item with "name_to_find" in the inventory items with the "type".
+// a value from 0 to N-1 is returned if it's found, where N is the number of
+// items in the inventory.
+integer find_in_inventory(string name_to_find, integer inv_type, integer exact_match)
+{
+    integer num_inv = llGetInventoryNumber(inv_type);
+    if (num_inv == 0) return -1;  // nothing there!
+    integer inv;
+    for (inv = 0; inv < num_inv; inv++) {
+        if (exact_match && (llGetInventoryName(inv_type, inv) == name_to_find) )
+            return inv;
+        else if (!exact_match && is_prefix(llGetInventoryName(inv_type, inv), name_to_find))
+            return inv;
+    }
+    return -2;  // failed to find it.
+}
+
+//////////////
+
+integer MAX_CHAT_LINE = 900;
+    // the most characters we'll try to say in one chat.
+
+dump_list_to_log(list to_show)
+{
+    string text = dump_list(to_show);  // get some help from the other version.
+    integer len = llStringLength(text);
+    integer i;
+    for (i = 0; i < len; i += MAX_CHAT_LINE) {
+        integer last_bit = i + MAX_CHAT_LINE - 1;
+        if (last_bit >= len) last_bit = len - 1;
+        string next_line = llGetSubString(text, i, last_bit);
+        llWhisper(0, next_line);
+    }
+}
+
+// returns a printable form of the list.
+string dump_list(list to_show)
+{
+    integer len = llGetListLength(to_show);
+    integer i;
+    string text;
+    for (i = 0; i < len; i++) {
+        string next_line = llList2String(to_show, i);
+        if (find_substring(next_line, " ") >= 0) {
+            // this guy has a space in it, so quote it.
+            next_line = "\"" + next_line + "\"";
+        }
+        text = text + next_line;
+        if (i < len - 1) text = text + " ";
+    }
+    return text;
+}
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.8.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+// end hufflets.
+//////////////
+
+// no huffotronic trap state for startup, because this script will actually
+// run (and is expected) inside a huffotronic updater object.
+
+default
+{
+    state_entry()
+    {
+        auto_retire();  // only allow the most recent revision.
+        initialize();
+        state awaiting_commands;
+    }
+}
+
+state awaiting_commands
+{
+    state_entry()
+    {
+        if (DEBUGGING) log_it("<awaiting_commands>");
+        careful_crankup();  // we always start by getting everyone running.
+        current_server = NULL_KEY;  // forget previous server.
+        listen_for_orders(FALSE);
+        inventory_request_channel = 0;  // no inventory request channel either.
+        update_channel = 0;  // no channel currently.
+        updates_needed = [];  // we know of no needs right now.
+        known_script_dependencies = [];  // no deps either.
+    }
+
+    state_exit() { llSetTimerEvent(0.0); }
+
+    listen(integer channel, string name, key id, string message)
+    {
+        if ((id != llGetOwner()) && (llGetOwnerKey(id) != llGetOwner())) {
+            return;  // must be same owner to ensure proper perms.
+        }
+        if (process_update_news(channel, name, id, message))
+            state establish_private_channel;
+    }
+}
+
+state establish_private_channel
+{
+    state_entry()
+    {
+        if (DEBUGGING) log_it("<establish_private_channel>");
+        llListen(update_channel, "", current_server, "");
+        listen_for_orders(TRUE);
+        if (inventory_request_channel)
+            llSay(inventory_request_channel, REQUEST_INVENTORY_PREFIX + (string)update_channel);
+        else
+            llSay(OLD_REQUEST_INVENTORY_CHANNEL, REQUEST_INVENTORY_PREFIX + (string)update_channel);
+        llSetTimerEvent(MAXIMUM_UPDATE_TIME_ALLOWED);
+    }
+
+    state_exit() { llSetTimerEvent(0); }
+
+    listen(integer channel, string name, key id, string message)
+    {
+        if ((id != llGetOwner()) && (llGetOwnerKey(id) != llGetOwner())) {
+            return;  // must be same owner to ensure proper perms.
+        }
+        if (process_update_news(channel, name, id, message)) {
+            // ready for a state change, but what kind?
+            if (llGetListLength(updates_needed)) {
+//log_it("have a list of updates now.");
+                state performing_update;
+            } else {
+//log_it("no updates needed in list, going back");
+                state awaiting_commands;
+            }
+        }
+    }
+    
+    timer() {
+        if (DEBUGGING) {
+            log_it("timed out establishing channel with server " + (string)inventory_request_channel);
+        }
+        whack_updater_record(current_server);
+        state awaiting_commands;
+    }
+
+    on_rez(integer parm) { state default; }
+}
+
+state performing_update
+{
+    state_entry()
+    {
+        // must re-listen after a state change.
+        llListen(update_channel, "", current_server, "");
+        listen_for_orders(TRUE);
+        if (DEBUGGING) log_it("<performing_update>");
+        llSetTimerEvent(UPDATE_TIMER_INTERVAL);
+        update_start_time = llGetUnixTime();
+    }
+
+    state_exit() { llSetTimerEvent(0.0); }
+
+    listen(integer channel, string name, key id, string message)
+    {
+        if ((id != llGetOwner()) && (llGetOwnerKey(id) != llGetOwner())) {
+            return;  // must be same owner to ensure proper perms.
+        }
+        if (process_update_news(channel, name, id, message)) {
+            // normal finish of update process.
+            state awaiting_commands;
+        }
+    }
+
+    timer() {
+        if (llGetListLength(updates_needed) == 0) {
+//log_it("nothing to update, leaving perform state.");
+            state awaiting_commands;  // we've got nothing to do.
+        } else {
+            // see if all our requested scripts are there yet; if not, we're not done updating.
+            integer ready = check_on_update_presence();
+            if (ready) {
+                if (DEBUGGING) log_it("reporting scripts are current.");
+                llSay(update_channel, SCRIPTS_ARE_CURRENT);
+            }
+        }
+        if (llAbs(update_start_time - llGetUnixTime()) >= MAXIMUM_UPDATE_TIME_ALLOWED) {
+            if (DEBUGGING) { log_it("timeout during update process with server " + (string)inventory_request_channel); }
+            whack_updater_record(current_server);
+            state awaiting_commands;
+        }
+    }
+
+    on_rez(integer parm) { state default; }
+}
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/hufflets_v6.3.txt b/huffware/huffotronic_updater_freebie_v5.3/hufflets_v6.3.txt
new file mode 100755 (executable)
index 0000000..aa9a0e1
--- /dev/null
@@ -0,0 +1,742 @@
+
+// huffware script: hufflets, by fred huffhines.
+//
+// functions that are commonly used but really are too simple to implement via IPC.
+// these just get copied and pasted into other scripts.
+// *note* that means you should not drop this script into an object.  it will not
+// do anything for you.  instead, copy what you need out of here.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+integer DEBUGGING = FALSE;
+    // if this is set to true, then some functions produce noisier results.
+
+//////////////
+
+// diagnostic hufflets...
+
+integer debug_num = 0;
+
+// a debugging output method.  can be disabled entirely in one place.
+log_it(string to_say)
+{
+    debug_num++;
+    // tell this to the owner.    
+    llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on an unusual channel for chat if it's not intended for general public.
+//    llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on open chat that anyone can hear.  we take off the bling for this one.
+//    llSay(0, to_say);
+}
+
+//////////////
+
+// mathematical hufflets...
+
+// returns a floating point absolute value.
+float fabs(float take_absolute_value) {
+    if (take_absolute_value >= 0.0) return take_absolute_value;
+    else return -1.0 * take_absolute_value;
+}
+
+//////////////
+
+// time hufflets...
+
+// shows a somewhat pretty printed version of the number of seconds.
+string time_text(integer seconds)
+{
+    float s_min = 60; float s_hour = 60 * s_min; float s_day = 24 * s_hour;
+    float s_year = 365.25 * s_day;
+    
+    if (seconds < s_min) return (string)seconds + " seconds";
+    else if (seconds < s_hour) return float_chop(seconds / s_min) + " minutes";
+    else if (seconds < s_day) return float_chop(seconds / s_hour) + " hours";
+    else if (seconds < s_year) return float_chop(seconds / s_day) + " days";
+    else return float_chop(seconds / s_year) + " years";
+}
+
+//////////////
+
+// string processing hufflets...
+
+// the string processing methods are not case sensitive.
+  
+// returns TRUE if the "pattern" is found in the "full_string".
+integer matches_substring(string full_string, string pattern)
+{ return (find_substring(full_string, pattern) >= 0); }
+
+// returns the index of the first occurrence of "pattern" inside
+// the "full_string".  if it is not found, then a negative number is returned.
+integer find_substring(string full_string, string pattern)
+{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); }
+
+// returns TRUE if the "prefix" string is the first part of "compare_with".
+integer is_prefix(string compare_with, string prefix)
+{ return find_substring(compare_with, prefix) == 0; }
+
+// takes any redundant space characters out of the string.
+string compress_spaces(string s)
+{
+    string to_return;
+    integer in_space = FALSE;
+    integer i;
+    for (i = 0; i < llStringLength(s); i++) {
+        string chunk = llGetSubString(s, i, i);
+        if (chunk == " ") {
+            if (in_space) {
+                // we're have already seen a space.  don't keep this too.
+                //continue;  no such keyword in lsl.
+            } else {
+                in_space = TRUE;
+                to_return += chunk;
+            }
+        } else {
+            // the current character was not a space, so just add it.
+            in_space = FALSE;
+            to_return += chunk;
+        }
+    }
+    return to_return;
+}
+
+//////////////
+
+// sim-related hufflets...
+
+// returns TRUE if the value in "to_check" specifies a legal x or y value in a sim.
+integer valid_sim_value(float to_check)
+{
+    if (to_check < 0.0) return FALSE;
+    if (to_check >= 257.0) return FALSE;
+    return TRUE;
+}
+
+// returns TRUE if the "to_check" vector is a location outside of the current sim.
+integer outside_of_sim(vector to_check)
+{
+    return !valid_sim_value(to_check.x) || !valid_sim_value(to_check.y);
+}
+
+//////////////
+
+// list processing hufflets...
+
+// locates the string "text" in the list to "search_in".
+integer find_in_list(list search_in, string text)
+{ 
+    integer len = llGetListLength(search_in);
+    integer i; 
+    for (i = 0; i < len; i++) { 
+        if (llList2String(search_in, i) == text) 
+            return i; 
+    } 
+    return -1;
+}
+
+// removes the entry at "index" and instead inserts the list "to_insert"
+// at that position.
+list replace_entry(list to_modify, integer index, list to_insert)
+{
+    if (llGetListLength(to_modify) == 0) return to_insert;  // special case for empty.
+    return llListReplaceList(to_modify, to_insert, index, index);
+}
+
+// returns the portion of the list between start and end, but only if they are
+// valid compared with the list length.  an attempt to use negative start or
+// end values also returns a blank list.
+list chop_list(list to_chop, integer start, integer end)
+{
+    integer last_len = llGetListLength(to_chop) - 1;
+    if ( (start < 0) || (end < 0) || (start > last_len) || (end > last_len) ) return [];
+    return llList2List(to_chop, start, end);
+}
+
+//////////////
+
+integer MAX_CHAT_LINE = 1008;
+    // the most characters we'll try to say in one chat.
+
+dump_list_to_log(list to_show)
+{
+    string text = dump_list(to_show);  // get some help from the other version.
+    integer len = llStringLength(text);
+    integer i;
+    for (i = 0; i < len; i += MAX_CHAT_LINE) {
+        integer last_bit = i + MAX_CHAT_LINE - 1;
+        if (last_bit >= len) last_bit = len - 1;
+        string next_line = llGetSubString(text, i, last_bit);
+        log_it(next_line);
+    }
+}
+
+// returns a printable form of the list.
+string dump_list(list to_show)
+{
+    integer len = llGetListLength(to_show);
+    integer i;
+    string text;
+    for (i = 0; i < len; i++) {
+        string next_line = llList2String(to_show, i);
+        if (find_substring(next_line, " ") >= 0) {
+            // this guy has a space in it, so quote it.
+            next_line = "'" + next_line + "'";
+        }
+        text = text + next_line;
+        if (i < len - 1) text = text + " ";
+    }
+    return text;
+}
+
+// extracts space separated elements from a string, and honors quoting of either
+// variety as long as the quotes come in pairs.  this enables the inclusion of
+// spaces in the elements of the set.  note that this function requires a well-formed
+// string where there are no multiple space characters in a row.
+list parse_quoted_strings(string to_parse)
+{
+    list to_return;  // will pile up what we find in the string.
+    integer quoting = FALSE;  // are we inside quotes?
+    string curr_quote = "";  // what is current quote char, if any?
+    string accum;  // accumulates parts of the current element.
+    // loop over the string and apply our rules.
+    integer i;
+    for (i = 0; i < llStringLength(to_parse); i++) {
+        string c = llGetSubString(to_parse, i, i);
+        if (!quoting && (c == " ")) {
+            // this space marks the end of a word.
+            if (llStringLength(accum) > 0) {
+//log_it("space adding to set: " + accum);
+                to_return += [ accum ];
+                accum = "";
+            }
+        } else if (quoting && (c == curr_quote)) {
+            // done with quotes, so add the quoted item, even if nil.
+            to_return += [ accum ];
+//log_it("quote adding to set: " + accum);
+            accum = "";
+            quoting = FALSE;
+        } else if (!quoting && ( (c == "'") || (c == "\"") ) ) {
+            // we've started into quoting mode.
+            quoting = TRUE;
+            curr_quote = c;
+        } else {
+            // if no condition applies, just add this to the accumulator.
+            accum += c;
+        }
+    }
+    // add the last thing we accumulated.
+    if (llStringLength(accum) > 0) {
+//log_it("last add to set: " + accum);
+        to_return += [ accum ];
+    }
+    return to_return;
+}
+
+//////////////
+
+// action queue for postponed activities.  the first field held in a list item here
+// is an integer action code.  the format of the remaining parameters is up to the
+// caller, and they can be used as the final parameters for when the queued action
+// gets handled.
+list action_queue;
+
+// looks at the action code at the head of the queue without removing the action.
+integer peek_action_code()
+{
+    list fields = llParseString2List(llList2String(action_queue, 0), [HUFFWARE_PARM_SEPARATOR], []);
+    return extract_action_code(fields);
+}
+
+// extracts the action code from a retrieved list.
+integer extract_action_code(list to_parse) { return llList2Integer(to_parse, 0); }
+
+// removes the current head of the action queue and returns it.
+list pop_action_record()
+{
+    if (llGetListLength(action_queue) == 0) {
+//log_it("failure in action q: no entries.");
+        return [];
+    }
+    list top_action = llParseString2List(llList2String(action_queue, 0), [HUFFWARE_PARM_SEPARATOR], []);
+    action_queue = llDeleteSubList(action_queue, 0, 0);
+    return top_action;
+}
+
+// adds a new action to the end of the action queue.
+push_action_record(integer action, list added_parms)
+{
+    action_queue += [ wrap_parameters([action] + added_parms) ];
+}
+
+//////////////
+
+// randomizing hufflets...
+
+// returns a number at most "maximum" and at least "minimum".
+// if "allow_negative" is TRUE, then the return may be positive or negative.
+float randomize_within_range(float minimum, float maximum, integer allow_negative)
+{
+    if (minimum > maximum) {
+        // flip the two if they are reversed.
+        float temp = minimum; minimum = maximum; maximum = temp;
+    }
+    float to_return = minimum + llFrand(maximum - minimum);
+    if (allow_negative) {
+        if (llFrand(1.0) < 0.5) to_return *= -1.0;
+    }
+    return to_return;
+}
+
+// returns a random vector where x,y,z will be between "minimums" and "maximums"
+// x,y,z components.  if "allow_negative" is true, then any component will
+// randomly be negative or positive.
+vector random_bound_vector(vector minimums, vector maximums, integer allow_negative)
+{
+    return <randomize_within_range(minimums.x, maximums.x, allow_negative),
+        randomize_within_range(minimums.y, maximums.y, allow_negative),
+        randomize_within_range(minimums.z, maximums.z, allow_negative)>;
+}
+
+// returns a vector whose components are between minimum and maximum.
+// if allow_negative is true, then they can be either positive or negative.
+vector random_vector(float minimum, float maximum, integer allow_negative)
+{
+    return random_bound_vector(<minimum, minimum, minimum>,
+        <maximum, maximum, maximum>, allow_negative);
+}
+
+//////////////
+
+// vector hufflets...
+
+// returns TRUE if a is less than b in any component.
+integer vector_less_than(vector a, vector b)
+{ return (a.x < b.x) || (a.y < b.y) || (a.z < b.z); }
+
+// returns TRUE if a is greater than b in any component.
+integer vector_greater_than(vector a, vector b)
+{ return (a.x > b.x) || (a.y > b.y) || (a.z > b.z); }
+
+// returns text for a floating point number, but includes only
+// three digits after the decimal point.
+string float_chop(float to_show)
+{
+    integer mant = llAbs(llRound(to_show * 1000.0) / 1000);
+    string neg_sign;
+    if (to_show < 0.0) neg_sign = "-";
+    string dec_s = (string)((llRound(to_show * 1000.0) - mant * 1000) / 1000.0);
+    dec_s = llGetSubString(llGetSubString(dec_s, find_substring(dec_s, ".") + 1, -1), 0, 2);
+    // strip off all trailing zeros.
+    while (llGetSubString(dec_s, -1, -1) == "0")
+        dec_s = llDeleteSubString(dec_s, -1, -1);
+    string to_return = neg_sign + (string)mant;
+    if (llStringLength(dec_s)) to_return += "." + dec_s;
+    return to_return;
+}
+
+// returns a prettier form for vector text, with chopped floats.
+string vector_chop(vector to_show)
+{
+    return "<" + float_chop(to_show.x) + ","
+        + float_chop(to_show.y) + ","
+        + float_chop(to_show.z) + ">";
+}
+
+// prints the list of vectors with trimmed floats.
+string vector_list_chop(list to_show)
+{
+    integer list_len = llGetListLength(to_show);
+    string to_return;
+    integer indy;
+    for (indy = 0; indy < list_len; indy++) {
+        if (indy != 0) to_return += HUFFWARE_ITEM_SEPARATOR;
+        to_return += vector_chop((vector)llList2String(to_show, indy));
+    }
+    return to_return;
+}
+
+// returns a list with two components; a new vector and a boolean.
+// the new vector starts from "starting_point".  it will have a vector
+// between "minimum_addition" and "maximum_addition" added to it.
+// if it is over the "minimum_allowed" or the "maximum_allowed", then
+// it is reset to whichever it would have crossed over.  two booleans
+// are also returned to indicate when the lower and upper limits were
+// exceeded (in that order).
+list limit_and_add(vector starting_point,
+    vector minimum_allowed, vector maximum_allowed,
+    vector minimum_addition, vector maximum_addition)
+{
+    integer too_low = FALSE;
+    integer too_high = FALSE;
+    vector new_location = starting_point;
+    vector addition = random_bound_vector(minimum_addition, maximum_addition, FALSE);
+//log_it("start=" + (string)starting_point + " addin=" + (string)addition);
+    new_location += addition;
+    if (vector_less_than(new_location, minimum_allowed)) {
+        too_low = TRUE;
+        new_location = minimum_allowed;
+    } else if (vector_greater_than(new_location, maximum_allowed)) {
+        too_high = TRUE;
+        new_location = maximum_allowed;
+    }
+    return [ new_location, too_low, too_high ];
+}
+
+//////////////
+
+// SL name hufflets...
+
+// returns the position of the last space in "look_within" or a negative number.
+integer find_last_space(string look_within)
+{
+    integer indy = llStringLength(look_within) - 1;
+    while ( (indy >= 0) && (llGetSubString(look_within, indy, indy) != " ") ) indy--;
+    return indy;
+}
+
+// returns the first name for an avatar with the "full_name".
+string first_name(string full_name)
+{
+    integer finding = find_last_space(full_name);
+    if (finding >= 0) return llGetSubString(full_name, 0, finding - 1);
+    return full_name;  // failed to find space.
+}
+
+// returns the last name for an avatar with the "full_name".
+string last_name(string full_name)
+{
+    integer finding = find_last_space(full_name);
+    if (finding >= 0) return llGetSubString(full_name, finding + 1, -1);
+    return full_name;  // failed to find space.
+}
+
+//////////////
+
+// variable handling hufflets...
+
+// substitutes a variable's name for its value.  note that variables are assumed to start
+// with a dollar sign character, which should not be provided in the "variable_name" parameter.
+string substitute_variable(string substitute_within, string variable_name, string variable_value)
+{
+    string to_return = substitute_within;
+//log_it("before var sub: " + substitute_within);
+    integer posn;
+    while ( (posn = find_substring(to_return, "$" + variable_name)) >= 0) {
+        // we found an occurrence of the variable.
+        to_return = llDeleteSubString(to_return, posn, -1)  // keep part before.
+            + variable_value  // add the value in place of the variable name.
+            + llDeleteSubString(to_return, 0, posn + llStringLength(variable_name));
+                // keep part after.
+    }
+//log_it("after var sub: " + to_return);
+    return to_return;
+}
+
+// in "substitute_within", this finds any occurrences of items in the "variables_names"
+// and replaces those with the corresponding item from "variable_values".
+// note: this can be very stack intensive, so it might be better just to use multiple
+// calls to the substitute_variable function.
+string substitute_variable_list(string substitute_within, list variable_names, list variable_values)
+{
+    string to_return = substitute_within;
+    integer vars = llGetListLength(variable_names);
+    if (vars != llGetListLength(variable_values)) {
+        log_it("error in substitute_variable_list: inequal number of variable names vs. values.");
+        return to_return;
+    }
+    integer indy;
+    for (indy = 0; indy < vars; indy++) {
+        to_return = substitute_variable(to_return,
+            llList2String(variable_names, indy),
+            llList2String(variable_values, indy));
+    }
+    return to_return;
+}
+
+// parses a variable definition to find the name of the variable and its value.
+// this returns two strings [X, Y], if "to_split" is in the form X=Y.
+list separate_variable_definition(string to_split)
+{
+    integer equals_indy = llSubStringIndex(to_split, "=");
+    // we don't support missing an equals sign, and we don't support it as the first character.
+    if (equals_indy <= 0) return [];  // no match.
+    string x = llGetSubString(to_split, 0, equals_indy - 1);
+    string y = llGetSubString(to_split, equals_indy + 1, -1);
+    to_split = "";  // save space.
+    return [ llStringTrim(x, STRING_TRIM), llStringTrim(y, STRING_TRIM) ];
+}
+
+// returns a non-empty string if "to_check" defines a value for "variable_name".
+// this must be in the form "X=Y", where X is the variable_name and Y is the value.
+string get_variable_value(string to_check, string variable_name)
+{
+    list x_y = separate_variable_definition(to_check);
+    if (llGetListLength(x_y) != 2) return "";  // failure to parse a variable def at all.
+    if (!is_prefix(llList2String(x_y, 0), variable_name)) return "";  // no match.
+    return llList2String(x_y, 1);  // a match!
+}
+
+//////////////
+
+// inventory hufflets...
+
+// locates the item with "name_to_find" in the inventory items with the "type".
+// a value from 0 to N-1 is returned if it's found, where N is the number of
+// items in the inventory.
+integer find_in_inventory(string name_to_find, integer inv_type)
+{
+    integer num_inv = llGetInventoryNumber(inv_type);
+    if (num_inv == 0) return -1;  // nothing there!
+    integer inv;
+    for (inv = 0; inv < num_inv; inv++) {
+        if (llGetInventoryName(inv_type, inv) == name_to_find)
+            return inv;
+    }
+    return -2;  // failed to find it.
+}
+
+// looks for an inventory item with the same prefix as the "basename_to_seek".
+integer find_basename_in_inventory(string basename_to_seek, integer inv_type)
+{
+    integer num_inv = llGetInventoryNumber(inv_type);
+    if (num_inv == 0) return -1;  // nothing there!
+    integer indy;
+    for (indy = 0; indy < num_inv; indy++) {
+        if (is_prefix(llGetInventoryName(inv_type, indy), basename_to_seek))
+            return indy;
+    }
+    return -2;  // failed to find it.
+}
+
+//////////////
+//
+// imported from auto-retire, which is the official source!...
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+// note that this new, lower memory version, depends on the inventory functions returning
+// items in alphabetical order.
+scrub_items_by_type(string this_guy, integer inventory_type)
+{
+    list removal_list;
+    integer outer;
+    for (outer = 0; outer < llGetInventoryNumber(inventory_type); outer++) {
+        string curr = llGetInventoryName(inventory_type, outer);
+        list split = compute_basename_and_version(curr);
+        // make sure there was a comparable version number in this name.
+        if ( (curr != this_guy) && llGetListLength(split)) {
+            string curr_base = llList2String(split, 0);
+            float curr_ver = (float)llList2String(split, 1);
+//log_it("outer: " + curr_base + " / " + (string)curr_ver);
+            integer inner;
+            for (inner = outer + 1; inner < llGetInventoryNumber(inventory_type); inner++) {
+                string next_guy = llGetInventoryName(inventory_type, inner);
+                list comp_split = compute_basename_and_version(next_guy);
+                if (llGetListLength(comp_split)) {
+                    string comp_base = llList2String(comp_split, 0);
+                    float comp_ver = (float)llList2String(comp_split, 1);
+                    // okay, now we can actually compare.
+                    if (curr_base != comp_base) {
+                        // break out of inner loop.  we are past where the names can matter.
+                        inner = 2 * llGetInventoryNumber(inventory_type);
+                    } else {
+//log_it("inner: " + comp_base + " / " + (string)comp_ver);
+                        if (curr_ver <= comp_ver) {
+                            // the script at inner index is comparable or better than
+                            // the script at the outer index.
+                            removal_list += curr;
+                        } else {
+                            // this inner script must be inferior to the outer one,
+                            // somehow, which defies our expectation of alphabetical ordering.
+                            removal_list += next_guy;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    // now actually do the deletions.
+    for (outer = 0; outer < llGetListLength(removal_list); outer++) {
+        string to_whack = llList2String(removal_list, outer);
+        log_it("removing older asset: " + to_whack);
+        llRemoveInventory(to_whack);
+    }
+}
+
+// ensures that only the latest version of any script or object is kept in our inventory.
+destroy_older_versions()
+{
+    // firstly, iterate across scripts to clean out older versions.
+    scrub_items_by_type(llGetScriptName(), INVENTORY_SCRIPT);
+    // secondly, try to clean out the objects.
+    scrub_items_by_type(llGetScriptName(), INVENTORY_OBJECT);
+}
+
+//////////////
+
+// interprocess communication hufflets...  i.e., IPC parts.
+
+// a repository for commonly used inter-process communication (IPC) source
+// code.  hopefully in the future this will be worthwhile coming to first,
+// when a new link message API is being built.
+
+// an example link message API...
+//////////////
+integer SERVICE_X_HUFFWARE_ID = -14000;
+    // a unique ID within the huffware system for this script.
+string HUFFWARE_PARM_SEPARATOR = "{~~~}";
+    // this pattern is an uncommon thing to see in text, so we use it to separate
+    // our commands in link messages.
+string HUFFWARE_ITEM_SEPARATOR = "{|||}";
+    // used to separate lists of items from each other when stored inside a parameter.
+    // this allows lists to be passed as single string parameters if needed.
+integer REPLY_DISTANCE = 100008;  // offset added to service's huffware id in reply IDs.
+//////////////
+
+// then the main body of IPC support functions.
+
+// joins a list of parameters using the parameter sentinel for the library.
+string wrap_parameters(list to_flatten)
+{ return llDumpList2String(to_flatten, HUFFWARE_PARM_SEPARATOR); }
+
+// joins a list of sub-items using the item sentinel for the library.
+string wrap_item_list(list to_wrap)
+{ return llDumpList2String(to_wrap, HUFFWARE_ITEM_SEPARATOR); }
+
+// handles when blank strings need to come through the pipe.
+string wrap_blank_string(string to_wrap)
+{
+    if (llStringLength(to_wrap)) return to_wrap;  // that one is okay.
+    return "\"\"";  // return a quoted nothing as a signal for a blank.
+}
+
+// undoes a previously wrapped blank string.
+string interpret_blank_string(string to_unwrap)
+{
+    if (to_unwrap == "\"\"") return "";  // that was an encoded blank.
+    return to_unwrap;  // no encoding.
+}
+
+// a simple version of a reply for a command that has been executed.  the parameters
+// might contain an outcome or result of the operation that was requested.
+send_reply(integer destination, list parms, string command)
+{
+    llMessageLinked(destination, SERVICE_X_HUFFWARE_ID + REPLY_DISTANCE, command,
+        llDumpList2String(parms, HUFFWARE_PARM_SEPARATOR));
+}
+
+// this should be invoked from the link_message event handler to process the requests
+// for whatever service this library script provides.
+handle_link_message(integer sender, integer huff_id, string msg, key id)
+{
+log_it("link msg: " + (string)sender + " " + (string)huff_id + " msg=" + msg + " id=" + (string)id);
+    // this check is more for the server; the server should listen on the main huffware id.
+    if (huff_id != SERVICE_X_HUFFWARE_ID) {
+        // the check below would make more sense in the client; it should listen on huffware id + REPLY_DISTANCE.
+        if (huff_id != SERVICE_X_HUFFWARE_ID + REPLY_DISTANCE) return;  // totally not for us.
+        // this is a reply to a previous request.
+        if (msg == "moobert") {
+            // handle the response.
+        }
+        // done with reply handling.
+        return;
+    }
+    // separate the list out
+    list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
+    
+    //example usage of parsed pieces.
+    key k = (key)llList2String(parms, 0);
+    string s = interpret_blank_string(llList2String(parms, 1));
+    integer i = (integer)llList2String(parms, 2);
+    vector v = (vector)llList2String(parms, 3);
+    
+    // we interpret the "msg" as a command.  the "id" has extra parameters.
+    if (msg == "unflux") {
+        // do something
+    }
+}
+
+//////////////
+
+// graphical hufflets...
+
+// a replacement for the deprecated llMakeExplosion function; this bangs the
+// "texture" out as a particle with the "size" in meters.  the number of 
+// particles in a burst is passed in "particle_count".  the function will
+// generate a timer event after "timeout" seconds (pass zero for no timeout).
+make_explosion(string texture, vector size, integer particle_count,
+    float timeout)
+{
+    llParticleSystem([PSYS_PART_FLAGS, PSYS_PART_WIND_MASK | PSYS_PART_EMISSIVE_MASK,
+        PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_EXPLODE,
+        PSYS_PART_START_SCALE, size,
+        PSYS_PART_END_SCALE, size,
+        PSYS_SRC_BURST_PART_COUNT, particle_count,
+        PSYS_PART_MAX_AGE, 2,
+        PSYS_SRC_TEXTURE, texture]);
+    llSetTimerEvent(timeout);
+}
+// the event handler for the particle system to be shut down again.
+//timer() { llParticleSystem([]); }
+
+//////////////
+
+// the following state mumbo jumbo is so that our scripts do not go crazy when the huffotronic
+// updater is rezzed.  that device has most of our scripts in it, and cannot always crush their
+// state fast enough to stop them all.
+default
+{
+    state_entry() { if (llSubStringIndex(llGetObjectName(),  "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry() {
+        log_it("memory left " + (string)llGetFreeMemory());
+    }
+}
diff --git a/huffware/huffotronic_updater_freebie_v5.3/huffware_id_registry_v3.2.txt b/huffware/huffotronic_updater_freebie_v5.3/huffware_id_registry_v3.2.txt
new file mode 100755 (executable)
index 0000000..d2bd4e6
--- /dev/null
@@ -0,0 +1,44 @@
+
+linked message IDs used already:
+
+    10008 jaunter library - teleportation and physics helper methods.
+    10009 menutini library - a menuing system for requesting input from the avatar.
+    10010 noteworthy library - reads notecards with a specific signature.
+    10011 sitting script - alerts when avatar sits down.
+    10012 remotely personable - can be told what text label to show and when to hide itself, plus *dancing*.
+    10013 hud manager - can control the hud links at behest of other scripts.
+    10014 sensor plugin - runs a hud manager by putting names of avatars up on buttons.
+    10015 hud configurator - loads a configuration file for the HUD it lives in.
+    10016 hud menu manager - accepts a set of menus and customizes them to the avatar.
+    10017 data cow - manages a list of text.
+    10018 party culiar particle system.
+    10019 hud substituter - replaces patterns inside strings for the hud.
+    10020 set comparator - provides operations on sets.
+    10021 inventory exchanger - allows a root prim to keep its child prims synched.
+    10022 jaunt configuration - a split piece of jaunt wik rez that handles config.
+    10023 avatar timer manager - tracks time available to an avatar and generates alerts.
+    10024 change click action - makes the object behave differently when clicked.
+    10025 jaunt rezolator - handling of children rezzed by root jaunter.
+    10026 avatar choice memory - tracks which items an avatar has already chosen.
+    10027 viewscreen blitter - handles requests to show URLs and textures in opensim.
+    10028 slate reader - manages notecards filled with readable information.
+    10029 jaunter button pusher - specialized button pusher for jaunter-style teleporters.
+    10030 sound player - triggers the playing of sound files from inventory.
+    10031 animote configula - ejected configuration and menu functions to reduce memory usage.
+    10032 huff-search pointer - supports searchbert and other search engines.
+    10033 menu list manager - generalized management of a collection of menus.
+    10034 searchbert menus - controller actions for searchbert's menu system.
+    10035 general button pusher - used by prim buttons to signal that they have been clicked.
+    ...
+    10042 card configurator - uses noteworthy to process notecards into lists of configuration items.
+
+script pins in use, at the moment:
+    -100008 unused?
+
+reply distance:
+    REPLY_DISTANCE = 100008
+    this amount is added to the huffware ID to get the reply ID of the service.
+
+on rez IDs for communicating information to created object:
+    10008 auto-rez jaunter startup code
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/inventory_exchanger_v3.7.txt b/huffware/huffotronic_updater_freebie_v5.3/inventory_exchanger_v3.7.txt
new file mode 100755 (executable)
index 0000000..7a5b6cd
--- /dev/null
@@ -0,0 +1,488 @@
+
+// huffware script: inventory exchanger, by fred huffhines.
+//
+// manages inventory between a root prim and its children.  this script should be placed in
+// all prims that need to exchange contents.  the root prim drives the exchange process based
+// on a registered set of assets that it is told to synchronize in each child.  all the child
+// prims that have this script will ensure they are up to date with respect to those assets.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+// inventory exchanger link message API...
+//////////////
+// do not redefine these constants.
+integer INVENTORY_EXCHANGER_HUFFWARE_ID = 10021;
+    // the unique id within the huffware system for the jaunt script to
+    // accept commands on.  this is used in llMessageLinked as the num parameter.
+string HUFFWARE_PARM_SEPARATOR = "{~~~}";
+    // this pattern is an uncommon thing to see in text, so we use it to separate
+    // our commands in link messages.
+//string HUFFWARE_ITEM_SEPARATOR = "{|||}";
+    // used to separate lists of items from each other when stored inside a parameter.
+    // this allows lists to be passed as single string parameters if needed.
+integer REPLY_DISTANCE = 100008;  // offset added to service's huffware id in reply IDs.
+// commands available via the library:
+string REGISTER_ASSETS_COMMAND = "#regis#";
+    // makes the root prim's inventory exchanger track a set of items which each participating
+    // child prim should have.  this command is only available to the root prim; child prims
+    // cannot register any assets but instead can get updates on the assets.  the parameter
+    // required is a list of asset definitions, wrapped by the huffware parm separator.  each
+    // asset definition is in turn a two part list wrapped with the asset field separator.
+string ASSET_FIELD_SEPARATOR = "&&";  // separates the type from the name in our list.
+string WILDCARD_INVENTORY_NAME = "ALL";
+    // this keyword can be used to register all of the inventory items available of the
+    // specified type.  this should be passed as the inventory item name in the second half of
+    // an asset definition list.
+string WHACK_INVENTORY_SIGNAL = "DEL*";
+    // ensures that the child prim doesn't maintain *any* of the inventory of the type specified.
+string AVAILABLE_ASSETS_EVENT = "#gotz#";
+    // the root prim publishes this event to list out what items it has available.  the child
+    // prims need to ask for anything that they're currently missing.  the included parameters
+    // are in the same format as the register assets command, but no wildcards will be present;
+    // all asset names will be fully expanded.
+string REQUEST_UPDATES = "#nupd#";
+    // used by the child prim to request an updated version of an asset or assets.  the parameters
+    // should follow the register assets command.
+string FINISHED_UPDATING = "#donq#";
+    // a signal sent from the root prim to the child prim when the root has finished updating
+    // the assets requested.  this lets the child know that it can clean out any outdated versions
+    // of items which got an update.  there are no parameters to this, because the child prim
+    // should have at most one update outstanding at a time.
+//////////////
+
+// constants...
+
+integer TIMER_PERIOD = 600;  // the rate at which the root sends out updates (in seconds).
+
+integer INVEXCH_SCRIPT_PIN = -343781294;  // used to slam scripts into the target prim.
+
+// global variables...
+
+list assets_available;
+    // the set of items that we will provide on demand.  this list has items wrapped with the
+    // asset separator, where the unwrapped entries each provide the type (integer) and the
+    // name of the item.
+
+// gives out items that are asked for in "requested_assets".  these go from the
+// root prim into the kids.
+populate_child_prim(integer link_num, list requested_assets)
+{
+    key asking_child = llGetLinkKey(link_num);  // the kid that needs the update.
+    // look at the assets one by one and pass them over.
+    integer undy;
+    for (undy = 0; undy < llGetListLength(requested_assets); undy++) {
+        // unwrap the next asset definition from the requests.
+        list asset_def = llParseString2List(llList2String(requested_assets, undy),
+            [ASSET_FIELD_SEPARATOR], []);
+        integer type = llList2Integer(asset_def, 0);
+        string name = llList2String(asset_def, 1);
+//log_it("root is giving link " + (string)link_num + " item " + name);
+        // fork over the item itself.
+        if (type != INVENTORY_SCRIPT) {
+            llGiveInventory(asking_child, name);            
+        } else {
+            llRemoteLoadScriptPin(asking_child, name, INVEXCH_SCRIPT_PIN, TRUE, 0);
+        }
+    }
+
+    // we send this event when done updating the kid.     
+    llMessageLinked(link_num, INVENTORY_EXCHANGER_HUFFWARE_ID, FINISHED_UPDATING, NULL_KEY);
+}
+
+// changes the run state of all the scripts besides this one.
+knock_around_other_scripts(integer running_state)
+{
+    // we won't start anything if we're running inside the updater object.
+    if (find_substring(llGetObjectName(), "huffotronic") >= 0) return;
+    
+    integer indy;
+    string self_script = llGetScriptName();
+    // we set all other scripts to the running state requested.
+    for (indy = 0; indy < llGetInventoryNumber(INVENTORY_SCRIPT); indy++) {
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, indy);
+        if (curr_script != self_script) {
+            // this one seems ripe for being set to the state requested.
+            llSetScriptState(curr_script, running_state);
+        }
+    }
+}
+
+// kicks forward the periodic activities of the exchange process.
+handle_timer()
+{
+    if (llGetLinkNumber() != 1) {
+        // child prim activities only.
+//        knock_around_other_scripts(TRUE);
+        return;
+    } else {
+        // the rest are root prim timer activities.
+        if (llGetListLength(assets_available) == 0) return;  // nothing to do here.
+        // the root needs to send out link messages telling kids what good stuff its got.
+        llMessageLinked(LINK_ALL_CHILDREN, INVENTORY_EXCHANGER_HUFFWARE_ID,
+            AVAILABLE_ASSETS_EVENT, wrap_parameters(assets_available));
+    }
+
+}
+
+// given a packed up list of items, this breaks out each asset definition and
+// begins using the set as our full list of available items.
+consume_asset_registration(string packed_assets)
+{
+    assets_available = llParseString2List(packed_assets, [HUFFWARE_PARM_SEPARATOR], []);
+//hmmm: future enhancement might be to allow multiple clients of the root exchanger.
+//      currently we only support one script inside the root prim listing assets for inv exch.
+//      that's shown just above where we're just replacing the entire registered assets list.
+
+    integer indy;
+    for (indy = 0; indy < llGetListLength(assets_available); indy++) {
+        list asset_def = llParseString2List(llList2String(assets_available, indy),
+            [ASSET_FIELD_SEPARATOR], []);
+        integer type = llList2Integer(asset_def, 0);
+        string name = llList2String(asset_def, 1);
+        if (name == WILDCARD_INVENTORY_NAME) {
+            // argh, we need to patch up the list for a wildcard option.
+            // first, whack the item with the wildcard in it.
+            assets_available = llDeleteSubList(assets_available, indy, indy);
+            // now revert the list index for the missing item.
+            indy--;
+            // and finally add all the items of that type.  if one of them is named
+            // after the wildcard character, well, we disallow that craziness.
+            integer inv_pos;
+            for (inv_pos = 0; inv_pos < llGetInventoryNumber(type); inv_pos++) {
+                string inv_name = llGetInventoryName(type, inv_pos);
+                if (inv_name != WILDCARD_INVENTORY_NAME) {
+//log_it("added wild: " + inv_name);
+                    assets_available += [ llDumpList2String([type, inv_name], ASSET_FIELD_SEPARATOR) ];
+                }
+            }
+            
+        }
+    }
+    
+    // make sure we get this new set out to the interested parties.
+    handle_timer();    
+}
+
+// note that this new, lower memory version, depends on the inventory functions returning
+// items in alphabetical order.
+scrub_items_by_type(string this_guy, integer inventory_type)
+{
+    list removal_list;
+    integer outer;
+    for (outer = 0; outer < llGetInventoryNumber(inventory_type); outer++) {
+        string curr = llGetInventoryName(inventory_type, outer);
+        list split = compute_basename_and_version(curr);
+        // make sure there was a comparable version number in this name.
+        if ( (curr != this_guy) && llGetListLength(split)) {
+            string curr_base = llList2String(split, 0);
+            float curr_ver = (float)llList2String(split, 1);
+//log_it("outer: " + curr_base + " / " + (string)curr_ver);
+            integer inner;
+            for (inner = outer + 1; inner < llGetInventoryNumber(inventory_type); inner++) {
+                string next_guy = llGetInventoryName(inventory_type, inner);
+                list comp_split = compute_basename_and_version(next_guy);
+                if (llGetListLength(comp_split)) {
+                    string comp_base = llList2String(comp_split, 0);
+                    float comp_ver = (float)llList2String(comp_split, 1);
+                    // okay, now we can actually compare.
+                    if (curr_base != comp_base) {
+                        // break out of inner loop.  we are past where the names can matter.
+                        inner = 2 * llGetInventoryNumber(inventory_type);
+                    } else {
+//log_it("inner: " + comp_base + " / " + (string)comp_ver);
+                        if (curr_ver <= comp_ver) {
+                            // the script at inner index is comparable or better than
+                            // the script at the outer index.
+                            removal_list += curr;
+                        } else {
+                            // this inner script must be inferior to the outer one,
+                            // somehow, which defies our expectation of alphabetical ordering.
+                            removal_list += next_guy;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    // now actually do the deletions.
+    for (outer = 0; outer < llGetListLength(removal_list); outer++) {
+        string to_whack = llList2String(removal_list, outer);
+log_it("removing older asset: " + to_whack);
+        llRemoveInventory(to_whack);
+    }
+}
+
+// ensures that only the latest version of any script or object is kept in our inventory.
+// has been called destroy_older_versions in other scripts.
+clean_outdated_items()
+{
+    scrub_items_by_type(llGetScriptName(), INVENTORY_ALL);
+    // after cleaning up, make sure everything's running.
+    knock_around_other_scripts(TRUE);    
+}
+
+// locates the index of a specified type of inventory item with a particular name,
+// or returns a negative number.
+integer find_inventory(integer type, string name)
+{
+    integer indy;
+    for (indy = 0; indy < llGetInventoryNumber(type); indy++) {
+        if (name == llGetInventoryName(type, indy)) return indy;
+    }
+    return -1;
+}
+
+// called on child prims to make sure they have everything the root does.
+examine_inventory_freshness(string packed_assets)
+{
+    list missing_goods;  // fill this with the things we don't have in this prim.
+    list provisions = llParseString2List(packed_assets, [HUFFWARE_PARM_SEPARATOR], []);
+    integer indy;
+    // scan through all the items that are available and make sure we have each of them.
+    for (indy = 0; indy < llGetListLength(provisions); indy++) {
+        string prov = llList2String(provisions, indy);
+        list fields = llParseString2List(prov, [ASSET_FIELD_SEPARATOR], []);
+//log_it("checking: " + llList2String(fields, 1) + " type=" + (string)llList2Integer(fields, 0));
+        // look for the type and name specified.
+        integer type = llList2Integer(fields, 0);
+        string name = llList2String(fields, 1);
+        if (name == WHACK_INVENTORY_SIGNAL) {
+            // we see our special signifier, so remove all items of the type specified.
+            // yes, this is dangerous.
+            integer delo;
+            for (delo = llGetInventoryNumber(type) - 1; delo >= 0; delo--) {
+                llRemoveInventory(llGetInventoryName(type, delo));
+            }
+        } else {
+            integer found = find_inventory(type, name);
+            if (found < 0) {
+//log_it("found " + llList2String(fields, 1) + " was missing!");
+                // well, we are out of this one.  tell the root we want it.
+                missing_goods += [ prov ];
+            }
+        }
+    }
+    if (llGetListLength(missing_goods) > 0) {
+        // well we found some things that we're supposed to have, but do not.  we will
+        // get the root prim to fix us up.
+        llMessageLinked(LINK_ROOT, INVENTORY_EXCHANGER_HUFFWARE_ID,
+            REQUEST_UPDATES, wrap_parameters(missing_goods));
+    }
+}
+
+//hmmm: this is not such a good model?  why do the root and
+//  the child both need to be running this check on a timer?
+
+// processes inventory exchange commands in the root prim.  this is where assets must
+// be present so they can be distributed to the child prims.
+process_root_msg(integer link_num, integer service, string cmd, key parms)
+{
+    if (service != INVENTORY_EXCHANGER_HUFFWARE_ID) return;  // not for us.
+//string name="root";
+//if (llGetLinkNumber() != 1) name="child";
+//log_it(name + " got request " + cmd);
+    if (link_num == 1) {
+        // this request is also from the root prim, where this script lives currently.
+        if (cmd == REGISTER_ASSETS_COMMAND) {
+            // root prim accepts a list of assets to provide to the kids.
+            consume_asset_registration(parms);
+        }
+    } else {
+        // a request is coming from a child prim.
+        if (cmd == REQUEST_UPDATES) {
+            // the root prim responds to update requests by handing out presents.
+            populate_child_prim(link_num, llParseString2List(parms, [HUFFWARE_PARM_SEPARATOR], []));
+        }
+    }
+}
+
+// implements the child API, mainly to track missing assets and clean out outdated items.
+process_child_msg(integer link_num, integer service, string cmd, key parms)
+{
+    if (service != INVENTORY_EXCHANGER_HUFFWARE_ID) return;  // not for us.
+    if (link_num != 1) return;  // we don't listen to any chatter from other children.
+    if (cmd == AVAILABLE_ASSETS_EVENT) {
+        examine_inventory_freshness(parms);
+    } else if (cmd == FINISHED_UPDATING) {
+        clean_outdated_items();
+    }
+}
+
+// main processing function for the link messge API.
+handle_link_message(integer link_num, integer service, string cmd, key parms)
+{
+    if (llGetLinkNumber() == 1) process_root_msg(link_num, service, cmd, parms);
+    else process_child_msg(link_num, service, cmd, parms);
+}
+
+//////////////
+// beginning of hufflets...
+//////////////
+
+// diagnostic hufflets...
+
+integer debug_num = 0;
+
+// a debugging output method.  can be disabled entirely in one place.
+log_it(string to_say)
+{
+    debug_num++;
+    // tell this to the owner.    
+    llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on an unusual channel for chat if it's not intended for general public.
+//    llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on open chat that anyone can hear.  we take off the bling for this one.
+//    llSay(0, to_say);
+}
+
+// joins a list of parameters using the parameter sentinel for the library.
+string wrap_parameters(list to_flatten)
+{ return llDumpList2String(to_flatten, HUFFWARE_PARM_SEPARATOR); }
+
+// joins a list of sub-items using the item sentinel for the library.
+//string wrap_item_list(list to_wrap)
+//{ return llDumpList2String(to_wrap, HUFFWARE_ITEM_SEPARATOR); }
+
+// returns the index of the first occurrence of "pattern" inside
+// the "full_string".  if it is not found, then a negative number is returned.
+integer find_substring(string full_string, string pattern)
+{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); }
+
+// returns TRUE if the "prefix" string is the first part of "compare_with".
+integer is_prefix(string compare_with, string prefix)
+{ return find_substring(compare_with, prefix) == 0; }
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.5.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+// end hufflets
+//////////////
+
+default
+{
+    state_entry() { if (llSubStringIndex(llGetObjectName(),  "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry()
+    {
+        auto_retire();
+///log_it("\n\n\n\n\n\n\n\n\n** starting now............................");
+        // register child prims for script replacement.
+        if (llGetLinkNumber() != 1) llSetRemoteScriptAccessPin(INVEXCH_SCRIPT_PIN);
+        //new idea; only allow timers to run in root prim.
+        if (llGetLinkNumber() == 1) llSetTimerEvent(TIMER_PERIOD);
+        knock_around_other_scripts(TRUE);
+        // first run happens right at startup.
+        if (llGetLinkNumber() == 1) handle_timer();
+    }
+
+    timer() {
+        llSetTimerEvent(0);  // stop timer.
+        handle_timer();
+        llSetTimerEvent(TIMER_PERIOD);  // start timer.
+    }
+
+    link_message(integer link_num, integer service, string cmd, key parms) {
+        handle_link_message(link_num, service, cmd, parms);
+    }
+}
+
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/license__free_in_osgrid_only_v1.8.txt b/huffware/huffotronic_updater_freebie_v5.3/license__free_in_osgrid_only_v1.8.txt
new file mode 100755 (executable)
index 0000000..b28cd30
--- /dev/null
@@ -0,0 +1,26 @@
+--license version: april 24 2011--
+author: fred huffhines (osgrid and second life avatar)
+source: eepaw shop (see below)
+all content by fred huffhines is licensed by GPL [ http://www.gnu.org/licenses/gpl.html ] within the full metaverse (osgrid, opensims, second life, etc).
+note that any included content created by others may be licensed differently; all appropriate original copyright notices have been retained.
+price in osgrid: *free*
+    ** note: if you paid for this in osgrid, someone ripped you off.  please let fred huffhines (in osgrid) know about this.
+price in second life: *non-free*
+    ** note: the only avatar licensed to sell materials within this product is fred huffhines in second life.
+          if you bought this from someone else, please let fred huffhines know.
+price in other grids: *unspecified*
+    ** we're still developing a relationship with other grids, but any opensim-based-server user can use any of this content on their personal standalone/hypergrid sims for free.  we ask that you please do not share any of this content on commercial grids yet.
+    ** please let us know if you're interested in expanding our licensing to your commercial grid.
+
+* our store is called Eclectic Electric Patterns and Widgets (eepaw).  we started out in Second life, but have since branched out into opensim based simulators.  we have two locations for the store now, one in osgrid that is all ++freebies++, and another store in second life that is a mix of commercial objects and freebies (because the linden labs dingbats insist on charging exorbitant rates to rent space on their servers).
+
+* in osgrid: our brand new freebie store (as of march 2011) is in wright plaza at <228, 22, 21>
+    [ or http://slurl.com/secondlife/Wright%20Plaza/228/22/21 ].
+this will be our base of operations from now on, or until there's no power left in the global electrical grid.
+a lot of our new stuff will be available here first, and for free to osgrid avatars.
+please don't upload our stuff into second life, because that store (see below) kind of needs to pay for itself.  render unto caesar and all that.  so, thanks.
+
+* in second life: the eepaw shop is located way out in the "smoky" region of the atoll (heterocera).  Feel free to mosey over and check our other strange devices out, many others of which are also totally free...
+    http://slurl.com/secondlife/Smoky/14/84/405
+
+* we are personally very fond of each of you.  --fred huffhines.
diff --git a/huffware/huffotronic_updater_freebie_v5.3/menu_list_manager_v12.4.txt b/huffware/huffotronic_updater_freebie_v5.3/menu_list_manager_v12.4.txt
new file mode 100755 (executable)
index 0000000..22ddbe4
--- /dev/null
@@ -0,0 +1,533 @@
+
+// huffware script: menu list manager, by fred huffhines.
+//
+// deals with popping up menus when desired.  does not do any configuration
+// reading; that all must be fed to the script via link messages.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+// constants...
+
+string GIFTING_MENU_NAME = "#gifting#";
+    // the menu name that we support internally for giving gifts.
+
+// global variables...
+
+// these are the definition of the menuing system,
+// and are valid once we have heard our config.
+
+// unique identifiers of menu names.
+list menu_names;
+// a list of titles for the menus.
+list menu_titles;
+// the menu choices are strings containing encoded lists of button text labels.
+list menu_button_lists;
+
+// store current activated menu info...
+string _private_global_av_name;
+string _private_global_av_key;
+
+// menu list manager link message API.
+//////////////
+// do not redefine these constants.
+integer MENU_LIST_MANAGER_HUFFWARE_ID = 10033;
+    // the unique id within the huffware system for this sensor plugin script.
+string HUFFWARE_PARM_SEPARATOR = "{~~~}";
+    // this pattern is an uncommon thing to see in text, so we use it to separate
+    // our commands in link messages.
+string HUFFWARE_ITEM_SEPARATOR = "{|||}";
+    // used to separate lists of items from each other when stored inside a parameter.
+    // this allows lists to be passed as single string parameters if needed.
+integer REPLY_DISTANCE = 100008;  // offset added to service's huffware id in reply IDs.
+string MENU_LIST_MGR_RESET = "#ml-reset#";
+    // throws out any current menus and gets ready to load a new set.
+string MENU_LIST_MGR_ADD_MENU = "#ml-addmenu#";
+    // puts a new menu into our list.  this requires 3 parameters: menu name,
+    // menu title, menu button list.
+string MENU_LIST_MGR_SHOW_MENU = "#ml-shomenu#";
+    // a request that a particular menu be shown.  the first parameter is the menu index.
+    // the second parameter is the avatar name.  the third parameter is the avatar key.
+string MENU_LIST_MGR_GIVE_ITEM = "#ml-give#";
+    // brings up a gift giving menu.  parms are avatar name and avatar key.
+string MENU_LIST_MGR_MODIFY_MENU = "#ml-modmenu#";
+string MENU_LIST_KEEP_WORD = "KEEP";
+    // replaces a menu already in the list.  this requires 4 parameters: menu index, menu name,
+    // menu title, menu button list.  if a parameter is the MENU_LIST_KEEP_WORD, then that field
+    // is not changed.
+string MENU_LIST_MGR_CHOICE_MADE_ALERT = "#ml-event-picked";
+    // alerts the driver for this script that the owner has picked a choice.  the
+    // parameters include: the text of the choice, the name of the menu, the avatar name,
+    // the avatar key.
+//
+//////////////
+// joins a list of parameters using the parameter sentinel for the library.
+string wrap_parameters(list to_flatten)
+{ return llDumpList2String(to_flatten, HUFFWARE_PARM_SEPARATOR); }
+//////////////
+
+// requires HUD sensor plugin v21.4 or better.
+//////////////
+// do not redefine these constants.
+integer SENSOR_PLUGIN_HUFFWARE_ID = 10014;
+    // the unique id within the huffware system for this sensor plugin script.
+string GIVE_ALL_AVATARS = "#santa#";
+    // if this is received, the sensor plugin attempts to hand out an object passed as
+    // the first parameter to every avatar it knows about.
+//////////////
+
+// requires menutini v3.9 or better...
+//////////////
+// do not redefine these constants.
+integer MENUTINI_HUFFWARE_ID = 10009;
+    // the unique id within the huffware system for the jaunt script to
+    // accept commands on.  this is used in llMessageLinked as the num parameter.
+// commands available via menutini:
+string SHOW_MENU_COMMAND = "#menu#";
+    // the command that tells menutini to show a menu defined by parameters
+    // that are passed along.  these must be: the menu name, the menu's title
+    // (which is really the info to show as content in the main box of the menu),
+    // the wrapped list of commands to show as menu buttons, the menu system
+    // channel's for listening, and the key to listen to.
+//
+//////////////
+// joins a list of sub-items using the item sentinel for the library.
+string wrap_item_list(list to_wrap)
+{ return llDumpList2String(to_wrap, HUFFWARE_ITEM_SEPARATOR); }
+//
+//////////////
+
+// replaces occurences of $owner and $avatar with the owner's name and the
+// targeted avatars name.  _first and _last added to the name cause the first
+// or last name to be used instead.
+string senso_substitute_vars(string to_substitute, string avatars_name)
+{
+    string owners_name = llKey2Name(llGetOwner());
+    return substitute_variable_list(to_substitute,
+        [ "avatar_first", "avatar_last", "avatar",
+            "owner_first", "owner_last", "owner" ],
+        [ first_name(avatars_name), last_name(avatars_name), avatars_name,
+           first_name(owners_name), last_name(owners_name), owners_name ]);
+}
+
+integer random_channel() { return -(integer)(llFrand(40000) + 20000); }
+
+simply_display_menu(string menu_name, string title, list buttons,
+    string av_name, string av_key)
+{
+    // we only manage one global now for this stuff...
+    _private_global_av_name = av_name;
+    _private_global_av_key = av_key;
+
+    integer menu_channel = random_channel();
+    key listen_to = llGetOwner();
+//log_it("passing in key to use: " + (string)listen_to);
+    llMessageLinked(LINK_THIS, MENUTINI_HUFFWARE_ID, SHOW_MENU_COMMAND,
+        menu_name + HUFFWARE_PARM_SEPARATOR
+        + title + HUFFWARE_PARM_SEPARATOR
+        + wrap_item_list(buttons) + HUFFWARE_PARM_SEPARATOR
+        + (string)menu_channel + HUFFWARE_PARM_SEPARATOR
+        + (string)listen_to);
+}
+
+replace_vars_and_display_menu(integer menu_index, string av_name, string av_key)
+{
+    // patch the avatar names and owner names before displaying.
+    string title = senso_substitute_vars(llList2String(menu_titles, menu_index), av_name);
+    string button_text = llList2String(menu_button_lists, menu_index);
+
+    simply_display_menu(llList2String(menu_names, menu_index), title,
+        llParseString2List(button_text, [HUFFWARE_ITEM_SEPARATOR], []),
+        av_name, av_key);
+}
+
+// shows a menu by index.
+show_menu(integer index, string av_name, string av_key)
+{
+    replace_vars_and_display_menu(index, av_name, av_key);
+//log_it("sent the menu request...  memory left=" + (string)llGetFreeMemory());
+}
+
+// handles the response message when the user chooses a button.
+react_to_menu(string menu_name, string av_name, string av_key, string which_choice)
+{
+//log_it("you clicked " + which_choice + " item for av " + av_name);
+    handle_chosen_menu_item(menu_name, av_name, av_key, which_choice);
+}
+
+// the gift giving menu.  we show the menu after looking through the
+// inventory.  if the owner picks an item, we'll deal with that response.
+show_gift_menu(string av_name, string av_key)
+{
+    string menu_name = "gift";
+    string menu_title = "Pick a gift for " + av_name + "...";
+    list menu_button_list = [];
+    integer indy;
+    for (indy = 0; indy < llGetInventoryNumber(INVENTORY_OBJECT); indy++) {
+        menu_button_list += llGetInventoryName(INVENTORY_OBJECT, indy);
+    }
+    for (indy = 0; indy < llGetInventoryNumber(INVENTORY_TEXTURE); indy++) {
+        string item_name = llGetInventoryName(INVENTORY_TEXTURE, indy);
+        if (!is_prefix(item_name, "ean hud keys"))
+            menu_button_list += item_name;
+    }
+    for (indy = 0; indy < llGetInventoryNumber(INVENTORY_LANDMARK); indy++) {
+        menu_button_list += llGetInventoryName(INVENTORY_LANDMARK, indy);
+    }
+    for (indy = 0; indy < llGetInventoryNumber(INVENTORY_NOTECARD); indy++) {
+        string item_name = llGetInventoryName(INVENTORY_NOTECARD, indy);
+        if ( (find_substring(item_name, "help") < 0)
+            && (find_substring(item_name, "Help") < 0)
+            && (find_substring(item_name, "config") < 0) )
+            menu_button_list += item_name;
+    }
+    for (indy = 0; indy < llGetInventoryNumber(INVENTORY_CLOTHING); indy++) {
+        menu_button_list += llGetInventoryName(INVENTORY_CLOTHING, indy);
+    }
+    for (indy = 0; indy < llGetInventoryNumber(INVENTORY_GESTURE); indy++) {
+        menu_button_list += llGetInventoryName(INVENTORY_GESTURE, indy);
+    }
+    
+    if (llGetListLength(menu_button_list) == 0) {
+        llOwnerSay("There are no objects loaded in the hud currently.  I can make no gifts.");
+    }
+    simply_display_menu(GIFTING_MENU_NAME, menu_title, menu_button_list,
+        av_name, av_key);
+}
+
+// handles a request for this library itself.
+process_hud_menu_mgr_request(integer sender, integer num, string msg, key id)
+{
+    list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
+    if (msg == MENU_LIST_MGR_RESET) {
+        llResetScript();
+    } else if (msg == MENU_LIST_MGR_ADD_MENU) {
+        if (llGetListLength(parms) < 3) {
+            log_it("too few parms to add menu.");
+            return;
+        }
+//hmmm: sloppy list processing!
+        menu_names += [ llList2String(parms, 0) ];
+        menu_titles += [ llList2String(parms, 1) ];
+        menu_button_lists += [ llList2String(parms, 2) ];
+    } else if (msg == MENU_LIST_MGR_GIVE_ITEM) {
+        // give over a gift.
+        string av_name = llList2String(parms, 0);
+        string av_key = llList2String(parms, 1);
+        show_gift_menu(av_name, av_key);
+    } else if (msg == MENU_LIST_MGR_SHOW_MENU) {
+        if (llGetListLength(parms) < 3) {
+            log_it("too few parms to show menu.");
+            return;
+        }
+//log_it("key comes as " + llList2String(parms, 2));
+        show_menu((integer)llList2String(parms, 0), llList2String(parms, 1),
+            (key)llList2String(parms, 2));
+//log_it("show menu...  memory left=" + (string)llGetFreeMemory());
+    } else if (msg == MENU_LIST_MGR_MODIFY_MENU) {
+        if (llGetListLength(parms) < 4) {
+            log_it("too few parms to replace menu.");
+            return;
+        }
+        integer indo = (integer)llList2String(parms, 0);
+//log_it("modif menu request, indo is " + (string)indo);
+        if (indo >= llGetListLength(menu_names)) {
+//log_it("index too large in replace menu");
+            return;
+        }
+//log_it("replacing entry " + (string)indo + " with " + llList2String(parms, 1) + ", " + llList2String(parms, 2) + ", " + llList2String(parms, 3));
+        if (llList2String(parms, 1) != MENU_LIST_KEEP_WORD)
+            menu_names = replace_entry(menu_names, indo, [ llList2String(parms, 1) ]);
+        if (llList2String(parms, 2) != MENU_LIST_KEEP_WORD)
+            menu_titles = replace_entry(menu_titles, indo, [ llList2String(parms, 2) ]);
+        if (llList2String(parms, 3) != MENU_LIST_KEEP_WORD)
+            menu_button_lists = replace_entry(menu_button_lists, indo, [ llList2String(parms, 3) ]);
+//log_it("new menu button:" + llList2String(parms, 3));
+    }
+}
+
+// processes a message from a link.  some of this must be handled
+// by the driver script that handles our configuration.
+handle_link_message(integer sender, integer num, string msg, key id)
+{
+    if (num == MENU_LIST_MANAGER_HUFFWARE_ID) {
+        process_hud_menu_mgr_request(sender, num, msg, id);
+    }
+    
+    if ( (num != MENUTINI_HUFFWARE_ID + REPLY_DISTANCE)
+        || (msg != SHOW_MENU_COMMAND) ) return;  // not for us.
+
+//log_it("menu reply: sndr=" + (string)sender + " num=" + (string)num + " msg=" + msg + " id=" + (string)id);
+    list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
+    string menu_name = llList2String(parms, 0);
+    string which_choice = llList2String(parms, 1);
+///not used:    key av_key = llList2String(parms, 2);
+//hmmm: should the menutini also offer av name and key options???
+    react_to_menu(menu_name, _private_global_av_name,
+        _private_global_av_key, which_choice);
+}
+
+//////////////
+
+// gives a present to the avatar named here.
+give_gift(string av_name, string av_key, string present)
+{
+//log_it("trying to give a gift of " + present + " to " + av_name);
+    if (av_name != "ALL") {
+        llGiveInventory(av_key, present);
+        llOwnerSay("gave " + present + " to " + av_name);
+    } else {
+        // send the request back to the sensor plugin for processing.
+        llMessageLinked(LINK_ROOT, SENSOR_PLUGIN_HUFFWARE_ID,
+            GIVE_ALL_AVATARS, wrap_parameters([present]));
+    }
+}
+        
+handle_chosen_menu_item(string menu_name, string av_name,
+    string av_key, string which_choice)
+{
+    if (menu_name == GIFTING_MENU_NAME) {
+        give_gift(av_name, av_key, which_choice);
+    }
+
+    // send the alert to the driver.
+    llMessageLinked(LINK_ROOT, MENU_LIST_MANAGER_HUFFWARE_ID + REPLY_DISTANCE,
+        MENU_LIST_MGR_CHOICE_MADE_ALERT,
+        wrap_parameters([which_choice, menu_name, av_name, av_key]));
+}
+
+//////////////
+// from hufflets...
+//
+integer debug_num = 0;
+
+// a debugging output method.  can be disabled entirely in one place.
+log_it(string to_say)
+{
+    debug_num++;
+    // tell this to the owner.    
+    llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on open chat, but use an unusual channel.
+//    llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+}
+
+integer find_substring(string full_string, string pattern)
+{
+    string full_lower = llToLower(full_string);
+    return llSubStringIndex(full_lower, pattern);
+}
+
+//////////////
+
+// returns the position of the last space in "look_within" or a negative number.
+integer find_last_space(string look_within)
+{
+    integer indy = llStringLength(look_within) - 1;
+    while ( (indy >= 0) && (llGetSubString(look_within, indy, indy) != " ") ) indy--;
+    return indy;
+}
+
+// returns the first name for an avatar with the "full_name".
+string first_name(string full_name)
+{
+    integer finding = find_last_space(full_name);
+    if (finding >= 0) return llGetSubString(full_name, 0, finding - 1);
+    return full_name;  // failed to find space.
+}
+
+// returns the last name for an avatar with the "full_name".
+string last_name(string full_name)
+{
+    integer finding = find_last_space(full_name);
+    if (finding >= 0) return llGetSubString(full_name, finding + 1, -1);
+    return full_name;  // failed to find space.
+}
+
+// substitutes a variable's name for its value.  note that variables are assumed to start
+// with a dollar sign character, which should not be provided in the "variable_name" parameter.
+string substitute_variable(string substitute_within, string variable_name, string variable_value)
+{
+    string to_return = substitute_within;
+    integer posn;
+    while ( (posn = find_substring(to_return, "$" + variable_name)) >= 0) {
+        // we found an occurrence of the variable.
+        to_return = llDeleteSubString(to_return, posn, -1)  // keep part before.
+            + variable_value  // add the value in place of the variable name.
+            + llDeleteSubString(to_return, 0, posn + llStringLength(variable_name));
+                // keep part after.
+    }
+    return to_return;
+}
+
+// in "substitute_within", this finds any occurrences of items in the "variables_names"
+// and replaces those with the corresponding item from "variable_values".
+string substitute_variable_list(string substitute_within, list variable_names, list variable_values)
+{
+    string to_return = substitute_within;
+    integer vars = llGetListLength(variable_names);
+    if (vars != llGetListLength(variable_values)) {
+//        log_it("error in substitute_variable_list: inequal number of variable names vs. values.");
+        return to_return;
+    }
+    integer indy;
+    for (indy = 0; indy < vars; indy++) {
+        to_return = substitute_variable(to_return,
+            llList2String(variable_names, indy),
+            llList2String(variable_values, indy));
+    }
+    return to_return;
+}
+
+// locates the string "text" in the list to "search_in".
+integer find_in_list(list search_in, string text)
+{ 
+    integer len = llGetListLength(search_in);
+    integer i; 
+    for (i = 0; i < len; i++) { 
+        if (llList2String(search_in, i) == text) 
+            return i; 
+    } 
+    return -1;
+}
+
+// removes the entry at "index" and instead inserts the list "to_insert"
+// at that position.
+list replace_entry(list to_modify, integer index, list to_insert)
+{
+    if (llGetListLength(to_modify) == 0) return to_insert;  // special case for empty.
+    return llListReplaceList(to_modify, to_insert, index, index);
+}
+
+// returns TRUE if the "prefix" string is the first part of "compare_with".
+integer is_prefix(string compare_with, string prefix)
+{ return (llSubStringIndex(compare_with, prefix) == 0); }
+//
+// end hufflets
+//////////////
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.5.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+//log_it("invpo=" + (string)posn);
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+//log_it("pos=" + (string)space_v_posn);
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+//log_it("space v@" + (string)space_v_posn);
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+//log_it("indy=" + (string)space_v_posn);
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+//log_it("saw case of previously redundant item, aieee.  flattened: " + to_chop_up);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+default
+{
+    state_entry() { if (llSubStringIndex(llGetObjectName(),  "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry()
+    {
+        auto_retire();  // make sure newest addition is only version of script.
+        // reset all our variables.
+        menu_names = [];
+        menu_titles = [];
+        menu_button_lists = [];
+        _private_global_av_name = "";
+        _private_global_av_key = NULL_KEY;
+    }
+
+    link_message(integer sender, integer num, string msg, key id)
+    { handle_link_message(sender, num, msg, id); }
+}
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/menutini_library_v5.9.txt b/huffware/huffotronic_updater_freebie_v5.3/menutini_library_v5.9.txt
new file mode 100755 (executable)
index 0000000..abd2711
--- /dev/null
@@ -0,0 +1,436 @@
+
+// huffware script: menutini library, by fred huffhines.
+//
+// this is a library script for menuing that provides a way to remote control the
+// menu, somewhat.  another script can zing link messages at this script and a menu
+// will be shown based on the specified description and buttons.  when the user
+// selects an answer, that result is sent back in a link message reply.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+// useful constants you might want to change:
+
+integer TIMEOUT_FOR_MENU = 42;
+    // timeout for the menu in seconds.
+//hmmm: may want this to be selectable from menu request.
+//      or may even want to never time out!
+//      if we managed a list of ongoing menus, that would work.
+//      currently it cannot.
+
+integer DEBUGGING = FALSE;
+    // if this is true, then extra info will be printed when handling a menu.
+
+string NEXT_MENU_TEXT = "Next >>";
+    // what the next item will say for showing next menu page.
+
+// menutini link message API...
+//////////////
+// do not redefine these constants.
+integer MENUTINI_HUFFWARE_ID = 10009;
+    // the unique id within the huffware system for the jaunt script to
+    // accept commands on.  this is used in llMessageLinked as the num parameter.
+string HUFFWARE_PARM_SEPARATOR = "{~~~}";
+    // this pattern is an uncommon thing to see in text, so we use it to separate
+    // our commands in link messages.
+string HUFFWARE_ITEM_SEPARATOR = "{|||}";
+    // used to separate lists of items from each other when stored inside a parameter.
+    // this allows lists to be passed as single string parameters if needed.
+integer REPLY_DISTANCE = 100008;  // offset added to service's huffware id in reply IDs.
+string SHOW_MENU_COMMAND = "#menu#";
+    // the command that tells menutini to show a menu defined by parameters
+    // that are passed along.  these must be: the menu name, the menu's title
+    // (which is really the info to show as content in the main box of the menu),
+    // the wrapped list of commands to show as menu buttons, the menu system
+    // channel's for listening, and the key to listen to.
+    // the reply will include: the menu name, the choice made and the key for
+    // the avatar.
+//
+//////////////
+// joins a list of sub-items using the item sentinel for the library.
+string wrap_item_list(list to_wrap)
+{ return llDumpList2String(to_wrap, HUFFWARE_ITEM_SEPARATOR); }
+//
+//////////////
+
+// global variables...
+
+list _private_global_buttons;  // holds onto the active set of menu options.
+string _private_global_av_key;  // the key for the avatar who clicks the menu.
+string _private_global_title;  // holds onto current title text.
+
+integer _menu_base_start = 0;  // position in the items of the current menu.
+
+integer listening_id = 0;
+    // the current id of our listening for the menu.  it's an id returned by LSL
+    // that we need to track so we can cancel the listen.
+
+integer menu_system_channel = 0;
+    // messages come back to us from this channel when user clicks the dialog.
+    // this is set later and the default is meaningless.
+
+string global_menu_name = "";
+    // hangs onto the current menu's name.
+
+//hmmm: note; to manage multiple concurrent menus on different channels,
+//      we must make these into lists.  then the timeouts should apply
+//      individually to these instead of overall (if we even do timeouts;
+//      it's nicer if menus never stop being active).
+
+
+// displays the menu requested.  it's "menu_name" is an internal name that is
+// not displayed to the user.  the "title" is the content shown in the main area
+// of the menu.  "commands_in" is the list of menu items to show as buttons.
+// the "menu_channel" is where the user's clicked response will be sent.  the
+// "listen_to" key is the avatar expected to click the menu, which is needed to
+// listen to his response.
+show_menu(string menu_name, string title, list buttons,
+    integer menu_channel, key listen_to)
+{
+    // save our new parms.
+    global_menu_name = menu_name;
+    _private_global_title = title;
+    _private_global_buttons = buttons;
+    menu_system_channel = menu_channel;
+    _private_global_av_key = listen_to;
+    if (DEBUGGING) {
+        log_it("menu name: " + global_menu_name);
+        log_it("title: " + _private_global_title);
+        log_it("buttons: " + (string)buttons);
+        log_it("channel: " + (string)menu_system_channel);
+        log_it("listen key: " + (string)listen_to);
+    }
+
+    integer add_next = FALSE;  // true if we should add a next menu item.
+
+    // the math here incorporates current button position.
+    integer current = _menu_base_start;
+    integer max_buttons = llGetListLength(buttons) - current;
+
+    if (max_buttons > 12) {
+        // limitation of SL: menus have a max of 12 buttons.
+        max_buttons = 12;
+        add_next = TRUE;
+    } else if (llGetListLength(buttons) > 12) {
+        // we already have been adding next.  let's make sure this gets
+        // a wrap-around next button.
+        add_next = TRUE;
+    }
+    // chop out what we can use in a menu.
+    list trunc_buttons = llList2List(buttons, current, current + max_buttons - 1);
+    if (add_next) {
+        // we were asked to add a menu item for the next screen.
+        trunc_buttons = llList2List(trunc_buttons, 0, 10) + NEXT_MENU_TEXT;
+    }
+
+    listening_id = llListen(menu_channel, "", listen_to, "");
+    list commands;
+    integer i;
+    // take only the prefix of the string, to avoid getting a length complaint.
+    for (i = 0; i < llGetListLength(trunc_buttons); i++) {
+        string curr = llList2String(trunc_buttons, i);
+        integer last_pos = 23;  // default maximum, highest possible is 24.
+        if (llStringLength(curr) - 1 < last_pos) last_pos = llStringLength(curr) - 1;
+        curr = llGetSubString(curr, 0, last_pos);
+        commands += curr;
+    }
+    llDialog(listen_to, title, commands, menu_channel);
+    llSetTimerEvent(TIMEOUT_FOR_MENU);
+}
+
+// shuts down any connection we might have had with any active menu.  we will not
+// send any responses after this point (although we might already have responded when
+// the user clicked the menu).
+clear_menu()
+{
+    llListenRemove(listening_id);
+    llSetTimerEvent(0.0);
+}
+
+// a simple version of a reply for a command that has been executed.  the parameters
+// might contain an outcome or result of the operation that was requested.
+// ours do differ from normal in that we send back the channel as the number parameter
+// instead of enforcing that being MENU_HUFFWARE_ID.
+send_reply(integer destination, integer channel, list parms, string command)
+{
+    llMessageLinked(destination, channel, command,
+        llDumpList2String(parms, HUFFWARE_PARM_SEPARATOR));
+}
+
+// processes the menu requests.
+handle_link_message(integer sender, integer huff_id, string msg, key id)
+{
+    if (huff_id != MENUTINI_HUFFWARE_ID) return;  // not for us.
+
+    if (msg == SHOW_MENU_COMMAND) {
+        _menu_base_start = 0;  // reset the position in the menus.
+        // separate the list out.
+//log_it("id showing: " + id);
+        list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
+        // toss any existing menu info.
+        clear_menu();
+//log_it("key here early: " + llList2String(parms, 4));
+        show_menu(llList2String(parms, 0), llList2String(parms, 1),
+            llParseString2List(llList2String(parms, 2),
+                [HUFFWARE_ITEM_SEPARATOR], []),
+            (integer)llList2String(parms, 3),
+            (key)llList2String(parms, 4));
+    }
+}
+
+// process the response when the user chooses a menu item.  this causes our
+// caller to be told what was selected.
+process_menu_response(integer channel, string name, key id, string message)
+{
+    if (channel != menu_system_channel) return;  // not for us.
+
+    if (message == NEXT_MENU_TEXT) {
+        // this is the special choice, so we need to go to the next page.
+        _menu_base_start += 11;
+        if (_menu_base_start > llGetListLength(_private_global_buttons)) {
+            // we have wrapped around the list.  go to the start again.
+            _menu_base_start = 0;
+        }
+        show_menu(global_menu_name, _private_global_title,
+            _private_global_buttons, menu_system_channel,
+            _private_global_av_key);
+        return;  // handled by opening a new menu.
+    }
+    
+    string calculated_name;
+    integer indy;
+    // first try for an exact match.
+    for (indy = 0; indy < llGetListLength(_private_global_buttons); indy++) {
+        string curr = llList2String(_private_global_buttons, indy);
+        if (curr == message) {
+            // correct the answer based on the full button string.
+            calculated_name = curr;
+        }
+    }
+    if (calculated_name == "") {
+        // try an imprecise match if the exact matching didn't work.
+        for (indy = 0; indy < llGetListLength(_private_global_buttons); indy++) {
+            string curr = llList2String(_private_global_buttons, indy);
+            if (is_prefix(curr, message)) {
+                // correct the answer based on the full button string.
+                calculated_name = curr;
+            }
+        }
+    }
+    if (calculated_name != "") {
+        // only send a response if that menu choice made sense to us.
+        send_reply(LINK_THIS, MENUTINI_HUFFWARE_ID + REPLY_DISTANCE,
+            [ global_menu_name, calculated_name, _private_global_av_key ],
+            SHOW_MENU_COMMAND);
+    }
+}
+
+//////////////
+// from hufflets...
+
+integer debug_num = 0;
+
+// a debugging output method.  can be disabled entirely in one place.
+log_it(string to_say)
+{
+    debug_num++;
+    // tell this to the owner.    
+    llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on open chat, but use an unusual channel.
+//    llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+}
+
+//////////////
+
+// returns TRUE if the "prefix" string is the first part of "compare_with".
+integer is_prefix(string compare_with, string prefix)
+{ return (llSubStringIndex(compare_with, prefix) == 0); }
+
+//////////////
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.8.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+//hmmm: extract this code to a menutini example!
+
+//////////////
+// how to invoke a menu (assuming menutini is in same prim as calling script):
+//
+list buttons;  // holds onto the set of menu options.
+//
+integer random_channel() { return -(integer)(llFrand(40000) + 20000); }
+//
+example_invocation()
+{
+    string menu_name = "grumfazoid";
+    string title = "These united colors of ben's futon have unfortunately run.";
+    buttons = [ "garp out", "sklonar", "fuzzlenog" ];
+    integer menu_channel = random_channel();
+    key listen_to = llGetOwner();
+    llMessageLinked(LINK_THIS, MENUTINI_HUFFWARE_ID, SHOW_MENU_COMMAND,
+        menu_name + HUFFWARE_PARM_SEPARATOR
+        + title + HUFFWARE_PARM_SEPARATOR
+        + wrap_item_list(buttons) + HUFFWARE_PARM_SEPARATOR
+        + (string)menu_channel + HUFFWARE_PARM_SEPARATOR
+        + (string)listen_to);
+}
+//
+// how to handle the response message when the user chooses a button.
+//
+react_to_menu(string menu_name, string which_choice)
+{
+    // one might use the menu_name when dealing with multiple different menus.
+
+    integer indy = 0;
+    // find the specified item and process it.
+    while (indy < llGetListLength(buttons)) {
+        // see if the current destination matches.
+        if (llSubStringIndex(llList2String(buttons, indy), which_choice) == 0) {
+            // this is the chosen item.
+//            process_menu_item(indy);  // using numerical numbering.
+// this function must be implemented in your own code; it is what handles the
+// user picking a particular button on the menu.
+            return;            
+        }
+        indy++;
+    }
+    llSay(0, "did not find menu option");
+}
+
+// an example for menu handling.  this gets the response from menutini library
+// and calls the menu processing method "react_to_menu".
+example_handle_link_message(integer sender, integer num, string msg, key id)
+{
+    if (num != MENUTINI_HUFFWARE_ID + REPLY_DISTANCE) return;  // not for us.
+    if (msg != SHOW_MENU_COMMAND) return;  // also not for us.
+    list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
+    string menu_name = llList2String(parms, 0);
+    string which_choice = llList2String(parms, 1);
+    react_to_menu(menu_name, which_choice);
+}
+
+// then inside a state, you need an event handler like so:
+//
+// link_message(integer sender, integer num, string msg, key id)
+// { example_handle_link_message(sender, num, msg, id); }
+
+//
+// end invocation sample code...
+//////////////
+
+default
+{
+    state_entry() { if (llSubStringIndex(llGetObjectName(),  "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry() { auto_retire(); }
+
+    link_message(integer sender, integer huff_id, string msg, key id)
+    { handle_link_message(sender, huff_id, msg, id); }
+
+    listen(integer channel, string name, key id, string message)
+    { process_menu_response(channel, name, id, message); }
+    
+    // if the timer goes off, then the user has ignored the menu for longer than the
+    // timeout.  we need to turn off our listen and ignore that menu.
+    timer() { clear_menu(); }
+}
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/non-script_giver_v2.8.txt b/huffware/huffotronic_updater_freebie_v5.3/non-script_giver_v2.8.txt
new file mode 100755 (executable)
index 0000000..07e7c93
--- /dev/null
@@ -0,0 +1,412 @@
+
+// huffware script: non-script giver, by fred huffhines.
+//
+// gives all objects, notecards, etc contained in an object when that object is touched.
+// does not give out scripts, since these are generally not something that should be handed
+// to the customer.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+integer ONLY_GIVE_TO_OWNER = TRUE;
+    // if this is true, then only the owner will receive a copy of the items.
+
+integer GIVE_UNCOPYABLES = FALSE;
+    // this flag is dangerous when true, because it means that uncopyable objects will still
+    // be handed to the target.  if that target refuses the non-copyable items, then the items
+    // will be lost forever.  that is not so good if you've sold the person a non-copy item.
+
+string EXCLUDED_NOTECARD = "product description";
+    // a special case; if there is a giftorse configuration card, we won't hand that out.
+
+integer DEBUGGING = FALSE;  // set to true for noisier runs.
+
+key first_toucher;  // tracks who clicked on the object to get contents.
+
+// give out pictures, notecards, objects, etc. that are hiding in the object.
+really_give_out_contents(key give_to)
+{
+    list all_to_give = [];  // the set we will hand over in a batch.
+    list uncopyables = [];  // the list we have to do individually.
+    // find out how many items there are.
+    integer count = llGetInventoryNumber(INVENTORY_ALL);
+    // iterate across all the items and add them to the gift list if appropriate.
+    integer indy;
+    for (indy = 0; indy < count; indy++) {
+        string item_name = llGetInventoryName(INVENTORY_ALL, indy);
+        integer type = llGetInventoryType(item_name);
+        if ( (type != INVENTORY_SCRIPT) 
+            && ( (type != INVENTORY_NOTECARD) || (item_name != EXCLUDED_NOTECARD) ) ) {
+            // it's okay to add this item; it's not a script and we are not skipping the special notecard.
+            integer mask = MASK_OWNER;
+            if (!ONLY_GIVE_TO_OWNER) mask = MASK_EVERYONE;
+            integer perms = llGetInventoryPermMask(item_name, mask);
+            if (perms & PERM_COPY) {
+                // a normal object that we can hand out.
+                all_to_give += item_name;
+            } else {
+                uncopyables += item_name;
+            }
+        }
+    }
+    // hand the customer the whole set as one big chunk, named after the object.
+    llGiveInventoryList(give_to, llGetObjectName(), all_to_give);
+
+    // handle any problematic items.  we cannot copy these objects into a category folder,
+    // so we can either not try to copy them (a lot safer) or we can try to deliver them
+    // normally as individual items.  the latter choice is more dangerous, because if the
+    // owner discards these items rather than keeping them, the items will be lost forever!
+    if (llGetListLength(uncopyables) > 0) {
+        string plural = " ";
+        string is_verb = "is ";
+        string third_noun_subj = "it ";
+        string third_noun_obj = "it ";
+        if (llGetListLength(uncopyables) > 1) {
+            plural = "s ";
+            is_verb = "are ";
+            third_noun_subj = "they ";
+            third_noun_obj = "them ";
+        }
+        
+        string uncopyable_message = "will be left inside the object.  To get " + third_noun_obj
+            + ", please copy " + third_noun_obj + "\nmanually from this object into your inventory.";
+        if (GIVE_UNCOPYABLES) {
+            uncopyable_message = "will be moved over to your inventory."
+            + "\nPlease look in your main folders for "
+            + third_noun_obj + "(e.g., in Objects or Textures).";
+        }
+        
+        string failure_message = "The item" + plural
+            + "[" + llDumpList2String(uncopyables, "; ") + "]\n"
+            + is_verb + "not copyable; " + third_noun_subj
+            + uncopyable_message;
+            
+        if (llGetOwner() == give_to) {
+            // the object can be moved to inventory, but not with the category method.
+            llOwnerSay(failure_message);
+        } else {
+            // this seems like a weird case; it will probably just fail anyhow?
+            // if the item's not copyable and you're not the owner of this object,
+            // how can we give it to you?
+            llInstantMessage(give_to, failure_message);
+        }
+        
+        // now that we've announced this weird situation, handle it appropriately.
+        if (GIVE_UNCOPYABLES) {
+            for (indy = 0; indy < llGetListLength(uncopyables); indy++) {
+                string item_name = llList2String(uncopyables, indy);
+                llGiveInventory(give_to, item_name);
+            }
+        }  // otherwise leave them be.
+    }
+}
+
+
+//////////////
+// code borrowed from menutini to raise a menu asking if they actually meant to get all
+// the contents.  an opensim inventory bug makes all the folders look foolish if we
+// do any inventory giving accidentally.
+//////////////
+
+// global variables...
+
+list _private_global_buttons;  // holds onto the active set of menu options.
+string _private_global_av_key;  // the key for the avatar who clicks the menu.
+string _private_global_title;  // holds onto current title text.
+
+integer _menu_base_start = 0;  // position in the items of the current menu.
+
+integer listening_id = 0;
+    // the current id of our listening for the menu.  it's an id returned by LSL
+    // that we need to track so we can cancel the listen.
+
+integer menu_system_channel = -123;
+    // messages come back to us from this channel when user clicks the dialog.
+    // this is set later and the default is meaningless.
+
+string global_menu_name = "";
+    // hangs onto the current menu's name.
+
+//hmmm: note; to manage multiple concurrent menus on different channels,
+//      we must make these into lists.  then the timeouts should apply
+//      individually to these instead of overall (if we even do timeouts;
+//      it's nicer if menus never stop being active).
+
+string NEXT_MENU_TEXT = "Next >>";
+    // what the next item will say for showing next menu page.
+    
+//integer TIMEOUT_FOR_MENU = 42;
+    // timeout for the menu in seconds.
+
+// displays the menu requested.  it's "menu_name" is an internal name that is
+// not displayed to the user.  the "title" is the content shown in the main area
+// of the menu.  "commands_in" is the list of menu items to show as buttons.
+// the "menu_channel" is where the user's clicked response will be sent.  the
+// "listen_to" key is the avatar expected to click the menu, which is needed to
+// listen to his response.
+show_menu(string menu_name, string title, list buttons,
+    integer menu_channel, key listen_to)
+{
+    // save our new parms.
+    global_menu_name = menu_name;
+    _private_global_title = title;
+    _private_global_buttons = buttons;
+    menu_system_channel = menu_channel;
+    _private_global_av_key = listen_to;
+    if (DEBUGGING) {
+        log_it("menu name: " + global_menu_name);
+        log_it("title: " + _private_global_title);
+        log_it("buttons: " + (string)buttons);
+        log_it("channel: " + (string)menu_system_channel);
+        log_it("listen key: " + (string)listen_to);
+    }
+
+    integer add_next = FALSE;  // true if we should add a next menu item.
+
+    // the math here incorporates current button position.
+    integer current = _menu_base_start;
+    integer max_buttons = llGetListLength(buttons) - current;
+
+    if (max_buttons > 12) {
+        // limitation of SL: menus have a max of 12 buttons.
+        max_buttons = 12;
+        add_next = TRUE;
+    } else if (llGetListLength(buttons) > 12) {
+        // we already have been adding next.  let's make sure this gets
+        // a wrap-around next button.
+        add_next = TRUE;
+    }
+    // chop out what we can use in a menu.
+    list trunc_buttons = llList2List(buttons, current, current + max_buttons - 1);
+    if (add_next) {
+        // we were asked to add a menu item for the next screen.
+        trunc_buttons = llList2List(trunc_buttons, 0, 10) + NEXT_MENU_TEXT;
+    }
+
+    listening_id = llListen(menu_channel, "", listen_to, "");
+    list commands;
+    integer i;
+    // take only the prefix of the string, to avoid getting a length complaint.
+    for (i = 0; i < llGetListLength(trunc_buttons); i++) {
+        string curr = llList2String(trunc_buttons, i);
+        integer last_pos = 23;  // default maximum, highest possible is 24.
+        if (llStringLength(curr) - 1 < last_pos) last_pos = llStringLength(curr) - 1;
+        curr = llGetSubString(curr, 0, last_pos);
+        commands += curr;
+    }
+    llDialog(listen_to, title, commands, menu_channel);
+}
+
+// shuts down any connection we might have had with any active menu.  we will not
+// send any responses after this point (although we might already have responded when
+// the user clicked the menu).
+clear_menu()
+{
+    llListenRemove(listening_id);
+}
+
+// process the response when the user chooses a menu item.
+process_menu_response(integer channel, string name, key id, string message)
+{
+  if (channel != menu_system_channel) return;  // not for us.
+  
+    if (message == NEXT_MENU_TEXT) {
+        // this is the special choice, so we need to go to the next page.
+        _menu_base_start += 11;
+        if (_menu_base_start > llGetListLength(_private_global_buttons)) {
+            // we have wrapped around the list.  go to the start again.
+            _menu_base_start = 0;
+        }
+        show_menu(global_menu_name, _private_global_title,
+            _private_global_buttons, menu_system_channel,
+            _private_global_av_key);
+        return;  // handled by opening a new menu.
+    }
+    
+    string calculated_name;
+    integer indy;
+    // first try for an exact match.
+    for (indy = 0; indy < llGetListLength(_private_global_buttons); indy++) {
+        string curr = llList2String(_private_global_buttons, indy);
+        if (curr == message) {
+            // correct the answer based on the full button string.
+            calculated_name = curr;
+        }//////////////[copy starting here...]//////////////
+
+    }
+    if (calculated_name == "") {
+        // try an imprecise match if the exact matching didn't work.
+        for (indy = 0; indy < llGetListLength(_private_global_buttons); indy++) {
+            string curr = llList2String(_private_global_buttons, indy);
+            if (is_prefix(curr, message)) {
+                // correct the answer based on the full button string.
+                calculated_name = curr;
+            }
+        }
+    }
+    if (calculated_name == "yes") {
+        // only send a response if that menu choice made sense to us.
+        really_give_out_contents(first_toucher);
+        clear_menu();
+    }
+}
+
+// end from menutini.
+//////////////
+
+
+//////////////
+//
+// borrowed from hufflets...
+
+integer debug_num = 0;
+
+// a debugging output method.  can be disabled entirely in one place.
+log_it(string to_say)
+{
+    debug_num++;
+    // tell this to the owner.    
+    llOwnerSay(llGetDate() + ": " + llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+//llWhisper(0, llGetDate() + ": " + llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on an unusual channel for chat if it's not intended for general public.
+//    llSay(108, llGetDate() + ": " + llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on open chat that anyone can hear.  we take off the bling for this one.
+//    llSay(0, to_say);
+}
+
+// returns the index of the first occurrence of "pattern" inside
+// the "full_string".  if it is not found, then a negative number is returned.
+integer find_substring(string full_string, string pattern)
+{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); }
+
+// returns TRUE if the "prefix" string is the first part of "compare_with".
+integer is_prefix(string compare_with, string prefix)
+{ return (llSubStringIndex(compare_with, prefix) == 0); }
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.8.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+default
+{
+    state_entry() { if (llSubStringIndex(llGetObjectName(),  "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry() { 
+        auto_retire();
+    }
+    
+    on_rez(integer param) { llResetScript(); }
+
+    touch_start(integer num) {
+        first_toucher = llDetectedKey(0);
+        // are we only supposed to give stuff to the owner?
+        if (ONLY_GIVE_TO_OWNER && (first_toucher != llGetOwner()) ) {
+            first_toucher = NULL_KEY;
+            return;  // bail out.
+        }
+        show_menu("askreally", "Would you like a copy of this object's contents?",
+            ["yes", "no"], -18264, first_toucher);
+    }
+
+    listen(integer channel, string name, key id, string message)
+    { process_menu_response(channel, name, id, message); }
+
+}
diff --git a/huffware/huffotronic_updater_freebie_v5.3/noteworthy_library_v12.4.txt b/huffware/huffotronic_updater_freebie_v5.3/noteworthy_library_v12.4.txt
new file mode 100755 (executable)
index 0000000..4d56bac
--- /dev/null
@@ -0,0 +1,541 @@
+
+// huffware script: noteworthy library, by fred huffhines.
+//
+// a handy approach to reading a notecard.  this version supports requiring
+// a 'signature' in the notecard's first line, so that multiple notecards can
+// exist in an object without being misinterpreted.  the script is accessed via
+// its link message API, so it can be used in an object without all this code
+// needing to be embedded in another script.  it also supports queuing up requests
+// to read notecards, so multiple scripts can use it to read their specific
+// notecards without any special handling (besides waiting a bit longer).
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+// constants that can be adapted to your needs...
+
+integer DEBUGGING = FALSE;
+    // if this is true, then a lot of extra noise is generated when notecards are read.
+
+float TIME_OUT_ON_ONE_NOTECARD = 42.0;
+    // we allow one line of a notecard this long to be read before we decide it's hosed.
+    // some sims are very very slow, and a time-out of one minute has been found lacking;
+    // we saw at least one case where the first notecard line to be read took longer than
+    // 60 seconds.  that's why we kept cranking this time-out up...
+
+// constants that should not be changed...
+
+// outcomes from handling a line in a notecard.
+integer STILL_READING = -8;  // the notecard seems good, but isn't finished.
+integer BAD_NOTECARD = -9;  // this notecard doesn't have our signature.
+integer DONE_READING = -10;  // the notecard is finished being read.
+
+integer LIST_ELEMENT_GUESS = 200;  // guess at how many bytes average list element uses.
+
+integer MAXIMUM_LINES_USED = 4;
+    // we will read this many lines at a time from the appropriate notecard.
+
+// this is the noteworthy library's API that is available via link messages.
+//////////////
+// do not redefine these constants.
+integer NOTEWORTHY_HUFFWARE_ID = 10010;
+    // the unique id within the huffware system for the noteworthy script to
+    // accept commands on.  this is used in llMessageLinked as the num parameter.
+string HUFFWARE_PARM_SEPARATOR = "{~~~}";
+    // this pattern is an uncommon thing to see in text, so we use it to separate
+    // our commands in link messages.
+string HUFFWARE_ITEM_SEPARATOR = "{|||}";
+    // used to separate lists of items from each other when stored inside a parameter.
+    // this allows lists to be passed as single string parameters if needed.
+integer REPLY_DISTANCE = 100008;  // offset added to service's huffware id in reply IDs.
+string BAD_NOTECARD_INDICATOR = "bad_notecard";
+    // indicates that the notecard reading process has failed to find an appropriate one.
+    
+//STRIKE THIS from everywhere.
+//string BUSY_READING_INDICATOR = "busy_already";
+//    // this return value indicates that the script is already in use by some other script.
+//    // the calling script should try again later.
+
+string NOTECARD_READ_CONTINUATION = "continue!";
+    // returned as first parameter if there is still more data to handle.
+// commands available via the noteworthy library:
+string READ_NOTECARD_COMMAND = "#read_note#";
+    // command used to tell the script to read notecards.  needs a signature to find
+    // in the card as the first parameter, and a randomly generated response code for
+    // the second parameter.  the response code is used to uniquely identify a set of
+    // pending notecard readings (hopefully).  the signature can be blank.
+    // the results will be fired back as the string value returned, which will have
+    // as first element the notecard's name (or BAD_NOTECARD_INDICATOR if none was
+    // found) and as subsequent elements an embedded list that was read from the
+    // notecard.  this necessarily limits the size of the notecards that we can read
+    // and return.
+string READ_SPECIFIC_NOTECARD_COMMAND = "#read_thisun#";
+    // like the read notecard command, but specifies the notecard name to use.  only that
+    // specific notecard file will be consulted.  first and second parm are still signature
+    // and response code, third parm is the notecard name.
+//
+//////////////
+// joins a list of parameters using the parameter sentinel for the library.
+string wrap_parameters(list to_flatten)
+{ return llDumpList2String(to_flatten, HUFFWARE_PARM_SEPARATOR); }
+//////////////
+
+// global variables...
+
+string requested_signature = "";
+    // if this is non-empty, then it must be found in the first line of the script.
+
+integer only_read_one_notecard = FALSE;  // true if just one specific notecard should be used.
+
+string global_notecard_name;  // the name of the card we're reading now.
+key global_query_id = NULL_KEY;  // the identifier of our data query.
+integer current_response_code = 0;  // the code that our client uses for its reading.
+list global_query_contents;  // the lines we have read from the notecard.
+
+integer line_number = 0;  // which line are we at in notecard?
+
+integer found_signature_line = FALSE;  // did we find our first line in a notecard yet?
+
+integer trying_notecard_at = -1;  // where we are currently looking for a good notecard.
+
+list pending_signatures;  // signatures from queued requests for reading.
+list pending_response_codes;  // response codes for the queued requests.
+list pending_notecard_names;  // card names if it's a specific request.
+
+//////////////
+
+startup_initialize()
+{
+    pending_signatures = [];
+    pending_response_codes = [];
+    pending_notecard_names = [];
+    current_response_code = 0;
+    llSetTimerEvent(0.0);
+}
+
+reset_for_reading(string signature, integer response_code)
+{
+    requested_signature = signature;
+    current_response_code = response_code;
+    llSetTimerEvent(TIME_OUT_ON_ONE_NOTECARD);  // don't allow a read to happen forever.
+    global_query_contents = [];
+    global_notecard_name = "";
+    line_number = 0;
+    found_signature_line = FALSE;
+    trying_notecard_at = -1;
+    global_query_id = NULL_KEY;
+}
+
+// use the existing global notecard setting to start reading.
+select_specific_notecard()
+{
+    global_query_id = NULL_KEY;  // reset the query id so we don't get bogus answers.
+    line_number = 0;  // reset line number again.
+    global_query_id = llGetNotecardLine(global_notecard_name, 0);
+}
+
+// picks the notecard at the "index" (from 0 to num_notecards - 1) and
+// starts reading it.
+select_notecard(integer index)
+{
+    global_query_id = NULL_KEY;  // reset the query id so we don't get bogus answers.
+    string card_specified = llGetInventoryName(INVENTORY_NOTECARD, index);
+    if (card_specified == "") return;   // not good.  bad index.
+    global_notecard_name = card_specified;
+    line_number = 0;  // reset line number again.
+    // we have a new file name, so load up the destinations, hopefully.
+    global_query_id = llGetNotecardLine(global_notecard_name, 0);
+}
+
+// increments our index in the count of notecards that the object has, and start
+// reading the next notecard (at the index).
+integer try_next_notecard()
+{
+    if (only_read_one_notecard) {
+        return FALSE;  // we were only going to try one.
+    }
+    // reset some values that might have applied before.
+    global_notecard_name = "";
+    // skip to the next card.
+    trying_notecard_at++;
+    // make sure we're not over the count of cards.
+    if (trying_notecard_at >= llGetInventoryNumber(INVENTORY_NOTECARD)) {
+        // this is a problem.  we didn't find anything suitable.
+        return FALSE;
+    }
+    // so, now we'll try the next notecard to look for our signature.
+    select_notecard(trying_notecard_at);
+    return TRUE;
+}
+
+// process a line of text that we received from the current notecard.
+integer handle_notecard_line(key query_id, string data)
+{
+    // if we're not at the end of the notecard we're reading...
+    if (data != EOF) {
+        // there's more to read in the notecard still.
+        if (data != "") {
+            // make sure we even have a signature to look for.
+            if (!found_signature_line && (requested_signature == "")) {
+                // no signature means that we always find it.
+                found_signature_line = TRUE;
+            }
+            // did we already get our signature?  if not, see if this is it.
+            if (!found_signature_line && (data != requested_signature) ) {
+                // this is a bad notecard.  skip it.
+                if (!try_next_notecard()) {
+                    // we have no more to work with.
+                    return BAD_NOTECARD;
+                }
+                return STILL_READING;  // we'll keep going.
+            } else if (!found_signature_line && (data == requested_signature) ) {
+                // this is a good signature line, so record that and then skip it.
+                found_signature_line = TRUE;
+            } else {
+                if (DEBUGGING
+                    && ( ( (requested_signature == "") && (line_number == 0) )
+                        || ( (requested_signature != "") && (line_number == 1) ) ) ) {
+                    log_it("started reading " + global_notecard_name + "...");
+                }
+                // don't record any lines that are comments.
+                if ( (llGetSubString(data, 0, 0) != "#")
+                    && (llGetSubString(data, 0, 0) != ";") ) {
+                    // add the non-blank line to our list.
+                    global_query_contents += data;
+                    // make sure we still have enough space to keep going.
+                    if (llGetListLength(global_query_contents) >= MAXIMUM_LINES_USED) {
+                        // ooh, make sure we pause before blowing our heap&stack.
+                        send_reply(LINK_THIS, [ NOTECARD_READ_CONTINUATION,
+                            current_response_code ], READ_NOTECARD_COMMAND, TRUE);                
+                    }
+                }
+            }
+        }
+        line_number++;  // increase the line count.
+        // reset the timer rather than timing out, if we actually got some data.
+        llSetTimerEvent(TIME_OUT_ON_ONE_NOTECARD);        
+        // request the next line from the notecard.
+        global_query_id = llGetNotecardLine(global_notecard_name, line_number);
+        if (global_query_id == NULL_KEY) {
+//log_it("failed to restart notecard reading.");
+            return DONE_READING;
+//is that the best outcome?
+        }
+        return STILL_READING;
+    } else {
+        // that's the end of the notecard.  we need some minimal verification that it
+        // wasn't full of garbage.
+        if (!found_signature_line) {
+            if (DEBUGGING) log_it("never found signature in " + global_notecard_name);
+            if (!try_next_notecard()) {
+                return BAD_NOTECARD;  // we failed to find a good line?
+            } else {
+                // the next notecard's coming through now.
+                return STILL_READING;
+            }
+        } else {
+//            if (DEBUGGING) log_it("found signature.");
+            // saw the signature line, so this is a good one.
+            return DONE_READING;
+        }
+    }
+}
+
+// only sends reply; does not reset notecard process.
+send_reply(integer destination, list parms, string command,
+    integer include_query)
+{
+//integer items = llGetListLength(parms);
+//if (include_query) items += llGetListLength(global_query_contents);
+//log_it("pre-sending " + (string)items + " items, mem=" + (string)llGetFreeMemory());
+
+   if (!include_query) {
+        llMessageLinked(destination, NOTEWORTHY_HUFFWARE_ID + REPLY_DISTANCE,
+            command, llDumpList2String(parms, HUFFWARE_PARM_SEPARATOR));
+    } else {
+        llMessageLinked(destination, NOTEWORTHY_HUFFWARE_ID + REPLY_DISTANCE,
+            command,
+            llDumpList2String(parms + global_query_contents, HUFFWARE_PARM_SEPARATOR));
+    }
+    global_query_contents = [];
+//log_it("post-sending, mem=" + (string)llGetFreeMemory());
+}
+
+// a simple version of a reply for a command that has been executed.  the parameters
+// might contain an outcome or result of the operation that was requested.
+send_reply_and_reset(integer destination, list parms, string command,
+    integer include_query)
+{
+    llSetTimerEvent(0.0);  // stop the timer, since we're about to reply.
+    send_reply(destination, parms, command, include_query);
+    current_response_code = 0;  // reset back to default so we can start another read.
+    global_query_id = NULL_KEY;  // reset so we accept no more data.
+}
+
+// if there are other pending notecard reads, this goes to the next one listed.
+dequeue_next_request()
+{
+    if (llGetListLength(pending_signatures)) {
+        // get the info from the next pending item.
+        string sig = llList2String(pending_signatures, 0);
+        integer response_code = llList2Integer(pending_response_codes, 0);
+        string notecard = llList2String(pending_notecard_names, 0);
+        // whack the head of the queue since we grabbed the info.
+        pending_signatures = llDeleteSubList(pending_signatures, 0, 0);
+        pending_response_codes = llDeleteSubList(pending_response_codes, 0, 0);
+        pending_notecard_names = llDeleteSubList(pending_notecard_names, 0, 0);
+        if (llStringLength(notecard)) {
+            global_notecard_name = notecard;
+            select_specific_notecard();
+        } else {
+            reset_for_reading(sig, response_code);
+        }
+    }
+}
+
+// deals with one data server answer from the notecard.
+process_notecard_line(key query_id, string data)
+{
+    // try to consume a line from the notecard.
+    integer outcome = handle_notecard_line(query_id, data);
+    if (outcome == DONE_READING) {
+        // that was a valid notecard and we read all of it.
+        if (DEBUGGING) log_it("finished reading " + global_notecard_name + ".");
+        // send back the results.
+        send_reply_and_reset(LINK_THIS, [ global_notecard_name, current_response_code ],
+            READ_NOTECARD_COMMAND, TRUE);
+    } else if (outcome == BAD_NOTECARD) {
+        // bail; this problem must be addressed by other means.
+        if (DEBUGGING) log_it("failed to find an appropriate notecard");
+        send_reply_and_reset(LINK_THIS, [ BAD_NOTECARD_INDICATOR, current_response_code ],
+            READ_NOTECARD_COMMAND, FALSE);
+    } else if (outcome == STILL_READING) {
+        // we have a good card and are still processing it.
+        return;
+    } else {
+        if (DEBUGGING) log_it("unknown outcome from handle_notecard_line");
+        // again, bail out.  we have no idea what happened with this.
+        send_reply_and_reset(LINK_THIS, [ BAD_NOTECARD_INDICATOR, current_response_code ],
+            READ_NOTECARD_COMMAND, FALSE);
+    }
+    // if we have reached here, we should crank up the next queued notecard reading.
+    dequeue_next_request();
+}
+
+// processes requests from our users.
+handle_link_message(integer which, integer num, string msg, key id)
+{
+    if (num != NOTEWORTHY_HUFFWARE_ID) return;  // not for us.
+
+    if (msg == READ_NOTECARD_COMMAND) {
+        only_read_one_notecard = FALSE;  // general inquiry for any card.
+        list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
+//log_it("read notecard--parms are: " + (string)parms);
+        string signature = llList2String(parms, 0);
+        integer response_code = llList2Integer(parms, 1);
+//log_it("got signature " + signature + " and respcode " + (string)response_code);
+//holding:        if (!current_response_code) {
+            // go ahead and process this request; we aren't busy.
+            reset_for_reading(signature, response_code);
+            if (!try_next_notecard()) {
+                if (DEBUGGING) log_it("failed to find any appropriate notecards at all.");
+                send_reply_and_reset(LINK_THIS, [ BAD_NOTECARD_INDICATOR, response_code ],
+                    READ_NOTECARD_COMMAND, FALSE);
+                return;
+            }
+//holding:        } else {
+//holding:            // we're already busy.
+//holding://            send_reply_and_reset(LINK_THIS, [ BUSY_READING_INDICATOR, response_code ],
+//holding://                READ_NOTECARD_COMMAND, FALSE);
+//holding:            // stack this request; another is in progress.
+//holding:            pending_signatures += signature;
+//holding:            pending_response_codes += response_code;
+//holding:            pending_notecard_names += "";
+//holding:        }
+    } else if (msg == READ_SPECIFIC_NOTECARD_COMMAND) {
+        only_read_one_notecard = TRUE;  // they want one particular card.
+        list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
+//log_it("read specific--parms are: " + (string)parms);
+        string signature = llList2String(parms, 0);
+        integer response_code = llList2Integer(parms, 1);
+        string notecard_name = llList2String(parms, 2);
+//log_it("got signature " + signature + " and respcode " + (string)response_code);
+//holding:        if (!current_response_code) {
+            // go ahead and process this request; we aren't busy.
+            reset_for_reading(signature, response_code);
+            global_notecard_name = notecard_name;  // set our global.
+            select_specific_notecard();
+//holding:        } else {
+//holding:            // we're already busy.
+//holding://            send_reply_and_reset(LINK_THIS, [ BUSY_READING_INDICATOR, response_code ],
+//holding://                READ_NOTECARD_COMMAND, FALSE);
+//holding:            // stack this request; another is in progress.
+//holding:            pending_signatures += signature;
+//holding:            pending_response_codes += response_code;
+//holding:            pending_notecard_names += notecard_name;
+//holding:        }
+    }
+}
+
+
+///////////////
+
+// from hufflets...
+
+integer debug_num = 0;
+
+// a debugging output method.  can be disabled entirely in one place.
+log_it(string to_say)
+{
+    debug_num++;
+    // tell this to the owner.    
+    llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on open chat, but use an unusual channel.
+//    llSay(108, (string)debug_num + "- " + to_say);
+}
+
+//////////////
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.4.
+// distributed under BSD-like license.
+//   partly based on the self-upgrading scripts from markov brodsky and jippen faddoul.
+// the function auto_retire() should be added *inside* a version numbered script that
+// you wish to give the capability of self-upgrading.
+//   this script supports a notation for versions embedded in script names where a 'v'
+// is followed by a number in the form "major.minor", e.g. "grunkle script by ted v8.2".
+// when the containing script is dropped into an object with a different version, the
+// most recent version eats any existing ones.
+//   keep in mind that this code must be *copied* into your script you wish to add
+// auto-retirement capability to.
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+//log_it("invpo=" + (string)posn);
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+//log_it("pos=" + (string)space_v_posn);
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+//log_it("space v@" + (string)space_v_posn);
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+//log_it("indy=" + (string)space_v_posn);
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+//log_it("saw case of previously redundant item, aieee.  flattened: " + to_chop_up);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+// end hufflets.
+//////////////
+
+default {
+    state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry() {
+        auto_retire();
+        startup_initialize();
+    }
+    
+    state_exit() {
+        llSetTimerEvent(0);
+    }
+    
+    // we don't do anything until we're given a command to read a notecard.
+    link_message(integer which, integer num, string msg, key id) 
+    {
+        if (num != NOTEWORTHY_HUFFWARE_ID) return;  // not for us.
+        handle_link_message(which, num, msg, id);
+    }
+    
+    on_rez(integer parm) { state rerun; }
+    
+    timer() {
+        llSetTimerEvent(0.0);  // stop any timer now.
+        // let the caller know this has failed out.
+//        if (DEBUGGING) log_it("time out processing '" + requested_signature + "'");
+        send_reply_and_reset(LINK_THIS, [ BAD_NOTECARD_INDICATOR, current_response_code ],
+            READ_NOTECARD_COMMAND, FALSE);
+        current_response_code = 0;  // we gave up on that one.
+        dequeue_next_request();  // get next reading started if we have anything to read.
+    }
+
+    dataserver(key query_id, string data) {
+        // make sure this data is for us.
+        if (global_query_id != query_id) return;
+        // yep, seems to be.
+        process_notecard_line(query_id, data);
+    }
+}
+
+
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/particle_projector_v3.1.txt b/huffware/huffotronic_updater_freebie_v5.3/particle_projector_v3.1.txt
new file mode 100755 (executable)
index 0000000..f3201ee
--- /dev/null
@@ -0,0 +1,210 @@
+
+// huffware script: particle projector, by fred huffhines.
+//
+// takes a texture from one side of the object and projects it in a forward X and upward Z direction.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+// constants that can be modified for different behavior.
+float TEXTURE_DISPLAY_LIFE_SPAN = 14.0;  // how long the texture should be shown.
+
+integer SIDE_TO_ACQUIRE_TEXTURE_FROM = 1;  // which side of the object should give us our display texture?
+
+float MAGNIFIER = 4.0;  // how much to expand the texture as it runs.
+
+vector TEXTURE_ACCELERATION = <0.02, 0.0, 0.01>;  // change in speed of the texture over time.
+
+// global variables used in the script.
+
+string texture_to_display = "95c7bb7c-d777-26b1-4052-acd778bfcb40";  // a simple default texture.
+
+// displays a particle system with a texture that gradually enlarges over the object.
+texture_hallucination()
+{
+    // get a bounding box around the object.
+    list bounds = llGetBoundingBox(llGetKey());
+    // calculate the size of the object, which will be our starting particle size.
+    vector size = (vector)llList2String(bounds, 1) - (vector)llList2String(bounds, 0);
+    float chosen_dimension = size.x;
+    if (size.y > chosen_dimension) chosen_dimension = size.y;
+    if (size.z > chosen_dimension) chosen_dimension = size.z;
+    llParticleSystem([
+        // properties regarding the generated particles.
+        PSYS_PART_FLAGS, PSYS_PART_BOUNCE_MASK | PSYS_PART_INTERP_SCALE_MASK,
+        PSYS_PART_START_SCALE, <chosen_dimension, chosen_dimension, 0.0>,
+        PSYS_PART_END_SCALE, <chosen_dimension * MAGNIFIER, chosen_dimension * MAGNIFIER, 0.0>,
+        PSYS_PART_MAX_AGE, TEXTURE_DISPLAY_LIFE_SPAN,
+        PSYS_PART_START_COLOR, <1.0, 1.0, 1.0>,
+        PSYS_PART_END_COLOR, <1.0, 1.0, 1.0>,
+        PSYS_PART_START_ALPHA, 1.0,
+        PSYS_PART_END_ALPHA, 1.0,
+        // properties for the particle system's source.
+        PSYS_SRC_MAX_AGE, TEXTURE_DISPLAY_LIFE_SPAN,
+        PSYS_SRC_TEXTURE, texture_to_display,
+        PSYS_SRC_ACCEL, TEXTURE_ACCELERATION,
+        PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_DROP,
+        PSYS_SRC_BURST_PART_COUNT, 1,
+        PSYS_SRC_BURST_RADIUS, chosen_dimension,
+        PSYS_SRC_BURST_RATE, TEXTURE_DISPLAY_LIFE_SPAN,
+        PSYS_SRC_BURST_SPEED_MIN, 0.01,
+        PSYS_SRC_BURST_SPEED_MAX, 0.01,
+        PSYS_SRC_ANGLE_BEGIN, 0.0,
+        PSYS_SRC_ANGLE_END, 0.0,
+        PSYS_SRC_OMEGA, <0.0, 0.0, 0.0>
+    ]);
+}
+
+// from hufflets...
+
+// returns a number at most "maximum" and at least "minimum".
+// if "allow_negative" is TRUE, then the return may be positive or negative.
+float randomize_within_range(float minimum, float maximum, integer allow_negative)
+{
+    if (minimum > maximum) {
+        // flip the two if they are reversed.
+        float temp = minimum; minimum = maximum; maximum = temp;
+    }
+    float to_return = minimum + llFrand(maximum - minimum);
+    if (allow_negative) {
+        if (llFrand(1.0) < 0.5) to_return *= -1.0;
+    }
+    return to_return;
+}
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.5.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+//log_it("invpo=" + (string)posn);
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+//log_it("pos=" + (string)space_v_posn);
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+//log_it("space v@" + (string)space_v_posn);
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+//log_it("indy=" + (string)space_v_posn);
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+//log_it("saw case of previously redundant item, aieee.  flattened: " + to_chop_up);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+default
+{
+    state_entry() { if (llSubStringIndex(llGetObjectName(),  "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default {
+    state_entry() {
+        auto_retire();
+    }
+
+    touch_start(integer touch_count) {
+        integer count = llGetInventoryNumber(INVENTORY_TEXTURE);
+        if (count > 0) {
+            // try getting a texture out of inventory.
+            integer which_texture = llRound(randomize_within_range(0, count - 1, FALSE));
+            texture_to_display = llGetInventoryName(INVENTORY_TEXTURE, which_texture);
+//if (texture_to_display == "") llOwnerSay("fail!  got bad index from random.");
+        } else {
+            // if none in inventory, load the texture to show from our side.
+            texture_to_display = llGetTexture(SIDE_TO_ACQUIRE_TEXTURE_FROM);
+        }
+//llOwnerSay("texture is now: " + texture_to_display);
+        texture_hallucination();
+        llSetTimerEvent(TEXTURE_DISPLAY_LIFE_SPAN + 0.1);
+    }
+
+    timer() {
+        llParticleSystem([]);  // zap it back out again.
+        llSetTimerEvent(0);  // turn off timer.
+    }
+}
diff --git a/huffware/huffotronic_updater_freebie_v5.3/party_culiar_v5.7.txt b/huffware/huffotronic_updater_freebie_v5.3/party_culiar_v5.7.txt
new file mode 100755 (executable)
index 0000000..ed0382a
--- /dev/null
@@ -0,0 +1,646 @@
+
+// huffware script: "party culiar", by fred huffhines
+//
+// this is yet another particle system script, based on ideas seen
+// in several scripts by various authors and assisted by the lsl wiki.
+// it has the useful characteristic of being able to load its parameters
+// from a notecard, thus making script modifications for the particle
+// system unnecessary.  
+// on initial rez, if a notecard exists, then it is read for particle
+// system parameters named similarly to the variables below (see the
+// particle archetype notecard for more details).  if the object is
+// rezzed with an existing particle system already read from a notecard
+// that's still present, it will keep playing that particle system.
+// if a notecard is added or changed, then the particle variables are
+// read again.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+// party culiar link message API.
+//////////////
+integer PARTYCULIAR_HUFFWARE_ID = 10018;
+    // a unique ID within the huffware system for this script.
+string HUFFWARE_PARM_SEPARATOR = "~~~";
+    // three tildes is an uncommon thing to have otherwise, so we use it to separate
+    // our commands in linked messages.
+//
+string PARTYCULIAR_POWER_COMMAND = "#powrpcl";
+    // tells the particle system to either turn on or off.  the single parameter is
+    // either "on", "1", "true", or "off", "0", "false".
+//////////////
+
+// constants...
+float MIN_ROTATION = 0.1;  // rotations used for omega value to rotate particles.
+float MAX_ROTATION = 4.0;
+
+// items controlled by the notecard...
+
+// noisiness...
+integer debug = FALSE;  // controls whether logging occurs.
+
+integer start_enabled = FALSE;  // if true, particle system starts up right away.
+
+// color...
+integer interpolate_colors = TRUE;
+integer randomize_colors = FALSE;
+vector starting_color = <0.5, 0.7, 0.9>;
+vector ending_color = <0.0, 1.0, 0.3>;
+
+// particle size...
+integer interpolate_size = TRUE;
+vector final_dimensions = <1.8, 1.8, 1.8>;
+vector initial_dimensions = <0.72, 0.72, 0.72>;
+
+// transparency...
+//   1.0 means completely opaque and 0.0 means completely transparent.
+float initial_opacity = 1.0;
+float final_opacity = 0.3;
+
+// target following (or not)...
+integer follow_target_key = FALSE;
+key target_key = NULL_KEY;
+
+// speed and acceleration of particles...
+float minimum_velocity = 0.4;
+float maximum_velocity = 1.4;
+vector acceleration = <0.0, 0.4, 0.7>;
+integer wind_blown = TRUE;  // are particles affected by wind?
+
+// freaky effects for particles...
+integer bounce_off_z_height = FALSE;  // start at height of containing object.
+integer glowing = FALSE;  // particles will glow.
+integer source_centered = TRUE;  // particles start at object's center.
+integer point_toward_velocity = TRUE;  // rotate vertical axis toward velocity.
+
+string particle_texture = "";  // image used for the particle, if any.
+
+// when randomizing colors, these control how long a choice lasts.
+float minimum_hold_time = 5.0;
+float maximum_hold_time = 20.0;
+
+// timing (in seconds)...
+float lifespan_for_particle = 3.0;
+float lifespan_for_system = 0.0;  // 0 means unlimited.
+float creation_rate = 0.2;  // delay between particle creations.
+integer siblings = 7;  // how many to create at once.
+
+// how to exude particles.
+integer emission_pattern
+    = PSYS_SRC_PATTERN_ANGLE;  // 2D emission between given angles.
+    // = PSYS_SRC_PATTERN_DROP;  // just plop them at center of object.
+    // = PSYS_SRC_PATTERN_EXPLODE;  // spew them out as if exploded.
+    // = PSYS_SRC_PATTERN_ANGLE_CONE;  // 3D emission between given angles.
+    // = PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY;  // inverse of angle cone.
+
+// pattern details...
+float starting_angle = 1.0;  // initial angle where particles are emitted.
+float ending_angle = 0.0;  // bounding angle for emission.
+vector rotation_between_bursts = <0.2, 0.2, 0.2>;  // used in angle patterns.
+float burst_radius = 2.0;  // emission distance from source, unused for source centered.
+
+// add-in...  huffware script: notecard library, by fred huffhines
+
+string current_notecard_name;  // the name of the card we're reading now.
+key current_query_id;  // the query ID for the current notecard.
+list query_contents;  // the lines we have read from the notecard.
+integer line_number;  // which line are we at in notecard?
+
+initialize()
+{
+    current_notecard_name = "";
+    current_query_id = NULL_KEY;
+    query_contents = [];
+    line_number = 0;
+}
+
+string dump_list(list to_show)
+{
+    integer len = llGetListLength(to_show);
+    integer i;
+    string text;
+    for (i = len - 1; i >= 0; i--) {
+        text += llList2String(to_show, i) + "\n";
+    }
+    return text;
+}
+
+//////////////
+
+create_particle_system()
+{
+    if (randomize_colors) {
+        starting_color = <randomize_within_range(0.28, 0.95, FALSE),
+            randomize_within_range(0.28, 0.95, FALSE),
+            randomize_within_range(0.28, 0.95, FALSE)>;
+        ending_color = <randomize_within_range(0.14, 0.85, FALSE),
+            randomize_within_range(0.14, 0.85, FALSE),
+            randomize_within_range(0.14, 0.85, FALSE)>;
+    }
+    
+    list system_parameters;
+    integer flags_for_particle_effects = 0;
+
+    if (interpolate_colors)
+        flags_for_particle_effects = PSYS_PART_INTERP_COLOR_MASK | flags_for_particle_effects;
+    system_parameters += [ PSYS_PART_START_COLOR, starting_color,
+        PSYS_PART_END_COLOR, ending_color ];    
+
+    if (interpolate_size)
+        flags_for_particle_effects = PSYS_PART_INTERP_SCALE_MASK | flags_for_particle_effects;
+    system_parameters += [ PSYS_PART_START_SCALE, initial_dimensions,
+        PSYS_PART_END_SCALE, final_dimensions ];
+
+    system_parameters += [ PSYS_PART_START_ALPHA, initial_opacity,
+        PSYS_PART_END_ALPHA, final_opacity ];
+
+    if (follow_target_key && (target_key != NULL_KEY) ) {
+        flags_for_particle_effects = PSYS_PART_TARGET_POS_MASK | flags_for_particle_effects;
+        system_parameters += [ PSYS_SRC_TARGET_KEY, target_key ];
+    }
+
+    system_parameters += [ PSYS_SRC_BURST_SPEED_MIN, minimum_velocity,
+        PSYS_SRC_BURST_SPEED_MAX, maximum_velocity,
+        PSYS_SRC_ACCEL, acceleration ];
+    if (wind_blown)
+        flags_for_particle_effects = PSYS_PART_WIND_MASK | flags_for_particle_effects;
+
+    if (particle_texture != "")
+        system_parameters += [ PSYS_SRC_TEXTURE, particle_texture ];
+
+    if (emission_pattern != 0)
+        system_parameters += [ PSYS_SRC_PATTERN, emission_pattern ];
+
+    system_parameters += [ PSYS_PART_MAX_AGE, lifespan_for_particle,
+        PSYS_SRC_MAX_AGE, lifespan_for_system,
+        PSYS_SRC_BURST_PART_COUNT, siblings,
+        PSYS_SRC_BURST_RATE, creation_rate ];
+
+//hmmm: only add if used?
+    system_parameters += [ PSYS_SRC_ANGLE_BEGIN, starting_angle,
+        PSYS_SRC_ANGLE_END, ending_angle,
+        PSYS_SRC_OMEGA, rotation_between_bursts ];
+        
+    // assorted effects...
+    if (bounce_off_z_height)
+        flags_for_particle_effects = PSYS_PART_BOUNCE_MASK | flags_for_particle_effects;
+    if (glowing)
+        flags_for_particle_effects = PSYS_PART_EMISSIVE_MASK | flags_for_particle_effects;
+    if (point_toward_velocity)
+        flags_for_particle_effects = PSYS_PART_FOLLOW_VELOCITY_MASK | flags_for_particle_effects;
+    if (source_centered)
+        flags_for_particle_effects = PSYS_PART_FOLLOW_SRC_MASK | flags_for_particle_effects;
+    else
+        system_parameters += [ PSYS_SRC_BURST_RADIUS, burst_radius ];  // okay to use.
+
+    // now that we're done accumulating the flags, we can add them to our list.
+    system_parameters += [ PSYS_PART_FLAGS, flags_for_particle_effects ];  // must be last.
+
+    // and finally, we are ready to create the particle system of our dreams...    
+    llParticleSystem(system_parameters);
+}
+
+// returns a non-empty string if "to_check" defines contents for "variable_name".
+string defines_variable(string to_check, string variable_name)
+{
+    // clean initial spaces.
+    while (llGetSubString(to_check, 0, 0) == " ")
+        to_check = llDeleteSubString(to_check, 0, 0);
+    if (!is_prefix(to_check, variable_name)) return "";
+    to_check = llDeleteSubString(to_check, 0, llStringLength(variable_name) - 1);
+    // clean any spaces or valid assignment characters.
+    while ( (llGetSubString(to_check, 0, 0) == " ")
+            || (llGetSubString(to_check, 0, 0) == "=")
+            || (llGetSubString(to_check, 0, 0) == ",") )
+        to_check = llDeleteSubString(to_check, 0, 0);
+    if (debug)
+        log_it("set " + variable_name + " = " + to_check);    
+    // return what's left of the string.
+    return to_check;
+}
+
+parse_variable_definition(string to_parse)
+{
+    string content;  // filled after finding a variable name.
+    string texture_name;  // temporary used in reading texture name.
+    
+    if ( (content = defines_variable(to_parse, "debug")) != "")
+        debug = (integer)content;
+    else if ( (content = defines_variable(to_parse, "interpolate_colors")) != "")
+        interpolate_colors = (integer)content;
+    else if ( (content = defines_variable(to_parse, "randomize_colors")) != "")
+        randomize_colors = (integer)content;
+    else if ( (content = defines_variable(to_parse, "starting_color")) != "")
+        starting_color = (vector)content;
+    else if ( (content = defines_variable(to_parse, "ending_color")) != "")
+        ending_color = (vector)content;
+    else if ( (content = defines_variable(to_parse, "interpolate_size")) != "")
+        interpolate_size = (integer)content;
+    else if ( (content = defines_variable(to_parse, "initial_dimensions")) != "")
+        initial_dimensions = (vector)content;
+    else if ( (content = defines_variable(to_parse, "final_dimensions")) != "")
+        final_dimensions = (vector)content;
+    else if ( (content = defines_variable(to_parse, "initial_opacity")) != "")
+        initial_opacity = (float)content;
+    else if ( (content = defines_variable(to_parse, "final_opacity")) != "")
+        final_opacity = (float)content;
+    else if ( (content = defines_variable(to_parse, "follow_target_key")) != "")
+        follow_target_key = (integer)content;
+    else if ( (content = defines_variable(to_parse, "target_key")) != "")
+        target_key = (string)content;
+    else if ( (content = defines_variable(to_parse, "minimum_velocity")) != "")
+        minimum_velocity = (float)content;
+    else if ( (content = defines_variable(to_parse, "maximum_velocity")) != "")
+        maximum_velocity = (float)content;
+    else if ( (content = defines_variable(to_parse, "wind_blown")) != "")
+        wind_blown = (integer)content;
+    else if ( (content = defines_variable(to_parse, "acceleration")) != "")
+        acceleration = (vector)content;
+    else if ( (content = defines_variable(to_parse, "bounce_off_z_height")) != "")
+        bounce_off_z_height = (integer)content;
+    else if ( (content = defines_variable(to_parse, "glowing")) != "")
+        glowing = (integer)content;
+    else if ( (content = defines_variable(to_parse, "source_centered")) != "")
+        source_centered = (integer)content;
+    else if ( (content = defines_variable(to_parse, "point_toward_velocity")) != "")
+        point_toward_velocity = (integer)content;
+    else if ( (content = defines_variable(to_parse, "particle_texture")) != "")
+        particle_texture = (string)content;
+    else if ( (content = defines_variable(to_parse, "lifespan_for_particle")) != "")
+        lifespan_for_particle = (float)content;
+    else if ( (content = defines_variable(to_parse, "lifespan_for_system")) != "")
+        lifespan_for_system = (float)content;
+    else if ( (content = defines_variable(to_parse, "creation_rate")) != "")
+        creation_rate = (float)content;
+    else if ( (content = defines_variable(to_parse, "siblings")) != "")
+        siblings = (integer)content;
+    else if ( (content = defines_variable(to_parse, "minimum_hold_time")) != "")
+        minimum_hold_time = (float)content;
+    else if ( (content = defines_variable(to_parse, "maximum_hold_time")) != "")
+        maximum_hold_time = (float)content;
+    else if ( (content = defines_variable(to_parse, "emission_pattern")) != "") {
+        texture_name = (string)content;
+        // translate the short hand name into an emission_pattern value.
+        if (texture_name == "ANGLE") emission_pattern = PSYS_SRC_PATTERN_ANGLE;
+        else if (texture_name == "DROP") emission_pattern = PSYS_SRC_PATTERN_DROP;
+        else if (texture_name == "EXPLODE") emission_pattern = PSYS_SRC_PATTERN_EXPLODE;
+        else if (texture_name == "ANGLE_CONE") emission_pattern = PSYS_SRC_PATTERN_ANGLE_CONE;
+        else if (texture_name == "ANGLE_CONE_EMPTY")
+            emission_pattern = PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY;
+//log_it("emission pattern is now " + (string)emission_pattern);
+    }
+    else if ( (content = defines_variable(to_parse, "starting_angle")) != "")
+        starting_angle = (float)content;
+    else if ( (content = defines_variable(to_parse, "ending_angle")) != "")
+        ending_angle = (float)content;
+    else if ( (content = defines_variable(to_parse, "rotation_between_bursts")) != "") {
+        if (content == "random")
+            rotation_between_bursts = <randomize_within_range(MIN_ROTATION, MAX_ROTATION, TRUE),
+                randomize_within_range(MIN_ROTATION, MAX_ROTATION, TRUE),
+                randomize_within_range(MIN_ROTATION, MAX_ROTATION, TRUE)>;
+        else
+            rotation_between_bursts = (vector)content;
+    } else if ( (content = defines_variable(to_parse, "burst_radius")) != "")
+        burst_radius = (float)content;
+    else if ( (content = defines_variable(to_parse, "running")) != "")
+        start_enabled = (integer)content;
+
+    // special cases for key to follow...
+    if (target_key == "owner") target_key = llGetOwner();
+    else if (target_key == "self") target_key = llGetKey();
+    // special cases for texture.
+    if (particle_texture == "inventory")
+        particle_texture = llGetInventoryName(INVENTORY_TEXTURE, 0);
+
+}
+
+process_particle_settings(list particle_definitions)
+{
+    integer current_item = 0;
+    integer max_items = llGetListLength(particle_definitions);
+    while (current_item < max_items) {
+        string curr_line = llList2String(particle_definitions, current_item);
+        parse_variable_definition(curr_line);
+        current_item++;
+    }
+}
+
+randomize_timer()
+{
+    if (randomize_colors) {
+        llSetTimerEvent(randomize_within_range(maximum_hold_time,
+            minimum_hold_time, FALSE));
+    }
+}
+
+stop_timer() { llSetTimerEvent(0.0); }
+
+check_for_notecard()
+{
+    if (current_notecard_name != "") return;
+    // if there's a notecard, then we will start reading it.
+    if (llGetInventoryNumber(INVENTORY_NOTECARD) > 0) {
+        current_notecard_name = llGetInventoryName(INVENTORY_NOTECARD, 0);
+        line_number = 0;
+        query_contents = [];
+        current_query_id = llGetNotecardLine(current_notecard_name, 0);
+        llSetTimerEvent(80);
+    }
+}
+
+// this should be invoked from the link_message event handler to process the requests
+// for whatever service this library script provides.
+handle_link_message(integer sender, integer huff_id, string msg, key id)
+{
+    if (huff_id != PARTYCULIAR_HUFFWARE_ID) {
+        return;
+    }
+//llOwnerSay("link msg: " + (string)sender + " " + (string)huff_id + " msg=" + msg + " id=" + (string)id);
+    if (msg == PARTYCULIAR_POWER_COMMAND) {
+        string cmd = id;
+        if ( (find_substring(id, "on") == 0)
+            || (find_substring(id, "1") == 0)
+            || (find_substring(id, "true") == 0) ) {
+            // they want to crank the particle system up.
+            create_particle_system();
+            randomize_timer();
+        } else if ( (find_substring(id, "off") == 0)
+            || (find_substring(id, "0") == 0)
+            || (find_substring(id, "false") == 0) ) {
+            // the request is to shut the party down now.
+            llParticleSystem([]);
+            stop_timer();
+        }
+    }
+}
+
+//////////////
+// from hufflets...
+
+//////////////
+
+integer debug_num = 0;
+
+// a debugging output method.  can be disabled entirely in one place.
+log_it(string to_say)
+{
+    debug_num++;
+    // tell this to the owner.    
+    llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on open chat, but use an unusual channel.
+//    llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+}
+
+//////////////
+
+// joins a list of parameters using the parameter sentinel for the library.
+string wrap_parameters(list to_flatten)
+{ return llDumpList2String(to_flatten, HUFFWARE_PARM_SEPARATOR); }
+
+// handles when blank strings need to come through the pipe.
+string wrap_blank_string(string to_wrap)
+{
+    if (llStringLength(to_wrap)) return to_wrap;  // that one is okay.
+    return "\"\"";  // return a quoted nothing as a signal for a blank.
+}
+
+// undoes a previously wrapped blank string.
+string interpret_blank_string(string to_unwrap)
+{
+    if (to_unwrap == "\"\"") return "";  // that was an encoded blank.
+    return to_unwrap;  // no encoding.
+}
+
+// a simple version of a reply for a command that has been executed.  the parameters
+// might contain an outcome or result of the operation that was requested.
+send_reply(integer destination, list parms, string command)
+{
+    llMessageLinked(destination, PARTYCULIAR_HUFFWARE_ID, command,
+        llDumpList2String(parms, HUFFWARE_PARM_SEPARATOR));
+}
+
+//////////////
+
+// returns a number at most maximum and at least minimum.
+// if "allow_negative" is TRUE, then the return may be positive or negative.
+float randomize_within_range(float minimum, float maximum, integer allow_negative)
+{
+    float to_return = minimum + llFrand(maximum - minimum);
+    if (allow_negative) {
+        if (llFrand(1.0) < 0.5) to_return *= -1.0;
+    }
+    return to_return;
+}
+
+// the string processing methods are not case sensitive.
+  
+// returns TRUE if the "pattern" is found in the "full_string".
+integer matches_substring(string full_string, string pattern)
+{ return (find_substring(full_string, pattern) >= 0); }
+
+// returns the index of the first occurrence of "pattern" inside
+// the "full_string".  if it is not found, then a negative number is returned.
+integer find_substring(string full_string, string pattern)
+{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); }
+
+// returns TRUE if the "prefix" string is the first part of "compare_with".
+integer is_prefix(string compare_with, string prefix)
+{ return find_substring(compare_with, prefix) == 0; }
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.5.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+//log_it("invpo=" + (string)posn);
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+//log_it("pos=" + (string)space_v_posn);
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+//log_it("space v@" + (string)space_v_posn);
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+//log_it("indy=" + (string)space_v_posn);
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+//log_it("saw case of previously redundant item, aieee.  flattened: " + to_chop_up);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+default {
+    state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry()
+    {
+        auto_retire();
+        initialize();
+        if (start_enabled) {
+            create_particle_system();
+            randomize_timer();
+        } else {
+            llParticleSystem([]);
+            stop_timer();
+        }
+        check_for_notecard();
+    }
+    
+    timer() {
+        if (current_query_id != NULL_KEY) {
+            // there's been a failure of some sort; we were supposed to get a notecard to read.
+            llWhisper(0, "Failed to read the notecard, now restarting.");
+            llResetScript();
+        }
+        create_particle_system();
+        randomize_timer();
+    }
+    
+    on_rez(integer parm) {
+        target_key = llGetKey();  // reset to get rid of weird wrong keys.
+        llResetScript();
+    }
+
+    link_message(integer sender, integer num, string msg, key id) {
+        handle_link_message(sender, num, msg, id);
+    }
+
+    changed(integer change_type) {
+        if (change_type != CHANGED_INVENTORY) {
+            // we only care about inventory changes here.
+            return;
+        }
+        llResetScript();
+    }
+    
+    dataserver(key query_id, string data) {
+        if (query_id != current_query_id) {
+log_it("not our query id somehow?  weird query had: id=" + (string)query_id + " data=" + (string)data);
+            // to heck with all this weirdness; if there's a failure, start over.
+            llResetScript();
+        }
+        // if we're not at the end of the notecard we're reading...
+        if (data != EOF) {
+            if (!line_number) {
+                if (data != "#party culiar") {
+                    // this card has the wrong signature at the top.  quit bothering
+                    // with it now.
+                    return;
+                }
+                log_it("starting to read notecard " + current_notecard_name + "...");    
+            }
+            if (data != "") {
+                 // add the non-blank line to our destination list.
+                query_contents += data;
+//log_it("line " + (string)line_number + ": data=" + data);
+            }
+            line_number++;  // increase the line count.
+            // request the next line from the notecard.
+            current_query_id = llGetNotecardLine(current_notecard_name, line_number);
+        } else {
+            // no more data, so we're done with this card.
+            current_query_id = NULL_KEY;
+            if (!llGetListLength(query_contents)) {
+                // nothing was read?  the heck with this card.
+                current_notecard_name = "";  // toss bad card.
+                return;
+            }
+//log_it("notecard said:\n" + dump_list(query_contents));
+            process_particle_settings(query_contents);
+            if (start_enabled) {
+                create_particle_system();
+                randomize_timer();
+            } else {
+                llParticleSystem([]);
+                stop_timer();
+            }
+            log_it("done reading notecard " + current_notecard_name + ".");
+        }
+    }
+}
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/rotanium_rotato_v2.7.txt b/huffware/huffotronic_updater_freebie_v5.3/rotanium_rotato_v2.7.txt
new file mode 100755 (executable)
index 0000000..99ff84c
--- /dev/null
@@ -0,0 +1,258 @@
+
+// huffware script: rotanium rotato, by fred huffhines.
+//
+// causes the object to rotate according to the parameters set below.
+// this can use herky-jerky timed rotation with llSetRot or it can use
+// smooth rotation with llTargetOmega.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+integer RANDOMIZE_ROTATION = TRUE;
+
+integer SMOOTH_ROTATION = TRUE;
+    // if this is true, then the object will rotate smoothly rather than
+    // being rotated by the timer.
+
+float SMOOTH_TIMER_FREQUENCY = 14.0;
+    // the smooth rotater doesn't need to hit the timer all that often.
+
+float SMOOTH_CHANCE_FOR_ADJUSTING = 0.8;
+    // we won't always change the smooth rotation, even though our timer
+    // is pretty slow.
+
+float SMOOTH_ROTATION_GAIN_MAX = 0.0490873852122;
+    // the gain is how fast we will rotate in radians per second.
+    // PI / 2 is about 90 degrees per second, which seems way too fast.
+    // 0.196349540849 is about PI / 16, 0.0981747704244 is about PI / 32,
+    // and 0.0490873852122 is about PI / 64.
+
+float JERKY_TIMER_FREQUENCY = 0.50;
+    // this is the fastest that llSetRot rotation can happen anyhow,
+    // so we fire the timer at this rate.
+    
+float JERKY_CHANCE_FOR_ADJUSTING = 0.1;
+    // this is the probability of changing the current direction.
+
+vector current_add_in = <0.0, 0.0, 0.4>;
+    // randomly assigned to if RANDOMIZE_ROTATION is true.
+
+float current_gain = -0.05;
+    // speed of smooth rotation; will randomly change if RANDOMIZE_ROTATION is true.
+
+float MIN_ADDITION = 0.01;
+    // smallest amount of change we will ever have.
+float MAX_ADDITION = 7.0;
+    // largest amount of change we will ever have.
+
+// sets the gain and add in to random choices.
+randomize_values()
+{
+    current_gain = randomize_within_range(0.001, SMOOTH_ROTATION_GAIN_MAX, TRUE);
+    current_add_in = random_vector(MIN_ADDITION, MAX_ADDITION, TRUE);
+}
+
+// performs the timed rotation that has been configured for us.
+rotate_as_requested()
+{
+    if (SMOOTH_ROTATION) {
+        // our slack timer went off, so randomize the rotation if requested.
+        if (RANDOMIZE_ROTATION && (llFrand(1.0) >= SMOOTH_CHANCE_FOR_ADJUSTING) )
+            randomize_values();
+        // make sure we are using the rotational values we were asked to.
+        llTargetOmega(current_add_in, current_gain, 1.0);
+    } else {
+        // herky jerky rotation.
+    
+//hmmm: seeing that GetRot or GetLocalRot might be useful at different times.
+        rotation curr_rot = llGetLocalRot();  // get our current state.
+        vector euler_curr = llRot2Euler(curr_rot);  // turn into euler coords.
+        euler_curr *= RAD_TO_DEG;  // convert to degrees.
+        vector new_rot = euler_curr + current_add_in;  // add our adjustment in.
+        new_rot *= DEG_TO_RAD; // convert to radians.
+        rotation quat = llEuler2Rot(new_rot); // convert to quaternion
+        llSetLocalRot(quat);  // rotate the object
+//will the local work for a single prim?
+//in the current case, we do want just the local rot to change.
+    
+        // change the rotation add-in if the mood strikes us.
+        float starter = 0.420;  // where we start looking for change.
+        float change_cap = starter + JERKY_CHANCE_FOR_ADJUSTING;
+        float randomness = llFrand(1.000);
+        if ( (randomness <= change_cap) && (randomness >= starter) ) {
+            // time for a change in the rotation.
+            if (RANDOMIZE_ROTATION)
+                randomize_values();
+        }
+    }
+}
+
+//////////////
+// start of hufflets...
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.5.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+//log_it("invpo=" + (string)posn);
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+//log_it("pos=" + (string)space_v_posn);
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+//log_it("space v@" + (string)space_v_posn);
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+//log_it("indy=" + (string)space_v_posn);
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+//log_it("saw case of previously redundant item, aieee.  flattened: " + to_chop_up);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+// returns a number at most "maximum" and at least "minimum".
+// if "allow_negative" is TRUE, then the return may be positive or negative.
+float randomize_within_range(float minimum, float maximum, integer allow_negative)
+{
+    if (minimum > maximum) {
+        // flip the two if they are reversed.
+        float temp = minimum; minimum = maximum; maximum = temp;
+    }
+    float to_return = minimum + llFrand(maximum - minimum);
+    if (allow_negative) {
+        if (llFrand(1.0) < 0.5) to_return *= -1.0;
+    }
+    return to_return;
+}
+
+// returns a random vector where x,y,z will be between "minimums" and "maximums"
+// x,y,z components.  if "allow_negative" is true, then any component will
+// randomly be negative or positive.
+vector random_bound_vector(vector minimums, vector maximums, integer allow_negative)
+{
+    return <randomize_within_range(minimums.x, maximums.x, allow_negative),
+        randomize_within_range(minimums.y, maximums.y, allow_negative),
+        randomize_within_range(minimums.z, maximums.z, allow_negative)>;
+}
+
+// returns a vector whose components are between minimum and maximum.
+// if allow_negative is true, then they can be either positive or negative.
+vector random_vector(float minimum, float maximum, integer allow_negative)
+{
+    return random_bound_vector(<minimum, minimum, minimum>,
+        <maximum, maximum, maximum>, allow_negative);
+}
+
+// end hufflets
+//////////////
+
+default
+{
+    state_entry() { if (llSubStringIndex(llGetObjectName(),  "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry()
+    {
+        auto_retire();
+        // if needed, we will set our initial random rotation.
+        if (RANDOMIZE_ROTATION) randomize_values();
+        // do a first rotate, so we move right at startup.  otherwise we won't move
+        // until after our first timer hits.
+        rotate_as_requested();
+        // now set the timer for our mode.
+        if (SMOOTH_ROTATION) {
+            llSetTimerEvent(SMOOTH_TIMER_FREQUENCY);
+        } else {
+            llSetTimerEvent(JERKY_TIMER_FREQUENCY);
+        }
+    }
+
+    timer() { rotate_as_requested(); }
+}
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/take_touches_v0.2.txt b/huffware/huffotronic_updater_freebie_v5.3/take_touches_v0.2.txt
new file mode 100755 (executable)
index 0000000..62af88d
--- /dev/null
@@ -0,0 +1,19 @@
+
+// huffware script: take touches, by fred huffhines.
+//
+// eats any touches from an avatar so that other prims do not know they happened.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+default
+{
+    state_entry() { }
+
+    touch_start(integer total_number)
+    {
+        // do nothing.
+    }
+}
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/text_label_v3.9.txt b/huffware/huffotronic_updater_freebie_v5.3/text_label_v3.9.txt
new file mode 100755 (executable)
index 0000000..0fddf07
--- /dev/null
@@ -0,0 +1,170 @@
+
+// huffware script: text label, by fred huffhines
+//
+// a super simple script for giving an object readable text.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+string OBJECT_LABEL = "default";  // change this if you want a specific name.
+
+vector LABEL_COLOR = <0.3, 0.9, 0.4>;  // color of the text above object.
+
+set_text()
+{
+    string object_label = OBJECT_LABEL;
+    // reset the label to a decorated version of object name if it was default.
+    if (object_label == "default") object_label = llGetObjectName();
+    integer indy;
+    integer keep_going = TRUE;
+    while (keep_going) {
+        indy = find_substring(object_label, "\\n");
+        if (indy < 0) {
+            keep_going = FALSE;
+        } else {
+            object_label = llGetSubString(object_label, 0, indy - 1)
+                + "\n" + llGetSubString(object_label, indy + 2, -1);
+        }
+    }
+//log_it("setting text: " + object_label);
+    llSetText(object_label, LABEL_COLOR, 1.0);
+}
+
+//////////////
+// from hufflets...
+
+integer debug_num = 0;
+
+// a debugging output method.  can be disabled entirely in one place.
+log_it(string to_say)
+{
+    debug_num++;
+    // tell this to the owner.    
+    llOwnerSay(llGetDate() + ": " + llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+//llWhisper(0, llGetDate() + ": " + llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on an unusual channel for chat if it's not intended for general public.
+//    llSay(108, llGetDate() + ": " + llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on open chat that anyone can hear.  we take off the bling for this one.
+//    llSay(0, to_say);
+}
+
+// returns the index of the first occurrence of "pattern" inside
+// the "full_string".  if it is not found, then a negative number is returned.
+integer find_substring(string full_string, string pattern)
+{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); }
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.8.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+//end hufflets...
+//////////////
+
+default
+{
+    state_entry() { if (llSubStringIndex(llGetObjectName(),  "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry() { auto_retire(); set_text(); }
+    on_rez(integer start_parm) { state default; }
+    touch_start(integer count) { set_text(); }
+    changed(integer change) { if (change & CHANGED_INVENTORY) set_text(); }
+}
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/texture_mover_v3.0.txt b/huffware/huffotronic_updater_freebie_v5.3/texture_mover_v3.0.txt
new file mode 100755 (executable)
index 0000000..57a1e64
--- /dev/null
@@ -0,0 +1,237 @@
+
+// huffware script: texture mover, by fred huffhines
+//
+// moves a texture across the object, either by smooth animation or brute force offsetting.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+// constants...
+
+integer USE_TEXTURE_ANIMATION = TRUE;
+    // by default, we use texture animation which is a lower-lag way to
+    // make the texture move.  if this is false, then we use a timed update
+    // to move the texture instead.
+    // note: these modes are actually really different.  the texture animation
+    // is included here because i wanted to smooth out some objects that used
+    // texture movement.
+
+// used in both types...
+
+float TIMER_INTERVAL = 0.2;  // how fast do we move texture?
+
+// for timed movement...
+
+vector OFFSET_MOVEMENT = ZERO_VECTOR;  // no movement.
+//vector OFFSET_MOVEMENT = <0.0, -0.02, 0.0>;
+    // how much to move the texture by in x,y,z directions.
+
+//float OFFSET_ROTATION = 0.0;  // in degrees.
+float OFFSET_ROTATION = -1.0;  // in degrees.
+
+// for texture animations...
+
+float MOVER_TIMER_DIVISOR = 30.0;
+    // makes the movement comparable to timed method.
+
+float ROTATER_TIMER_DIVISOR = 7.0;
+    // makes the rotation comparable to timed method.
+
+// iterative movement of texture.
+move_texture(vector offset)
+{
+    vector current = llGetTextureOffset(ALL_SIDES);
+    current += offset;
+    if (current.x > 1.0) current.x = -1.0;
+    if (current.x < -1.0) current.x = 1.0;
+    if (current.y > 1.0) current.y = -1.0;
+    if (current.y < -1.0) current.y = 1.0;
+    llOffsetTexture(current.x, current.y, ALL_SIDES);
+}
+
+// iterative rotation of texture.  "spin" is measured in degrees.
+spin_texture(float spin)
+{
+    float rot = llGetTextureRot(ALL_SIDES);  // get our current state.
+    rot += spin * DEG_TO_RAD;  // add some rotation.
+    llRotateTexture(rot, ALL_SIDES); //rotate the object   
+}
+
+initialize_texture_mover()
+{
+    if (!USE_TEXTURE_ANIMATION) {
+        // we're stuck with the timed update style for movement.
+        llSetTimerEvent(TIMER_INTERVAL);
+        // turn off previous animation.
+        llSetTextureAnim(0, ALL_SIDES, 0, 0, 0, 0, 0);
+    } else {
+        // we can just set the texture movement here and be done with it.
+        integer x_frames = 1;
+        integer y_frames = 1;
+        llSetTimerEvent(0);  // we don't use a timer.
+
+//hmmm: how do we combine rotation and offsets?  currently mutually exclusive.
+
+        if (OFFSET_MOVEMENT != ZERO_VECTOR) {
+            float timer_interval = TIMER_INTERVAL / MOVER_TIMER_DIVISOR;
+log_it("getting x_frames=" + (string)x_frames
++ " y_frames=" + (string)y_frames
++ " timer_intvl=" + (string)timer_interval);
+
+            llSetTextureAnim(ANIM_ON | LOOP | SMOOTH, ALL_SIDES,
+                x_frames, y_frames,
+                0, 100, timer_interval);
+        } else if (OFFSET_ROTATION != 0.0) {
+            float timer_interval = TIMER_INTERVAL / ROTATER_TIMER_DIVISOR;
+            // we're actually not using the rotation at all here, except for
+            // the sign.  that seems pretty busted.
+            if (OFFSET_ROTATION < 0) timer_interval *= -1.0;
+            llSetTextureAnim(ANIM_ON | LOOP | SMOOTH | ROTATE, ALL_SIDES,
+                0, 0,
+                0, TWO_PI, timer_interval);
+        }
+    }
+}
+
+//////////////
+// from hufflets...
+
+integer debug_num = 0;
+
+// a debugging output method.  can be disabled entirely in one place.
+log_it(string to_say)
+{
+    debug_num++;
+    // tell this to the owner.    
+    llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on an unusual channel for chat if it's not intended for general public.
+//    llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on open chat that anyone can hear.  we take off the bling for this one.
+//    llSay(0, to_say);
+}
+//
+//////////////
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.5.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+//log_it("invpo=" + (string)posn);
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+//log_it("pos=" + (string)space_v_posn);
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+//log_it("space v@" + (string)space_v_posn);
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+//log_it("indy=" + (string)space_v_posn);
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+//log_it("saw case of previously redundant item, aieee.  flattened: " + to_chop_up);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+default {
+    state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry() {
+        auto_retire();
+        initialize_texture_mover();
+    }
+
+    timer() {
+        // the timed approach is the only one so far that allows both a
+        // movement of the texture and a rotation.
+        if (OFFSET_MOVEMENT != ZERO_VECTOR) move_texture(OFFSET_MOVEMENT);
+        if (OFFSET_ROTATION != 0.0) spin_texture(OFFSET_ROTATION);
+    }
+}
+
diff --git a/huffware/huffotronic_updater_freebie_v5.3/texture_shower_v2.7.txt b/huffware/huffotronic_updater_freebie_v5.3/texture_shower_v2.7.txt
new file mode 100755 (executable)
index 0000000..629dd58
--- /dev/null
@@ -0,0 +1,480 @@
+
+// huffware script: texture shower, by fred huffhines.
+//
+// displays a texture from its inventory when the name is spoken in chat.
+//
+// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+// do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+
+// global constants...
+
+string SHOWER_SIGNATURE = "#texture_shower";
+    // the notecard must begin with this as its first line for it to be
+    // recognized as our configuration card.
+
+// global variables...
+
+string global_notecard_name;  // name of our notecard in the object's inventory.
+integer response_code;  // set to uniquely identify the notecard read in progress.
+list global_config_list;  // a collection of configuration parameters from our notecard.
+integer global_config_index;  // allows wrap-around feature, which we don't use here.
+
+list texture_names;  // the set of textures we've got in inventory.
+
+integer LISTENING_CHANNEL = 1008;
+    // the default listening channel is hardly ever used; the channel should
+    // come from the notecard.
+
+//////////////
+
+// processes the variables that come from the notecard.
+parse_variable_definition(string to_parse)
+{
+    string content;  // filled after finding a variable name.
+    if ( (content = get_variable_value(to_parse, "channel")) != "") {
+//        log_it("channel=" + content);
+        LISTENING_CHANNEL = (integer)content;
+    }
+}
+
+//////////////
+
+// sets the viewing sides' textures to a particular inventory item.
+set_texture_to_item(string inv_tex)
+{
+    llSetTexture(inv_tex, 1);
+    llSetTexture(inv_tex, 3);
+}
+
+// sets our viewing sides back to the default texture.
+reset_textures()
+{
+    string tex_cur = llGetTexture(0);
+    set_texture_to_item(tex_cur);
+}
+
+// startup for the very early part of the object's lifetime.
+initialize_phase_1()
+{
+    // reset our relevant variables.
+    global_notecard_name = "";
+    global_config_list = [];
+    global_config_index = 0;
+    texture_names = [];
+
+    // request that the noteworthy library start looking for our notecard.
+    request_configuration();
+
+//    log_it("started, free mem=" + (string)llGetFreeMemory());
+}
+
+// this gets us ready to enter our active state.
+initialize_phase_2()
+{
+    // listen for commands on the channel we're configured for.
+    llListen(LISTENING_CHANNEL, "", NULL_KEY, "");
+    // load all the texture names we know about.    
+    integer texture_count = llGetInventoryNumber(INVENTORY_TEXTURE);
+    integer indy;
+    for (indy = 0; indy < texture_count; indy++)
+        texture_names += llGetInventoryName(INVENTORY_TEXTURE, indy);
+}
+
+manage_those_voices(integer channel, string message)
+{
+    if (channel != LISTENING_CHANNEL) return;  // not for us.
+    if (message == "reset-texture") {
+        reset_textures();
+        return;
+    }
+    // wasn't a command to reset, so let's see if we know it.
+    integer texture_count = llGetListLength(texture_names);
+    integer indy;
+    for (indy = 0; indy < texture_count; indy++) {
+        integer posn = llListFindList(texture_names, (message));
+        if (posn >= 0) {
+            // found one...
+            set_texture_to_item(message);
+            return;
+        }
+    }    
+}
+
+//////////////
+
+// this chunk largely comes from the example noteworthy usage...
+
+// requires noteworthy library v8.4 or better.
+//////////////
+// do not redefine these constants.
+integer NOTEWORTHY_HUFFWARE_ID = 10010;
+    // the unique id within the huffware system for the noteworthy script to
+    // accept commands on.  this is used in llMessageLinked as the num parameter.
+string HUFFWARE_PARM_SEPARATOR = "{~~~}";
+    // this pattern is an uncommon thing to see in text, so we use it to separate
+    // our commands in link messages.
+string HUFFWARE_ITEM_SEPARATOR = "{|||}";
+    // used to separate lists of items from each other when stored inside a parameter.
+    // this allows lists to be passed as single string parameters if needed.
+integer REPLY_DISTANCE = 100008;  // offset added to service's huffware id in reply IDs.
+// commands available via the noteworthy library:
+string READ_NOTECARD_COMMAND = "#read_note#";
+    // command used to tell the script to read notecards.  needs a signature to find
+    // in the card as the first parameter, and a randomly generated response code for
+    // the second parameter.  the response code is used to uniquely identify a set of
+    // pending notecard readings (hopefully).  the signature can be empty or missing.
+    // the results will be fired back as the string value returned, which will have
+    // as first element the notecard's name (or "bad_notecard" if none was found) and
+    // as subsequent elements an embedded list that was read from the notecard.  this
+    // necessarily limits the size of the notecards that we can read and return.
+//
+//////////////
+// joins a list of parameters using the parameter sentinel for the library.
+string wrap_parameters(list to_flatten)
+{ return llDumpList2String(to_flatten, HUFFWARE_PARM_SEPARATOR); }
+//////////////
+
+// this function fires off a request to the noteworthy library via a link message.
+// noteworthy will look for a notecard with our particular signature in it and
+// if it finds one, it will read the configuration therein.  an empty string is
+// returned if noteworthy couldn't find anything.
+request_configuration()
+{
+    global_notecard_name = "";  // reset any previous card.
+    // try to find a notecard with our configuration.
+    response_code = -1 * (integer)randomize_within_range(23, 80000, FALSE);
+    string parms_sent = wrap_parameters([SHOWER_SIGNATURE, response_code]);
+    llMessageLinked(LINK_THIS, NOTEWORTHY_HUFFWARE_ID, READ_NOTECARD_COMMAND,
+         parms_sent);
+}
+
+// processes link messages received from the noteworthy library and others.
+// if the message indicates we should change states, then TRUE is returned.
+integer handle_link_message(integer which, integer num, string msg, key id)
+{
+    if (num != NOTEWORTHY_HUFFWARE_ID + REPLY_DISTANCE)
+        return FALSE;  // not for us.
+    // process the result of reading the notecard.
+    list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
+    string notecard_name = llList2String(parms, 0);
+    integer response_for = llList2Integer(parms, 1);
+    if (response_for != response_code) return FALSE;  // oops, this isn't for us.
+    if (notecard_name != "bad_notecard") {
+        // a valid notecard has been found.
+        global_notecard_name = notecard_name;  // record its name for later use.
+        global_config_index = 0;  // we are starting over in the config list.
+        // snag all but the first two elements for our config now.
+        global_config_list = llList2List(parms, 2, -1);
+        // and process the file as a set of definitions.
+        process_ini_config();
+        return TRUE;
+    } else {
+        // we hated the notecards we found, or there were none.
+        log_it("We apologize; there seem to be no notecards with a first line of '"
+            + SHOWER_SIGNATURE
+            + "'.  We can't read any configuration until that situation improves.");
+    }
+    return FALSE;
+}
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.5.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+//log_it("invpo=" + (string)posn);
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+//log_it("pos=" + (string)space_v_posn);
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+//log_it("space v@" + (string)space_v_posn);
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+//log_it("indy=" + (string)space_v_posn);
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+//log_it("saw case of previously redundant item, aieee.  flattened: " + to_chop_up);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+//////////////
+// from hufflets...
+//
+
+integer debug_num = 0;
+
+// a debugging output method.  can be disabled entirely in one place.
+log_it(string to_say)
+{
+    debug_num++;
+    // tell this to the owner.    
+    llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on an unusual channel for chat if it's not intended for general public.
+//    llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
+    // say this on open chat that anyone can hear.  we take off the bling for this one.
+//    llSay(0, to_say);
+}
+
+// locates the string "text" in the list to "search_in".
+integer find_in_list(list search_in, string text)
+{ 
+    integer len = llGetListLength(search_in);
+    integer i; 
+    for (i = 0; i < len; i++) { 
+        if (llList2String(search_in, i) == text) 
+            return i; 
+    } 
+    return -1;
+}
+
+// returns TRUE if the "prefix" string is the first part of "compare_with".
+integer is_prefix(string compare_with, string prefix)
+{ return (llSubStringIndex(compare_with, prefix) == 0); }
+
+// returns a number at most "maximum" and at least "minimum".
+// if "allow_negative" is TRUE, then the return may be positive or negative.
+float randomize_within_range(float minimum, float maximum, integer allow_negative)
+{
+    if (minimum > maximum) {
+        // flip the two if they are reversed.
+        float temp = minimum; minimum = maximum; maximum = temp;
+    }
+    float to_return = minimum + llFrand(maximum - minimum);
+    if (allow_negative) {
+        if (llFrand(1.0) < 0.5) to_return *= -1.0;
+    }
+    return to_return;
+}
+
+// strips the spaces off of the beginning and end of a string.
+string strip_spaces(string to_strip)
+{
+    // clean out initial spaces.
+    while (llGetSubString(to_strip, 0, 0) == " ")
+        to_strip = llDeleteSubString(to_strip, 0, 0);
+    // clean out ending spaces.
+    while (llGetSubString(to_strip, -1, -1) == " ")
+        to_strip = llDeleteSubString(to_strip, -1, -1);
+    return to_strip;
+}
+
+// parses a variable definition to find the name of the variable and its value.
+// this returns two strings [X, Y], if "to_split" is in the form X=Y.
+list separate_variable_definition(string to_split)
+{
+    integer equals_indy = llSubStringIndex(to_split, "=");
+    // we don't support missing an equals sign, and we don't support it as the first character.
+    if (equals_indy <= 0) return [];  // no match.
+    string x = llGetSubString(to_split, 0, equals_indy - 1);
+    string y = llGetSubString(to_split, equals_indy + 1, -1);
+    to_split = "";  // save space.
+    return [ strip_spaces(x), strip_spaces(y) ];
+}
+
+// returns a non-empty string if "to_check" defines a value for "variable_name".
+// this must be in the form "X=Y", where X is the variable_name and Y is the value.
+string get_variable_value(string to_check, string variable_name)
+{
+    list x_y = separate_variable_definition(to_check);
+    if (llGetListLength(x_y) != 2) return "";  // failure to parse a variable def at all.
+    if (!is_prefix(llList2String(x_y, 0), variable_name)) return "";  // no match.
+    return llList2String(x_y, 1);  // a match!
+}
+
+// examines all entries that we got from the notecard to see if any contain definitions.
+// this is basically an INI file reader, but it uses a list instead of a file.
+// ini files provide a format with multiple sections of config information, like so:
+//    [section_1]
+//    name1=value1
+//    name2=value2 ...etc...
+//    [section_2]
+//    name1=value1 ...etc...
+process_ini_config()
+{
+//    log_it("scanning notecard for variable definitions...");
+    integer indy;
+    integer count = llGetListLength(global_config_list);
+
+    // iterate across the items in our configuration to look for ones that are not done yet.            
+    for (indy = global_config_index; indy < count; indy++) {
+        string line = llList2String(global_config_list, indy);
+        // search for a section beginning.
+        if (llGetSubString(line, 0, 0) == "[") {
+            // we found the start of a section name.  now read the contents.
+            indy++;  // skip section line.
+            log_it("reading section: " + llGetSubString(line, 1, -2));
+        }
+        integer sec_indy;
+        for (sec_indy = indy; sec_indy < count; sec_indy++) {
+            // read the lines in the section.
+            line = llList2String(global_config_list, sec_indy);
+            if (llGetSubString(line, 0, 0) != "[") {
+                // try to interpret this line as a variable setting.  this is just
+                // one example of a way to handle the config file; one might instead
+                // want to do something below once a whole section is read.
+                parse_variable_definition(line);
+            } else {
+                // we're at the beginning of a new section now, so start processing its
+                // configuration in the outer loop.
+                indy = sec_indy - 1;  // set indy to proper beginning of section.
+                global_config_index = indy;  // remember where we had read to.
+                sec_indy = count + 3;  // skip remainder of inner loop.
+            }
+        }
+    }
+
+    global_config_index = 0;  // reset outer position if want to re-read.
+}
+
+// locates the item with "name_to_find" in the inventory items with the "type".
+// a value from 0 to N-1 is returned if it's found, where N is the number of
+// items in the inventory.
+integer find_in_inventory(string name_to_find, integer inv_type)
+{
+    integer num_inv = llGetInventoryNumber(inv_type);
+    if (num_inv == 0) return -1;  // nothing there!
+    integer inv;
+    for (inv = 0; inv < num_inv; inv++) {
+        if (llGetInventoryName(inv_type, inv) == name_to_find)
+            return inv;
+    }
+    return -2;  // failed to find it.
+}
+// end hufflets
+//////////////
+
+default {
+    state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry() {
+        auto_retire();  // make sure newest addition is only version of script.
+        initialize_phase_1();
+    }
+
+    on_rez(integer param) { state rerun; }
+    
+    // reset when we see changes to our notecard configuration.
+    changed(integer change) {
+        if (change & CHANGED_INVENTORY) {
+            llSleep(3.14159265358);  // delay to avoid interfering with upgrade.
+            state rerun;
+        }
+    }
+
+    // process the response from the noteworthy library.
+    link_message(integer which, integer num, string msg, key id) {
+        integer retval = handle_link_message(which, num, msg, id);
+        if (retval) state configured;  // skip states if we were asked to.
+    }
+}
+
+state configured
+{
+    state_entry() {
+        initialize_phase_2();
+    }
+    
+    listen(integer channel, string name, key id, string message) {
+        manage_those_voices(channel, message);
+    }
+
+    link_message(integer which, integer num, string msg, key id) {
+        manage_those_voices(num, msg);
+    }
+
+    // reset when we see changes to our notecard configuration.
+    changed(integer change) {
+        if (change & CHANGED_INVENTORY) {
+            llSleep(3.14159265358);  // delay to avoid interfering with upgrade.
+            state default;
+        }
+    }
+}
diff --git a/huffware/huffotronic_updater_freebie_v5.3/zenmondos_mailbox_v0.4.txt b/huffware/huffotronic_updater_freebie_v5.3/zenmondos_mailbox_v0.4.txt
new file mode 100755 (executable)
index 0000000..4e6eb83
--- /dev/null
@@ -0,0 +1,276 @@
+
+// huffware script: zen mondo's mailbox, modified by fred huffhines.
+//
+// original attributions are below.
+//
+// my changes are licensed via:
+//   this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
+//   do not use it in objects without fully realizing you are implicitly accepting that license.
+//
+
+//////////////////////////////////////
+// ZenMondo's Mailbox by ZenMondo Wormser
+//
+// Displays Online Status, as well as accepting
+// notecard "mail" to be be delivered to owner.
+//
+// Cleans Up after itself, deletes non-notecards.
+//
+//
+// LICENSE:
+//
+//  This script is given for free, and may NOT be 
+//  resold or used in a commercial product.
+//  You may copy and distribute this script for free.
+//  When you redistribute or copy this script, you must
+//  do so with FULL PERMS (modify, copy, and transfer),
+//  and leave this notice intact.
+//
+//  You are free to modify this code, but any derivitive
+//  scripts must not be used in a commercial product or
+//  or sold, and must contain this license.  
+//
+//  This script is provided free for learning 
+//  purposes, take it apart, break it, fix it, 
+//  learn something.  If you come up with something 
+//  clever, share it.
+//
+//  Questions about codepoetry (scripting) can always be addressed
+//  to me, ZenMondo Wormser.
+//
+///////////////////////////////////////// 
+
+key online_query;
+
+key name_query;
+
+string user_name;
+
+
+//////////////
+// huffware script: auto-retire, by fred huffhines, version 2.5.
+// distributed under BSD-like license.
+//   !!  keep in mind that this code must be *copied* into another
+//   !!  script that you wish to add auto-retirement capability to.
+// when a script has auto_retire in it, it can be dropped into an
+// object and the most recent version of the script will destroy
+// all older versions.
+//
+// the version numbers are embedded into the script names themselves.
+// the notation for versions uses a letter 'v', followed by two numbers
+// in the form "major.minor".
+// major and minor versions are implicitly considered as a floating point
+// number that increases with each newer version of the script.  thus,
+// "hazmap v0.1" might be the first script in the "hazmap" script continuum,
+// and "hazmap v3.2" is a more recent version.
+//
+// example usage of the auto-retirement script:
+//     default {
+//         state_entry() {
+//            auto_retire();  // make sure newest addition is only version of script.
+//        }
+//     }
+// this script is partly based on the self-upgrading scripts from markov brodsky
+// and jippen faddoul.
+//////////////
+auto_retire() {
+    string self = llGetScriptName();  // the name of this script.
+    list split = compute_basename_and_version(self);
+    if (llGetListLength(split) != 2) return;  // nothing to do for this script.
+    string basename = llList2String(split, 0);  // script name with no version attached.
+    string version_string = llList2String(split, 1);  // the version found.
+    integer posn;
+    // find any scripts that match the basename.  they are variants of this script.
+    for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
+        string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
+        if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
+            // found a basic match at least.
+            list inv_split = compute_basename_and_version(curr_script);
+            if (llGetListLength(inv_split) == 2) {
+                // see if this script is more ancient.
+                string inv_version_string = llList2String(inv_split, 1);  // the version found.
+                // must make sure that the retiring script is completely the identical basename;
+                // just matching in the front doesn't make it a relative.
+                if ( (llList2String(inv_split, 0) == basename)
+                    && ((float)inv_version_string < (float)version_string) ) {
+                    // remove script with same name from inventory that has inferior version.
+                    llRemoveInventory(curr_script);
+                }
+            }
+        }
+    }
+}
+//
+// separates the base script name and version number.  used by auto_retire.
+list compute_basename_and_version(string to_chop_up)
+{
+    // minimum script name is 2 characters plus a version.
+    integer space_v_posn;
+    // find the last useful space and 'v' combo.
+    for (space_v_posn = llStringLength(to_chop_up) - 3;
+        (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
+        space_v_posn--) {
+        // look for space and v but do nothing else.
+    }
+    if (space_v_posn < 2) return [];  // no space found.
+    // now we zoom through the stuff after our beloved v character and find any evil
+    // space characters, which are most likely from SL having found a duplicate item
+    // name and not so helpfully renamed it for us.
+    integer indy;
+    for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
+        if (llGetSubString(to_chop_up, indy, indy) == " ") {
+            // found one; zap it.  since we're going backwards we don't need to
+            // adjust the loop at all.
+            to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
+        }
+    }
+    string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
+    // ditch the space character for our numerical check.
+    string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
+    // strip out a 'v' if there is one.
+    if (llGetSubString(chop_suffix, 0, 0) == "v")
+        chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
+    // if valid floating point number and greater than zero, that works for our version.
+    string basename = to_chop_up;  // script name with no version attached.
+    if ((float)chop_suffix > 0.0) {
+        // this is a big success right here.
+        basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
+        return [ basename, chop_suffix ];
+    }
+    // seems like we found nothing useful.
+    return [];
+}
+//
+//////////////
+
+
+default {
+    state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; }
+    on_rez(integer parm) { state rerun; }
+}
+state rerun { state_entry() { state default; } }
+
+state real_default
+{
+    state_entry()
+    {
+        auto_retire();
+        llAllowInventoryDrop(TRUE);
+        name_query = llRequestAgentData(llGetOwner(), DATA_NAME);
+        llSetText("Setting Up, Ready in one minute.", <0,1,1>, 1.0);
+        llSetTimerEvent(60);
+        
+        
+    }
+
+    timer()
+    {
+        online_query = llRequestAgentData(llGetOwner(),DATA_ONLINE);          
+    }
+    
+    dataserver(key queryid, string data)
+    {
+        
+        if(queryid == name_query)
+        {
+            user_name = data;
+            //llSay(0, data + " " + user_name);
+        }
+        
+        if(queryid == online_query)
+        {
+            integer online = (integer) data;
+        
+            if(online)
+            {
+                llSetText(user_name + " is ONLINE\nDrop a NoteCard into me\nto Send " + user_name + " a message.", <0,1,0>, 1.0);
+                return;   
+            }
+        
+            else
+            {
+                llSetText(user_name + " is OFFLINE\nDrop a NoteCard into me\nto Send " + user_name + " a message.", <1,0,0>, 1.0);
+                return;
+            }
+       
+        }     
+    }
+    
+    changed(integer mask)
+    {
+        if(mask & (CHANGED_ALLOWED_DROP | CHANGED_INVENTORY))
+        {
+            integer num_notes = llGetInventoryNumber(INVENTORY_NOTECARD);
+            
+            if(num_notes > 0)
+            {
+                string note_name = llGetInventoryName(INVENTORY_NOTECARD, 0);
+                            
+                llSay(0, "Sending Notecard, '" + note_name +"' please stand by.");
+            
+                llGiveInventory(llGetOwner(), note_name);
+                
+                llInstantMessage(llGetOwner(), "A NoteCard has been sent to you: " + note_name);
+                llSay(0, "The Notecard, " + note_name + " has been sent. Thank you.");
+                
+            
+                llRemoveInventory(note_name);
+                
+                num_notes = llGetInventoryNumber(INVENTORY_NOTECARD);
+                
+                while(num_notes > 0) // They dropped more than one notecard. Clean it up
+                {   
+                    note_name = llGetInventoryName(INVENTORY_NOTECARD, 0);
+                      
+                    llSay(0, "Deleting " + note_name + ". It was not submitted.  Try Dropping one note at a time.");
+                    
+                    llRemoveInventory(note_name);
+                    
+                    num_notes = llGetInventoryNumber(INVENTORY_NOTECARD);
+                    
+                }
+                
+            }
+            
+            else //Not a Notecard
+            {
+               //find out what was dropped and remove it.  
+                
+                
+                list inventory;
+                integer num_inv = llGetInventoryNumber(INVENTORY_ALL); // Should be 2
+                integer counter = 0;
+                while(counter < num_inv)
+                {
+                    inventory += [llGetInventoryName(INVENTORY_ALL, counter)];
+                    counter ++;   
+                }
+                
+                // WHat we expect to find
+                list this_script = [llGetScriptName()];
+                
+                //Delete this script (which belong in the inventory) from the list
+                integer index = llListFindList(inventory, this_script);
+                inventory = llDeleteSubList(inventory, index, index);
+                
+                
+                index = llGetListLength(inventory);
+                
+                
+                //Just in case they snuck in more than one inventory item
+                while (index >= 1)
+                {                
+                    llSay(0, "That was not a notecard. Removing " + llList2String(inventory, 0));
+                    llRemoveInventory(llList2String(inventory, 0));
+                    inventory = llDeleteSubList(inventory, 0, 0);
+                    index = llGetListLength(inventory);   
+                } 
+            }
+        }
+    }
+    
+    on_rez(integer start_param)
+    {
+        llResetScript();
+    }
+}