diff --git a/src/sbbs3/js_internal.c b/src/sbbs3/js_internal.c
index 7fa8ed28a4ecb94bc78ec8cf998471741c79acce..17f5e49337e77bd9f8c6d97804f3c60ca004c3ff 100644
--- a/src/sbbs3/js_internal.c
+++ b/src/sbbs3/js_internal.c
@@ -400,10 +400,29 @@ js_execfile(JSContext *cx, uintN argc, jsval *arglist)
 	JS_DefineProperty(cx, js_scope, "argv", OBJECT_TO_JSVAL(nargv)
 		,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
 
-	for(i=arg; i<argc; i++)
-		JS_SetElement(cx, nargv, i-arg, &argv[i]);
+	uintN nargc = 0;
+	for(i=arg; i<argc; i++) {
+		if(JSVAL_IS_OBJECT(argv[i]) && !JSVAL_IS_NULL(argv[i])) {
+			JSObject* objarg = JSVAL_TO_OBJECT(argv[i]);
+			if(JS_IsArrayObject(cx, objarg)) {		/* argument is an array (e.g. of strings) */
+				jsuint array_length = 0;
+				if(JS_GetArrayLength(cx, objarg, &array_length) && array_length) {
+					for(jsuint n = 0; n < array_length; n++) {
+						if(JS_GetElement(cx, objarg, n, &val)) {
+							JS_SetElement(cx, nargv, nargc, &val);
+							nargc++;
+						}
+					}
+					continue;
+				}
+
+			}
+		}
+		JS_SetElement(cx, nargv, nargc, &argv[i]);
+		nargc++;
+	}
 
-	JS_DefineProperty(cx, js_scope, "argc", INT_TO_JSVAL(argc-arg)
+	JS_DefineProperty(cx, js_scope, "argc", INT_TO_JSVAL(nargc)
 		,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
 
 	js_obj = js_CreateInternalJsObject(cx, js_scope, js_callback, NULL);
@@ -709,15 +728,18 @@ static jsSyncMethodSpec js_functions[] = {
 	,316
 	},
 	{"flatten_string",	js_flatten,			1,	JSTYPE_VOID,	JSDOCSTR("[string]")
-	,JSDOCSTR("flattens a string, optimizing allocated memory used for concatenated strings")
+	,JSDOCSTR("flatten a string, optimizing allocated memory used for concatenated strings")
 	,316
 	},
-	{"exec",	js_execfile,			1,	JSTYPE_NUMBER,	JSDOCSTR("filename [, startup_dir], obj [,...]")
-	,JSDOCSTR("Executes a script optionally with a custom scope.  The main difference between this "
-	"and load() is that scripts called this way can call exit() without terminating the caller.  If it does, any "
-	"on_exit() handlers will be evaluated in scripts scope when the script exists. "
-	"NOTE: To get a child of the current scope, you need to create an object in the current scope. "
-	"An anonymous object can be created using 'new function(){}'.")
+	{"exec",	js_execfile,			1,	JSTYPE_NUMBER,	JSDOCSTR("filename [, startup_dir], <i>object</i> scope [,...]")
+	,JSDOCSTR("execute a script within the specified scope.  The main difference between this method "
+	"and <tt>load()</tt> is that scripts called this way can call <tt>exit()</tt> without terminating the caller.  If it does, any "
+	"<tt>on_exit()</tt> handlers will be evaluated in scripts scope when the script exists. <br>"
+	"NOTE: to get a child of the current scope, you need to create an object in the current scope. "
+	"An anonymous object can be created using '<tt>new function(){}</tt>'. <br>"
+	"NOTE: array-type arguments are treated specially: each element of the array is passed "
+	"as a separate argument (e.g. string) to the script in <tt>argv[]</tt>.  "
+	"This allows one script to generate a variable-length list of arguments to be passed to another.")
 	,31702
 	},
 	{0}