1 /* morla - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
2  *                                      <bakunin@morlardf.net>
3  *
4  * This source code is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Public License as published
6  * by the Free Software Foundation; either version 2 of the License,
7  * or (at your option) any later version.
8  *
9  * This source code is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12  * Please refer to the GNU Public License for more details.
13  *
14  * You should have received a copy of the GNU Public License along with
15  * this source code; if not, write to:
16  * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #else
22 # error Use configure; make; make install
23 #endif
24 
25 #include "editor.h"
26 
27 #ifdef USE_JS
28 
29 #ifdef USE_NGS_JS
30 static int js_stderr (void *context, unsigned char *buffer,
31 		      unsigned int amount);
32 
33 static JSMethodResult js_alert (void *context, JSInterpPtr interp, int argc,
34 				JSType * argv, JSType * result_return,
35 				char *error_return);
36 static JSMethodResult js_confirm (void *context, JSInterpPtr interp, int argc,
37 				  JSType * argv, JSType * result_return,
38 				  char *error_return);
39 static JSMethodResult js_get_value (void *context, JSInterpPtr interp,
40 				    int argc, JSType * argv,
41 				    JSType * result_return,
42 				    char *error_return);
43 static JSMethodResult js_set_value (void *context, JSInterpPtr interp,
44 				    int argc, JSType * argv,
45 				    JSType * result_return,
46 				    char *error_return);
47 static gchar *js_set_value_real (JSType * var);
48 static JSMethodResult js_morla_version (void *context, JSInterpPtr interp,
49 					int argc, JSType * argv,
50 					JSType * result_return,
51 					char *error_return);
52 
53 #elif USE_MOZILLA_JS
54 static void js_stderr (JSContext * context, const char *message,
55 		       JSErrorReport * report);
56 
57 static JSBool js_alert (JSContext * cx, JSObject * obj, uintN argc,
58 			jsval * argv, jsval * rval);
59 static JSBool js_confirm (JSContext * cx, JSObject * obj, uintN argc,
60 			  jsval * argv, jsval * rval);
61 static JSBool js_get_value (JSContext * cx, JSObject * obj, uintN argc,
62 			    jsval * argv, jsval * rval);
63 static JSBool js_set_value (JSContext * cx, JSObject * obj, uintN argc,
64 			    jsval * argv, jsval * rval);
65 static JSBool js_version (JSContext * cx, JSObject * obj, uintN argc,
66 			  jsval * argv, jsval * rval);
67 
68 static JSClass globalClass = {
69   "Global", 0, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
70   JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
71   JS_FinalizeStub
72 };
73 
74 static JSFunctionSpec functions[] = {
75   {"morla_alert", js_alert, 0},
76   {"morla_confirm", js_confirm, 0},
77   {"morla_get_value", js_get_value, 0},
78   {"morla_set_value", js_set_value, 0},
79   {"morla_version", js_version, 0},
80   {0}
81 };
82 #endif
83 
84 /* Javascript engine init: */
85 struct js_t *
js_new(void)86 js_new (void)
87 {
88   struct js_t *js;
89 
90 #ifdef USE_NGS_JS
91   JSInterpPtr interp;
92   JSInterpOptions options;
93 
94   JSType var;
95 
96   js_init_default_options (&options);
97 
98   options.s_stderr = js_stderr;
99   options.s_context = NULL;
100 
101   interp = js_create_interp (&options);
102 
103   js = g_malloc0 (sizeof (struct js_t));
104   js->interp = interp;
105 
106   /* Variables: */
107   var.type = JS_TYPE_BOOLEAN;
108   var.u.i = JS_MORLA_OK;
109   js_set_var (interp, "MORLA_OK", &var);
110 
111   var.type = JS_TYPE_BOOLEAN;
112   var.u.i = JS_MORLA_FAILURE;
113   js_set_var (interp, "MORLA_FAILURE", &var);
114 
115   var.type = JS_TYPE_INTEGER;
116   var.u.i = 0;
117   js_set_var (interp, "MORLA_VALUE", &var);
118 
119   var.type = JS_TYPE_INTEGER;
120   var.u.i = 1;
121   js_set_var (interp, "MORLA_LANGUAGE", &var);
122 
123   var.type = JS_TYPE_INTEGER;
124   var.u.i = 2;
125   js_set_var (interp, "MORLA_DATATYPE", &var);
126 
127   var.type = JS_TYPE_INTEGER;
128   var.u.i = 3;
129   js_set_var (interp, "MORLA_TYPE", &var);
130 
131   var.type = JS_TYPE_INTEGER;
132   var.u.i = RDF_OBJECT_BLANK;
133   js_set_var (interp, "MORLA_RDF_BLANKNODE", &var);
134 
135   var.type = JS_TYPE_INTEGER;
136   var.u.i = RDF_OBJECT_LITERAL;
137   js_set_var (interp, "MORLA_RDF_LITERAL", &var);
138 
139   var.type = JS_TYPE_INTEGER;
140   var.u.i = RDF_OBJECT_RESOURCE;
141   js_set_var (interp, "MORLA_RDF_RESOURCE", &var);
142 
143   /* Functions: */
144   js_create_global_method (interp, "morla_alert", js_alert, js, NULL);
145   js_create_global_method (interp, "morla_confirm", js_confirm, js, NULL);
146   js_create_global_method (interp, "morla_get_value", js_get_value, js, NULL);
147   js_create_global_method (interp, "morla_set_value", js_set_value, js, NULL);
148   js_create_global_method (interp, "morla_version", js_morla_version, js,
149 			   NULL);
150 
151 #elif USE_MOZILLA_JS
152   JSRuntime *rt;
153   JSContext *cx;
154   JSObject *global;
155 
156   GString *str;
157   gchar *buffer;
158   jsval rval;
159 
160   rt = JS_NewRuntime (0x400000L);
161   cx = JS_NewContext (rt, 0x1000);
162 
163   global = JS_NewObject (cx, &globalClass, NULL, NULL);
164 
165   JS_InitStandardClasses (cx, global);
166   JS_DefineFunctions (cx, global, functions);
167 
168   str = g_string_new (NULL);
169   g_string_append_printf (str, "var MORLA_OK=%d;\n", JS_MORLA_OK);
170   g_string_append_printf (str, "var MORLA_FAILURE=%d;\n", JS_MORLA_FAILURE);
171   g_string_append_printf (str, "var MORLA_VALUE=0;\n");
172   g_string_append_printf (str, "var MORLA_LANGUAGE=1;\n");
173   g_string_append_printf (str, "var MORLA_DATATYPE=2;\n");
174   g_string_append_printf (str, "var MORLA_TYPE=3;\n");
175   g_string_append_printf (str, "var MORLA_RDF_BLANKNODE=%d;\n",
176 			  RDF_OBJECT_BLANK);
177   g_string_append_printf (str, "var MORLA_RDF_LITERAL=%d;\n",
178 			  RDF_OBJECT_LITERAL);
179   g_string_append_printf (str, "var MORLA_RDF_RESOURCE=%d;\n",
180 			  RDF_OBJECT_RESOURCE);
181   g_string_append_printf (str, "var morla_status=0;\n");
182 
183   buffer = g_string_free (str, FALSE);
184   JS_EvaluateScript (cx, global, buffer, strlen (buffer), "script", 0, &rval);
185   g_free (buffer);
186 
187   JS_SetErrorReporter (cx, js_stderr);
188 
189   js = g_malloc0 (sizeof (struct js_t));
190   js->rt = rt;
191   js->cx = cx;
192   js->global = global;
193 
194   JS_SetContextPrivate (cx, js);
195 #endif
196 
197   return js;
198 }
199 
200 /* Destroy everything: */
201 void
js_destroy(struct js_t * js)202 js_destroy (struct js_t *js)
203 {
204   if (!js)
205     return;
206 
207 #ifdef USE_NGS_JS
208   js_destroy_interp (js->interp);
209 
210 #elif USE_MOZILLA_JS
211   JS_DestroyContext (js->cx);
212   JS_DestroyRuntime (js->rt);
213 #endif
214 
215   g_free (js);
216 }
217 
218 /* Change the current widget: */
219 void
js_current_widget(struct js_t * js,GtkWidget * widget)220 js_current_widget (struct js_t *js, GtkWidget * widget)
221 {
222   if (js && widget)
223     js->widget = widget;
224 }
225 
226 /* Evaluate a js script. The script is ok if the morla_status variabile will be
227  * setted to: MORLA_OK: */
228 gboolean
js_evaluate(struct js_t * js,gchar * buffer)229 js_evaluate (struct js_t *js, gchar * buffer)
230 {
231 #ifdef USE_NGS_JS
232   JSType type;
233 
234   type.type = JS_TYPE_BOOLEAN;
235   type.u.i = 0;
236 
237   js_set_var (js->interp, "morla_status", &type);
238 
239   js_eval (js->interp, buffer);
240 
241   js_get_var (js->interp, "morla_status", &type);
242 
243   if (type.type == JS_TYPE_BOOLEAN || type.type == JS_TYPE_INTEGER)
244     return type.u.i == JS_MORLA_OK ? JS_MORLA_OK : JS_MORLA_FAILURE;
245 
246   return JS_MORLA_FAILURE;
247 #elif USE_MOZILLA_JS
248   jsval rval;
249   gchar *str;
250 
251   str = "morla_status=0;\n";
252   if (JS_EvaluateScript
253       (js->cx, js->global, str, strlen (str), "script", 0, &rval) == JS_FALSE)
254     return JS_MORLA_FAILURE;
255 
256   if (JS_EvaluateScript
257       (js->cx, js->global, buffer, strlen (buffer), NULL, 0,
258        &rval) == JS_FALSE)
259     return JS_MORLA_FAILURE;
260 
261   str = "morla_status;\n";
262   if (JS_EvaluateScript
263       (js->cx, js->global, str, strlen (str), "script", 0, &rval) == JS_FALSE)
264     return JS_MORLA_FAILURE;
265 
266   return !JSVAL_TO_DOUBLE (rval) ? JS_MORLA_OK : JS_MORLA_FAILURE;
267 #endif
268 }
269 
270 #ifdef USE_NGS_JS
271 static int
js_stderr(void * context,unsigned char * buffer,unsigned int amount)272 js_stderr (void *context, unsigned char *buffer, unsigned int amount)
273 {
274   return fwrite (buffer, 1, amount, stderr);
275 }
276 #elif USE_MOZILLA_JS
277 static void
js_stderr(JSContext * context,const char * message,JSErrorReport * report)278 js_stderr (JSContext * context, const char *message, JSErrorReport * report)
279 {
280   if (!report)
281     {
282       g_fprintf (stderr, "%s", message);
283       return;
284     }
285 
286   g_fprintf (stderr, "Line %d: %s\n", report->lineno, message);
287 }
288 #endif
289 
290 /* FUNCTIONS ********************************************************************/
291 
292 #ifdef USE_NGS_JS
293 /* Alert: */
294 static gchar *
js_alert_real(int argc,JSType * argv)295 js_alert_real (int argc, JSType * argv)
296 {
297   GString *str = g_string_new (NULL);
298   gint i;
299 
300   for (i = 0; i < argc; i++)
301     {
302       JSType *arg = &argv[i];
303 
304       switch (arg->type)
305 	{
306 	case JS_TYPE_STRING:
307 	  if (i > 0)
308 	    str = g_string_append (str, " ");
309 
310 	  if (arg->u.s && arg->u.s->data)
311 	    str =
312 	      g_string_append_len (str, (const gchar *) arg->u.s->data,
313 				   arg->u.s->len);
314 	  break;
315 
316 	case JS_TYPE_INTEGER:
317 	case JS_TYPE_BOOLEAN:
318 	  if (i > 0)
319 	    str = g_string_append (str, " ");
320 
321 	  g_string_append_printf (str, "%ld", arg->u.i);
322 	  break;
323 
324 	case JS_TYPE_DOUBLE:
325 	  if (i > 0)
326 	    str = g_string_append (str, " ");
327 
328 	  g_string_append_printf (str, "%f", arg->u.d);
329 	  break;
330 
331 	case JS_TYPE_ARRAY:
332 	  if (i > 0)
333 	    str = g_string_append (str, " ");
334 
335 	  g_string_append_printf (str, "Array");
336 	  break;
337 
338 	default:
339 	  return NULL;
340 	  break;
341 	}
342     }
343 
344   return g_string_free (str, FALSE);
345 }
346 
347 static JSMethodResult
js_alert(void * context,JSInterpPtr interp,int argc,JSType * argv,JSType * result_return,char * error_return)348 js_alert (void *context, JSInterpPtr interp, int argc, JSType * argv,
349 	  JSType * result_return, char *error_return)
350 {
351   gchar *buffer;
352 
353   if (!(buffer = js_alert_real (argc, argv)))
354     {
355       sprintf (error_return, "morla_alert: illegal argument");
356       return JS_ERROR;
357     }
358 
359 
360   dialog_msg (buffer);
361   g_free (buffer);
362 
363   result_return->type = JS_TYPE_NULL;
364   return JS_OK;
365 }
366 
367 /* Confirm: */
368 static JSMethodResult
js_confirm(void * context,JSInterpPtr interp,int argc,JSType * argv,JSType * result_return,char * error_return)369 js_confirm (void *context, JSInterpPtr interp, int argc, JSType * argv,
370 	    JSType * result_return, char *error_return)
371 {
372   gchar *buffer;
373 
374   if (!(buffer = js_alert_real (argc, argv)))
375     {
376       sprintf (error_return, "morla_confirm: illegal argument");
377       return JS_ERROR;
378     }
379 
380   if (dialog_ask (buffer) == GTK_RESPONSE_OK)
381     result_return->u.i = 1;
382   else
383     result_return->u.i = 0;
384 
385   g_free (buffer);
386 
387   result_return->type = JS_TYPE_BOOLEAN;
388   return JS_OK;
389 }
390 
391 static JSMethodResult
js_get_value(void * context,JSInterpPtr interp,int argc,JSType * argv,JSType * result_return,char * error_return)392 js_get_value (void *context, JSInterpPtr interp, int argc, JSType * argv,
393 	      JSType * result_return, char *error_return)
394 {
395   struct js_t *js = context;
396   struct template_value_t *value;
397 
398   value = template_value_new (js->widget);
399 
400   js_type_make_array (js->interp, result_return, 4);
401 
402   if (value->value)
403     js_type_make_string (js->interp, &result_return->u.array->data[0],
404 			 (unsigned char *) value->value,
405 			 strlen (value->value));
406   else
407     js_type_make_string (js->interp, &result_return->u.array->data[0],
408 			 (unsigned char *) "", 0);
409 
410   if (value->lang)
411     js_type_make_string (js->interp, &result_return->u.array->data[1],
412 			 (unsigned char *) value->lang, strlen (value->lang));
413   else
414     js_type_make_string (js->interp, &result_return->u.array->data[1],
415 			 (unsigned char *) "", 0);
416 
417   if (value->datatype)
418     js_type_make_string (js->interp, &result_return->u.array->data[2],
419 			 (unsigned char *) value->datatype,
420 			 strlen (value->datatype));
421   else
422     js_type_make_string (js->interp, &result_return->u.array->data[2],
423 			 (unsigned char *) "", 0);
424 
425   result_return->u.array->data[3].type = JS_TYPE_INTEGER;
426   result_return->u.array->data[3].u.i = value->type;
427 
428   template_value_free (value);
429 
430   return JS_OK;
431 }
432 
433 static JSMethodResult
js_set_value(void * context,JSInterpPtr interp,int argc,JSType * argv,JSType * result_return,char * error_return)434 js_set_value (void *context, JSInterpPtr interp, int argc, JSType * argv,
435 	      JSType * result_return, char *error_return)
436 {
437   struct template_value_t *value;
438   struct js_t *js = context;
439 
440   if (argc != 1)
441     {
442       sprintf (error_return, "morla_set_value: too many argument");
443       return JS_ERROR;
444     }
445 
446   if (argv[0].type != JS_TYPE_ARRAY || !argv[0].u.array)
447     {
448       sprintf (error_return, "morla_set_value: the argument is not an array");
449       return JS_ERROR;
450     }
451 
452   value = g_malloc0 (sizeof (struct template_value_t));
453 
454 #define JS_SET_VALUE( x , y ) \
455   if (argv[0].u.array->length >= y + 1) \
456     value->x = js_set_value_real (argv[0].u.array->data + y); \
457   else \
458     value->x = NULL; \
459 
460   JS_SET_VALUE (value, 0);
461   JS_SET_VALUE (lang, 1);
462   JS_SET_VALUE (datatype, 2);
463 
464   if (argv[0].u.array->length >= 3 && argv[0].u.array->data + 3)
465     {
466       JSType *var = argv[0].u.array->data + 3;
467 
468       if (var->type == JS_TYPE_INTEGER && var->u.i >= 0 && var->u.i <= 3)
469 	value->type = var->u.i;
470       else
471 	value->type = RDF_OBJECT_RESOURCE;
472 
473     }
474 
475   template_value_set (js->widget, value);
476   template_value_free (value);
477 
478   result_return->type = JS_TYPE_NULL;
479   return JS_OK;
480 }
481 
482 static gchar *
js_set_value_real(JSType * var)483 js_set_value_real (JSType * var)
484 {
485   if (var->type == JS_TYPE_INTEGER || var->type == JS_TYPE_BOOLEAN)
486     return g_strdup_printf ("%ld", var->u.i);
487 
488   else if (var->type == JS_TYPE_STRING && var->u.s->len)
489     return g_strndup ((const gchar *) var->u.s->data, var->u.s->len);
490 
491   else if (var->type == JS_TYPE_DOUBLE)
492     return g_strdup_printf ("%f", var->u.d);
493 
494   return NULL;
495 }
496 
497 static JSMethodResult
js_morla_version(void * context,JSInterpPtr interp,int argc,JSType * argv,JSType * result_return,char * error_return)498 js_morla_version (void *context, JSInterpPtr interp, int argc, JSType * argv,
499 		  JSType * result_return, char *error_return)
500 {
501   struct js_t *js = context;
502   js_type_make_string (js->interp, result_return, (unsigned char *) VERSION,
503 		       strlen (VERSION));
504   return JS_OK;
505 }
506 
507 #elif USE_MOZILLA_JS
508 static JSBool
js_alert(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)509 js_alert (JSContext * cx, JSObject * obj, uintN argc, jsval * argv,
510 	  jsval * rval)
511 {
512   JSString *str;
513 
514   if ((!JSVAL_IS_STRING (argv[0]) && !JSVAL_IS_NUMBER (argv[0]))
515       || !(str = JS_ValueToString (cx, argv[0])))
516     return JS_FALSE;
517 
518   dialog_msg (JS_GetStringBytes (str));
519   return JS_TRUE;
520 }
521 
522 static JSBool
js_confirm(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)523 js_confirm (JSContext * cx, JSObject * obj, uintN argc, jsval * argv,
524 	    jsval * rval)
525 {
526   JSString *str;
527 
528   if ((!JSVAL_IS_STRING (argv[0]) && !JSVAL_IS_NUMBER (argv[0]))
529       || !(str = JS_ValueToString (cx, argv[0])))
530     return JS_FALSE;
531 
532   if (dialog_ask (JS_GetStringBytes (str)) == GTK_RESPONSE_OK)
533     *rval = JSVAL_TRUE;
534   else
535     *rval = JSVAL_FALSE;
536 
537   return JS_TRUE;
538 }
539 
540 static JSBool
js_version(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)541 js_version (JSContext * cx, JSObject * obj, uintN argc, jsval * argv,
542 	    jsval * rval)
543 {
544   JSString *version = JS_NewStringCopyN (cx, VERSION, strlen (VERSION));
545   *rval = STRING_TO_JSVAL (version);
546   return JS_TRUE;
547 }
548 
549 static JSBool
js_get_value(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)550 js_get_value (JSContext * cx, JSObject * obj, uintN argc, jsval * argv,
551 	      jsval * rval)
552 {
553   struct template_value_t *value;
554   struct js_t *js = JS_GetContextPrivate (cx);
555   JSObject *array = JS_NewArrayObject (cx, 0, NULL);
556 
557   JSString *str;
558   jsval val;
559 
560   value = template_value_new (js->widget);
561 
562   if (value->value)
563     {
564       str = JS_NewStringCopyN (cx, value->value, strlen (value->value));
565       val = STRING_TO_JSVAL (str);
566       JS_SetElement (cx, array, 0, &val);
567     }
568   else
569     {
570       str = JS_NewStringCopyN (cx, "", 0);
571       val = STRING_TO_JSVAL (str);
572       JS_SetElement (cx, array, 0, &val);
573     }
574 
575   if (value->lang)
576     {
577       str = JS_NewStringCopyN (cx, value->lang, strlen (value->lang));
578       val = STRING_TO_JSVAL (str);
579       JS_SetElement (cx, array, 1, &val);
580     }
581   else
582     {
583       str = JS_NewStringCopyN (cx, "", 0);
584       val = STRING_TO_JSVAL (str);
585       JS_SetElement (cx, array, 1, &val);
586     }
587 
588   if (value->datatype)
589     {
590       str = JS_NewStringCopyN (cx, value->datatype, strlen (value->datatype));
591       val = STRING_TO_JSVAL (str);
592       JS_SetElement (cx, array, 2, &val);
593     }
594   else
595     {
596       str = JS_NewStringCopyN (cx, "", 0);
597       val = STRING_TO_JSVAL (str);
598       JS_SetElement (cx, array, 2, &val);
599     }
600 
601   val = DOUBLE_TO_JSVAL (value->type);
602   JS_SetElement (cx, array, 3, &val);
603   template_value_free (value);
604 
605   *rval = OBJECT_TO_JSVAL (array);
606   return JS_TRUE;
607 }
608 
609 static JSBool
js_set_value(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)610 js_set_value (JSContext * cx, JSObject * obj, uintN argc, jsval * argv,
611 	      jsval * rval)
612 {
613   struct template_value_t *value;
614   struct js_t *js = JS_GetContextPrivate (cx);
615   JSObject *array;
616   JSString *str;
617   JSBool found;
618   jsval val;
619 
620   if (!JS_IsArrayObject (cx, JSVAL_TO_OBJECT (argv[0])))
621     {
622       JS_ReportError (cx, "morla_set_value: the argument is not an array");
623       return JS_FALSE;
624     }
625 
626   array = JSVAL_TO_OBJECT (argv[0]);
627   value = g_malloc0 (sizeof (struct template_value_t));
628 
629 #define JS_SET_VALUE( x , y ) \
630   if (JS_HasElement(cx, array, y, &found) == JS_TRUE && \
631       found == JS_TRUE && \
632       JS_GetElement (cx, array, y, &val) && \
633       (JSVAL_IS_STRING (val) || JSVAL_IS_NUMBER (val)) && \
634       (str = JS_ValueToString (cx, val))) \
635       value->x = g_strdup (JS_GetStringBytes (str)); \
636   else \
637     value->x = NULL;
638 
639   JS_SET_VALUE (value, 0);
640   JS_SET_VALUE (lang, 1);
641   JS_SET_VALUE (datatype, 2);
642 
643   if (JS_HasElement (cx, array, 3, &found) == JS_TRUE && found == JS_TRUE
644       && JS_GetElement (cx, array, 3, &val) && JSVAL_IS_NUMBER (val))
645     value->type = (gint) JSVAL_TO_DOUBLE (val);
646 
647   template_value_set (js->widget, value);
648   template_value_free (value);
649 
650   return JS_TRUE;
651 }
652 
653 #endif
654 #endif
655 
656 /* EOF */
657