1 /* A simple, extensible s-exp evaluation engine.
2  *
3  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4  *
5  * This library is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Michael Zucchi <notzed@ximian.com>
18  */
19 
20 /*
21  *   The following built-in s-exp's are supported:
22  *
23  *   list = (and list*)
24  *      perform an intersection of a number of lists, and return that.
25  *
26  *   bool = (and bool*)
27  *      perform a boolean AND of boolean values.
28  *
29  *   list = (or list*)
30  *      perform a union of a number of lists, returning the new list.
31  *
32  *   bool = (or bool*)
33  *      perform a boolean OR of boolean values.
34  *
35  *   gint = (+ int*)
36  *      Add integers.
37  *
38  *   string = (+ string*)
39  *      Concat strings.
40  *
41  *   time_t = (+ time_t*)
42  *      Add time_t values.
43  *
44  *   gint = (- gint int*)
45  *      Subtract integers from the first.
46  *
47  *   time_t = (- time_t*)
48  *      Subtract time_t values from the first.
49  *
50  *   gint = (cast-int string|int|bool)
51  *         Cast to an integer value.
52  *
53  *   string = (cast-string string|int|bool)
54  *         Cast to a string value.
55  *
56  *   Comparison operators:
57  *
58  *   bool = (< gint gint)
59  *   bool = (> gint gint)
60  *   bool = (= gint gint)
61  *
62  *   bool = (< string string)
63  *   bool = (> string string)
64  *   bool = (= string string)
65  *
66  *   bool = (< time_t time_t)
67  *   bool = (> time_t time_t)
68  *   bool = (= time_t time_t)
69  *      Perform a comparision of 2 integers, 2 string values, or 2 time values.
70  *
71  *   Function flow:
72  *
73  *   type = (if bool function)
74  *   type = (if bool function function)
75  *      Choose a flow path based on a boolean value
76  *
77  *   type = (begin  func func func)
78  *         Execute a sequence.  The last function return is the return type.
79  */
80 
81 #include "evolution-data-server-config.h"
82 
83 #include <stdio.h>
84 #include <stdlib.h>
85 #include <time.h>
86 #include <string.h>
87 
88 #include "camel-sexp.h"
89 
90 #define p(x)			/* parse debug */
91 #define r(x)			/* run debug */
92 #define d(x)			/* general debug */
93 
94 struct _CamelSExpPrivate {
95 	GScanner *scanner;	/* for parsing text version */
96 	CamelSExpTerm *tree;	/* root of expression tree */
97 
98 	/* private stuff */
99 	jmp_buf failenv;
100 	gchar *error;
101 	GSList *operators;
102 
103 	/* TODO: may also need a pool allocator for term strings,
104 	 *       so we dont lose them in error conditions? */
105 	CamelMemChunk *term_chunks;
106 	CamelMemChunk *result_chunks;
107 };
108 
109 G_DEFINE_TYPE_WITH_PRIVATE (CamelSExp, camel_sexp, G_TYPE_OBJECT)
110 
111 static CamelSExpTerm * parse_list (CamelSExp *sexp, gint gotbrace);
112 static CamelSExpTerm * parse_value (CamelSExp *sexp);
113 
114 #ifdef TESTER
115 static void parse_dump_term (CamelSExpTerm *term, gint depth);
116 #endif
117 
118 typedef gboolean	(CamelSGeneratorFunc)	(gint argc,
119 						 CamelSExpResult **argv,
120 						 CamelSExpResult *result);
121 typedef gboolean	(CamelSOperatorFunc)	(gint argc,
122 						 CamelSExpResult **argv,
123 						 CamelSExpResult *result);
124 
125 /* FIXME: constant _TIME_MAX used in different files, move it somewhere */
126 #define _TIME_MAX	((time_t) INT_MAX)	/* Max valid time_t	*/
127 
128 static const GScannerConfig scanner_config =
129 {
130 	( (gchar *) " \t\r\n")		/* cset_skip_characters */,
131 	( (gchar *) G_CSET_a_2_z
132 	  "_+-<=>?"
133 	  G_CSET_A_2_Z)			/* cset_identifier_first */,
134 	( (gchar *) G_CSET_a_2_z
135 	  "_0123456789-<>?"
136 	  G_CSET_A_2_Z
137 	  G_CSET_LATINS
138 	  G_CSET_LATINC	)		/* cset_identifier_nth */,
139 	( (gchar *) ";\n" )		/* cpair_comment_single */,
140 
141 	FALSE				/* case_sensitive */,
142 
143 	TRUE				/* skip_comment_multi */,
144 	TRUE				/* skip_comment_single */,
145 	TRUE				/* scan_comment_multi */,
146 	TRUE				/* scan_identifier */,
147 	TRUE				/* scan_identifier_1char */,
148 	FALSE				/* scan_identifier_NULL */,
149 	TRUE				/* scan_symbols */,
150 	FALSE				/* scan_binary */,
151 	TRUE				/* scan_octal */,
152 	TRUE				/* scan_float */,
153 	TRUE				/* scan_hex */,
154 	FALSE				/* scan_hex_dollar */,
155 	TRUE				/* scan_string_sq */,
156 	TRUE				/* scan_string_dq */,
157 	TRUE				/* numbers_2_int */,
158 	FALSE				/* int_2_float */,
159 	FALSE				/* identifier_2_string */,
160 	TRUE				/* char_2_token */,
161 	FALSE				/* symbol_2_token */,
162 	FALSE				/* scope_0_fallback */,
163 };
164 
165 /**
166  * camel_sexp_fatal_error:
167  * @sexp: a #CamelSExp
168  * @why: a string format to use
169  * @...: parameters for the format
170  *
171  * Sets an error from the given format and stops execution.
172  * Int replaces previously set error, if any.
173  *
174  * Since: 3.4
175  **/
176 void
camel_sexp_fatal_error(CamelSExp * sexp,const gchar * why,...)177 camel_sexp_fatal_error (CamelSExp *sexp,
178                         const gchar *why,
179                         ...)
180 {
181 	va_list args;
182 
183 	/* jumps back to the caller of sexp->priv->failenv,
184 	 * only to be called from inside a callback */
185 
186 	g_free (sexp->priv->error);
187 
188 	va_start (args, why);
189 	sexp->priv->error = g_strdup_vprintf (why, args);
190 	va_end (args);
191 
192 	longjmp (sexp->priv->failenv, 1);
193 }
194 
195 /**
196  * camel_sexp_error:
197  * @sexp: a #CamelSExp
198  *
199  * Returns: (nullable): Set error string on the @sexp, or %NULL, when none is set
200  *
201  * Since: 3.4
202  **/
203 const gchar *
camel_sexp_error(CamelSExp * sexp)204 camel_sexp_error (CamelSExp *sexp)
205 {
206 	return sexp->priv->error;
207 }
208 
209 /**
210  * camel_sexp_result_new: (skip)
211  * @sexp: a #CamelSExp
212  * @type: type of the result, one of #CamelSExpResultType
213  *
214  * Returns: (transfer full): A new #CamelSExpResult result structure, associated with @sexp.
215  *    Free with camel_sexp_result_free(), when no longer needed.
216  *
217  * Since: 3.4
218  **/
219 CamelSExpResult *
camel_sexp_result_new(CamelSExp * sexp,gint type)220 camel_sexp_result_new (CamelSExp *sexp,
221                        gint type)
222 {
223 	CamelSExpResult *result;
224 
225 	result = camel_memchunk_alloc0 (sexp->priv->result_chunks);
226 	result->type = type;
227 	result->occuring_start = 0;
228 	result->occuring_end = _TIME_MAX;
229 	result->time_generator = FALSE;
230 
231 	return result;
232 }
233 
234 /**
235  * camel_sexp_result_free:
236  * @sexp: a #CamelSExp
237  * @result: (nullable): a #CamelSExpResult to free
238  *
239  * Frees the @result and its internal data. Does nothing,
240  * when the @result is %NULL.
241  *
242  * Since: 3.4
243  **/
244 void
camel_sexp_result_free(CamelSExp * sexp,CamelSExpResult * result)245 camel_sexp_result_free (CamelSExp *sexp,
246                         CamelSExpResult *result)
247 {
248 	if (result == NULL)
249 		return;
250 
251 	switch (result->type) {
252 	case CAMEL_SEXP_RES_ARRAY_PTR:
253 		g_ptr_array_free (result->value.ptrarray, TRUE);
254 		break;
255 	case CAMEL_SEXP_RES_BOOL:
256 	case CAMEL_SEXP_RES_INT:
257 	case CAMEL_SEXP_RES_TIME:
258 		break;
259 	case CAMEL_SEXP_RES_STRING:
260 		g_free (result->value.string);
261 		break;
262 	case CAMEL_SEXP_RES_UNDEFINED:
263 		break;
264 	default:
265 		g_return_if_reached ();
266 	}
267 	camel_memchunk_free (sexp->priv->result_chunks, result);
268 }
269 
270 /**
271  * camel_sexp_resultv_free:
272  * @sexp: a #CamelSExp
273  * @argc: a count of the @argv
274  * @argv: (array length=argc): an array of #CamelSExpResult to free
275  *
276  * Frees an array of results.
277  *
278  * Since: 3.4
279  **/
280 void
camel_sexp_resultv_free(CamelSExp * sexp,gint argc,CamelSExpResult ** argv)281 camel_sexp_resultv_free (CamelSExp *sexp,
282                          gint argc,
283                          CamelSExpResult **argv)
284 {
285 	gint i;
286 
287 	/* used in normal functions if they have to abort,
288 	 * and free their arguments */
289 
290 	for (i = 0; i < argc; i++) {
291 		camel_sexp_result_free (sexp, argv[i]);
292 	}
293 }
294 
295 /* implementations for the builtin functions */
296 
297 /* we can only itereate a hashtable from a called function */
298 struct IterData {
299 	gint count;
300 	GPtrArray *uids;
301 };
302 
303 /* ok, store any values that are in all sets */
304 static void
htand(gchar * key,gint value,struct IterData * iter_data)305 htand (gchar *key,
306        gint value,
307        struct IterData *iter_data)
308 {
309 	if (value == iter_data->count) {
310 		g_ptr_array_add (iter_data->uids, key);
311 	}
312 }
313 
314 /* or, store all unique values */
315 static void
htor(gchar * key,gint value,struct IterData * iter_data)316 htor (gchar *key,
317       gint value,
318       struct IterData *iter_data)
319 {
320 	g_ptr_array_add (iter_data->uids, key);
321 }
322 
323 static CamelSExpResult *
term_eval_and(CamelSExp * sexp,gint argc,CamelSExpTerm ** argv,gpointer data)324 term_eval_and (CamelSExp *sexp,
325                gint argc,
326                CamelSExpTerm **argv,
327                gpointer data)
328 {
329 	CamelSExpResult *result, *r1;
330 	GHashTable *ht = g_hash_table_new (g_str_hash, g_str_equal);
331 	struct IterData lambdafoo;
332 	gint type=-1;
333 	gint bool = TRUE;
334 	gint i;
335 	const gchar *oper;
336 
337 	r (printf ("( and\n"));
338 
339 	result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_UNDEFINED);
340 
341 	oper = "AND";
342 	sexp->priv->operators = g_slist_prepend (sexp->priv->operators, (gpointer) oper);
343 
344 	for (i = 0; bool && i < argc; i++) {
345 		r1 = camel_sexp_term_eval (sexp, argv[i]);
346 		if (type == -1)
347 			type = r1->type;
348 		if (type != r1->type) {
349 			camel_sexp_result_free (sexp, result);
350 			camel_sexp_result_free (sexp, r1);
351 			g_hash_table_destroy (ht);
352 			camel_sexp_fatal_error (sexp, "Invalid types in AND");
353 		} else if (r1->type == CAMEL_SEXP_RES_ARRAY_PTR) {
354 			gchar **a1;
355 			gint l1, j;
356 
357 			a1 = (gchar **) r1->value.ptrarray->pdata;
358 			l1 = r1->value.ptrarray->len;
359 			for (j = 0; j < l1; j++) {
360 				gpointer ptr;
361 				gint n;
362 				ptr = g_hash_table_lookup (ht, a1[j]);
363 				n = GPOINTER_TO_INT (ptr);
364 				g_hash_table_insert (ht, a1[j], GINT_TO_POINTER (n + 1));
365 			}
366 		} else if (r1->type == CAMEL_SEXP_RES_BOOL) {
367 			bool = bool && r1->value.boolean;
368 		}
369 		camel_sexp_result_free (sexp, r1);
370 	}
371 
372 	if (type == CAMEL_SEXP_RES_ARRAY_PTR) {
373 		lambdafoo.count = argc;
374 		lambdafoo.uids = g_ptr_array_new ();
375 		g_hash_table_foreach (ht, (GHFunc) htand, &lambdafoo);
376 		result->type = CAMEL_SEXP_RES_ARRAY_PTR;
377 		result->value.ptrarray = lambdafoo.uids;
378 	} else if (type == CAMEL_SEXP_RES_BOOL) {
379 		result->type = CAMEL_SEXP_RES_BOOL;
380 		result->value.boolean = bool;
381 	}
382 
383 	g_hash_table_destroy (ht);
384 	sexp->priv->operators = g_slist_remove (sexp->priv->operators, oper);
385 
386 	return result;
387 }
388 
389 static CamelSExpResult *
term_eval_or(CamelSExp * sexp,gint argc,CamelSExpTerm ** argv,gpointer data)390 term_eval_or (CamelSExp *sexp,
391               gint argc,
392               CamelSExpTerm **argv,
393               gpointer data)
394 {
395 	CamelSExpResult *result, *r1;
396 	GHashTable *ht = g_hash_table_new (g_str_hash, g_str_equal);
397 	struct IterData lambdafoo;
398 	gint type = -1;
399 	gint bool = FALSE;
400 	gint i;
401 	const gchar *oper;
402 
403 	r (printf ("(or \n"));
404 
405 	oper = "OR";
406 	sexp->priv->operators = g_slist_prepend (sexp->priv->operators, (gpointer) oper);
407 
408 	result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_UNDEFINED);
409 
410 	for (i = 0; !bool && i < argc; i++) {
411 		r1 = camel_sexp_term_eval (sexp, argv[i]);
412 		if (type == -1)
413 			type = r1->type;
414 		if (r1->type != type) {
415 			camel_sexp_result_free (sexp, result);
416 			camel_sexp_result_free (sexp, r1);
417 			g_hash_table_destroy (ht);
418 			camel_sexp_fatal_error (sexp, "Invalid types in OR");
419 		} else if (r1->type == CAMEL_SEXP_RES_ARRAY_PTR) {
420 			gchar **a1;
421 			gint l1, j;
422 
423 			a1 = (gchar **) r1->value.ptrarray->pdata;
424 			l1 = r1->value.ptrarray->len;
425 			for (j = 0; j < l1; j++) {
426 				g_hash_table_insert (ht, a1[j], (gpointer) 1);
427 			}
428 		} else if (r1->type == CAMEL_SEXP_RES_BOOL) {
429 			bool |= r1->value.boolean;
430 		}
431 		camel_sexp_result_free (sexp, r1);
432 	}
433 
434 	if (type == CAMEL_SEXP_RES_ARRAY_PTR) {
435 		lambdafoo.count = argc;
436 		lambdafoo.uids = g_ptr_array_new ();
437 		g_hash_table_foreach (ht, (GHFunc) htor, &lambdafoo);
438 		result->type = CAMEL_SEXP_RES_ARRAY_PTR;
439 		result->value.ptrarray = lambdafoo.uids;
440 	} else if (type == CAMEL_SEXP_RES_BOOL) {
441 		result->type = CAMEL_SEXP_RES_BOOL;
442 		result->value.boolean = bool;
443 	}
444 	g_hash_table_destroy (ht);
445 
446 	sexp->priv->operators = g_slist_remove (sexp->priv->operators, oper);
447 	return result;
448 }
449 
450 static CamelSExpResult *
term_eval_not(CamelSExp * sexp,gint argc,CamelSExpResult ** argv,gpointer data)451 term_eval_not (CamelSExp *sexp,
452                gint argc,
453                CamelSExpResult **argv,
454                gpointer data)
455 {
456 	gint res = TRUE;
457 	CamelSExpResult *result;
458 
459 	if (argc > 0) {
460 		if (argv[0]->type == CAMEL_SEXP_RES_BOOL
461 		    && argv[0]->value.boolean)
462 			res = FALSE;
463 	}
464 	result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_BOOL);
465 	result->value.boolean = res;
466 
467 	return result;
468 }
469 
470 /* this should support all arguments ...? */
471 static CamelSExpResult *
term_eval_lt(CamelSExp * sexp,gint argc,CamelSExpTerm ** argv,gpointer data)472 term_eval_lt (CamelSExp *sexp,
473               gint argc,
474               CamelSExpTerm **argv,
475               gpointer data)
476 {
477 	CamelSExpResult *result, *r1, *r2;
478 
479 	result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_UNDEFINED);
480 
481 	if (argc == 2) {
482 		r1 = camel_sexp_term_eval (sexp, argv[0]);
483 		r2 = camel_sexp_term_eval (sexp, argv[1]);
484 		if (r1->type != r2->type) {
485 			camel_sexp_result_free (sexp, r1);
486 			camel_sexp_result_free (sexp, r2);
487 			camel_sexp_result_free (sexp, result);
488 			camel_sexp_fatal_error (sexp, "Incompatible types in compare <");
489 		} else if (r1->type == CAMEL_SEXP_RES_INT) {
490 			result->type = CAMEL_SEXP_RES_BOOL;
491 			result->value.boolean = r1->value.number < r2->value.number;
492 		} else if (r1->type == CAMEL_SEXP_RES_TIME) {
493 			result->type = CAMEL_SEXP_RES_BOOL;
494 			result->value.boolean = r1->value.time < r2->value.time;
495 		} else if (r1->type == CAMEL_SEXP_RES_STRING) {
496 			result->type = CAMEL_SEXP_RES_BOOL;
497 			result->value.boolean = strcmp (r1->value.string, r2->value.string) < 0;
498 		}
499 		camel_sexp_result_free (sexp, r1);
500 		camel_sexp_result_free (sexp, r2);
501 	}
502 	return result;
503 }
504 
505 /* this should support all arguments ...? */
506 static CamelSExpResult *
term_eval_gt(CamelSExp * sexp,gint argc,CamelSExpTerm ** argv,gpointer data)507 term_eval_gt (CamelSExp *sexp,
508               gint argc,
509               CamelSExpTerm **argv,
510               gpointer data)
511 {
512 	CamelSExpResult *result, *r1, *r2;
513 
514 	result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_UNDEFINED);
515 
516 	if (argc == 2) {
517 		r1 = camel_sexp_term_eval (sexp, argv[0]);
518 		r2 = camel_sexp_term_eval (sexp, argv[1]);
519 		if (r1->type != r2->type) {
520 			camel_sexp_result_free (sexp, r1);
521 			camel_sexp_result_free (sexp, r2);
522 			camel_sexp_result_free (sexp, result);
523 			camel_sexp_fatal_error (sexp, "Incompatible types in compare >");
524 		} else if (r1->type == CAMEL_SEXP_RES_INT) {
525 			result->type = CAMEL_SEXP_RES_BOOL;
526 			result->value.boolean = r1->value.number > r2->value.number;
527 		} else if (r1->type == CAMEL_SEXP_RES_TIME) {
528 			result->type = CAMEL_SEXP_RES_BOOL;
529 			result->value.boolean = r1->value.time > r2->value.time;
530 		} else if (r1->type == CAMEL_SEXP_RES_STRING) {
531 			result->type = CAMEL_SEXP_RES_BOOL;
532 			result->value.boolean = strcmp (r1->value.string, r2->value.string) > 0;
533 		}
534 		camel_sexp_result_free (sexp, r1);
535 		camel_sexp_result_free (sexp, r2);
536 	}
537 	return result;
538 }
539 
540 /* this should support all arguments ...? */
541 static CamelSExpResult *
term_eval_eq(CamelSExp * sexp,gint argc,CamelSExpTerm ** argv,gpointer data)542 term_eval_eq (CamelSExp *sexp,
543               gint argc,
544               CamelSExpTerm **argv,
545               gpointer data)
546 {
547 	CamelSExpResult *result, *r1, *r2;
548 
549 	result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_BOOL);
550 
551 	if (argc == 2) {
552 		r1 = camel_sexp_term_eval (sexp, argv[0]);
553 		r2 = camel_sexp_term_eval (sexp, argv[1]);
554 		if (r1->type != r2->type) {
555 			result->value.boolean = FALSE;
556 		} else if (r1->type == CAMEL_SEXP_RES_INT) {
557 			result->value.boolean = r1->value.number == r2->value.number;
558 		} else if (r1->type == CAMEL_SEXP_RES_BOOL) {
559 			result->value.boolean = r1->value.boolean == r2->value.boolean;
560 		} else if (r1->type == CAMEL_SEXP_RES_TIME) {
561 			result->value.boolean = r1->value.time == r2->value.time;
562 		} else if (r1->type == CAMEL_SEXP_RES_STRING) {
563 			result->value.boolean = strcmp (r1->value.string, r2->value.string) == 0;
564 		}
565 		camel_sexp_result_free (sexp, r1);
566 		camel_sexp_result_free (sexp, r2);
567 	}
568 	return result;
569 }
570 
571 static CamelSExpResult *
term_eval_plus(CamelSExp * sexp,gint argc,CamelSExpResult ** argv,gpointer data)572 term_eval_plus (CamelSExp *sexp,
573                 gint argc,
574                 CamelSExpResult **argv,
575                 gpointer data)
576 {
577 	CamelSExpResult *result = NULL;
578 	gint type;
579 	gint i;
580 
581 	if (argc > 0) {
582 		type = argv[0]->type;
583 		switch (type) {
584 		case CAMEL_SEXP_RES_INT: {
585 			gint total = argv[0]->value.number;
586 			for (i = 1; i < argc && argv[i]->type == CAMEL_SEXP_RES_INT; i++) {
587 				total += argv[i]->value.number;
588 			}
589 			if (i < argc) {
590 				camel_sexp_resultv_free (sexp, argc, argv);
591 				camel_sexp_fatal_error (sexp, "Invalid types in (+ ints)");
592 			}
593 			result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_INT);
594 			result->value.number = total;
595 			break; }
596 		case CAMEL_SEXP_RES_STRING: {
597 			GString *string = g_string_new (argv[0]->value.string);
598 			for (i = 1; i < argc && argv[i]->type == CAMEL_SEXP_RES_STRING; i++) {
599 				g_string_append (string, argv[i]->value.string);
600 			}
601 			if (i < argc) {
602 				camel_sexp_resultv_free (sexp, argc, argv);
603 				camel_sexp_fatal_error (sexp, "Invalid types in (+ strings)");
604 			}
605 			result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_STRING);
606 			result->value.string = g_string_free (string, FALSE);
607 			break; }
608 		case CAMEL_SEXP_RES_TIME: {
609 			time_t total;
610 
611 			total = argv[0]->value.time;
612 
613 			for (i = 1; i < argc && argv[i]->type == CAMEL_SEXP_RES_TIME; i++)
614 				total += argv[i]->value.time;
615 
616 			if (i < argc) {
617 				camel_sexp_resultv_free (sexp, argc, argv);
618 				camel_sexp_fatal_error (sexp, "Invalid types in (+ time_t)");
619 			}
620 
621 			result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_TIME);
622 			result->value.time = total;
623 			break; }
624 		}
625 	}
626 
627 	if (result == NULL) {
628 		result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_INT);
629 		result->value.number = 0;
630 	}
631 
632 	return result;
633 }
634 
635 static CamelSExpResult *
term_eval_sub(CamelSExp * sexp,gint argc,CamelSExpResult ** argv,gpointer data)636 term_eval_sub (CamelSExp *sexp,
637                gint argc,
638                CamelSExpResult **argv,
639                gpointer data)
640 {
641 	CamelSExpResult *result = NULL;
642 	gint type;
643 	gint i;
644 
645 	if (argc > 0) {
646 		type = argv[0]->type;
647 		switch (type) {
648 		case CAMEL_SEXP_RES_INT: {
649 			gint total = argv[0]->value.number;
650 			for (i = 1; i < argc && argv[i]->type == CAMEL_SEXP_RES_INT; i++) {
651 				total -= argv[i]->value.number;
652 			}
653 			if (i < argc) {
654 				camel_sexp_resultv_free (sexp, argc, argv);
655 				camel_sexp_fatal_error (sexp, "Invalid types in -");
656 			}
657 			result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_INT);
658 			result->value.number = total;
659 			break; }
660 		case CAMEL_SEXP_RES_TIME: {
661 			time_t total;
662 
663 			total = argv[0]->value.time;
664 
665 			for (i = 1; i < argc && argv[i]->type == CAMEL_SEXP_RES_TIME; i++)
666 				total -= argv[i]->value.time;
667 
668 			if (i < argc) {
669 				camel_sexp_resultv_free (sexp, argc, argv);
670 				camel_sexp_fatal_error (sexp, "Invalid types in (- time_t)");
671 			}
672 
673 			result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_TIME);
674 			result->value.time = total;
675 			break; }
676 		}
677 	}
678 
679 	if (result == NULL) {
680 		result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_INT);
681 		result->value.number = 0;
682 	}
683 	return result;
684 }
685 
686 /* cast to gint */
687 static CamelSExpResult *
term_eval_castint(CamelSExp * sexp,gint argc,CamelSExpResult ** argv,gpointer data)688 term_eval_castint (CamelSExp *sexp,
689                    gint argc,
690                    CamelSExpResult **argv,
691                    gpointer data)
692 {
693 	CamelSExpResult *result;
694 
695 	if (argc != 1)
696 		camel_sexp_fatal_error (sexp, "Incorrect argument count to (gint )");
697 
698 	result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_INT);
699 	switch (argv[0]->type) {
700 	case CAMEL_SEXP_RES_INT:
701 		result->value.number = argv[0]->value.number;
702 		break;
703 	case CAMEL_SEXP_RES_BOOL:
704 		result->value.number = argv[0]->value.boolean != 0;
705 		break;
706 	case CAMEL_SEXP_RES_STRING:
707 		result->value.number = strtoul (argv[0]->value.string, NULL, 10);
708 		break;
709 	default:
710 		camel_sexp_result_free (sexp, result);
711 		camel_sexp_fatal_error (sexp, "Invalid type in (cast-int )");
712 	}
713 
714 	return result;
715 }
716 
717 /* cast to string */
718 static CamelSExpResult *
term_eval_caststring(CamelSExp * sexp,gint argc,CamelSExpResult ** argv,gpointer data)719 term_eval_caststring (CamelSExp *sexp,
720                       gint argc,
721                       CamelSExpResult **argv,
722                       gpointer data)
723 {
724 	CamelSExpResult *result;
725 
726 	if (argc != 1)
727 		camel_sexp_fatal_error (sexp, "Incorrect argument count to (cast-string )");
728 
729 	result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_STRING);
730 	switch (argv[0]->type) {
731 	case CAMEL_SEXP_RES_INT:
732 		result->value.string = g_strdup_printf ("%d", argv[0]->value.number);
733 		break;
734 	case CAMEL_SEXP_RES_BOOL:
735 		result->value.string = g_strdup_printf ("%d", argv[0]->value.boolean != 0);
736 		break;
737 	case CAMEL_SEXP_RES_STRING:
738 		result->value.string = g_strdup (argv[0]->value.string);
739 		break;
740 	default:
741 		camel_sexp_result_free (sexp, result);
742 		camel_sexp_fatal_error (sexp, "Invalid type in (gint )");
743 	}
744 
745 	return result;
746 }
747 
748 /* implements 'if' function */
749 static CamelSExpResult *
term_eval_if(CamelSExp * sexp,gint argc,CamelSExpTerm ** argv,gpointer data)750 term_eval_if (CamelSExp *sexp,
751               gint argc,
752               CamelSExpTerm **argv,
753               gpointer data)
754 {
755 	CamelSExpResult *result;
756 	gint doit;
757 
758 	if (argc >=2 && argc <= 3) {
759 		result = camel_sexp_term_eval (sexp, argv[0]);
760 		doit = (result->type == CAMEL_SEXP_RES_BOOL && result->value.boolean);
761 		camel_sexp_result_free (sexp, result);
762 		if (doit) {
763 			return camel_sexp_term_eval (sexp, argv[1]);
764 		} else if (argc > 2) {
765 			return camel_sexp_term_eval (sexp, argv[2]);
766 		}
767 	}
768 	return camel_sexp_result_new (sexp, CAMEL_SEXP_RES_UNDEFINED);
769 }
770 
771 /* implements 'begin' statement */
772 static CamelSExpResult *
term_eval_begin(CamelSExp * sexp,gint argc,CamelSExpTerm ** argv,gpointer data)773 term_eval_begin (CamelSExp *sexp,
774                  gint argc,
775                  CamelSExpTerm **argv,
776                  gpointer data)
777 {
778 	CamelSExpResult *result = NULL;
779 	gint i;
780 
781 	for (i = 0; i < argc; i++) {
782 		if (result != NULL)
783 			camel_sexp_result_free (sexp, result);
784 		result = camel_sexp_term_eval (sexp, argv[i]);
785 	}
786 	if (result != NULL)
787 		return result;
788 	else
789 		return camel_sexp_result_new (sexp, CAMEL_SEXP_RES_UNDEFINED);
790 }
791 
792 /**
793  * camel_sexp_term_eval: (skip)
794  * @sexp: a #CamelSExp
795  * @term: a #CamelSExpTerm to evaluate
796  *
797  * Evaluates a part of the expression.
798  *
799  * Returns: (transfer full): a newly allocated result of the evaluation. Free
800  *    the returned pointer with camel_sexp_result_free(), when no longer needed.
801  *
802  * Since: 3.4
803  **/
804 CamelSExpResult *
camel_sexp_term_eval(CamelSExp * sexp,CamelSExpTerm * term)805 camel_sexp_term_eval (CamelSExp *sexp,
806                       CamelSExpTerm *term)
807 {
808 	CamelSExpResult *result = NULL;
809 	gint i, argc;
810 	CamelSExpResult **argv;
811 
812 	/* this must only be called from inside term evaluation callbacks! */
813 
814 	g_return_val_if_fail (term != NULL, NULL);
815 
816 	r (printf ("eval term :\n"));
817 	r (parse_dump_term (term, 0));
818 
819 	switch (term->type) {
820 	case CAMEL_SEXP_TERM_STRING:
821 		r (printf (" (string \"%s\")\n", term->value.string));
822 		result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_STRING);
823 		/* erk, this shoul;dn't need to strdup this ... */
824 		result->value.string = g_strdup (term->value.string);
825 		break;
826 	case CAMEL_SEXP_TERM_INT:
827 		r (printf (" (gint %d)\n", term->value.number));
828 		result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_INT);
829 		result->value.number = term->value.number;
830 		break;
831 	case CAMEL_SEXP_TERM_BOOL:
832 		r (printf (" (gint %d)\n", term->value.number));
833 		result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_BOOL);
834 		result->value.boolean = term->value.boolean;
835 		break;
836 	case CAMEL_SEXP_TERM_TIME:
837 		r (printf (" (time_t %ld)\n", term->value.time));
838 		result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_TIME);
839 		result->value.time = term->value.time;
840 		break;
841 	case CAMEL_SEXP_TERM_IFUNC:
842 		if (term->value.func.sym && term->value.func.sym->f.ifunc)
843 			result = term->value.func.sym->f.ifunc (sexp, term->value.func.termcount, term->value.func.terms, term->value.func.sym->data);
844 		break;
845 	case CAMEL_SEXP_TERM_FUNC:
846 		/* first evaluate all arguments to result types */
847 		argc = term->value.func.termcount;
848 		argv = g_alloca (sizeof (argv[0]) * argc);
849 		for (i = 0; i < argc; i++) {
850 			argv[i] = camel_sexp_term_eval (sexp, term->value.func.terms[i]);
851 		}
852 		/* call the function */
853 		if (term->value.func.sym->f.func)
854 			result = term->value.func.sym->f.func (sexp, argc, argv, term->value.func.sym->data);
855 
856 		camel_sexp_resultv_free (sexp, argc, argv);
857 		break;
858 	default:
859 		camel_sexp_fatal_error (sexp, "Unknown type in parse tree: %d", term->type);
860 	}
861 
862 	if (result == NULL)
863 		result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_UNDEFINED);
864 
865 	return result;
866 }
867 
868 #ifdef TESTER
869 static void
eval_dump_result(CamelSExpResult * result,gint depth)870 eval_dump_result (CamelSExpResult *result,
871                   gint depth)
872 {
873 	gint i;
874 
875 	if (result == NULL) {
876 		printf ("null result???\n");
877 		return;
878 	}
879 
880 	for (i = 0; i < depth; i++)
881 		printf ("   ");
882 
883 	switch (result->type) {
884 	case CAMEL_SEXP_RES_ARRAY_PTR:
885 		printf ("array pointers\n");
886 		break;
887 	case CAMEL_SEXP_RES_INT:
888 		printf ("int: %d\n", result->value.number);
889 		break;
890 	case CAMEL_SEXP_RES_STRING:
891 		printf ("string: '%s'\n", result->value.string);
892 		break;
893 	case CAMEL_SEXP_RES_BOOL:
894 		printf ("bool: %c\n", result->value.boolean ? 't':'f');
895 		break;
896 	case CAMEL_SEXP_RES_TIME:
897 		printf ("time_t: %ld\n", (glong) result->value.time);
898 		break;
899 	case CAMEL_SEXP_RES_UNDEFINED:
900 		printf (" <undefined>\n");
901 		break;
902 	}
903 	printf ("\n");
904 }
905 #endif
906 
907 #ifdef TESTER
908 static void
parse_dump_term(CamelSExpTerm * term,gint depth)909 parse_dump_term (CamelSExpTerm *term,
910                  gint depth)
911 {
912 	gint i;
913 
914 	if (t == NULL) {
915 		printf ("null term??\n");
916 		return;
917 	}
918 
919 	for (i = 0; i < depth; i++)
920 		printf ("   ");
921 
922 	switch (term->type) {
923 	case CAMEL_SEXP_TERM_STRING:
924 		printf (" \"%s\"", term->value.string);
925 		break;
926 	case CAMEL_SEXP_TERM_INT:
927 		printf (" %d", term->value.number);
928 		break;
929 	case CAMEL_SEXP_TERM_BOOL:
930 		printf (" #%c", term->value.boolean ? 't':'f');
931 		break;
932 	case CAMEL_SEXP_TERM_TIME:
933 		printf (" %ld", (glong) term->value.time);
934 		break;
935 	case CAMEL_SEXP_TERM_IFUNC:
936 	case CAMEL_SEXP_TERM_FUNC:
937 		printf (" (function %s\n", term->value.func.sym->name);
938 		/*printf(" [%d] ", term->value.func.termcount);*/
939 		for (i = 0; i < term->value.func.termcount; i++) {
940 			parse_dump_term (term->value.func.terms[i], depth + 1);
941 		}
942 		for (i = 0; i < depth; i++)
943 			printf ("   ");
944 		printf (" )");
945 		break;
946 	case CAMEL_SEXP_TERM_VAR:
947 		printf (" (variable %s )\n", term->value.var->name);
948 		break;
949 	default:
950 		printf ("unknown type: %d\n", term->type);
951 	}
952 
953 	printf ("\n");
954 }
955 #endif
956 
957 static const gchar *time_functions[] = {
958 	"time-now",
959 	"make-time",
960 	"time-add-day",
961 	"time-day-begin",
962 	"time-day-end"
963 };
964 
965 static gboolean
occur_in_time_range_generator(gint argc,CamelSExpResult ** argv,CamelSExpResult * result)966 occur_in_time_range_generator (gint argc,
967                                CamelSExpResult **argv,
968                                CamelSExpResult *result)
969 {
970 	g_return_val_if_fail (result != NULL, FALSE);
971 	g_return_val_if_fail (argc == 2 || argc == 3, FALSE);
972 
973 	if ((argv[0]->type != CAMEL_SEXP_RES_TIME) || (argv[1]->type != CAMEL_SEXP_RES_TIME))
974 		return FALSE;
975 
976 	result->occuring_start = argv[0]->value.time;
977 	result->occuring_end = argv[1]->value.time;
978 
979 	return TRUE;
980 }
981 
982 static gboolean
binary_generator(gint argc,CamelSExpResult ** argv,CamelSExpResult * result)983 binary_generator (gint argc,
984                   CamelSExpResult **argv,
985                   CamelSExpResult *result)
986 {
987 	g_return_val_if_fail (result != NULL, FALSE);
988 	g_return_val_if_fail (argc == 2, FALSE);
989 
990 	if ((argv[0]->type != CAMEL_SEXP_RES_TIME) || (argv[1]->type != CAMEL_SEXP_RES_TIME))
991 		return FALSE;
992 
993 	result->occuring_start = argv[0]->value.time;
994 	result->occuring_end = argv[1]->value.time;
995 
996 	return TRUE;
997 }
998 
999 static gboolean
unary_generator(gint argc,CamelSExpResult ** argv,CamelSExpResult * result)1000 unary_generator (gint argc,
1001                  CamelSExpResult **argv,
1002                  CamelSExpResult *result)
1003 {
1004 	/* unary generator with end time */
1005 	g_return_val_if_fail (result != NULL, FALSE);
1006 	g_return_val_if_fail (argc == 1, FALSE);
1007 
1008 	if (argv[0]->type != CAMEL_SEXP_RES_TIME)
1009 		return FALSE;
1010 
1011 	result->occuring_start = 0;
1012 	result->occuring_end = argv[0]->value.time;
1013 
1014 	return TRUE;
1015 }
1016 
1017 static const struct {
1018 	const gchar *name;
1019 	CamelSGeneratorFunc *func;
1020 } generators[] = {
1021 	{"occur-in-time-range?", occur_in_time_range_generator},
1022 	{"due-in-time-range?", binary_generator},
1023 	{"has-alarms-in-range?", binary_generator},
1024 	{"completed-before?", unary_generator},
1025 };
1026 
1027 static gboolean
or_operator(gint argc,CamelSExpResult ** argv,CamelSExpResult * result)1028 or_operator (gint argc,
1029              CamelSExpResult **argv,
1030              CamelSExpResult *result)
1031 {
1032 	gint ii;
1033 
1034 	/*
1035 	 * A          B           A or B
1036 	 * ----       ----        ------
1037 	 * norm (0)   norm (0)    norm (0)
1038 	 * gen (1)    norm (0)    norm (0)
1039 	 * norm (0)   gen (1)     norm (0)
1040 	 * gen (1)    gen (1)     gen*(1)
1041 	 */
1042 
1043 	g_return_val_if_fail (result != NULL, FALSE);
1044 	g_return_val_if_fail (argc > 0, FALSE);
1045 
1046 	result->time_generator = TRUE;
1047 	for (ii = 0; ii < argc && result->time_generator; ii++) {
1048 		result->time_generator = argv[ii]->time_generator;
1049 	}
1050 
1051 	if (result->time_generator) {
1052 		result->occuring_start = argv[0]->occuring_start;
1053 		result->occuring_end = argv[0]->occuring_end;
1054 
1055 		for (ii = 1; ii < argc; ii++) {
1056 			result->occuring_start = MIN (result->occuring_start, argv[ii]->occuring_start);
1057 			result->occuring_end = MAX (result->occuring_end, argv[ii]->occuring_end);
1058 		}
1059 	}
1060 
1061 	return TRUE;
1062 }
1063 
1064 static gboolean
and_operator(gint argc,CamelSExpResult ** argv,CamelSExpResult * result)1065 and_operator (gint argc,
1066               CamelSExpResult **argv,
1067               CamelSExpResult *result)
1068 {
1069 	gint ii;
1070 
1071 	/*
1072 	 * A           B          A and B
1073 	 * ----        ----       ------- -
1074 	 * norm (0)     norm (0)    norm (0)
1075 	 * gen (1)      norm (0)    gen (1)
1076 	 * norm (0)     gen (1)     gen (1)
1077 	 * gen (1)      gen (1)     gen (1)
1078 	 * */
1079 
1080 	g_return_val_if_fail (result != NULL, FALSE);
1081 	g_return_val_if_fail (argc > 0, FALSE);
1082 
1083 	result->time_generator = FALSE;
1084 	for (ii = 0; ii < argc && !result->time_generator; ii++) {
1085 		result->time_generator = argv[ii]->time_generator;
1086 	}
1087 
1088 	if (result->time_generator) {
1089 		result->occuring_start = argv[0]->occuring_start;
1090 		result->occuring_end = argv[0]->occuring_end;
1091 
1092 		for (ii = 1; ii < argc; ii++) {
1093 			result->occuring_start = MAX (result->occuring_start, argv[ii]->occuring_start);
1094 			result->occuring_end = MIN (result->occuring_end, argv[ii]->occuring_end);
1095 		}
1096 	}
1097 
1098 	return TRUE;
1099 }
1100 
1101 static const struct {
1102 	const gchar *name;
1103 	CamelSOperatorFunc *func;
1104 } operators[] = {
1105 	{"or", or_operator},
1106 	{"and", and_operator}
1107 };
1108 
1109 static CamelSOperatorFunc *
get_operator_function(const gchar * fname)1110 get_operator_function (const gchar *fname)
1111 {
1112 	gint i;
1113 
1114 	g_return_val_if_fail (fname != NULL, NULL);
1115 
1116 	for (i = 0; i < sizeof (operators) / sizeof (operators[0]); i++)
1117 		if (strcmp (operators[i].name, fname) == 0)
1118 			return operators[i].func;
1119 
1120 	return NULL;
1121 }
1122 
1123 static inline gboolean
is_time_function(const gchar * fname)1124 is_time_function (const gchar *fname)
1125 {
1126 	gint i;
1127 
1128 	g_return_val_if_fail (fname != NULL, FALSE);
1129 
1130 	for (i = 0; i < sizeof (time_functions) / sizeof (time_functions[0]); i++)
1131 		if (strcmp (time_functions[i], fname) == 0)
1132 			return TRUE;
1133 
1134 	return FALSE;
1135 }
1136 
1137 static CamelSGeneratorFunc *
get_generator_function(const gchar * fname)1138 get_generator_function (const gchar *fname)
1139 {
1140 	gint i;
1141 
1142 	g_return_val_if_fail (fname != NULL, NULL);
1143 
1144 	for (i = 0; i < sizeof (generators) / sizeof (generators[0]); i++)
1145 		if (strcmp (generators[i].name, fname) == 0)
1146 			return generators[i].func;
1147 
1148 	return NULL;
1149 }
1150 
1151 /* this must only be called from inside term evaluation callbacks! */
1152 static CamelSExpResult *
camel_sexp_term_evaluate_occur_times(CamelSExp * sexp,CamelSExpTerm * term,time_t * start,time_t * end)1153 camel_sexp_term_evaluate_occur_times (CamelSExp *sexp,
1154                                       CamelSExpTerm *term,
1155                                       time_t *start,
1156                                       time_t *end)
1157 {
1158 	CamelSExpResult *result = NULL;
1159 	gint i, argc;
1160 	CamelSExpResult **argv;
1161 	gboolean ok = TRUE;
1162 
1163 	g_return_val_if_fail (term != NULL, NULL);
1164 	g_return_val_if_fail (start != NULL, NULL);
1165 	g_return_val_if_fail (end != NULL, NULL);
1166 
1167 	/*
1168 	printf ("eval term :\n");
1169 	parse_dump_term (t, 0);
1170 	*/
1171 
1172 	switch (term->type) {
1173 	case CAMEL_SEXP_TERM_STRING:
1174 		r (printf (" (string \"%s\")\n", term->value.string));
1175 		result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_STRING);
1176 		result->value.string = g_strdup (term->value.string);
1177 		break;
1178 	case CAMEL_SEXP_TERM_IFUNC:
1179 	case CAMEL_SEXP_TERM_FUNC:
1180 	{
1181 		CamelSGeneratorFunc *generator = NULL;
1182 		CamelSOperatorFunc *operator = NULL;
1183 
1184 		r (printf (" (function \"%s\"\n", term->value.func.sym->name));
1185 
1186 		result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_UNDEFINED);
1187 		argc = term->value.func.termcount;
1188 		argv = g_alloca (sizeof (argv[0]) * argc);
1189 
1190 		for (i = 0; i < argc; i++) {
1191 			argv[i] = camel_sexp_term_evaluate_occur_times (
1192 				sexp, term->value.func.terms[i], start, end);
1193 		}
1194 
1195 		if (is_time_function (term->value.func.sym->name)) {
1196 			/* evaluate time */
1197 			if (term->value.func.sym->f.func)
1198 				result = term->value.func.sym->f.func (sexp, argc, argv, term->value.func.sym->data);
1199 		} else if ((generator = get_generator_function (term->value.func.sym->name)) != NULL) {
1200 			/* evaluate generator function */
1201 			result->time_generator = TRUE;
1202 			ok = generator (argc, argv, result);
1203 		} else if ((operator = get_operator_function (term->value.func.sym->name)) != NULL)
1204 			/* evaluate operator function */
1205 			ok = operator (argc, argv, result);
1206 		else {
1207 			/* normal function: we need to scan all objects */
1208 			result->time_generator = FALSE;
1209 		}
1210 
1211 		camel_sexp_resultv_free (sexp, argc, argv);
1212 		break;
1213 	}
1214 	case CAMEL_SEXP_TERM_INT:
1215 	case CAMEL_SEXP_TERM_BOOL:
1216 	case CAMEL_SEXP_TERM_TIME:
1217 		break;
1218 	default:
1219 		ok = FALSE;
1220 		break;
1221 	}
1222 
1223 	if (!ok)
1224 		camel_sexp_fatal_error (sexp, "Error in parse tree");
1225 
1226 	if (result == NULL)
1227 		result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_UNDEFINED);
1228 
1229 	return result;
1230 }
1231 
1232 /*
1233   PARSER
1234 */
1235 
1236 static CamelSExpTerm *
parse_term_new(CamelSExp * sexp,gint type)1237 parse_term_new (CamelSExp *sexp,
1238                 gint type)
1239 {
1240 	CamelSExpTerm *term;
1241 
1242 	term = camel_memchunk_alloc0 (sexp->priv->term_chunks);
1243 	term->type = type;
1244 
1245 	return term;
1246 }
1247 
1248 static void
parse_term_free(CamelSExp * sexp,CamelSExpTerm * term)1249 parse_term_free (CamelSExp *sexp,
1250                  CamelSExpTerm *term)
1251 {
1252 	gint i;
1253 
1254 	if (term == NULL) {
1255 		return;
1256 	}
1257 
1258 	switch (term->type) {
1259 	case CAMEL_SEXP_TERM_INT:
1260 	case CAMEL_SEXP_TERM_BOOL:
1261 	case CAMEL_SEXP_TERM_TIME:
1262 	case CAMEL_SEXP_TERM_VAR:
1263 		break;
1264 
1265 	case CAMEL_SEXP_TERM_STRING:
1266 		g_free (term->value.string);
1267 		break;
1268 
1269 	case CAMEL_SEXP_TERM_FUNC:
1270 	case CAMEL_SEXP_TERM_IFUNC:
1271 		for (i = 0; i < term->value.func.termcount; i++) {
1272 			parse_term_free (sexp, term->value.func.terms[i]);
1273 		}
1274 		g_free (term->value.func.terms);
1275 		break;
1276 
1277 	default:
1278 		printf ("parse_term_free: unknown type: %d\n", term->type);
1279 	}
1280 	camel_memchunk_free (sexp->priv->term_chunks, term);
1281 }
1282 
1283 static CamelSExpTerm **
parse_values(CamelSExp * sexp,gint * len)1284 parse_values (CamelSExp *sexp,
1285               gint *len)
1286 {
1287 	gint token;
1288 	CamelSExpTerm **terms;
1289 	gint i, size = 0;
1290 	GScanner *gs = sexp->priv->scanner;
1291 	GSList *list = NULL, *l;
1292 
1293 	p (printf ("parsing values\n"));
1294 
1295 	while ( (token = g_scanner_peek_next_token (gs)) != G_TOKEN_EOF
1296 		&& token != ')') {
1297 		list = g_slist_prepend (list, parse_value (sexp));
1298 		size++;
1299 	}
1300 
1301 	/* go over the list, and put them backwards into the term array */
1302 	terms = g_malloc (size * sizeof (*terms));
1303 	l = list;
1304 	for (i = size - 1; i >= 0; i--) {
1305 		if (!l || !l->data) {
1306 			if (!l)
1307 				g_warn_if_fail (l != NULL);
1308 			if (l && !l->data)
1309 				g_warn_if_fail (l->data != NULL);
1310 
1311 			g_slist_free (list);
1312 			g_free (terms);
1313 
1314 			*len = 0;
1315 
1316 			return NULL;
1317 		}
1318 
1319 		terms[i] = l->data;
1320 		l = g_slist_next (l);
1321 	}
1322 	g_slist_free (list);
1323 
1324 	p (printf ("found %d subterms\n", size));
1325 	*len = size;
1326 
1327 	p (printf ("done parsing values\n"));
1328 	return terms;
1329 }
1330 
1331 /**
1332  * camel_sexp_parse_value: (skip)
1333  * @sexp: a #CamelSExp
1334  *
1335  * Returns: (nullable) (transfer none): a #CamelSExpTerm of the next token, or %NULL when there is none.
1336  *
1337  * Since: 3.4
1338  **/
1339 CamelSExpTerm *
camel_sexp_parse_value(CamelSExp * sexp)1340 camel_sexp_parse_value (CamelSExp *sexp)
1341 {
1342 	return parse_value (sexp);
1343 }
1344 
1345 static CamelSExpTerm *
parse_value(CamelSExp * sexp)1346 parse_value (CamelSExp *sexp)
1347 {
1348 	gint token, negative = FALSE;
1349 	CamelSExpTerm *term = NULL;
1350 	GScanner *gs = sexp->priv->scanner;
1351 	CamelSExpSymbol *sym;
1352 
1353 	p (printf ("parsing value\n"));
1354 
1355 	token = g_scanner_get_next_token (gs);
1356 	switch (token) {
1357 	case G_TOKEN_EOF:
1358 		break;
1359 	case G_TOKEN_LEFT_PAREN:
1360 		p (printf ("got brace, its a list!\n"));
1361 		return parse_list (sexp, TRUE);
1362 	case G_TOKEN_STRING:
1363 		p (printf ("got string '%s'\n", g_scanner_cur_value (gs).v_string));
1364 		term = parse_term_new (sexp, CAMEL_SEXP_TERM_STRING);
1365 		term->value.string = g_strdup (g_scanner_cur_value (gs).v_string);
1366 		break;
1367 	case '-':
1368 		p (printf ("got negative int?\n"));
1369 		token = g_scanner_get_next_token (gs);
1370 		if (token != G_TOKEN_INT) {
1371 			camel_sexp_fatal_error (sexp, "Invalid format for a integer value");
1372 			return NULL;
1373 		}
1374 
1375 		negative = TRUE;
1376 		/* fall through... */
1377 	case G_TOKEN_INT:
1378 		term = parse_term_new (sexp, CAMEL_SEXP_TERM_INT);
1379 		term->value.number = g_scanner_cur_value (gs).v_int;
1380 		if (negative)
1381 			term->value.number = -term->value.number;
1382 		p (printf ("got gint %d\n", term->value.number));
1383 		break;
1384 	case '#': {
1385 		gchar *str;
1386 
1387 		p (printf ("got bool?\n"));
1388 		token = g_scanner_get_next_token (gs);
1389 		if (token != G_TOKEN_IDENTIFIER) {
1390 			camel_sexp_fatal_error (sexp, "Invalid format for a boolean value");
1391 			return NULL;
1392 		}
1393 
1394 		str = g_scanner_cur_value (gs).v_identifier;
1395 
1396 		g_return_val_if_fail (str != NULL, NULL);
1397 		if (!(strlen (str) == 1 && (str[0] == 't' || str[0] == 'f'))) {
1398 			camel_sexp_fatal_error (sexp, "Invalid format for a boolean value");
1399 			return NULL;
1400 		}
1401 
1402 		term = parse_term_new (sexp, CAMEL_SEXP_TERM_BOOL);
1403 		term->value.boolean = (str[0] == 't');
1404 		break; }
1405 	case G_TOKEN_SYMBOL:
1406 		sym = g_scanner_cur_value (gs).v_symbol;
1407 		p (printf ("got symbol '%s'\n", sym->name));
1408 		switch (sym->type) {
1409 		case CAMEL_SEXP_TERM_FUNC:
1410 		case CAMEL_SEXP_TERM_IFUNC:
1411 			/* this is basically invalid, since we can't use function
1412 			 * pointers, but let the runtime catch it ... */
1413 			term = parse_term_new (sexp, sym->type);
1414 			term->value.func.sym = sym;
1415 			term->value.func.terms = parse_values (sexp, &term->value.func.termcount);
1416 			break;
1417 		case CAMEL_SEXP_TERM_VAR:
1418 			term = parse_term_new (sexp, sym->type);
1419 			term->value.var = sym;
1420 			break;
1421 		default:
1422 			camel_sexp_fatal_error (sexp, "Invalid symbol type: %s: %d", sym->name, sym->type);
1423 		}
1424 		break;
1425 	case G_TOKEN_IDENTIFIER:
1426 		p (printf ("got unknown identifider '%s'\n", g_scanner_cur_value (gs).v_identifier));
1427 		camel_sexp_fatal_error (sexp, "Unknown identifier: %s", g_scanner_cur_value (gs).v_identifier);
1428 		break;
1429 	default:
1430 		camel_sexp_fatal_error (sexp, "Unexpected token encountered: %d", token);
1431 	}
1432 	p (printf ("done parsing value\n"));
1433 
1434 	return term;
1435 }
1436 
1437 /* FIXME: this needs some robustification */
1438 static CamelSExpTerm *
parse_list(CamelSExp * sexp,gint gotbrace)1439 parse_list (CamelSExp *sexp,
1440             gint gotbrace)
1441 {
1442 	gint token;
1443 	CamelSExpTerm *term = NULL;
1444 	GScanner *gs = sexp->priv->scanner;
1445 
1446 	p (printf ("parsing list\n"));
1447 	if (gotbrace)
1448 		token = '(';
1449 	else
1450 		token = g_scanner_get_next_token (gs);
1451 	if (token =='(') {
1452 		token = g_scanner_get_next_token (gs);
1453 		switch (token) {
1454 		case G_TOKEN_SYMBOL: {
1455 			CamelSExpSymbol *sym;
1456 
1457 			sym = g_scanner_cur_value (gs).v_symbol;
1458 			p (printf ("got funciton: %s\n", sym->name));
1459 			term = parse_term_new (sexp, sym->type);
1460 			p (printf ("created new list %p\n", t));
1461 			/* if we have a variable, find out its base type */
1462 			while (sym->type == CAMEL_SEXP_TERM_VAR) {
1463 				sym = ((CamelSExpTerm *)(sym->data))->value.var;
1464 			}
1465 			if (sym->type == CAMEL_SEXP_TERM_FUNC
1466 			    || sym->type == CAMEL_SEXP_TERM_IFUNC) {
1467 				term->value.func.sym = sym;
1468 				term->value.func.terms = parse_values (sexp, &term->value.func.termcount);
1469 			} else {
1470 				parse_term_free (sexp, term);
1471 				camel_sexp_fatal_error (sexp, "Trying to call variable as function: %s", sym->name);
1472 			}
1473 			break; }
1474 		case G_TOKEN_IDENTIFIER:
1475 			camel_sexp_fatal_error (sexp, "Unknown identifier: %s", g_scanner_cur_value (gs).v_identifier);
1476 			break;
1477 		case G_TOKEN_LEFT_PAREN:
1478 			return parse_list (sexp, TRUE);
1479 		default:
1480 			camel_sexp_fatal_error (sexp, "Unexpected token encountered: %d", token);
1481 		}
1482 		token = g_scanner_get_next_token (gs);
1483 		if (token != ')') {
1484 			camel_sexp_fatal_error (sexp, "Missing ')'");
1485 		}
1486 	} else {
1487 		camel_sexp_fatal_error (sexp, "Missing '('");
1488 	}
1489 
1490 	p (printf ("returning list %p\n", term));
1491 
1492 	return term;
1493 }
1494 
1495 static void
free_symbol(gpointer key,gpointer value,gpointer data)1496 free_symbol (gpointer key,
1497              gpointer value,
1498              gpointer data)
1499 {
1500 	CamelSExpSymbol *sym = value;
1501 
1502 	g_free (sym->name);
1503 	g_free (sym);
1504 }
1505 
1506 static void
camel_sexp_finalize(GObject * object)1507 camel_sexp_finalize (GObject *object)
1508 {
1509 	CamelSExp *sexp = (CamelSExp *) object;
1510 
1511 	if (sexp->priv->tree) {
1512 		parse_term_free (sexp, sexp->priv->tree);
1513 		sexp->priv->tree = NULL;
1514 	}
1515 
1516 	camel_memchunk_destroy (sexp->priv->term_chunks);
1517 	camel_memchunk_destroy (sexp->priv->result_chunks);
1518 
1519 	g_scanner_scope_foreach_symbol (sexp->priv->scanner, 0, free_symbol, NULL);
1520 	g_scanner_destroy (sexp->priv->scanner);
1521 
1522 	g_free (sexp->priv->error);
1523 	sexp->priv->error = NULL;
1524 
1525 	/* Chain up to parent's finalize() method. */
1526 	G_OBJECT_CLASS (camel_sexp_parent_class)->finalize (object);
1527 }
1528 
1529 static void
camel_sexp_class_init(CamelSExpClass * class)1530 camel_sexp_class_init (CamelSExpClass *class)
1531 {
1532 	GObjectClass *object_class;
1533 
1534 	object_class = G_OBJECT_CLASS (class);
1535 	object_class->finalize = camel_sexp_finalize;
1536 }
1537 
1538 /* 'builtin' functions */
1539 static const struct {
1540 	const gchar *name;
1541 	CamelSExpFunc func;
1542 	gint type;	/* set to 1 if a function can perform shortcut
1543 			 * evaluation, or doesn't execute everything,
1544 			 * 0 otherwise */
1545 } symbols[] = {
1546 	{ "and",         (CamelSExpFunc) term_eval_and, 1 },
1547 	{ "or",          (CamelSExpFunc) term_eval_or, 1 },
1548 	{ "not",         (CamelSExpFunc) term_eval_not, 0 },
1549 	{ "<",           (CamelSExpFunc) term_eval_lt, 1 },
1550 	{ ">",           (CamelSExpFunc) term_eval_gt, 1 },
1551 	{ "=",           (CamelSExpFunc) term_eval_eq, 1 },
1552 	{ "+",           (CamelSExpFunc) term_eval_plus, 0 },
1553 	{ "-",           (CamelSExpFunc) term_eval_sub, 0 },
1554 	{ "cast-int",    (CamelSExpFunc) term_eval_castint, 0 },
1555 	{ "cast-string", (CamelSExpFunc) term_eval_caststring, 0 },
1556 	{ "if",          (CamelSExpFunc) term_eval_if, 1 },
1557 	{ "begin",       (CamelSExpFunc) term_eval_begin, 1 },
1558 };
1559 
1560 static void
camel_sexp_init(CamelSExp * sexp)1561 camel_sexp_init (CamelSExp *sexp)
1562 {
1563 	gint i;
1564 
1565 	sexp->priv = camel_sexp_get_instance_private (sexp);
1566 
1567 	sexp->priv->scanner = g_scanner_new (&scanner_config);
1568 	sexp->priv->term_chunks = camel_memchunk_new (16, sizeof (CamelSExpTerm));
1569 	sexp->priv->result_chunks = camel_memchunk_new (16, sizeof (CamelSExpResult));
1570 
1571 	/* load in builtin symbols? */
1572 	for (i = 0; i < G_N_ELEMENTS (symbols); i++) {
1573 		if (symbols[i].type == 1) {
1574 			camel_sexp_add_ifunction (
1575 				sexp, 0, symbols[i].name,
1576 				(CamelSExpIFunc) symbols[i].func,
1577 				(gpointer) &symbols[i]);
1578 		} else {
1579 			camel_sexp_add_function (
1580 				sexp, 0, symbols[i].name,
1581 				symbols[i].func,
1582 				(gpointer) &symbols[i]);
1583 		}
1584 	}
1585 }
1586 
1587 /**
1588  * camel_sexp_new:
1589  *
1590  * Returns: (transfer full): a new #CamelSExp
1591  *
1592  * Since: 3.4
1593  **/
1594 CamelSExp *
camel_sexp_new(void)1595 camel_sexp_new (void)
1596 {
1597 	return g_object_new (CAMEL_TYPE_SEXP, NULL);
1598 }
1599 
1600 /**
1601  * camel_sexp_add_function:
1602  * @sexp: a #CamelSExp
1603  * @scope: a scope
1604  * @name: a function name
1605  * @func: (scope call) (closure user_data): a function callback
1606  * @user_data: user data for @func
1607  *
1608  * Adds a function symbol which can not perform short evaluation.
1609  * Use camel_sexp_add_ifunction() for functions which can.
1610  *
1611  * Since: 3.4
1612  **/
1613 void
camel_sexp_add_function(CamelSExp * sexp,guint scope,const gchar * name,CamelSExpFunc func,gpointer user_data)1614 camel_sexp_add_function (CamelSExp *sexp,
1615                          guint scope,
1616                          const gchar *name,
1617                          CamelSExpFunc func,
1618                          gpointer user_data)
1619 {
1620 	CamelSExpSymbol *sym;
1621 
1622 	g_return_if_fail (CAMEL_IS_SEXP (sexp));
1623 	g_return_if_fail (name != NULL);
1624 
1625 	camel_sexp_remove_symbol (sexp, scope, name);
1626 
1627 	sym = g_malloc0 (sizeof (*sym));
1628 	sym->name = g_strdup (name);
1629 	sym->f.func = func;
1630 	sym->type = CAMEL_SEXP_TERM_FUNC;
1631 	sym->data = user_data;
1632 
1633 	g_scanner_scope_add_symbol (sexp->priv->scanner, scope, sym->name, sym);
1634 }
1635 
1636 /**
1637  * camel_sexp_add_ifunction:
1638  * @sexp: a #CamelSExp
1639  * @scope: a scope
1640  * @name: a function name
1641  * @ifunc: (scope call) (closure user_data): a function callback
1642  * @user_data: user data for @ifunc
1643  *
1644  * Adds a function symbol which can perform short evaluation,
1645  * or doesn't execute everything. Use camel_sexp_add_function()
1646  * for any other types of the function symbols.
1647  *
1648  * Since: 3.4
1649  **/
1650 void
camel_sexp_add_ifunction(CamelSExp * sexp,guint scope,const gchar * name,CamelSExpIFunc ifunc,gpointer user_data)1651 camel_sexp_add_ifunction (CamelSExp *sexp,
1652                           guint scope,
1653                           const gchar *name,
1654                           CamelSExpIFunc ifunc,
1655                           gpointer user_data)
1656 {
1657 	CamelSExpSymbol *sym;
1658 
1659 	g_return_if_fail (CAMEL_IS_SEXP (sexp));
1660 	g_return_if_fail (name != NULL);
1661 
1662 	camel_sexp_remove_symbol (sexp, scope, name);
1663 
1664 	sym = g_malloc0 (sizeof (*sym));
1665 	sym->name = g_strdup (name);
1666 	sym->f.ifunc = ifunc;
1667 	sym->type = CAMEL_SEXP_TERM_IFUNC;
1668 	sym->data = user_data;
1669 
1670 	g_scanner_scope_add_symbol (sexp->priv->scanner, scope, sym->name, sym);
1671 }
1672 
1673 /**
1674  * camel_sexp_add_variable:
1675  * @sexp: a #CamelSExp
1676  * @scope: a scope
1677  * @name: a variable name
1678  * @value: a variable value, as a #CamelSExpTerm
1679  *
1680  * Adds a variable named @name to the given @scope, set to the given @value.
1681  *
1682  * Since: 3.4
1683  **/
1684 void
camel_sexp_add_variable(CamelSExp * sexp,guint scope,const gchar * name,CamelSExpTerm * value)1685 camel_sexp_add_variable (CamelSExp *sexp,
1686                          guint scope,
1687                          const gchar *name,
1688                          CamelSExpTerm *value)
1689 {
1690 	CamelSExpSymbol *sym;
1691 
1692 	g_return_if_fail (CAMEL_IS_SEXP (sexp));
1693 	g_return_if_fail (name != NULL);
1694 
1695 	sym = g_malloc0 (sizeof (*sym));
1696 	sym->name = g_strdup (name);
1697 	sym->type = CAMEL_SEXP_TERM_VAR;
1698 	sym->data = value;
1699 
1700 	g_scanner_scope_add_symbol (sexp->priv->scanner, scope, sym->name, sym);
1701 }
1702 
1703 /**
1704  * camel_sexp_remove_symbol:
1705  * @sexp: a #CamelSExp
1706  * @scope: a scope
1707  * @name: a symbol name
1708  *
1709  * Revoes a symbol from a scope.
1710  *
1711  * Since: 3.4
1712  **/
1713 void
camel_sexp_remove_symbol(CamelSExp * sexp,guint scope,const gchar * name)1714 camel_sexp_remove_symbol (CamelSExp *sexp,
1715                           guint scope,
1716                           const gchar *name)
1717 {
1718 	gint oldscope;
1719 	CamelSExpSymbol *sym;
1720 
1721 	g_return_if_fail (CAMEL_IS_SEXP (sexp));
1722 	g_return_if_fail (name != NULL);
1723 
1724 	oldscope = g_scanner_set_scope (sexp->priv->scanner, scope);
1725 	sym = g_scanner_lookup_symbol (sexp->priv->scanner, name);
1726 	g_scanner_scope_remove_symbol (sexp->priv->scanner, scope, name);
1727 	g_scanner_set_scope (sexp->priv->scanner, oldscope);
1728 	if (sym != NULL) {
1729 		g_free (sym->name);
1730 		g_free (sym);
1731 	}
1732 }
1733 
1734 /**
1735  * camel_sexp_set_scope:
1736  * @sexp: a #CamelSExp
1737  * @scope: a scope to set
1738  *
1739  * sets the current scope for the scanner.
1740  *
1741  * Returns: the previous scope id
1742  *
1743  * Since: 3.4
1744  **/
1745 gint
camel_sexp_set_scope(CamelSExp * sexp,guint scope)1746 camel_sexp_set_scope (CamelSExp *sexp,
1747                       guint scope)
1748 {
1749 	g_return_val_if_fail (CAMEL_IS_SEXP (sexp), 0);
1750 
1751 	return g_scanner_set_scope (sexp->priv->scanner, scope);
1752 }
1753 
1754 /**
1755  * camel_sexp_input_text:
1756  * @sexp: a #CamelSExp
1757  * @text: a text buffer to scan
1758  * @len: the length of the text buffer
1759  *
1760  * Prepares to scan a text buffer.
1761  *
1762  * Since: 3.4
1763  **/
1764 void
camel_sexp_input_text(CamelSExp * sexp,const gchar * text,gint len)1765 camel_sexp_input_text (CamelSExp *sexp,
1766                        const gchar *text,
1767                        gint len)
1768 {
1769 	g_return_if_fail (CAMEL_IS_SEXP (sexp));
1770 	g_return_if_fail (text != NULL);
1771 
1772 	g_scanner_input_text (sexp->priv->scanner, text, len);
1773 }
1774 
1775 /**
1776  * camel_sexp_input_file:
1777  * @sexp: a #CamelSExp
1778  * @fd: a file descriptor
1779  *
1780  * Prepares to scan a file.
1781  *
1782  * Since: 3.4
1783  **/
1784 void
camel_sexp_input_file(CamelSExp * sexp,gint fd)1785 camel_sexp_input_file (CamelSExp *sexp,
1786                        gint fd)
1787 {
1788 	g_return_if_fail (CAMEL_IS_SEXP (sexp));
1789 
1790 	g_scanner_input_file (sexp->priv->scanner, fd);
1791 }
1792 
1793 /**
1794  * camel_sexp_parse:
1795  * @sexp: a #CamelSExp
1796  *
1797  * Since: 3.4
1798  **/
1799 gint
camel_sexp_parse(CamelSExp * sexp)1800 camel_sexp_parse (CamelSExp *sexp)
1801 {
1802 	g_return_val_if_fail (CAMEL_IS_SEXP (sexp), -1);
1803 
1804 	if (setjmp (sexp->priv->failenv)) {
1805 		g_warning ("Error in parsing: %s", sexp->priv->error);
1806 		return -1;
1807 	}
1808 
1809 	if (sexp->priv->tree)
1810 		parse_term_free (sexp, sexp->priv->tree);
1811 
1812 	sexp->priv->tree = parse_value (sexp);
1813 
1814 	return 0;
1815 }
1816 
1817 /**
1818  * camel_sexp_eval: (skip)
1819  * @sexp: a #CamelSExp
1820  *
1821  * Since: 3.4
1822  **/
1823 CamelSExpResult *
camel_sexp_eval(CamelSExp * sexp)1824 camel_sexp_eval (CamelSExp *sexp)
1825 {
1826 	g_return_val_if_fail (CAMEL_IS_SEXP (sexp), NULL);
1827 	g_return_val_if_fail (sexp->priv->tree != NULL, NULL);
1828 
1829 	if (setjmp (sexp->priv->failenv)) {
1830 		g_warning ("Error in execution: %s", sexp->priv->error);
1831 		return NULL;
1832 	}
1833 
1834 	return camel_sexp_term_eval (sexp, sexp->priv->tree);
1835 }
1836 
1837 /**
1838  * e_cal_backend_sexp_evaluate_occur_times:
1839  * @sexp: a #CamelSExp
1840  * @start: Start of the time window will be stored here.
1841  * @end: End of the time window will be stored here.
1842  *
1843  * Determines biggest time window given by expressions "occur-in-range" in sexp.
1844  *
1845  * Since: 3.4
1846  */
1847 gboolean
camel_sexp_evaluate_occur_times(CamelSExp * sexp,time_t * start,time_t * end)1848 camel_sexp_evaluate_occur_times (CamelSExp *sexp,
1849                                  time_t *start,
1850                                  time_t *end)
1851 {
1852 	CamelSExpResult *result;
1853 	gboolean generator;
1854 	g_return_val_if_fail (CAMEL_IS_SEXP (sexp), FALSE);
1855 	g_return_val_if_fail (sexp->priv->tree != NULL, FALSE);
1856 	g_return_val_if_fail (start != NULL, FALSE);
1857 	g_return_val_if_fail (end != NULL, FALSE);
1858 
1859 	*start = *end = -1;
1860 
1861 	if (setjmp (sexp->priv->failenv)) {
1862 		g_warning ("Error in execution: %s", sexp->priv->error);
1863 		return FALSE;
1864 	}
1865 
1866 	result = camel_sexp_term_evaluate_occur_times (
1867 		sexp, sexp->priv->tree, start, end);
1868 	generator = result->time_generator;
1869 
1870 	if (generator) {
1871 		*start = result->occuring_start;
1872 		*end = result->occuring_end;
1873 	}
1874 
1875 	camel_sexp_result_free (sexp, result);
1876 
1877 	return generator;
1878 }
1879 
1880 /**
1881  * camel_sexp_encode_bool:
1882  * @string: Destination #GString
1883  * @v_bool: the value
1884  *
1885  * Encode a bool into an s-expression @string.  Bools are
1886  * encoded using #t #f syntax.
1887  *
1888  * Since: 3.4
1889  **/
1890 void
camel_sexp_encode_bool(GString * string,gboolean v_bool)1891 camel_sexp_encode_bool (GString *string,
1892                         gboolean v_bool)
1893 {
1894 	if (v_bool)
1895 		g_string_append (string, " #t");
1896 	else
1897 		g_string_append (string, " #f");
1898 }
1899 
1900 /**
1901  * camel_sexp_encode_string:
1902  * @string: Destination #GString
1903  * @v_string: String expression.
1904  *
1905  * Add a c string @v_string to the s-expression stored in
1906  * the gstring @s.  Quotes are added, and special characters
1907  * are escaped appropriately.
1908  *
1909  * Since: 3.4
1910  **/
1911 void
camel_sexp_encode_string(GString * string,const gchar * v_string)1912 camel_sexp_encode_string (GString *string,
1913                           const gchar *v_string)
1914 {
1915 	gchar c;
1916 	const gchar *p;
1917 
1918 	if (v_string == NULL)
1919 		p = "";
1920 	else
1921 		p = v_string;
1922 	g_string_append (string, " \"");
1923 	while ((c = *p++)) {
1924 		if (c == '\\' || c == '\"' || c == '\'')
1925 			g_string_append_c (string, '\\');
1926 		g_string_append_c (string, c);
1927 	}
1928 	g_string_append_c (string, '\"');
1929 }
1930 
1931 #ifdef TESTER
1932 gint
main(gint argc,gchar ** argv)1933 main (gint argc,
1934       gchar **argv)
1935 {
1936 	CamelSExp *sexp;
1937 	gchar *t = "(+ \"foo\" \"\\\"\" \"bar\" \"\\\\ blah \\x \")";
1938 	CamelSExpResult *result;
1939 
1940 	sexp = camel_sexp_new ();
1941 
1942 	camel_sexp_add_variable (sexp, 0, "test", NULL);
1943 
1944 	if (argc < 2 || !argv[1])
1945 		return;
1946 
1947 	camel_sexp_input_text (sexp, t, t);
1948 	camel_sexp_parse (sexp);
1949 
1950 	if (sexp->priv->tree)
1951 		parse_dump_term (sexp->priv->tree, 0);
1952 
1953 	result = camel_sexp_eval (sexp);
1954 	if (result) {
1955 		eval_dump_result (result, 0);
1956 	} else {
1957 		printf ("no result?|\n");
1958 	}
1959 
1960 	return 0;
1961 }
1962 #endif
1963