diff --git a/src/sbbs3/jsexec.c b/src/sbbs3/jsexec.c new file mode 100644 index 0000000000000000000000000000000000000000..3b81c83b4d778e0f26264654fd717783cb527e8a --- /dev/null +++ b/src/sbbs3/jsexec.c @@ -0,0 +1,427 @@ +/* jsexec.c */ + +/* Execute a Synchronet JavaScript from the command-line */ + +/* $Id$ */ + +/**************************************************************************** + * @format.tab-size 4 (Plain Text/Source Code File Header) * + * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * + * * + * Copyright 2003 Rob Swindell - http://www.synchro.net/copyright.html * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * See the GNU General Public License for more details: gpl.txt or * + * http://www.fsf.org/copyleft/gpl.html * + * * + * Anonymous FTP access to the most recent released source is available at * + * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * + * * + * Anonymous CVS access to the development source and modification history * + * is available at cvs.synchro.net:/cvsroot/sbbs, example: * + * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login * + * (just hit return, no password is necessary) * + * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src * + * * + * For Synchronet coding style and modification guidelines, see * + * http://www.synchro.net/source.html * + * * + * You are encouraged to submit any modifications (preferably in Unix diff * + * format) via e-mail to mods@synchro.net * + * * + * Note: If this box doesn't appear square, then you need to fix your tabs. * + ****************************************************************************/ + +#define JAVASCRIPT + +#include "sbbs.h" + +JSRuntime* js_runtime; +JSContext* js_cx; +JSObject* js_glob; +ulong js_loop=0; +scfg_t scfg; +ulong js_max_bytes=JAVASCRIPT_MAX_BYTES; + +char *usage="\nusage: jsexec [-opts] module[.js] [args]\n" + "\navailable opts:" + "\n" + ; + +static JSBool +js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + uintN i; + JSString* str; + + for (i = 0; i < argc; i++) { + if((str=JS_ValueToString(cx, argv[i]))==NULL) + return(JS_FALSE); + puts(JS_GetStringBytes(str)); + } + + *rval = JSVAL_VOID; + return(JS_TRUE); +} + +static JSBool +js_print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + uintN i; + JSString * str; + + for (i = 0; i < argc; i++) { + if((str=JS_ValueToString(cx, argv[i]))==NULL) + return(JS_FALSE); + printf("%s",JS_GetStringBytes(str)); + } + printf("\n"); + + *rval = JSVAL_VOID; + return(JS_TRUE); +} + +static JSBool +js_printf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + char* p; + uintN i; + JSString * fmt; + JSString * str; + va_list arglist[64]; + + if((fmt = JS_ValueToString(cx, argv[0]))==NULL) + return(JS_FALSE); + + memset(arglist,0,sizeof(arglist)); // Initialize arglist to NULLs + + for (i = 1; i < argc && i<sizeof(arglist)/sizeof(arglist[0]); i++) { + if(JSVAL_IS_STRING(argv[i])) { + if((str=JS_ValueToString(cx, argv[i]))==NULL) + return(JS_FALSE); + arglist[i-1]=JS_GetStringBytes(str); + } + else if(JSVAL_IS_DOUBLE(argv[i])) + arglist[i-1]=(char*)(unsigned long)*JSVAL_TO_DOUBLE(argv[i]); + else if(JSVAL_IS_INT(argv[i]) || JSVAL_IS_BOOLEAN(argv[i])) + arglist[i-1]=(char *)JSVAL_TO_INT(argv[i]); + else + arglist[i-1]=NULL; + } + + if((p=JS_vsmprintf(JS_GetStringBytes(fmt),(char*)arglist))==NULL) + return(JS_FALSE); + + printf("%s",p); + JS_smprintf_free(p); + + *rval = JSVAL_VOID; + return(JS_TRUE); +} + +static JSBool +js_alert(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString * str; + + if((str=JS_ValueToString(cx, argv[0]))==NULL) + return(JS_FALSE); + + printf("!%s\n",JS_GetStringBytes(str)); + + *rval = JSVAL_VOID; + return(JS_TRUE); +} + +static JSBool +js_confirm(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString * str; + + if((str=JS_ValueToString(cx, argv[0]))==NULL) + return(JS_FALSE); + + *rval = BOOLEAN_TO_JSVAL(FALSE); + return(JS_TRUE); +} + +static JSBool +js_prompt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + char instr[81]; + JSString * prompt; + JSString * str; + + if((prompt=JS_ValueToString(cx, argv[0]))==NULL) + return(JS_FALSE); + + if(argc>1) { + if((str=JS_ValueToString(cx, argv[1]))==NULL) + return(JS_FALSE); + SAFECOPY(instr,JS_GetStringBytes(str)); + } else + instr[0]=0; + + printf("%s: ",JS_GetStringBytes(prompt)); + + if(!fgets(instr,sizeof(instr),stdin)) { + *rval = JSVAL_NULL; + return(JS_TRUE); + } + + if((str=JS_NewStringCopyZ(cx, instr))==NULL) + return(JS_FALSE); + + *rval = STRING_TO_JSVAL(str); + return(JS_TRUE); +} + +static jsMethodSpec js_global_functions[] = { + {"log", js_log, 1}, + {"print", js_print, 0}, + {"printf", js_printf, 1}, + {"alert", js_alert, 1}, + {"prompt", js_prompt, 1}, + {"confirm", js_confirm, 1}, + {0} +}; + +static void +js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) +{ + char line[64]; + char file[MAX_PATH+1]; + const char* warning; + + if(report==NULL) { + fprintf(stderr,"!JavaScript: %s", message); + return; + } + + if(report->filename) + sprintf(file," %s",report->filename); + else + file[0]=0; + + if(report->lineno) + sprintf(line," line %d",report->lineno); + else + line[0]=0; + + if(JSREPORT_IS_WARNING(report->flags)) { + if(JSREPORT_IS_STRICT(report->flags)) + warning="strict warning"; + else + warning="warning"; + } else + warning=""; + + fprintf(stderr,"!JavaScript %s%s%s: %s",warning,file,line,message); +} + +static JSBool +js_BranchCallback(JSContext *cx, JSScript *script) +{ + js_loop++; + +#if 0 + /* Terminated? */ + if(sbbs->terminated) { + JS_ReportError(cx,"Terminated"); + sbbs->js_loop=0; + return(JS_FALSE); + } +#endif + /* Infinite loop? */ + if(js_loop>JAVASCRIPT_BRANCH_LIMIT) { + JS_ReportError(cx,"Infinite loop (%lu branches) detected",js_loop); + js_loop=0; + return(JS_FALSE); + } + /* Give up timeslices every once in a while */ + if(!(js_loop%JAVASCRIPT_YIELD_FREQUENCY)) + YIELD(); + + if(!(js_loop%JAVASCRIPT_GC_FREQUENCY)) + JS_MaybeGC(cx); + + return(JS_TRUE); +} + +static BOOL js_init(void) +{ + fprintf(stderr,"JavaScript: Creating runtime: %lu bytes\n" + ,js_max_bytes); + + if((js_runtime = JS_NewRuntime(js_max_bytes))==NULL) + return(FALSE); + + fprintf(stderr,"JavaScript: Initializing context (stack: %lu bytes)\n" + ,JAVASCRIPT_CONTEXT_STACK); + + if((js_cx = JS_NewContext(js_runtime, JAVASCRIPT_CONTEXT_STACK))==NULL) + return(FALSE); + + JS_SetErrorReporter(js_cx, js_ErrorReporter); + + /* Global Object */ + if((js_glob=js_CreateGlobalObject(js_cx, &scfg, js_global_functions))==NULL) + return(FALSE); + + /* System Object */ + if(js_CreateSystemObject(js_cx, js_glob, &scfg, time(NULL), scfg.sys_inetaddr)==NULL) + return(FALSE); + + /* Socket Class */ + if(js_CreateSocketClass(js_cx, js_glob)==NULL) + return(FALSE); + + /* MsgBase Class */ + if(js_CreateMsgBaseClass(js_cx, js_glob, &scfg)==NULL) + return(FALSE); + + /* File Class */ + if(js_CreateFileClass(js_cx, js_glob)==NULL) + return(FALSE); + + /* User class */ + if(js_CreateUserClass(js_cx, js_glob, &scfg)==NULL) + return(FALSE); + + /* Area Objects */ + if(!js_CreateUserObjects(js_cx, js_glob, &scfg, NULL, NULL, NULL)) + return(FALSE); + + return(TRUE); +} + +long js_exec(const char *fname, char** args) +{ + int argc=0; + char path[MAX_PATH+1]; + JSObject* js_scope=NULL; + JSScript* js_script=NULL; + JSString* arg; + JSObject* argv; + jsval val; + jsval rval; + + if(strcspn(fname,"/\\")==strlen(fname)) { + sprintf(path,"%s%s",scfg.mods_dir,fname); + if(scfg.mods_dir[0]==0 || !fexistcase(path)) + sprintf(path,"%s%s",scfg.exec_dir,fname); + } else + sprintf(path,"%.*s",(int)sizeof(path)-4,fname); + /* Add extension if not specified */ + if(!strchr(path,'.')) + strcat(path,".js"); + + if(!fexistcase(path)) { + fprintf(stderr,"!Module file (%s) doesn't exist\n",path); + return(-1); + } + + js_scope=JS_NewObject(js_cx, NULL, NULL, js_glob); + + if(js_scope==NULL) { + fprintf(stderr,"!Error creating JS scope\n"); + return(-1); + } + + argv=JS_NewArrayObject(js_cx, 0, NULL); + + for(argc=0;args[argc];argc++) { + arg = JS_NewStringCopyZ(js_cx, args[argc]); + if(arg==NULL) + break; + val=STRING_TO_JSVAL(arg); + if(!JS_SetElement(js_cx, argv, argc, &val)) + break; + } + JS_DefineProperty(js_cx, js_scope, "argv", OBJECT_TO_JSVAL(argv) + ,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE); + JS_DefineProperty(js_cx, js_scope, "argc", INT_TO_JSVAL(argc) + ,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE); + + if((js_script=JS_CompileFile(js_cx, js_scope, path))==NULL) { + fprintf(stderr,"!Error executing %s\n",fname); + return(-1); + } + + JS_SetBranchCallback(js_cx, js_BranchCallback); + + JS_ExecuteScript(js_cx, js_scope, js_script, &rval); + + JS_DestroyScript(js_cx, js_script); + + JS_ClearScope(js_cx, js_scope); + + JS_GC(js_cx); + + return(JSVAL_TO_INT(rval)); +} + +/*********************/ +/* Entry point (duh) */ +/*********************/ +int main(int argc, char **argv) +{ + char error[512]; + char revision[16]; + char* module=NULL; + char* p; + + sscanf("$Revision$", "%*s %s", revision); + + fprintf(stderr,"\nJSexec v%s%c-%s (rev %s) - " + "Execute Synchronet JavaScript Modules\n" + ,VERSION,REVISION + ,PLATFORM_DESC + ,revision + ); + + if(argc<2) { + fprintf(stderr,usage); + return(1); + } + + module=argv[1]; + + if(module==NULL) { + fprintf(stderr,usage); + return(1); + } + + p=getenv("SBBSCTRL"); + if(p==NULL) { + fprintf(stderr,"\nSBBSCTRL environment variable not set.\n"); + fprintf(stderr,"\nExample: SET SBBSCTRL=/sbbs/ctrl\n"); + exit(1); + } + + memset(&scfg,0,sizeof(scfg)); + scfg.size=sizeof(scfg); + SAFECOPY(scfg.ctrl_dir,p); + + if(chdir(scfg.ctrl_dir)!=0) + fprintf(stderr,"!ERROR changing directory to: %s", scfg.ctrl_dir); + + printf("\nLoading configuration files from %s\n",scfg.ctrl_dir); + if(!load_cfg(&scfg,NULL,TRUE,error)) { + fprintf(stderr,"!ERROR loading configuration files: %s\n",error); + exit(1); + } + prep_dir(scfg.data_dir, scfg.temp_dir, sizeof(scfg.temp_dir)); + + if(!(scfg.sys_misc&SM_LOCAL_TZ)) + putenv("TZ=UTC0"); + + if(!js_init()) + return(1); + + return(js_exec(module,&argv[2])); +} + diff --git a/src/sbbs3/jsexec.dsp b/src/sbbs3/jsexec.dsp new file mode 100644 index 0000000000000000000000000000000000000000..9781d9b08b239c9b0c219b8ac8cf30da26b5a1e9 --- /dev/null +++ b/src/sbbs3/jsexec.dsp @@ -0,0 +1,90 @@ +# Microsoft Developer Studio Project File - Name="jsexec" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=jsexec - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "jsexec.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "jsexec.mak" CFG="jsexec - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "jsexec - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "jsexec - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "jsexec - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\..\include\mozilla\js" /I "../xpdev" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib msvc.win32.dll.debug/sbbs.lib ../../lib/mozilla/js/win32.debug/js32.lib /nologo /subsystem:console /machine:I386 /out:"msvc.win32.exe.release/jsexec.exe" + +!ELSEIF "$(CFG)" == "jsexec - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "jsexec___Win32_Debug" +# PROP BASE Intermediate_Dir "jsexec___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "jsexec___Win32_Debug" +# PROP Intermediate_Dir "jsexec___Win32_Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\include\mozilla\js" /I "../xpdev" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib msvc.win32.dll.debug/sbbs.lib ../../lib/mozilla/js/win32.debug/js32.lib /nologo /subsystem:console /debug /machine:I386 /out:"msvc.win32.exe.debug/jsexec.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "jsexec - Win32 Release" +# Name "jsexec - Win32 Debug" +# Begin Source File + +SOURCE=.\jsexec.c +# End Source File +# End Target +# End Project