[mw-devel] MW3 r963 - trunk/src

psycodom at sucs.org psycodom at sucs.org
Mon May 21 10:22:26 BST 2007


Author: psycodom
Date: 2007-05-21 10:22:26 +0100 (Mon, 21 May 2007)
New Revision: 963

Modified:
   trunk/src/iconv.c
   trunk/src/js.c
   trunk/src/js.h
   trunk/src/main.c
   trunk/src/sqlite.c
   trunk/src/sqlite.h
Log:
Fixes a handful of memory leaks.
Sorts the javascript sqlite code to return a useful error if a query fails rather than stopping execution.
Adds a handful of useful features to javascript.



Modified: trunk/src/iconv.c
===================================================================
--- trunk/src/iconv.c	2007-05-11 17:42:08 UTC (rev 962)
+++ trunk/src/iconv.c	2007-05-21 09:22:26 UTC (rev 963)
@@ -263,7 +263,7 @@
 	{
 		strcpy(buffcpy, buff);
 		conversion_result=convert_string_charset(buffcpy, "UTF-8", strlen(buffcpy), buff, "UTF-8", strlen(buff)+1, NULL, NULL, NULL, NULL, NULL);
-		
+		free(buffcpy);	
 	}
 	return conversion_result;
 }

Modified: trunk/src/js.c
===================================================================
--- trunk/src/js.c	2007-05-11 17:42:08 UTC (rev 962)
+++ trunk/src/js.c	2007-05-21 09:22:26 UTC (rev 963)
@@ -11,6 +11,7 @@
 #include <iconv.h>
 #include <sys/types.h>
 #include <pwd.h>
+#include <readline/readline.h>
 
 #include "bb.h"
 #include "proto.h"
@@ -19,7 +20,17 @@
 #include "script.h"
 #include "talker_privs.h"
 #include "iconv.h"
+#include "alias.h"
 
+extern Alias alias_list;
+extern Alias bind_list;
+extern Alias rpc_list;
+extern Alias event_list;
+extern Alias onoff_list;
+extern Alias ipc_list;
+extern Alias force_list;
+extern Alias shutdown_list;
+
 extern struct person *user;
 extern long userposn;
 extern unsigned long rights;
@@ -31,6 +42,9 @@
 static JSContext *jscx = NULL;
 static JSObject *jsroot = NULL;
 
