Newer
Older
/* js_global.c */
/* Synchronet JavaScript "global" object properties/methods for all servers */
/* $Id$ */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright 2008 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 JS_THREADSAFE /* needed for JS_SetContextThread */
#include "md5.h"
#include "htmlansi.h"
#include "ini_file.h"
#include "js_rtpool.h"
#include "js_request.h"
/* SpiderMonkey: */
#include <jsfun.h>
#define MAX_ANSI_SEQ 16
#define MAX_ANSI_PARAMS 8
typedef struct {
scfg_t *cfg;
jsSyncMethodSpec *methods;
} private_t;
/* Global Object Properites */
enum {
GLOB_PROP_ERRNO
,GLOB_PROP_ERRNO_STR
,GLOB_PROP_SOCKET_ERRNO
};
static JSBool js_system_get(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
jsint tiny;
tiny = JSVAL_TO_INT(id);
switch(tiny) {
case GLOB_PROP_SOCKET_ERRNO:
JS_NewNumberValue(cx,ERROR_VALUE,vp);
break;
case GLOB_PROP_ERRNO_STR:
if((js_str=JS_NewStringCopyZ(cx, strerror(errno)))==NULL)
return(JS_FALSE);
*vp = STRING_TO_JSVAL(js_str);
break;
#define GLOBOBJ_FLAGS JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED
static jsSyncPropertySpec js_global_properties[] = {
/* name, tinyid, flags, ver */
{ "errno" ,GLOB_PROP_ERRNO ,GLOBOBJ_FLAGS, 310 },
{ "errno_str" ,GLOB_PROP_ERRNO_STR ,GLOBOBJ_FLAGS, 310 },
{ "socket_errno" ,GLOB_PROP_SOCKET_ERRNO ,GLOBOBJ_FLAGS, 310 },
typedef struct {
JSRuntime* runtime;
JSContext* cx;
JSContext* parent_cx;
JSObject* obj;
JSScript* script;
msg_queue_t* msg_queue;
js_branch_t branch;
JSErrorReporter error_reporter;
JSNative log;
} background_data_t;
static void background_thread(void* arg)
background_data_t* bg = (background_data_t*)arg;
jsval result=JSVAL_VOID;
jsval exit_code;
msgQueueAttach(bg->msg_queue);
JS_SetContextThread(bg->cx);
JS_BEGINREQUEST(bg->cx);
if(!JS_ExecuteScript(bg->cx, bg->obj, bg->script, &result)
&& JS_GetProperty(bg->cx, bg->obj, "exit_code", &exit_code))
result=exit_code;
js_EvalOnExit(bg->cx, bg->obj, &bg->branch);
js_enqueue_value(bg->cx, bg->msg_queue, result, NULL);
JS_DestroyScript(bg->cx, bg->script);
JS_ENDREQUEST(bg->cx);
JS_DestroyContext(bg->cx);
jsrt_Release(bg->runtime);
free(bg);
}
static void
js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
background_data_t* bg;
if((bg=(background_data_t*)JS_GetContextPrivate(cx))==NULL)
return;
/* Use parent's context private data */
JS_SetContextPrivate(cx, JS_GetContextPrivate(bg->parent_cx));
/* Call parent's error reporter */
bg->error_reporter(cx, message, report);
/* Restore our context private data */
JS_SetContextPrivate(cx, bg);
}
static JSBool js_BranchCallback(JSContext *cx, JSScript* script)
{
background_data_t* bg;
if((bg=(background_data_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
if(bg->parent_cx!=NULL && !JS_IsRunning(bg->parent_cx)) /* die when parent dies */
return(JS_FALSE);
return js_CommonBranchCallback(cx,&bg->branch);
static JSBool
js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSBool retval;
background_data_t* bg;
if((bg=(background_data_t*)JS_GetContextPrivate(cx))==NULL)
return JS_FALSE;
/* Use parent's context private data */
JS_SetContextPrivate(cx, JS_GetContextPrivate(bg->parent_cx));
/* Call parent's log() function */
retval = bg->log(cx, obj, argc, argv, rval);
/* Restore our context private data */
JS_SetContextPrivate(cx, bg);
return retval;
}
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/* Create a new value in the new context with a value from the original context */
/* Note: objects (including arrays) not currently supported */
static jsval* js_CopyValue(JSContext* cx, jsval val, JSContext* new_cx, jsval* rval)
{
*rval = JSVAL_VOID;
if(cx==new_cx
|| JSVAL_IS_BOOLEAN(val)
|| JSVAL_IS_NULL(val)
|| JSVAL_IS_VOID(val)
|| JSVAL_IS_INT(val))
*rval = val;
else if(JSVAL_IS_NUMBER(val)) {
jsdouble d;
if(JS_ValueToNumber(cx,val,&d))
JS_NewNumberValue(new_cx,d,rval);
}
else {
JSString* str;
size_t len;
char* p;
if((p=js_ValueToStringBytes(cx,val,&len)) != NULL
&& (str=JS_NewStringCopyN(new_cx,p,len)) != NULL)
*rval=STRING_TO_JSVAL(str);
}
return rval;
}
static JSBool
js_load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
char path[MAX_PATH+1];
uintN i;
const char* filename;
JSScript* script;
JSObject* js_argv;
JSObject* exec_obj;
JSContext* exec_cx=cx;
JSBool success;
JSBool background=JS_FALSE;
*rval=JSVAL_VOID;
if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL)
exec_obj=JS_GetScopeChain(cx);
if(JSVAL_IS_BOOLEAN(argv[argn]))
background=JSVAL_TO_BOOLEAN(argv[argn++]);
if(background) {
if((bg=(background_data_t*)malloc(sizeof(background_data_t)))==NULL)
return(JS_FALSE);
memset(bg,0,sizeof(background_data_t));
/* Setup default values for branch settings */
bg->branch.limit=JAVASCRIPT_BRANCH_LIMIT;
bg->branch.gc_interval=JAVASCRIPT_GC_INTERVAL;
bg->branch.yield_interval=JAVASCRIPT_YIELD_INTERVAL;
if(JS_GetProperty(cx, obj,"js",&val)) /* copy branch settings from parent */
memcpy(&bg->branch,JS_GetPrivate(cx,JSVAL_TO_OBJECT(val)),sizeof(bg->branch));
bg->branch.terminated=NULL; /* could be bad pointer at any time */
bg->branch.counter=0;
bg->branch.gc_attempts=0;
if((bg->runtime = jsrt_GetNew(JAVASCRIPT_MAX_BYTES, 1000, __FILE__, __LINE__))==NULL)
return(JS_FALSE);
if((bg->cx = JS_NewContext(bg->runtime, JAVASCRIPT_CONTEXT_STACK))==NULL)
return(JS_FALSE);
JS_BEGINREQUEST(bg->cx);
if((bg->obj=js_CreateCommonObjects(bg->cx
,NULL /* node-specific config */
,NULL /* additional global methods */
,0 /* uptime */
,"" /* hostname */
,"" /* socklib_desc */
,&bg->branch /* js */
,NULL /* client */
,INVALID_SOCKET /* client_socket */
,NULL /* server props */
return(JS_FALSE);
bg->msg_queue = msgQueueInit(NULL,MSG_QUEUE_BIDIR);
js_CreateQueueObject(bg->cx, bg->obj, "parent_queue", bg->msg_queue);
/* Save parent's error reporter (for later use by our error reporter) */
bg->error_reporter=JS_SetErrorReporter(cx,NULL);
JS_SetErrorReporter(cx,bg->error_reporter);
JS_SetErrorReporter(bg->cx,js_ErrorReporter);
/* Set our branch callback (which calls the generic branch callback) */
JS_SetContextPrivate(bg->cx, bg);
JS_SetBranchCallback(bg->cx, js_BranchCallback);
/* Save parent's 'log' function (for later use by our log function) */
if(JS_GetProperty(cx, obj, "log", &val)) {
JSFunction* func;
if((func=JS_ValueToFunction(cx, val))!=NULL && !(func->flags&JSFUN_INTERPRETED)) {
bg->log=func->u.n.native;
JS_DefineFunction(bg->cx, bg->obj
,"log", js_log, func->nargs, func->flags);
}
}
exec_cx = bg->cx;
exec_obj = bg->obj;
} else if(JSVAL_IS_OBJECT(argv[argn])) {
JSObject* tmp_obj=JSVAL_TO_OBJECT(argv[argn++]);
if(!JS_ObjectIsFunction(cx,tmp_obj)) /* Scope specified */
exec_obj=tmp_obj;
}
if(argn==argc) {
JS_ReportError(cx,"no filename specified");
return(JS_FALSE);
}
if((filename=js_ValueToStringBytes(cx, argv[argn++], NULL))==NULL)
if(argc>argn || background) {
if((js_argv=JS_NewArrayObject(exec_cx, 0, NULL)) == NULL)
JS_DefineProperty(exec_cx, exec_obj, "argv", OBJECT_TO_JSVAL(js_argv)
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
JS_SetElement(exec_cx, js_argv, i-argn, js_CopyValue(cx,argv[i],exec_cx,&val));
JS_DefineProperty(exec_cx, exec_obj, "argc", INT_TO_JSVAL(argc-argn)
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
}
rc=JS_SUSPENDREQUEST(cx);
errno = 0;
if(isfullpath(filename))
strcpy(path,filename);
else {
sprintf(path,"%s%s",p->cfg->mods_dir,filename);
if(p->cfg->mods_dir[0]==0 || !fexistcase(path))
sprintf(path,"%s%s",p->cfg->exec_dir,filename);
JS_RESUMEREQUEST(cx, rc);
JS_ClearPendingException(exec_cx);
if((script=JS_CompileFile(exec_cx, exec_obj, path))==NULL)
return(JS_FALSE);
if(background) {
bg->script = script;
*rval = OBJECT_TO_JSVAL(js_CreateQueueObject(cx, obj, NULL, bg->msg_queue));
JS_ENDREQUEST(bg->cx);
success = _beginthread(background_thread,0,bg)!=-1;
} else {
success = JS_ExecuteScript(exec_cx, exec_obj, script, rval);
JS_DestroyScript(exec_cx, script);
}
return(success);
}
static JSBool
js_format(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
char* p;
JSString* str;
if((p=js_sprintf(cx, 0, argc, argv))==NULL) {
JS_ReportError(cx,"js_sprintf failed");
}
str = JS_NewStringCopyZ(cx, p);
js_sprintf_free(p);
if(str==NULL)
return(JS_FALSE);
static JSBool
js_yield(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
BOOL forced=TRUE;
if(argc)
JS_ValueToBoolean(cx, argv[0], &forced);
rc=JS_SUSPENDREQUEST(cx);
if(forced) {
YIELD();
} else {
MAYBE_YIELD();
}
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
static JSBool
js_mswait(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
clock_t start=msclock();
JS_ValueToInt32(cx,argv[0],&val);
rc=JS_SUSPENDREQUEST(cx);
JS_RESUMEREQUEST(cx, rc);
JS_NewNumberValue(cx,msclock()-start,rval);
static JSBool
js_random(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
int32 val=100;
if(argc)
JS_ValueToInt32(cx,argv[0],&val);
JS_NewNumberValue(cx,sbbs_random(val),rval);
return(JS_TRUE);
}
static JSBool
js_time(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JS_NewNumberValue(cx,time(NULL),rval);
return(JS_TRUE);
}
static JSBool
js_beep(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
int32 freq=500;
int32 dur=500;
JS_ValueToInt32(cx,argv[0],&freq);
JS_ValueToInt32(cx,argv[1],&dur);
rc=JS_SUSPENDREQUEST(cx);
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
static JSBool
js_exit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
if(argc)
JS_DefineProperty(cx, obj, "exit_code", argv[0]
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
static JSBool
js_crc16(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
char* p;
size_t len;
if(JSVAL_IS_VOID(argv[0]))
return(JS_TRUE);
if((p=js_ValueToStringBytes(cx, argv[0], &len))==NULL)
return(JS_FALSE);
rc=JS_SUSPENDREQUEST(cx);
*rval = INT_TO_JSVAL(crc16(p,len));
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
static JSBool
js_crc32(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
char* p;
size_t len;
if(JSVAL_IS_VOID(argv[0]))
return(JS_TRUE);
if((p=js_ValueToStringBytes(cx, argv[0], &len))==NULL)
return(JS_FALSE);
rc=JS_SUSPENDREQUEST(cx);
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
static JSBool
js_chksum(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
ulong sum=0;
char* p;
size_t len;
if(JSVAL_IS_VOID(argv[0]))
return(JS_TRUE);
if((p=js_ValueToStringBytes(cx, argv[0], &len))==NULL)
return(JS_FALSE);
rc=JS_SUSPENDREQUEST(cx); /* 3.8 seconds on Deuce's computer when len==UINT_MAX/8 */
while(len--) sum+=*(p++);
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
static JSBool
js_ascii(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
char* p;
char str[2];
int32 i=0;
JSString* js_str;
if(JSVAL_IS_VOID(argv[0]))
return(JS_TRUE);
if(JSVAL_IS_STRING(argv[0])) { /* string to ascii-int */
if((p=JS_GetStringBytes(JSVAL_TO_STRING(argv[0])))==NULL)
return(JS_FALSE);
return(JS_TRUE);
}
/* ascii-int to str */
JS_ValueToInt32(cx,argv[0],&i);
str[0]=(uchar)i;
str[1]=0;
if((js_str = JS_NewStringCopyZ(cx, str))==NULL)
return(JS_FALSE);
*rval = STRING_TO_JSVAL(js_str);
return(JS_TRUE);
}
static JSBool
js_ctrl(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
char ch;
char* p;
char str[2];
int32 i=0;
JSString* js_str;
if(JSVAL_IS_VOID(argv[0]))
return(JS_TRUE);
if(JSVAL_IS_STRING(argv[0])) {
if((p=JS_GetStringBytes(JSVAL_TO_STRING(argv[0])))==NULL)
return(JS_FALSE);
ch=*p;
} else {
JS_ValueToInt32(cx,argv[0],&i);
ch=(char)i;
}
str[0]=toupper(ch)&~0x40;
str[1]=0;
if((js_str = JS_NewStringCopyZ(cx, str))==NULL)
return(JS_FALSE);
*rval = STRING_TO_JSVAL(js_str);
return(JS_TRUE);
}
static JSBool
js_ascii_str(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
char* p;
char* buf;
JSString* str;
if(JSVAL_IS_VOID(argv[0]))
return(JS_TRUE);
if((p=js_ValueToStringBytes(cx, argv[0], NULL))==NULL)
return(JS_FALSE);
if((buf=strdup(p))==NULL)
return(JS_FALSE);
ascii_str(buf);
str = JS_NewStringCopyZ(cx, buf);
free(buf);
if(str==NULL)
*rval = STRING_TO_JSVAL(str);
return(JS_TRUE);
}
static JSBool
js_strip_ctrl(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
char* p;
char* buf;
JSString* js_str;
if(JSVAL_IS_VOID(argv[0]))
return(JS_TRUE);
if((p=js_ValueToStringBytes(cx, argv[0], NULL))==NULL)
return(JS_FALSE);
if((buf=strdup(p))==NULL)
return(JS_FALSE);
strip_ctrl(buf);
js_str = JS_NewStringCopyZ(cx, buf);
free(buf);
if(js_str==NULL)
return(JS_FALSE);
*rval = STRING_TO_JSVAL(js_str);
return(JS_TRUE);
}
static JSBool
js_strip_exascii(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
char* p;
char* buf;
JSString* js_str;
if(JSVAL_IS_VOID(argv[0]))
return(JS_TRUE);
if((p=js_ValueToStringBytes(cx, argv[0], NULL))==NULL)
return(JS_FALSE);
if((buf=strdup(p))==NULL)
return(JS_FALSE);
strip_exascii(buf);
js_str = JS_NewStringCopyZ(cx, buf);
free(buf);
if(js_str==NULL)
return(JS_FALSE);
*rval = STRING_TO_JSVAL(js_str);
return(JS_TRUE);
}
static JSBool
js_lfexpand(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
ulong i,j;
char* inbuf;
char* outbuf;
JSString* str;
if(JSVAL_IS_VOID(argv[0]))
return(JS_TRUE);
if((inbuf=js_ValueToStringBytes(cx, argv[0], NULL))==NULL)
return(JS_FALSE);
if((outbuf=(char*)malloc((strlen(inbuf)*2)+1))==NULL)
return(JS_FALSE);
for(i=j=0;inbuf[i];i++) {
if(inbuf[i]=='\n' && (!i || inbuf[i-1]!='\r'))
outbuf[j++]='\r';
outbuf[j++]=inbuf[i];
}
outbuf[j]=0;
str = JS_NewStringCopyZ(cx, outbuf);
free(outbuf);
if(str==NULL)
return(JS_FALSE);
*rval = STRING_TO_JSVAL(str);
return(JS_TRUE);
}
static int get_prefix(char *text, int *bytes, int *len, int maxlen)
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
{
int tmp_prefix_bytes,tmp_prefix_len;
int expect;
int depth;
*bytes=0;
*len=0;
tmp_prefix_bytes=0;
tmp_prefix_len=0;
depth=0;
expect=1;
if(text[0]!=' ')
expect=2;
while(expect) {
tmp_prefix_bytes++;
/* Skip CTRL-A codes */
while(text[tmp_prefix_bytes-1]=='\x01') {
tmp_prefix_bytes++;
if(text[tmp_prefix_bytes-1]=='\x01')
break;
tmp_prefix_bytes++;
}
tmp_prefix_len++;
if(text[tmp_prefix_bytes-1]==0 || text[tmp_prefix_bytes-1]=='\n' || text[tmp_prefix_bytes-1]=='\r')
break;
switch(expect) {
case 1: /* At start of possible quote (Next char should be space) */
if(text[tmp_prefix_bytes-1]!=' ')
expect=0;
else
expect++;
break;
case 2: /* At start of nick (next char should be alphanum or '>') */
case 3: /* At second nick initial (next char should be alphanum or '>') */
case 4: /* At third nick initial (next char should be alphanum or '>') */
if(text[tmp_prefix_bytes-1]==' ' || text[tmp_prefix_bytes-1]==0)
expect=0;
else
if(text[tmp_prefix_bytes-1]=='>')
expect=6;
else
expect++;
break;
case 5: /* After three regular chars, next HAS to be a '>') */
if(text[tmp_prefix_bytes-1]!='>')
expect=0;
else
expect++;
break;
case 6: /* At '>' next char must be a space */
if(text[tmp_prefix_bytes-1]!=' ')
expect=0;
else {
expect=1;
*len=tmp_prefix_len;
*bytes=tmp_prefix_bytes;
depth++;
/* Some editors don't put double spaces in between */
if(text[tmp_prefix_bytes]!=' ')
expect++;
}
break;
default:
expect=0;
break;
}
}
if(*bytes >= maxlen) {
lprintf(LOG_CRIT, "Prefix bytes %u is larger than buffer (%u) here: %*.*s",*bytes,maxlen,maxlen,maxlen,text);
*bytes=maxlen-1;
return(depth);
}
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
static void outbuf_append(char **outbuf, char **outp, char *append, int len, int *outlen)
{
char *p;
/* Terminate outbuf */
**outp=0;
/* Check if there's room */
if(*outp - *outbuf + len < *outlen) {
memcpy(*outp, append, len);
*outp+=len;
return;
}
/* Not enough room, double the size. */
*outlen *= 2;
p=realloc(*outbuf, *outlen);
if(p==NULL) {
/* Can't do it. */
*outlen/=2;
return;
}
/* Set outp for new buffer */
*outp=p+(*outp - *outbuf);
*outbuf=p;
memcpy(*outp, append, len);
*outp+=len;
return;
}
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
static int compare_prefix(char *old_prefix, int old_prefix_bytes, char *new_prefix, int new_prefix_bytes)
{
int i;
if(new_prefix_bytes != old_prefix_bytes) {
if(new_prefix_bytes < old_prefix_bytes) {
if(memcmp(old_prefix, new_prefix, new_prefix_bytes)!=0)
return(-1);
for(i=new_prefix_bytes; i<old_prefix_bytes; i++) {
if(!isspace(old_prefix[i]))
return(-1);
}
}
else {
if(memcmp(old_prefix, new_prefix, old_prefix_bytes)!=0)
return(-1);
for(i=old_prefix_bytes; i<new_prefix_bytes; i++) {
if(!isspace(new_prefix[i]))
return(-1);
}
}
return(0);
}
if(memcmp(old_prefix,new_prefix,new_prefix_bytes)!=0)
return(-1);
return(0);
}
static JSBool
js_word_wrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
int32 l,len=79;
int32 oldlen=79;
int32 crcount=0;
JSBool handle_quotes=JS_TRUE;
int ocol=1;
int icol=1;
uchar* inbuf;
char* outbuf;
char* outp;
char* prefix=NULL;
int prefix_len=0;
int prefix_bytes=0;
int old_prefix_bytes=0;
int outbuf_size=0;
if(JSVAL_IS_VOID(argv[0]))
return(JS_TRUE);
if((inbuf=js_ValueToStringBytes(cx, argv[0], NULL))==NULL)
return(JS_FALSE);
outbuf_size=strlen(inbuf)*3+1;
if((outbuf=(char*)malloc(outbuf_size))==NULL)
outp=outbuf;
if(argc>1)
JS_ValueToInt32(cx,argv[1],&len);
if(argc>2)
JS_ValueToInt32(cx,argv[2],&oldlen);
if(argc>3 && JSVAL_IS_BOOLEAN(argv[3]))
handle_quotes=JSVAL_TO_BOOLEAN(argv[3]);
if((linebuf=(char*)malloc((len*2)+2))==NULL) /* room for ^A codes ToDo: This isn't actually "enough" room */
return(JS_FALSE);
if(handle_quotes) {
if((prefix=(char *)malloc((len*2)+2))==NULL) { /* room for ^A codes ToDo: This isn't actually "enough" room */
free(linebuf);
return(JS_FALSE);
rc=JS_SUSPENDREQUEST(cx);
/* Get prefix from the first line (ouch) */
l=0;
i=0;
if(handle_quotes && (quote_count=get_prefix(inbuf, &prefix_bytes, &prefix_len, len*2+2))!=0) {
i+=prefix_bytes;
if(prefix_len>len/3*2) {
/* This prefix is insane (more than 2/3rds of the new width) hack it down to size */
/* Since we're hacking it, we will always end up with a hardcr on this line. */
/* ToDo: Something prettier would be nice. */
sprintf(prefix," %d> ",quote_count);
prefix_len=strlen(prefix);
prefix_bytes=strlen(prefix);
}
memcpy(prefix,inbuf,prefix_bytes);
/* Terminate prefix */
prefix[prefix_bytes]=0;
}
memcpy(linebuf,prefix,prefix_bytes);
l=prefix_bytes;
ocol=prefix_len+1;
icol=prefix_len+1;
old_prefix_bytes=prefix_bytes;
}
for(; inbuf[i]; i++) {
if(l>=len*2+2) {
l-=4;
linebuf[l]=0;
lprintf(LOG_CRIT, "Word wrap line buffer exceeded... munging line %s",linebuf);
}
switch(inbuf[i]) {
case '\r':
crcount++;
break;
case '\n':
if(handle_quotes && (quote_count=get_prefix(inbuf+i+1, &prefix_bytes, &prefix_len, len*2+2))!=0) {
/* Move the input pointer offset to the last char of the prefix */
i+=prefix_bytes;
}
if(!inbuf[i+1]) { /* EOF */
linebuf[l++]='\r';
linebuf[l++]='\n';
outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size);
l=0;
ocol=1;
}
/* If there's a new prefix, it is a hardcr */
else if(compare_prefix(prefix, old_prefix_bytes, inbuf+i+1-prefix_bytes, prefix_bytes)!=0) {
if(prefix_len>len/3*2) {
/* This prefix is insane (more than 2/3rds of the new width) hack it down to size */
/* Since we're hacking it, we will always end up with a hardcr on this line. */
/* ToDo: Something prettier would be nice. */
sprintf(prefix," %d> ",quote_count);
prefix_len=strlen(prefix);
prefix_bytes=strlen(prefix);
}
memcpy(prefix,inbuf+i+1-prefix_bytes,prefix_bytes);
/* Terminate prefix */
prefix[prefix_bytes]=0;
}
linebuf[l++]='\r';
linebuf[l++]='\n';
outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size);
memcpy(linebuf,prefix,prefix_bytes);
l=prefix_bytes;
ocol=prefix_len+1;
old_prefix_bytes=prefix_bytes;
}
else if(isspace(inbuf[i+1]) && inbuf[i+1] != '\n' && inbuf[i+1] != '\r') { /* Next line starts with whitespace. This is a "hard" CR. */
linebuf[l++]='\r';
linebuf[l++]='\n';
outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size);
l=prefix_bytes;
ocol=prefix_len+1;
}
else {
if(icol < oldlen) { /* If this line is overly long, It's impossible for the next word to fit */
/* k will equal the length of the first word on the next line */
for(k=0; inbuf[i+1+k] && (!isspace(inbuf[i+1+k])); k++);
if(icol+k+1 < oldlen) { /* The next word would have fit but isn't here. Must be a hard CR */
linebuf[l++]='\r';
linebuf[l++]='\n';
outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size);
if(prefix)
memcpy(linebuf,prefix,prefix_bytes);
l=prefix_bytes;
}
else { /* Not a hard CR... add space if needed */
linebuf[l++]=' ';