ARES-SDK/ARES/application/xset.lsl

224 lines
7.0 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* XSET Command (Standalone)
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* To see the full text of the ASCL, type 'help license' on any standard
* ARES distribution, or visit http://nanite-systems.com/ASCL for the
* current version.
*
* DISCLAIMER
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS
* IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
* DAMAGES HOWEVER CAUSED ON ANY THEORY OF LIABILITY ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* =========================================================================
*
*/
#include <ARES/a>
#define CLIENT_VERSION "1.2.1"
#define CLIENT_VERSION_TAGS "release"
/*
replaces (string) number with (integer)number
and (string)\"number\" with (string)number
... from db.lsl
*/
list process_keyname(list keyname) {
integer ki = count(keyname);
while(ki--) {
string ks = gets(keyname, ki);
if((string)((integer)ks) == ks)
keyname = alter(keyname, [(integer)ks], ki, ki);
else if(llOrd(ks, 0) == 0x22 && llOrd(ks, LAST) == 0x22)
keyname = alter(keyname, [substr(ks, 1, -2)], ki, ki);
}
return keyname;
}
list pipes;
list tags;
finish(key tag) {
integer ti = index(tags, tag);
if(~ti) {
integer r = (integer)getjs(tasks_queue, [tag, "r"]);
key ins = getjs(tasks_queue, [tag, "ins"]);
key outs = getjs(tasks_queue, [tag, "outs"]);
task_end(tag);
pipe_close([getk(pipes, ti)]);
pipes = delitem(pipes, ti);
tags = delitem(tags, ti);
// resolve_io(r, outs, ins);
resolve_i(r, ins);
#ifdef DEBUG
echo("xset: resolved");
#endif
} else {
echo("xset: spurious receipt: " + (string)tag);
}
}
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
list argv = splitnulls(m, " ");
integer quick_mode;
if(gets(argv, 1) == "-q") {
quick_mode = 1;
argv = delitem(argv, 1);
}
integer argc = count(argv);
string msg = "";
if(argc < 2) {
msg = "Syntax: " + PROGRAM_NAME + " [-q] <variable> <command>\n\nxset WILL hang if a job returns no output at all. If this is a risk, specify -q.\n\nNew in 1.2: The command 'db json' will be detected and parsed; try also 'xset <var> = <db.value>' as a nicer alternative.";
} else if(argc >= 3) {
string varname = gets(argv, 1);
string command = concat(delrange(argv, 0, 1), " ");
if(gets(argv, 2) == "=" || (gets(argv, 2) == "db" && gets(argv, 3) == "json")) { // short-circuit tedious 'db json' usage
if(gets(argv, 2) == "db")
argv = delitem(argv, 3);
string keyname = gets(argv, 3);
list keyparts = process_keyname(split(keyname, "."));
string value = getdbl(gets(keyparts, 0), delitem(keyparts, 0));
if(value != JSON_INVALID) {
setdb("env", varname, value); // not dbl
} else {
if(getdb("env", varname) != JSON_INVALID) // not dbl
deletedb("env", varname); // still not dbl
}
} else {
key tag = llGenerateKey();
key pipe = llGenerateKey();
pipes += pipe; // where we'll receive data from the subtask
tags += tag; // the receipt we'll get when the subtask resolves
task_begin(tag, jsobject([
"r", _resolved, // the PID we'll report to when we're done
"ins", ins, // input stream of initial calling environment (re-used as subtask's output stream)
"outs", outs, // output stream of initial calling environment (not seen by subtask)
"var", varname, // env variable we're updating
"command", command, // the command we're executing
"user", user, // who we're doing this for
"done", 0, // turns to 1 once we receive DONE
"data", quick_mode // turns to 1 once we receive data
]));
// xset ends a task when both 'done' and 'data' conditions are met
pipe_open(["p:" + (string)pipe + " notify " + PROGRAM_NAME + " data"]);
_resolved = 0;
}
} else {
msg = PROGRAM_NAME + ": insufficient arguments: " + m + "; see 'help xset' for usage";
}
if(msg != "")
print(outs, user, msg);
} else if(n == SIGNAL_DONE) {
key tag = substr(m, 0, 35);
integer got_data = (integer)getjs(tasks_queue, [tag, "data"]);
if(got_data) {
finish(tag);
} else {
tasks_queue = setjs(tasks_queue, [tag, "done"], "1");
}
} else if(n == SIGNAL_NOTIFY) {
list argv = splitnulls(m, " ");
string reason = gets(argv, 1);
if(reason == "pipe") {
integer pi = index(pipes, ins);
if(!~pi) {
echo("xset: spurious pipe: " + (string)ins);
} else {
#ifdef DEBUG
echo("xset: created pipe " + (string)ins);
#endif
key tag = getk(tags, pi);
string varname = getjs(tasks_queue, [tag, "var"]);
setdbl("env", [varname], "");
string command = getjs(tasks_queue, [tag, "command"]);
invoke(command, ins, tag, getjs(tasks_queue, [tag, "user"]));
#ifdef DEBUG
echo("xset: invoked " + command);
#endif
}
} else if(reason == "data") {
integer pi = index(pipes, ins);
if(!~pi) {
echo("xset: data from non-open pipe: " + (string)ins);
} else {
key tag = getk(tags, pi);
string buffer;
pipe_read(ins, buffer);
string varname = getjs(tasks_queue, [tag, "var"]);
#ifdef DEBUG
echo("xset: data received for " + varname);
#endif
string current_value = getdbl("env", [varname]);
if(current_value != "")
current_value += "\n";
setdb("env", varname, current_value + buffer);
integer got_done = (integer)getjs(tasks_queue, [tag, "done"]);
if(got_done) {
finish(tag);
} else {
tasks_queue = setjs(tasks_queue, [tag, "data"], "1");
}
}
} else {
echo("xset: unexpected notify: " + m);
}
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
set_mode(_mode | MODE_ACCEPT_DONE);
} else if(n == SIGNAL_UNKNOWN_SCRIPT) {
echo("[" + PROGRAM_NAME + "] failed to run '" + m + "' (kernel could not find the program specified)");
} else {
echo("[" + PROGRAM_NAME + "] unimplemented signal " + (string)n + ": " + m);
}
}
#include <ARES/program>