+enum bindtype { K_BIND=0, K_BIND_EVENT, K_BIND_ONOFF, K_BIND_IPC, K_BIND_FORCE, K_BIND_SHUTDOWN, K_BIND_ALIAS, K_BIND_RPC };
+#define K_BROADCAST 1
+
 JSClass globclass = {
 	"milliways", JSCLASS_HAS_PRIVATE,
 	JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,
@@ -52,6 +66,20 @@
 	JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
+JSClass js_userrecordclass = {
+	"userrecord", JSCLASS_HAS_PRIVATE,
+	JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+	JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
+	JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+JSClass js_termsizeclass = {
+	"termsize", JSCLASS_HAS_PRIVATE,
+	JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+	JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
+	JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
 // turns a jsval into a utf-8 string that mw can handle
 // expects the utf8_buffer to have been allocated and be at least buffer_length long
 // used to reduce mass code duplication
@@ -77,7 +105,7 @@
 
 
 /* Function for printing to standard out from javascript (helpful for
- * debugging and demonstrates how to call C from js)
+ * debugging and demonstrates how to call C from js) - also useful for event functions
  */
 static JSBool js_print(JSContext *cx, JSObject __attribute__((unused)) *obj, uintN argc, jsval *argv, jsval __attribute__((unused)) *rval) 
 {
@@ -135,6 +163,7 @@
 	return JS_FALSE;
 }
 
+
 /* say to the talker */
 static JSBool js_say(JSContext *cx, JSObject __attribute__((unused)) *obj, uintN argc, jsval *argv, jsval __attribute__((unused)) *rval) 
 {
@@ -163,6 +192,329 @@
 	return JS_FALSE;
 }
 
+/* send an rpc/rpb */
+static JSBool js_rpc(JSContext *cx, JSObject __attribute__((unused)) *obj, uintN argc, jsval *argv, jsval __attribute__((unused)) *rval) 
+{
+	char msg[MAXTEXTLENGTH]="";
+	char rpc_type[MAXTEXTLENGTH]="";
+	char username[NAMESIZE+1]="";
+	int broadcast=0;
+	
+	int conversion_result;
+	if (argc < 3) {
+		printf("Error: javascript rpc() expects 3 arguments\n");
+		return JS_FALSE;
+	}
+	if(JSVAL_IS_INT(argv[0])) {
+		if(JSVAL_TO_INT(argv[0]) == K_BROADCAST) {
+			broadcast=1;
+		}
+	}
+	
+	if (JSVAL_IS_STRING(argv[0])) {
+		conversion_result=jsval_to_utf8string(cx, argv[0], username, NAMESIZE+1);
+		if( conversion_result < 0) {
+			printf("Error: javascript rpc(): major argument conversion error\n");
+			return JS_FALSE;
+		}
+	}
+	
+	conversion_result=jsval_to_utf8string(cx, argv[1], rpc_type, MAXTEXTLENGTH-100);
+	if( conversion_result < 0) {
+		printf("Error: javascript rpc(): major argument conversion error\n");
+		
+		return JS_FALSE;
+	}
+	conversion_result=jsval_to_utf8string(cx, argv[2], msg, MAXTEXTLENGTH-100);
+	if( conversion_result < 0) {
+		printf("Error: javascript rpc(): major argument conversion error\n");
+		return JS_FALSE;
+	}
+	// something is empty
+	if( (broadcast==0 && username[0]=='\0') || rpc_type[0]=='\0') {
+		printf("Error: javascript rpc(): invalid arguments\n");
+		return JS_FALSE;
+	}
+	
+	sendrpc(username, rpc_type, msg, broadcast);
+	
+	return JS_TRUE;
+}
+
+/* send an ipc/ipb */
+static JSBool js_ipc(JSContext *cx, JSObject __attribute__((unused)) *obj, uintN argc, jsval *argv, jsval __attribute__((unused)) *rval) 
+{
+	char msg[MAXTEXTLENGTH]="";
+	char username[NAMESIZE+1]="";
+	int broadcast=0;
+	int conversion_result;
+	if (argc < 2) {
+		printf("Error: javascript ipc() expects 2 arguments\n");
+		return JS_FALSE;
+	}
+	if(JSVAL_IS_INT(argv[0])) {
+		if(JSVAL_TO_INT(argv[0]) == K_BROADCAST) {
+			broadcast=1;
+		}
+	}
+	if (JSVAL_IS_STRING(argv[0])) {
+		conversion_result=jsval_to_utf8string(cx, argv[0], username, NAMESIZE+1);
+		if( conversion_result < 0) {
+		printf("Error: javascript ipc(): major argument conversion error\n");
+			return JS_FALSE;
+		}
+	}
+	
+
+	conversion_result=jsval_to_utf8string(cx, argv[1], msg, MAXTEXTLENGTH-100);
+	if( conversion_result < 0) {
+		printf("Error: javascript ipc(): major argument conversion error\n");
+		return JS_FALSE;
+	}
+	// not broadcast and no username
+	if(broadcast==0 && username[0]=='\0') {
+		printf("Error: javascript ipc(): expects a username or K_BROADCAST\n");
+
+		return JS_FALSE;
+	}
+	
+	sendipc(username, msg, broadcast);
+	return JS_TRUE;
+}
+
+/* ask a user for extra input */
+static JSBool js_input(JSContext *cx, JSObject __attribute__((unused)) *obj, uintN argc, jsval *argv, jsval *rval) 
+{
+	JSString *the_jsstring, *jsstr;
+	size_t ucs2_length, prompt_length, line_length;
+	jschar *ucs2_string;
+	int conv_error;
+	
+	char *prompt=NULL, *line;
+	
+	if(argc > 0) {
+		if(JSVAL_IS_STRING(argv[0])) {
+			// convert prompt to local
+			the_jsstring = JS_ValueToString(cx, argv[0]);
+			ucs2_length=JS_GetStringLength(the_jsstring);
+			ucs2_string=JS_GetStringChars(the_jsstring);
+			prompt_length=sizeof(char)*((ucs2_length*3)+1); // bit ugly
+			prompt=malloc(prompt_length); 
+			if(prompt!=NULL) {
+				conv_error=convert_string_charset((char *)ucs2_string, "UCS-2", ucs2_length*sizeof(jschar), prompt, "LOCAL//TRANSLIT", prompt_length, NULL, NULL, NULL, NULL, NULL);
+				if(conv_error<0) {
+					fprintf(stderr, "js_input: convert_string_charset failed with error: %d\n", conv_error);
+					free(prompt);
+					prompt=NULL;
+				}
+			}		
+		} else {
+			printf("Warning: when a parameter is specified to javascript input() command it should be a string to use as the prompt\n");
+		}
+	}
+	if( prompt == NULL )
+	{
+		prompt=strdup("? ");
+	}
+
+	busy++;
+	if ((line=readline(prompt))==NULL) line=strdup("");
+	busy--;
+		
+	free(prompt);
+	
+	line_length = sizeof(jschar) * (strlen(line) + 1);
+	
+	ucs2_string = malloc( line_length );
+	if(ucs2_string!=NULL) {
+		
+		conv_error=convert_string_charset(line, "LOCAL", strlen(line),
+											(char *)ucs2_string, "UCS-2//TRANSLIT", line_length, 
+											NULL, NULL, NULL, NULL, NULL);
+		if(conv_error>=0) {
+			jsstr = JS_NewUCStringCopyZ(cx, ucs2_string);
+		} else {
+			jsstr = JS_NewStringCopyZ(cx, "(garbled string)");
+		}
+	
+		free(ucs2_string);
+	} else {
+		jsstr = JS_NewStringCopyZ(cx, "(garbled string)");		
+	}
+	*rval=STRING_TO_JSVAL(jsstr);
+	
+	return JS_TRUE;
+}
+
+
+/* beep */
+static JSBool js_beep(JSContext *cx, JSObject __attribute__((unused)) *obj, uintN argc, jsval *argv, jsval __attribute__((unused)) *rval) 
+{
+	int i, beeps=0;
+	if(argc < 1) {
+		beeps=1;
+	}
+	else if(JSVAL_IS_INT(argv[0])) {
+		beeps=JSVAL_TO_INT(argv[0]);
+		if(beeps < 1 || beeps > 5) {
+			beeps=0;
+			printf("Warning: javascript beep will only do between 1 and 5 beeps.\n");
+		}
+	} else {
+		printf("Warning: javascript beep command expects an integer.\n");
+	}
+	for(i=0;i<beeps;i++)
+	{
+		write(1,"\7",1);
+	}
+	
+	return JS_TRUE;
+}
+
+/* bind something to a javascript function */
+static JSBool js_bind(JSContext *cx, JSObject __attribute__((unused)) *obj, uintN argc, jsval *argv, jsval __attribute__((unused)) *rval) 
+{
+	char bind[MAXTEXTLENGTH]="";
+	char function_name[MAXTEXTLENGTH]="";
+	int conversion_result;
+	int bind_type=-1;
+	int i=1;
+	if (argc < 2) {
+		printf("Error in javascript: bind expects 2 arguments\n");
+		return JS_TRUE;
+	}
+		
+	if (JSVAL_IS_STRING(argv[0])) {
+		conversion_result=jsval_to_utf8string(cx, argv[0], bind, MAXTEXTLENGTH-100);
+		if( conversion_result != 0) {
+			printf("Error in string conversion binding javascript\n");
+			return JS_TRUE;
+			
+		}
+		bind_type=K_BIND;
+	} else if (JSVAL_IS_INT(argv[0])) {
+		bind_type=JSVAL_TO_INT(argv[0]);
+		if(bind_type == K_BIND || bind_type == K_BIND_ALIAS || bind_type == K_BIND_RPC) {
+			i++;
+			conversion_result=jsval_to_utf8string(cx, argv[1], bind, MAXTEXTLENGTH-100);
+			if( conversion_result != 0) {
+				printf("Error in string conversion binding javascript\n");
+				return JS_TRUE;
+			}	
+		}
+	} else {
+		printf("Error in javascript: bind expects first argument to be a string or a recognised bind id\n");
+		return JS_TRUE;
+	}
+	if (argc>= i-1 && JSVAL_IS_STRING(argv[i])) {
+		conversion_result=jsval_to_utf8string(cx, argv[i], function_name, MAXTEXTLENGTH-100);
+		if( conversion_result != 0) {
+			printf("Error in string conversion binding javascript\n");
+			return JS_TRUE;
+			
+		}
+	} else {
+		printf("Error in javascript: bind expects final argument to be a string.\n");
+		return JS_TRUE;
+	}
+
+	switch(bind_type) {
+		case K_BIND:
+			if(bind[0]=='\0') {
+				printf("Error: Empty bind\n");
+				return JS_TRUE;
+			}
+			if (AddLink(&bind_list, bind, function_name))
+			{
+				printf("Bind %s already exists. Redefined\n", bind);
+			}
+
+			break;
+		case K_BIND_ALIAS:
+			if(bind[0]=='\0') {
+				printf("Error: Empty bind\n");
+				return JS_TRUE;
+			}
+			if (AddLink(&alias_list, bind, function_name))
+			{
+				printf("Alias %s->%s already exists. Redefined\n", bind, function_name);
+			}
+
+			break;
+		case K_BIND_RPC:
+			if(bind[0]=='\0') {
+				printf("Error: Empty bind\n");
+				return JS_TRUE;
+			}
+			if (AddLink(&rpc_list, bind, function_name))
+			{
+				printf("RPC %s Bind %s already exists. Redefined\n", bind, function_name);
+			}
+
+			break;
+		case K_BIND_EVENT:
+			if(AddLink(&event_list, function_name, ""))
+			{
+				printf("Event bind %s already exists.\n", function_name);
+			}
+			break;
+		case K_BIND_ONOFF:
+			if(AddLink(&onoff_list, function_name, ""))
+			{
+				printf("CheckOnOff bind %s already exists.\n", function_name);
+			}
+			break;
+		case K_BIND_IPC:
+			if(AddLink(&ipc_list, function_name, ""))
+			{
+				printf("IPC bind %s already exists.\n", function_name);
+			}
+			break;
+		case K_BIND_FORCE:
+			if(AddLink(&force_list, function_name, ""))
+			{
+				printf("Force bind %s already exists.\n", function_name);
+			}
+			break;
+		case K_BIND_SHUTDOWN:
+			if(AddLink(&shutdown_list, function_name, ""))
+			{
+				printf("Shutdown bind %s already exists.\n", function_name);
+			}
+			
+			break;
+		default:
+			printf("Unknown bind type %d\n", bind_type);
+			break;
+	}
+	return JS_TRUE;
+}
+
+// return the users terminal dimensions
+static JSBool js_termsize(JSContext *cx, JSObject __attribute__((unused)) *obj, uintN argc, jsval *argv, jsval *rval) 
+{
+	JSObject *result_object;
+	jsval jv;
+	int width, height;
+	
+	result_object=JS_NewObject(cx, &js_termsizeclass, NULL, NULL);
+	if(result_object==NULL)
+	{
+		return JS_FALSE;
+	}
+	jv=OBJECT_TO_JSVAL(result_object);
+	
+	width=screen_w();
+	height=screen_h();
+	
+	JS_DefineProperty(cx, result_object, "width", INT_TO_JSVAL(width), NULL, NULL, 0);
+	JS_DefineProperty(cx, result_object, "height", INT_TO_JSVAL(height), NULL, NULL, 0);
+	
+	
+	*rval=jv;
+	return JS_TRUE;
+}
+
 // Provides a javascript function to query the wholist
 static JSBool js_wholist(JSContext *cx, JSObject __attribute__((unused)) *obj, uintN argc, jsval *argv, jsval *rval) {
 	struct person u;
@@ -179,44 +531,43 @@
 	JS_AddRoot(cx, res);
 
 	while (read(wfile,&w,sizeof(w))) {
-		JSObject *line;
+		JSObject *user_record;
 		JSString *jsstr;
-		jsval jv;
-		int rown=0;
+		jsval jv, user_jv;
 
 		if (w.posn < 0) continue;
 		lseek(ufile,w.posn,SEEK_SET);
 		read(ufile,&u,sizeof(u));
 
 		/* make a new row and populate it */
-		line = JS_NewArrayObject(cx, 0, NULL);
+		user_record = JS_NewObject(cx, &js_userrecordclass, NULL, NULL);
+		user_jv = OBJECT_TO_JSVAL(user_record);
 		/* user name */
 		jsstr = JS_NewStringCopyZ(cx, u.name);
 		jv = STRING_TO_JSVAL(jsstr);
-		JS_SetElement(cx, line, rown++, &jv);
+		JS_DefineProperty(cx, user_record, "username", jv, NULL, NULL, 0);
 		/* room number */
 		jv = INT_TO_JSVAL(u.room);
-		JS_SetElement(cx, line, rown++, &jv);
+		JS_DefineProperty(cx, user_record, "room", jv, NULL, NULL, 0);
 		/* idle time */
 		jv = INT_TO_JSVAL(time(0)-u.idletime);
-		JS_SetElement(cx, line, rown++, &jv);
+		JS_DefineProperty(cx, user_record, "idle", jv, NULL, NULL, 0);
 		/* chat modes */
 		jsstr = JS_NewStringCopyZ(cx, display_cmflags(u.chatmode));
 		jv = STRING_TO_JSVAL(jsstr);
-		JS_SetElement(cx, line, rown++, &jv);
+		JS_DefineProperty(cx, user_record, "chatmodes", jv, NULL, NULL, 0);
 		/* protection level */
 		jv = INT_TO_JSVAL((u.chatmode & CM_PROTMASK) >> CM_PROTSHIFT);
-		JS_SetElement(cx, line, rown++, &jv);
+		JS_DefineProperty(cx, user_record, "protection_level", jv, NULL, NULL, 0);
 		jv = INT_TO_JSVAL((u.chatprivs & CP_PROTMASK) >> CP_PROTSHIFT);
-		JS_SetElement(cx, line, rown++, &jv);
+		JS_DefineProperty(cx, user_record, "protection_power", jv, NULL, NULL, 0);
 		/* chat privs */
 		jsstr = JS_NewStringCopyZ(cx, display_cpflags(u.chatprivs & user->chatprivs));
 		jv = STRING_TO_JSVAL(jsstr);
-		JS_SetElement(cx, line, rown++, &jv);
+		JS_DefineProperty(cx, user_record, "chatprivs", jv, NULL, NULL, 0);
 
 		/* stick line into master array */
-		jv = OBJECT_TO_JSVAL(line);
-		JS_SetElement(cx, res, n++, &jv);
+		JS_SetElement(cx, res, n++, &user_jv);
 	}
 	close(wfile);
 	close(ufile);
@@ -292,7 +643,7 @@
 //		printf("dbresult_to_jsarray: node @ %p\n", (void *)node);
 		jsnode = dbdata_to_jsarray(cx, node, data->cols);
 		jv = OBJECT_TO_JSVAL(jsnode);
-		JS_SetElement(cx, jsarray, i, &jv);
+		JS_SetElement(cx, jsarray, data->rows-1-i, &jv);
 
 		node = node->next;
 		i++;
@@ -302,22 +653,103 @@
 	return jsarray;
 }
 
+// create a javascript array of column names from a db_result
+JSObject *dbresult_to_jscolnames(JSContext *cx, struct db_result *data) {
+	JSObject *jsarray;
+//	JSObject *jsnode;
+	jsval jv;
+//	struct db_data *node;
+	jschar *ucsstr;
+	int i, conv_error;
+	size_t data_length;
+	JSString *jsstr;
 
+	if (data == NULL) return NULL;
 
+	jsarray = JS_NewArrayObject(cx, 0, NULL);
+	JS_AddRoot(cx, jsarray);
+	
+	for(i=0;i<data->cols;i++) {
+		data_length = sizeof(jschar) * (strlen(data->colNames[i]) + 1);
+		
+		ucsstr = malloc( data_length );
+		if(ucsstr!=NULL) {
+			
+			conv_error=convert_string_charset(data->colNames[i], "UTF-8", strlen(data->colNames[i]),
+												(char *)ucsstr, "UCS-2", data_length, 
+												NULL, NULL, NULL, NULL, NULL);
+			if(conv_error>=0) {
+				jsstr = JS_NewUCStringCopyZ(cx, ucsstr);
+			} else {
+				jsstr = JS_NewStringCopyZ(cx, "(garbled string)");
+			}	
+			free(ucsstr);
+		} else {
+			jsstr = JS_NewStringCopyZ(cx, "(garbled string)");
+		}
+		jv = STRING_TO_JSVAL(jsstr);
+		JS_SetElement(cx, jsarray, i, &jv);
+		
+	}
+	
+	JS_RemoveRoot(cx, jsarray);
+
+	return jsarray;
+}
+
+// creates a javascript db object from a struct js_db_result
+// on success it return JS_TRUE and the pointer db_object points to the jsval of the new object
+// on fail returns JS_FALSE
+JSBool dbresult_to_jsdbobject(JSContext *cx, struct js_db_result *data, jsval *db_object)
+{
+	JSObject *result_object;
+	JSObject *jsarray, *jscolumns;
+	jsval jsarray_val, jscolumns_val;
+	
+	result_object=JS_NewObject(cx, &js_dbresultclass, NULL, NULL);
+	if(result_object==NULL) {
+		return JS_FALSE;
+	}
+	*db_object=OBJECT_TO_JSVAL(result_object);
+	
+	JS_DefineProperty(cx, result_object, "db_error", INT_TO_JSVAL(data->db_error), NULL, NULL, JSPROP_READONLY);
+	JS_DefineProperty(cx, result_object, "query_error", INT_TO_JSVAL(data->query_error), NULL, NULL, JSPROP_READONLY);
+	if(data->error_text!=NULL) {
+		
+		JS_DefineProperty(cx, result_object, "error_text", STRING_TO_JSVAL(JS_NewStringCopyZ(cx, data->error_text)), NULL, NULL, JSPROP_READONLY);
+	} else {
+		JS_DefineProperty(cx, result_object, "error_text", STRING_TO_JSVAL(JS_NewStringCopyZ(cx, "No Error")), NULL, NULL, JSPROP_READONLY);
+	}
+	if(data->query_result!=NULL) {
+		jsarray=dbresult_to_jsarray(cx, data->query_result);
+		jsarray_val=OBJECT_TO_JSVAL(jsarray);
+		JS_DefineProperty(cx, result_object, "data", jsarray_val, NULL, NULL, 0);
+		jscolumns=dbresult_to_jscolnames(cx, data->query_result);
+		jscolumns_val=OBJECT_TO_JSVAL(jscolumns);
+		JS_DefineProperty(cx, result_object, "column_names", jscolumns_val, NULL, NULL, 0);
+		
+	} else {
+		JS_DefineProperty(cx, result_object, "data", JSVAL_NULL, NULL, NULL, 0);
+		JS_DefineProperty(cx, result_object, "column_names", JSVAL_NULL, NULL, NULL, 0);
+	}	
+	return JS_TRUE;
+}
+
 // Provides a javascript function to query an sqlite3 database
 // This probably wants updating to not return JS_FALSE as that halts js execution
 // far better to return an error code in rsval which the javascript can handle
 static JSBool js_doquery(JSContext *cx, JSObject __attribute__((unused)) *obj, uintN argc, jsval *argv, jsval *rval) {
-	struct db_result *dbres;
+	struct js_db_result *dbres;
 	char *dbname;
 	char query[MAXTEXTLENGTH*2]; // for now queries will be at most 4096 bytes or will fail.
 								 // if this becomes a problem dynamic allocation of the utf8 buffer might be needed although getting the right size is a pita
 	//JSObject *result; // result object were creating
-	JSObject *resarray;
+	jsval resobject_jsval;
 	int myid, conversion_result;
 	char path[1024];
 	struct passwd *pw;
-
+	JSBool retval;
+	
 	if ((pw=getpwuid(getuid()))==NULL) {
 		fprintf(stderr, "Error getting user information\n");
 		return JS_FALSE;
@@ -353,35 +785,21 @@
 
 	myid=geteuid();
 	seteuid(getuid());
-	dbres = db_query(path, query);
+	dbres = js_db_query(path, query);
 	seteuid(myid);
 
 	if (!dbres) {
+		printf("Major error in javascript database query.\n");
 		return JS_FALSE;
 	}
 
-	if (0 == dbres->rows) {
-		// No results found (not an error)
-		return JS_TRUE;
-	}
-
-	resarray = dbresult_to_jsarray(cx, dbres);
-	if (!resarray) {
-		return JS_FALSE;
-	}
-/*	result = JS_NewObject(cx, &js_dbresultclass, NULL, NULL);
-	// seems pointless to create an object with a data array and a numrows when the array itself has a .length.
-	// also something here was making mw segv so it's being done wrong.
-	// so now we just return the array.
+	retval = dbresult_to_jsdbobject(cx, dbres, &resobject_jsval);
 	
-	JS_DefineProperty(cx, result, "numrows", INT_TO_JSVAL(dbres->rows), NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT);
-	JS_DefineProperty(cx, result, "data", OBJECT_TO_JSVAL(resarray), NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE);
-
-	//JS_SetPrivate(cx, result, dbres);
-*/
-	db_free(dbres);
-	*rval = OBJECT_TO_JSVAL(resarray);
-	return JS_TRUE;
+	
+	js_db_free(dbres);
+	
+	*rval = resobject_jsval;
+	return retval;
 }
 
 /* prints the type of a jsval */
@@ -427,7 +845,7 @@
 			fprintf(stderr, "js_exec: argc %d was NULL!\n",i);
 			continue;
 		}
-		//ucarg=utf8_to_jsstring(argvc[i], &uclen, &utferror);
+		// convert the arguments from utf8 to ucs2 for the javascript engine.
 		utferror=convert_string_charset(argvc[i], "UTF-8", strlen(argvc[i]), (char *)unicode_arg, "UCS-2", sizeof(jschar) * MAXTEXTLENGTH, NULL, NULL, NULL, NULL, "?");
 		if(utferror>=0) {
 			if(utferror & WOUTPUTTOOSHORT) {
@@ -471,6 +889,7 @@
 
 	if (er->linebuf != NULL) {
 		len = er->tokenptr - er->linebuf + 1;
+		if(len<2) len=2;
 		pointer = malloc(len); memset(pointer,
 			'-', len); pointer[len-1]='\0'; 
 		pointer[len-2]='^'; 
@@ -551,6 +970,8 @@
 		printf("The script '%s' does not appear to be utf-8.  Some characters may have been discared.  Please ensure this file is saved as UTF-8\n", filename);
 	}
 	
+	// length is currently the number of bytes iconv used (including the null char it created)
+	// thus the number of actual chars in the string required is:
 	length=(length/sizeof(jschar))-1;
 	/* Compile the js file specified */
 	/* script = JS_CompileScript(jscx, jsroot, body, len, filename, lineno); */
@@ -570,7 +991,10 @@
 		printf("Failed to execute js script: %s\n", filename);
 		return 0;
 	}
-
+	
+	// now the script has been run we can destroy it (the context retains the functions/objects it created)
+	
+	JS_DestroyScript(jscx, script);
 	return 1;
 }
 
@@ -593,6 +1017,16 @@
 	return 0;
 }
 
+// cleans up the javascript environment
+int stop_js(void)
+{
+	JS_DestroyContext(jscx);
+	JS_DestroyRuntime(jsrt);
+	return 0;
+	
+}
+
+// starts the javascript engine.
 int setup_js(void)
 {
 	JSBool builtins;
@@ -633,19 +1067,38 @@
 	JS_DefineFunction(jscx, jsroot, "exec", js_mwexec, 1, 0);
 	JS_DefineFunction(jscx, jsroot, "say", js_say, 1, 0);
 	JS_DefineFunction(jscx, jsroot, "wholist", js_wholist, 0, 1);
-	
+	JS_DefineFunction(jscx, jsroot, "rpc", js_rpc, 3, 0);
+	JS_DefineFunction(jscx, jsroot, "ipc", js_ipc, 2, 0);
+
 	JS_DefineProperty(jscx, jsroot, "whoami", STRING_TO_JSVAL(JS_NewStringCopyZ(jscx,user->name)), NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
 
+	JS_DefineFunction(jscx, jsroot, "beep", js_beep, 1, 0);
+	JS_DefineFunction(jscx, jsroot, "input", js_input, 2, 0);
+	JS_DefineFunction(jscx, jsroot, "termsize", js_termsize, 0, 0);
+
+	JS_DefineFunction(jscx, jsroot, "bind", js_bind, 2, 0);
+
+	// Set the bind type constants
+	JS_DefineProperty(jscx, jsroot, "K_BIND_EVENT", INT_TO_JSVAL(K_BIND_EVENT), NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
+	JS_DefineProperty(jscx, jsroot, "K_BIND_IPC", INT_TO_JSVAL(K_BIND_IPC), NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
+	JS_DefineProperty(jscx, jsroot, "K_BIND_ONOFF", INT_TO_JSVAL(K_BIND_ONOFF), NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
+	JS_DefineProperty(jscx, jsroot, "K_BIND_FORCE", INT_TO_JSVAL(K_BIND_FORCE), NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
+	JS_DefineProperty(jscx, jsroot, "K_BIND_SHUTDOWN", INT_TO_JSVAL(K_BIND_SHUTDOWN), NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
+	JS_DefineProperty(jscx, jsroot, "K_BIND_RPC", INT_TO_JSVAL(K_BIND_RPC), NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
+	JS_DefineProperty(jscx, jsroot, "K_BIND_ALIAS", INT_TO_JSVAL(K_BIND_ALIAS), NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
+
+	// Broadcast constant (for ipc,rpc)
+	JS_DefineProperty(jscx, jsroot, "K_BROADCAST", INT_TO_JSVAL(K_BROADCAST), NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
+	
+
 	/* not for bbs user */
 	if (is_local) {
 		JS_DefineFunction(jscx, jsroot, "dbquery", js_doquery, 2, 1);
 	}
 	
 	/* need additional functions : 
-	 * - one to bind functions to events (bind?) - yes, saves needing two files per javascript.  code written outside of a function is executed when the script loads
 	 * - one to load another script (include?) - possibly although most scripts are loaded from the .mwrc or using .load
 	 * - get system date/time - no, 'new Date()' will get this information for you in a Date object.
-	 * - input line of text (does anyone use this) - yes, it's nice to be able to prompt the user for extra information.  I specifically use it in my batchdo mwscript that allows me to specify a set of commands to do at once.
 	 */
 
 	return 0;

Modified: trunk/src/js.h
===================================================================
--- trunk/src/js.h	2007-05-11 17:42:08 UTC (rev 962)
+++ trunk/src/js.h	2007-05-21 09:22:26 UTC (rev 963)
@@ -3,4 +3,5 @@
 int load_jsfile(FILE *f, char *filename);
 int load_js(char *filename);
 int is_js(char *name);
+int stop_js(void);
 int setup_js(void);

Modified: trunk/src/main.c
===================================================================
--- trunk/src/main.c	2007-05-11 17:42:08 UTC (rev 962)
+++ trunk/src/main.c	2007-05-21 09:22:26 UTC (rev 963)
@@ -965,6 +965,7 @@
 	RoomDestroy(&myroom);
 	DestroyDirections();
 	ClearStack();
+	stop_js();
 	alarm_cleanup();
 
 	/* dont display logoff text if quiet, or if dropped */
@@ -974,7 +975,7 @@
 	}
 
 	mwlog("LOGOUT");
-
+	sleep(1); //dodgy hack for race condition in checkonoff, cunningly we currently get woken by the very message we're waiting for.
 	who_delete(getpid());
 #ifdef RWHO
 	rwhocli_userlogout(user->name);

Modified: trunk/src/sqlite.c
===================================================================
--- trunk/src/sqlite.c	2007-05-11 17:42:08 UTC (rev 962)
+++ trunk/src/sqlite.c	2007-05-21 09:22:26 UTC (rev 963)
@@ -99,6 +99,55 @@
 	return new;
 }
 
+struct js_db_result* js_db_query(char *dbname, char *query)
+{
+	struct js_db_result *new;
+
+	sqlite3 *db;
+//	printf("sizeof js_db_result:%d\n", (int)sizeof(struct js_db_result));
+	new = malloc(sizeof(struct js_db_result));
+	
+	if(new == NULL) {
+		return NULL;
+	}
+	
+	new->error_text=NULL;	
+	new->query_error=0;
+	new->db_error=0;
+	
+	
+	if (dbname == NULL) {
+		new->error_text=strdup("Database name was NULL");
+		new->db_error=-1;
+		return new;
+	}
+
+
+	db = db_open(dbname);
+	if (!db) {
+		new->error_text=strdup("Failed to open database");
+		new->db_error=-2;
+		return new;
+	}
+
+	new->query_result = malloc(sizeof(struct db_result));
+	if(new->query_result == NULL) {
+		new->error_text=strdup("malloc error for query result");
+		new->db_error=-3;
+	}
+	
+	new->query_result->rows = -1;
+	new->query_result->cols = -1;
+	new->query_result->colNames = NULL;
+	new->query_result->data = NULL;
+
+	new->query_error = sqlite3_exec(db, query, db_callback, new->query_result, &new->error_text);
+
+	db_close(db);
+	return new;
+}
+
+
 /* how many rows in that result */
 int db_numrows(struct db_result *result)
 {
@@ -170,3 +219,13 @@
 	free(result);
 }
 
+void js_db_free(struct js_db_result *result)
+{
+	if(result->query_result!=NULL) {
+		db_free(result->query_result);
+	}
+	if(result->error_text!=NULL) {
+		sqlite3_free(result->error_text);
+	}
+	free(result);
+}

Modified: trunk/src/sqlite.h
===================================================================
--- trunk/src/sqlite.h	2007-05-11 17:42:08 UTC (rev 962)
+++ trunk/src/sqlite.h	2007-05-21 09:22:26 UTC (rev 963)
@@ -13,6 +13,15 @@
 	struct db_data *data;
 };
 
+struct js_db_result {
+	struct db_result *query_result;
+	int db_error;
+	int query_error;
+	char *error_text;
+};
+
 struct db_result *db_query(char *dbname, char *query);
+struct js_db_result* js_db_query(char *dbname, char *query);
 void db_getrow(struct db_result *result, int *row);
 void db_free(struct db_result *result);
+void js_db_free(struct js_db_result *result);





More information about the mw-devel mailing list