Commit c9c42c87 authored by rswindell's avatar rswindell

Support JavaScript property name expansion using JS:<name> @-code where name is

the name of a scalar property in either the current scope (by default) or the
scope of the object passed to:
- bbs.menu()
- console.putmsg()
- console.printfile()
- console.printtail()

The 'name' cannot be an array element (e.g. myprop[0]) or a nested object
reference (e.g. myobj.myprop): just a single property name that can be
converted to a string.

Also, bbs.menu() now accepts an optional print-mode argument (default: P_NONE).
parent d3d10271
......@@ -71,7 +71,7 @@ static char* separate_thousands(const char* src, char *dest, size_t maxlen, char
/****************************************************************************/
/* Returns 0 if invalid @ code. Returns length of @ code if valid. */
/****************************************************************************/
int sbbs_t::show_atcode(const char *instr)
int sbbs_t::show_atcode(const char *instr, JSObject* obj)
{
char str[128],str2[128],*tp,*sp,*p;
int len;
......@@ -144,7 +144,7 @@ int sbbs_t::show_atcode(const char *instr)
*p=0;
}
cp = atcode(sp, str2, sizeof(str2), &pmode, centered);
cp = atcode(sp, str2, sizeof(str2), &pmode, centered, obj);
if(cp==NULL)
return(0);
......@@ -210,7 +210,7 @@ static const char* getpath(scfg_t* cfg, const char* path)
return path;
}
const char* sbbs_t::atcode(char* sp, char* str, size_t maxlen, long* pmode, bool centered)
const char* sbbs_t::atcode(char* sp, char* str, size_t maxlen, long* pmode, bool centered, JSObject* obj)
{
char* tp = NULL;
uint i;
......@@ -1092,6 +1092,13 @@ const char* sbbs_t::atcode(char* sp, char* str, size_t maxlen, long* pmode, bool
return nulstr;
}
if(strncmp(sp, "JS:", 3) == 0) {
jsval val;
if(JS_GetProperty(js_cx, obj == NULL ? js_glob : obj, sp + 3, &val))
JSVALUE_TO_STRBUF(js_cx, val, str, maxlen, NULL);
return str;
}
if(!strncmp(sp,"EXEC:",5)) {
exec_bin(sp+5,&main_csi);
return(nulstr);
......
......@@ -1170,6 +1170,8 @@ js_menu(JSContext *cx, uintN argc, jsval *arglist)
sbbs_t* sbbs;
jsrefcount rc;
char *menu;
int32 mode = P_NONE;
JSObject* obj = JS_GetScopeChain(cx);
if(!js_argc(cx, argc, 1))
return(JS_FALSE);
......@@ -1181,11 +1183,23 @@ js_menu(JSContext *cx, uintN argc, jsval *arglist)
if (!str)
return(JS_FALSE);
uintN argn = 1;
if(argc > argn && JSVAL_IS_NUMBER(argv[argn])) {
if(!JS_ValueToInt32(cx,argv[argn], &mode))
return JS_FALSE;
argn++;
}
if(argc > argn && JSVAL_IS_OBJECT(argv[argn])) {
if((obj = JSVAL_TO_OBJECT(argv[argn])) == NULL)
return JS_FALSE;
argn++;
}
JSSTRING_TO_MSTRING(cx, str, menu, NULL);
if(!menu)
return JS_FALSE;
rc=JS_SUSPENDREQUEST(cx);
bool result = sbbs->menu(menu);
bool result = sbbs->menu(menu, mode, obj);
free(menu);
JS_RESUMEREQUEST(cx, rc);
......@@ -4425,8 +4439,10 @@ static jsSyncMethodSpec js_bbs_functions[] = {
,314
},
/* menuing */
{"menu", js_menu, 1, JSTYPE_BOOLEAN, JSDOCSTR("base_filename")
,JSDOCSTR("display a menu file from the text/menu directory")
{"menu", js_menu, 1, JSTYPE_BOOLEAN, JSDOCSTR("base_filename [,mode=<tt>P_NONE</tt>] [,object scope]")
,JSDOCSTR("display a menu file from the text/menu directory.<br>"
"See <tt>P_*</tt> in <tt>sbbsdefs.js</tt> for <i>mode</i> flags.<br>"
"When <i>scope</i> is specified, <tt>@JS:property@</tt> codes will expand the referenced property names.")
,310
},
{"menu_exists", js_menu_exists, 1, JSTYPE_BOOLEAN, JSDOCSTR("base_filename")
......@@ -4453,7 +4469,7 @@ static jsSyncMethodSpec js_bbs_functions[] = {
/* xtrn programs/modules */
{"exec", js_exec, 2, JSTYPE_NUMBER, JSDOCSTR("cmdline [,mode=<tt>EX_NONE</tt>] [,startup_dir]")
,JSDOCSTR("execute a program, optionally changing current directory to <i>startup_dir</i> "
"(see <tt>EX_*</tt> in <tt>sbbsdefs.js</tt> for valid <i>mode</i> bits)")
"(see <tt>EX_*</tt> in <tt>sbbsdefs.js</tt> for valid <i>mode</i> flags.)")
,310
},
{"exec_xtrn", js_exec_xtrn, 1, JSTYPE_BOOLEAN, JSDOCSTR("xtrn_number_or_code")
......@@ -4466,11 +4482,11 @@ static jsSyncMethodSpec js_bbs_functions[] = {
,310
},
{"telnet_gate", js_telnet_gate, 1, JSTYPE_VOID, JSDOCSTR("address [,mode=<tt>TG_NONE</tt>]")
,JSDOCSTR("external Telnet gateway (see <tt>TG_*</tt> in <tt>sbbsdefs.js</tt> for valid <i>mode</i> bits)")
,JSDOCSTR("external Telnet gateway (see <tt>TG_*</tt> in <tt>sbbsdefs.js</tt> for valid <i>mode</i> flags.)")
,310
},
{"rlogin_gate", js_rlogin_gate, 1, JSTYPE_VOID, JSDOCSTR("address [,client-user-name=<tt>user.alias</tt>, server-user-name=<tt>user.name</tt>, terminal=<tt>console.terminal</tt>] [,mode=<tt>TG_NONE</tt>]")
,JSDOCSTR("external RLogin gateway (see <tt>TG_*</tt> in <tt>sbbsdefs.js</tt> for valid <i>mode</i> bits)")
,JSDOCSTR("external RLogin gateway (see <tt>TG_*</tt> in <tt>sbbsdefs.js</tt> for valid <i>mode</i> flags.)")
,316
},
/* security */
......
......@@ -1216,6 +1216,7 @@ js_writeln(JSContext *cx, uintN argc, jsval *arglist)
static JSBool
js_putmsg(JSContext *cx, uintN argc, jsval *arglist)
{
uintN argn;
jsval *argv=JS_ARGV(cx, arglist);
int32 mode=0;
int32 columns=0;
......@@ -1223,6 +1224,7 @@ js_putmsg(JSContext *cx, uintN argc, jsval *arglist)
sbbs_t* sbbs;
char* cstr;
jsrefcount rc;
JSObject* obj = JS_GetScopeChain(cx);
if((sbbs=(sbbs_t*)js_GetClassPrivate(cx, JS_THIS_OBJECT(cx, arglist), &js_console_class))==NULL)
return(JS_FALSE);
......@@ -1233,20 +1235,28 @@ js_putmsg(JSContext *cx, uintN argc, jsval *arglist)
if (!str)
return(JS_FALSE);
if(argc>1 && JSVAL_IS_NUMBER(argv[1])) {
if(!JS_ValueToInt32(cx,argv[1],&mode))
argn = 1;
if(argc>argn && JSVAL_IS_NUMBER(argv[argn])) {
if(!JS_ValueToInt32(cx,argv[argn],&mode))
return JS_FALSE;
argn++;
}
if(argc>argn && JSVAL_IS_NUMBER(argv[argn])) {
if(!JS_ValueToInt32(cx,argv[argn],&columns))
return JS_FALSE;
argn++;
}
if(argc>2 && JSVAL_IS_NUMBER(argv[2])) {
if(!JS_ValueToInt32(cx,argv[2],&columns))
if(argc>argn && JSVAL_IS_OBJECT(argv[argn])) {
if((obj = JSVAL_TO_OBJECT(argv[argn])) == NULL)
return JS_FALSE;
argn++;
}
JSSTRING_TO_MSTRING(cx, str, cstr, NULL);
if(cstr==NULL)
return JS_FALSE;
rc=JS_SUSPENDREQUEST(cx);
sbbs->putmsg(cstr, mode, columns);
sbbs->putmsg(cstr, mode, columns, obj);
free(cstr);
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
......@@ -1262,6 +1272,7 @@ js_printfile(JSContext *cx, uintN argc, jsval *arglist)
sbbs_t* sbbs;
char* cstr;
jsrefcount rc;
JSObject* obj = JS_GetScopeChain(cx);
if((sbbs=(sbbs_t*)js_GetClassPrivate(cx, JS_THIS_OBJECT(cx, arglist), &js_console_class))==NULL)
return(JS_FALSE);
......@@ -1270,20 +1281,28 @@ js_printfile(JSContext *cx, uintN argc, jsval *arglist)
if (!str)
return(JS_FALSE);
if(argc>1 && JSVAL_IS_NUMBER(argv[1])) {
if(!JS_ValueToInt32(cx,argv[1],&mode))
uintN argn = 1;
if(argc>argn && JSVAL_IS_NUMBER(argv[argn])) {
if(!JS_ValueToInt32(cx,argv[argn],&mode))
return JS_FALSE;
argn++;
}
if(argc>2 && JSVAL_IS_NUMBER(argv[2])) {
if(!JS_ValueToInt32(cx,argv[2],&columns))
if(argc>argn && JSVAL_IS_NUMBER(argv[argn])) {
if(!JS_ValueToInt32(cx,argv[argn],&columns))
return JS_FALSE;
argn++;
}
if(argc>argn && JSVAL_IS_OBJECT(argv[argn])) {
if((obj = JSVAL_TO_OBJECT(argv[argn])) == NULL)
return JS_FALSE;
argn++;
}
JSSTRING_TO_MSTRING(cx, str, cstr, NULL);
if(cstr==NULL)
return JS_FALSE;
rc=JS_SUSPENDREQUEST(cx);
bool result = sbbs->printfile(cstr,mode,columns);
bool result = sbbs->printfile(cstr,mode,columns, obj);
free(cstr);
JS_RESUMEREQUEST(cx, rc);
......@@ -1304,6 +1323,7 @@ js_printtail(JSContext *cx, uintN argc, jsval *arglist)
JSString* js_str=NULL;
char* cstr;
jsrefcount rc;
JSObject* obj = JS_GetScopeChain(cx);
if((sbbs=(sbbs_t*)js_GetClassPrivate(cx, JS_THIS_OBJECT(cx, arglist), &js_console_class))==NULL)
return(JS_FALSE);
......@@ -1322,8 +1342,12 @@ js_printtail(JSContext *cx, uintN argc, jsval *arglist)
if(!JS_ValueToInt32(cx,argv[i],&columns))
return JS_FALSE;
}
} else if(JSVAL_IS_STRING(argv[i]))
} else if(JSVAL_IS_STRING(argv[i])) {
js_str = JS_ValueToString(cx, argv[i]);
} else if(JSVAL_IS_OBJECT(argv[i])) {
if((obj = JSVAL_TO_OBJECT(argv[i])) == NULL)
return JS_FALSE;
}
}
if(js_str==NULL)
......@@ -1336,7 +1360,7 @@ js_printtail(JSContext *cx, uintN argc, jsval *arglist)
if(cstr==NULL)
return JS_FALSE;
rc=JS_SUSPENDREQUEST(cx);
bool result = sbbs->printtail(cstr,lines,mode,columns);
bool result = sbbs->printtail(cstr,lines,mode,columns,obj);
free(cstr);
JS_RESUMEREQUEST(cx, rc);
......@@ -1951,19 +1975,19 @@ js_term_supports(JSContext *cx, uintN argc, jsval *arglist)
static jsSyncMethodSpec js_console_functions[] = {
{"inkey", js_inkey, 0, JSTYPE_STRING, JSDOCSTR("[mode=<tt>K_NONE</tt>] [,timeout=<tt>0</tt>]")
,JSDOCSTR("get a single key with optional <i>timeout</i> in milliseconds (defaults to 0, for no wait), "
"returns an empty string if there is no input (e.g. timeout occurs), "
"see <tt>K_*</tt> in <tt>sbbsdefs.js</tt> for <i>mode</i> bits")
,JSDOCSTR("get a single key with optional <i>timeout</i> in milliseconds (defaults to 0, for no wait).<br>"
"Returns an empty string if there is no input (e.g. timeout occurs).<br>"
"See <tt>K_*</tt> in <tt>sbbsdefs.js</tt> for <i>mode</i> flags.")
,311
},
{"getkey", js_getkey, 0, JSTYPE_STRING, JSDOCSTR("[mode=<tt>K_NONE</tt>]")
,JSDOCSTR("get a single key, with wait, "
"see <tt>K_*</tt> in <tt>sbbsdefs.js</tt> for <i>mode</i> bits")
,JSDOCSTR("get a single key, with wait.<br>"
"See <tt>K_*</tt> in <tt>sbbsdefs.js</tt> for <i>mode</i> flags.")
,310
},
{"getstr", js_getstr, 0, JSTYPE_STRING, JSDOCSTR("[string] [,maxlen=<tt>128</tt>] [,mode=<tt>K_NONE</tt>] [,history[]]")
,JSDOCSTR("get a text string from the user, "
"see <tt>K_*</tt> in <tt>sbbsdefs.js</tt> for <i>mode</i> bits.<br>"
,JSDOCSTR("get a text string from the user.<br>"
"See <tt>K_*</tt> in <tt>sbbsdefs.js</tt> for <i>mode</i> flags.<br>"
"<i>history[]</i>, added in v3.17, allows a command history (string array) to be recalled using the up/down arrow keys."
)
,310
......@@ -2042,10 +2066,11 @@ static jsSyncMethodSpec js_console_functions[] = {
,JSDOCSTR("display one or more values as raw strings followed by a single carriage-return/line-feed pair (new-line)")
,315
},
{"putmsg", js_putmsg, 1, JSTYPE_VOID, JSDOCSTR("text [,mode=<tt>P_NONE</tt>] [,orig_columns=0]")
,JSDOCSTR("display message text (Ctrl-A codes, @-codes, pipe codes, etc), "
"see <tt>P_*</tt> in <tt>sbbsdefs.js</tt> for <i>mode</i> bits.<br>"
"When <tt>P_WORDWRAP</tt> mode flag is specified, <i>orig_columns</i> specifies the original text column width, if known")
{"putmsg", js_putmsg, 1, JSTYPE_VOID, JSDOCSTR("text [,mode=<tt>P_NONE</tt>] [,orig_columns=0] [,object scope]")
,JSDOCSTR("display message text (Ctrl-A codes, @-codes, pipe codes, etc.).<br> "
"See <tt>P_*</tt> in <tt>sbbsdefs.js</tt> for <i>mode</i> flags.<br>"
"When <tt>P_WORDWRAP</tt> mode flag is specified, <i>orig_columns</i> specifies the original text column width, if known.<br>"
"When <i>scope</i> is specified, <tt>@JS:property@</tt> codes will expand the referenced property names.")
,310
},
{"center", js_center, 1, JSTYPE_VOID, JSDOCSTR("text [,width]")
......@@ -2060,13 +2085,14 @@ static jsSyncMethodSpec js_console_functions[] = {
,JSDOCSTR("returns the printed-length (number of columns) of the specified <i>text</i>, accounting for Ctrl-A codes")
,310
},
{"printfile", js_printfile, 1, JSTYPE_BOOLEAN, JSDOCSTR("filename [,mode=<tt>P_NONE</tt>] [,orig_columns=0")
{"printfile", js_printfile, 1, JSTYPE_BOOLEAN, JSDOCSTR("filename [,mode=<tt>P_NONE</tt>] [,orig_columns=0] [,object scope]")
,JSDOCSTR("print a message text file with optional mode.<br>"
"When <tt>P_WORDWRAP</tt> mode flag is specified, <i>orig_columns</i> specifies the original text column width, if known")
"When <tt>P_WORDWRAP</tt> mode flag is specified, <i>orig_columns</i> specifies the original text column width, if known.<br>"
"When <i>scope</i> is specified, <tt>@JS:property@</tt> codes will expand the referenced property names.")
,310
},
{"printtail", js_printtail, 2, JSTYPE_BOOLEAN, JSDOCSTR("filename, lines [,mode=<tt>P_NONE</tt>] [,orig_columns=0]")
,JSDOCSTR("print last x lines of file with optional mode")
{"printtail", js_printtail, 2, JSTYPE_BOOLEAN, JSDOCSTR("filename, lines [,mode=<tt>P_NONE</tt>] [,orig_columns=0] [,object scope]")
,JSDOCSTR("print the last <i>n</i> lines of file with optional mode, original column width, and scope.")
,310
},
{"editfile", js_editfile, 1, JSTYPE_BOOLEAN, JSDOCSTR("filename")
......
......@@ -48,7 +48,7 @@
/* for pauses, aborts and ANSI. 'str' is the path of the file to print */
/* Called from functions menu and text_sec */
/****************************************************************************/
bool sbbs_t::printfile(const char* fname, long mode, long org_cols)
bool sbbs_t::printfile(const char* fname, long mode, long org_cols, JSObject* obj)
{
char* buf;
char fpath[MAX_PATH+1];
......@@ -119,7 +119,7 @@ bool sbbs_t::printfile(const char* fname, long mode, long org_cols)
buf[l]=0;
if((mode&P_UTF8) && !term_supports(UTF8))
utf8_normalize_str(buf);
putmsg(buf,mode,org_cols);
putmsg(buf,mode,org_cols, obj);
}
free(buf);
} else { // Line-at-a-time mode
......@@ -138,7 +138,7 @@ bool sbbs_t::printfile(const char* fname, long mode, long org_cols)
break;
if((mode&P_UTF8) && !term_supports(UTF8))
utf8_normalize_str(buf);
if(putmsg(buf, mode|P_SAVEATR, org_cols) != '\0') // early-EOF?
if(putmsg(buf, mode|P_SAVEATR, org_cols, obj) != '\0') // early-EOF?
break;
}
free(buf);
......@@ -157,7 +157,7 @@ bool sbbs_t::printfile(const char* fname, long mode, long org_cols)
return true;
}
bool sbbs_t::printtail(const char* fname, int lines, long mode, long org_cols)
bool sbbs_t::printtail(const char* fname, int lines, long mode, long org_cols, JSObject* obj)
{
char* buf;
char fpath[MAX_PATH+1];
......@@ -215,7 +215,7 @@ bool sbbs_t::printtail(const char* fname, int lines, long mode, long org_cols)
}
p--;
}
putmsg(p,mode,org_cols);
putmsg(p,mode,org_cols, obj);
}
if(mode&P_NOABORT && online==ON_REMOTE) {
SYNC;
......@@ -228,7 +228,7 @@ bool sbbs_t::printtail(const char* fname, int lines, long mode, long org_cols)
/****************************************************************************/
/* Displays a menu file (e.g. from the text/menu directory) */
/****************************************************************************/
bool sbbs_t::menu(const char *code, long mode)
bool sbbs_t::menu(const char *code, long mode, JSObject* obj)
{
char path[MAX_PATH+1];
const char *next= "msg";
......@@ -261,7 +261,7 @@ bool sbbs_t::menu(const char *code, long mode)
mode |= P_OPENCLOSE | P_CPM_EOF;
if(column == 0)
mode |= P_NOCRLF;
return printfile(path, mode);
return printfile(path, mode, /* org_cols: */0, obj);
}
bool sbbs_t::menu_exists(const char *code, const char* ext, char* path)
......
......@@ -49,7 +49,7 @@
/* the attributes prior to displaying the message are always restored. */
/* Stops parsing/displaying upon CTRL-Z (only in P_CPM_EOF mode). */
/****************************************************************************/
char sbbs_t::putmsg(const char *buf, long mode, long org_cols)
char sbbs_t::putmsg(const char *buf, long mode, long org_cols, JSObject* obj)
{
uint tmpatr;
char tmp2[256],tmp3[128];
......@@ -366,7 +366,7 @@ char sbbs_t::putmsg(const char *buf, long mode, long org_cols)
continue;
}
bool was_tos = tos;
i=show_atcode((char *)str+l); /* returns 0 if not valid @ code */
i=show_atcode((char *)str+l, obj); /* returns 0 if not valid @ code */
l+=i; /* i is length of code string */
if(tos && !was_tos && (sys_status&SS_ABORT) && !lines_printed) /* Aborted at (auto) pause prompt (e.g. due to CLS)? */
sys_status &= ~SS_ABORT; /* Clear the abort flag (keep displaying the msg/file) */
......
......@@ -656,7 +656,7 @@ public:
,const char *to, const char* from, const char** editor=NULL, const char** charset=NULL);
char* quotes_fname(int xedit, char* buf, size_t len);
char* msg_tmp_fname(int xedit, char* fname, size_t len);
char putmsg(const char *str, long mode, long org_cols = 0);
char putmsg(const char *str, long mode, long org_cols = 0, JSObject* obj = NULL);
bool msgabort(void);
bool email(int usernumber, const char *top = NULL, const char *title = NULL
, long mode = WM_NONE, smb_t* resmb = NULL, smbmsg_t* remsg = NULL);
......@@ -801,9 +801,9 @@ public:
char handle_ctrlkey(char ch, long mode=0);
/* prntfile.cpp */
bool printfile(const char* fname, long mode, long org_cols = 0);
bool printtail(const char* fname, int lines, long mode, long org_cols = 0);
bool menu(const char *code, long mode = 0);
bool printfile(const char* fname, long mode, long org_cols = 0, JSObject* obj = NULL);
bool printtail(const char* fname, int lines, long mode, long org_cols = 0, JSObject* obj = NULL);
bool menu(const char *code, long mode = 0, JSObject* obj = NULL);
bool menu_exists(const char *code, const char* ext=NULL, char* realpath=NULL);
int uselect(int add, uint n, const char *title, const char *item, const uchar *ar);
......@@ -814,8 +814,8 @@ public:
void redrwstr(char *strin, int i, int l, long mode);
/* atcodes.cpp */
int show_atcode(const char *code);
const char* atcode(char* sp, char* str, size_t maxlen, long* pmode = NULL, bool centered = false);
int show_atcode(const char *code, JSObject* obj = NULL);
const char* atcode(char* sp, char* str, size_t maxlen, long* pmode = NULL, bool centered = false, JSObject* obj = NULL);
/* getnode.cpp */
int getsmsg(int usernumber, bool clearline = false);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment