Merging gst-plugins-ugly
[platform/upstream/gstreamer.git] / gst / realmedia / asmrules.c
1 /* GStreamer
2  * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library 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.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include <string.h>
21 #include <stdlib.h>
22
23 #include "asmrules.h"
24
25 #define MAX_RULE_LENGTH 2048
26
27 /* define to enable some more debug */
28 #undef DEBUG
29
30 static GstASMNode *
31 gst_asm_node_new (void)
32 {
33   GstASMNode *node;
34
35   node = g_new0 (GstASMNode, 1);
36   node->type = GST_ASM_NODE_UNKNOWN;
37
38   return node;
39 }
40
41 static void
42 gst_asm_node_free (GstASMNode * node)
43 {
44   if (node->left)
45     gst_asm_node_free (node->left);
46   if (node->right)
47     gst_asm_node_free (node->right);
48   if (node->type == GST_ASM_NODE_VARIABLE && node->data.varname)
49     g_free (node->data.varname);
50   g_free (node);
51 }
52
53 static gfloat
54 gst_asm_operator_eval (GstASMOp optype, gfloat left, gfloat right)
55 {
56   gfloat result = 0.0;
57
58   switch (optype) {
59     case GST_ASM_OP_GREATER:
60       result = (gfloat) (left > right);
61       break;
62     case GST_ASM_OP_LESS:
63       result = (gfloat) (left < right);
64       break;
65     case GST_ASM_OP_GREATEREQUAL:
66       result = (gfloat) (left >= right);
67       break;
68     case GST_ASM_OP_LESSEQUAL:
69       result = (gfloat) (left <= right);
70       break;
71     case GST_ASM_OP_EQUAL:
72       result = (gfloat) (left == right);
73       break;
74     case GST_ASM_OP_NOTEQUAL:
75       result = (gfloat) (left != right);
76       break;
77     case GST_ASM_OP_AND:
78       result = (gfloat) (left && right);
79       break;
80     case GST_ASM_OP_OR:
81       result = (gfloat) (left || right);
82       break;
83     default:
84       break;
85   }
86   return result;
87 }
88
89 static gfloat
90 gst_asm_node_evaluate (GstASMNode * node, GHashTable * vars)
91 {
92   gfloat result = 0.0;
93
94   if (node == NULL)
95     return 0.0;
96
97   switch (node->type) {
98     case GST_ASM_NODE_VARIABLE:
99     {
100       gchar *val;
101
102       val = g_hash_table_lookup (vars, node->data.varname);
103       if (val)
104         result = (gfloat) atof (val);
105       break;
106     }
107     case GST_ASM_NODE_INTEGER:
108       result = (gfloat) node->data.intval;
109       break;
110     case GST_ASM_NODE_FLOAT:
111       result = node->data.floatval;
112       break;
113     case GST_ASM_NODE_OPERATOR:
114     {
115       gfloat left, right;
116
117       left = gst_asm_node_evaluate (node->left, vars);
118       right = gst_asm_node_evaluate (node->right, vars);
119
120       result = gst_asm_operator_eval (node->data.optype, left, right);
121       break;
122     }
123     default:
124       break;
125   }
126   return result;
127 }
128
129 #define IS_SPACE(p) (((p) == ' ') || ((p) == '\n') || \
130                      ((p) == '\r') || ((p) == '\t'))
131 #define IS_RULE_DELIM(p) (((p) == ',') || ((p) == ';') || ((p) == ')'))
132 #define IS_OPERATOR(p) (((p) == '>') || ((p) == '<') || \
133                         ((p) == '=') || ((p) == '!') || \
134                         ((p) == '&') || ((p) == '|'))
135 #define IS_NUMBER(p) ((((p) >= '0') && ((p) <= '9')) || ((p) == '.'))
136 #define IS_CHAR(p) (!IS_OPERATOR(ch) && !IS_RULE_DELIM(ch) && (ch != '\0'))
137
138 #define IS_OP_TOKEN(t) (((t) == GST_ASM_TOKEN_AND) || ((t) == GST_ASM_TOKEN_OR))
139 #define IS_COND_TOKEN(t) (((t) == GST_ASM_TOKEN_LESS) || ((t) == GST_ASM_TOKEN_LESSEQUAL) || \
140                 ((t) == GST_ASM_TOKEN_GREATER) || ((t) == GST_ASM_TOKEN_GREATEREQUAL) || \
141                 ((t) == GST_ASM_TOKEN_EQUAL) || ((t) == GST_ASM_TOKEN_NOTEQUAL))
142
143 typedef struct
144 {
145   const gchar *buffer;
146   gint pos;
147   gchar ch;
148
149   GstASMToken token;
150   gchar val[MAX_RULE_LENGTH];
151 } GstASMScan;
152
153 #define NEXT_CHAR(scan) ((scan)->ch = (scan)->buffer[(scan)->pos++])
154 #define THIS_CHAR(scan) ((scan)->ch)
155
156 static GstASMScan *
157 gst_asm_scan_new (const gchar * buffer)
158 {
159   GstASMScan *scan;
160
161   scan = g_new0 (GstASMScan, 1);
162   scan->buffer = buffer;
163   NEXT_CHAR (scan);
164
165   return scan;
166 }
167
168 static void
169 gst_asm_scan_free (GstASMScan * scan)
170 {
171   g_free (scan);
172 }
173
174 static void
175 gst_asm_scan_string (GstASMScan * scan, gchar delim)
176 {
177   gchar ch;
178   gint i = 0;
179
180   ch = THIS_CHAR (scan);
181   while ((ch != delim) && (ch != '\0')) {
182     if (i < MAX_RULE_LENGTH - 1)
183       scan->val[i++] = ch;
184     ch = NEXT_CHAR (scan);
185     if (ch == '\\')
186       ch = NEXT_CHAR (scan);
187   }
188   scan->val[i] = '\0';
189
190   if (ch == delim)
191     NEXT_CHAR (scan);
192
193   scan->token = GST_ASM_TOKEN_STRING;
194 }
195
196 static void
197 gst_asm_scan_number (GstASMScan * scan)
198 {
199   gchar ch;
200   gint i = 0;
201   gboolean have_float = FALSE;
202
203   ch = THIS_CHAR (scan);
204   /* real strips all spaces that are not inside quotes for numbers */
205   while ((IS_NUMBER (ch) || IS_SPACE (ch))) {
206     if (i < (MAX_RULE_LENGTH - 1) && !IS_SPACE (ch))
207       scan->val[i++] = ch;
208     if (ch == '.')
209       have_float = TRUE;
210     ch = NEXT_CHAR (scan);
211   }
212   scan->val[i] = '\0';
213
214   if (have_float)
215     scan->token = GST_ASM_TOKEN_FLOAT;
216   else
217     scan->token = GST_ASM_TOKEN_INT;
218 }
219
220 static void
221 gst_asm_scan_identifier (GstASMScan * scan)
222 {
223   gchar ch;
224   gint i = 0;
225
226   ch = THIS_CHAR (scan);
227   /* real strips all spaces that are not inside quotes for identifiers */
228   while ((IS_CHAR (ch) || IS_SPACE (ch))) {
229     if (i < (MAX_RULE_LENGTH - 1) && !IS_SPACE (ch))
230       scan->val[i++] = ch;
231     ch = NEXT_CHAR (scan);
232   }
233   scan->val[i] = '\0';
234
235   scan->token = GST_ASM_TOKEN_IDENTIFIER;
236 }
237
238 static void
239 gst_asm_scan_print_token (GstASMScan * scan)
240 {
241 #ifdef DEBUG
242   switch (scan->token) {
243     case GST_ASM_TOKEN_NONE:
244       g_print ("none\n");
245       break;
246     case GST_ASM_TOKEN_EOF:
247       g_print ("EOF\n");
248       break;
249
250     case GST_ASM_TOKEN_INT:
251       g_print ("INT %d\n", atoi (scan->val));
252       break;
253     case GST_ASM_TOKEN_FLOAT:
254       g_print ("FLOAT %f\n", atof (scan->val));
255       break;
256     case GST_ASM_TOKEN_IDENTIFIER:
257       g_print ("ID %s\n", scan->val);
258       break;
259     case GST_ASM_TOKEN_STRING:
260       g_print ("STRING %s\n", scan->val);
261       break;
262
263     case GST_ASM_TOKEN_HASH:
264       g_print ("HASH\n");
265       break;
266     case GST_ASM_TOKEN_SEMICOLON:
267       g_print ("SEMICOLON\n");
268       break;
269     case GST_ASM_TOKEN_COMMA:
270       g_print ("COMMA\n");
271       break;
272     case GST_ASM_TOKEN_EQUAL:
273       g_print ("==\n");
274       break;
275     case GST_ASM_TOKEN_NOTEQUAL:
276       g_print ("!=\n");
277       break;
278     case GST_ASM_TOKEN_AND:
279       g_print ("&&\n");
280       break;
281     case GST_ASM_TOKEN_OR:
282       g_print ("||\n");
283       break;
284     case GST_ASM_TOKEN_LESS:
285       g_print ("<\n");
286       break;
287     case GST_ASM_TOKEN_LESSEQUAL:
288       g_print ("<=\n");
289       break;
290     case GST_ASM_TOKEN_GREATER:
291       g_print (">\n");
292       break;
293     case GST_ASM_TOKEN_GREATEREQUAL:
294       g_print (">=\n");
295       break;
296     case GST_ASM_TOKEN_DOLLAR:
297       g_print ("$\n");
298       break;
299     case GST_ASM_TOKEN_LPAREN:
300       g_print ("(\n");
301       break;
302     case GST_ASM_TOKEN_RPAREN:
303       g_print (")\n");
304       break;
305     default:
306       break;
307   }
308 #endif
309 }
310
311 static GstASMToken
312 gst_asm_scan_next_token (GstASMScan * scan)
313 {
314   gchar ch;
315
316   ch = THIS_CHAR (scan);
317
318   /* skip spaces */
319   while (IS_SPACE (ch))
320     ch = NEXT_CHAR (scan);
321
322   /* remove \ which is common in front of " */
323   while (ch == '\\')
324     ch = NEXT_CHAR (scan);
325
326   switch (ch) {
327     case '#':
328       scan->token = GST_ASM_TOKEN_HASH;
329       NEXT_CHAR (scan);
330       break;
331     case ';':
332       scan->token = GST_ASM_TOKEN_SEMICOLON;
333       NEXT_CHAR (scan);
334       break;
335     case ',':
336       scan->token = GST_ASM_TOKEN_COMMA;
337       NEXT_CHAR (scan);
338       break;
339     case '=':
340       scan->token = GST_ASM_TOKEN_EQUAL;
341       if (NEXT_CHAR (scan) == '=')
342         NEXT_CHAR (scan);
343       break;
344     case '!':
345       if (NEXT_CHAR (scan) == '=') {
346         scan->token = GST_ASM_TOKEN_NOTEQUAL;
347         NEXT_CHAR (scan);
348       }
349       break;
350     case '&':
351       scan->token = GST_ASM_TOKEN_AND;
352       if (NEXT_CHAR (scan) == '&')
353         NEXT_CHAR (scan);
354       break;
355     case '|':
356       scan->token = GST_ASM_TOKEN_OR;
357       if (NEXT_CHAR (scan) == '|')
358         NEXT_CHAR (scan);
359       break;
360     case '<':
361       scan->token = GST_ASM_TOKEN_LESS;
362       if (NEXT_CHAR (scan) == '=') {
363         scan->token = GST_ASM_TOKEN_LESSEQUAL;
364         NEXT_CHAR (scan);
365       }
366       break;
367     case '>':
368       scan->token = GST_ASM_TOKEN_GREATER;
369       if (NEXT_CHAR (scan) == '=') {
370         scan->token = GST_ASM_TOKEN_GREATEREQUAL;
371         NEXT_CHAR (scan);
372       }
373       break;
374     case '$':
375       scan->token = GST_ASM_TOKEN_DOLLAR;
376       NEXT_CHAR (scan);
377       break;
378     case '(':
379       scan->token = GST_ASM_TOKEN_LPAREN;
380       NEXT_CHAR (scan);
381       break;
382     case ')':
383       scan->token = GST_ASM_TOKEN_RPAREN;
384       NEXT_CHAR (scan);
385       break;
386     case '"':
387       NEXT_CHAR (scan);
388       gst_asm_scan_string (scan, '"');
389       break;
390     case '\'':
391       NEXT_CHAR (scan);
392       gst_asm_scan_string (scan, '\'');
393       break;
394     case '0':
395     case '1':
396     case '2':
397     case '3':
398     case '4':
399     case '5':
400     case '6':
401     case '7':
402     case '8':
403     case '9':
404       gst_asm_scan_number (scan);
405       break;
406     case '\0':
407       scan->token = GST_ASM_TOKEN_EOF;
408       break;
409     default:
410       gst_asm_scan_identifier (scan);
411       break;
412   }
413   gst_asm_scan_print_token (scan);
414   return scan->token;
415 }
416
417 static GstASMRule *
418 gst_asm_rule_new (void)
419 {
420   GstASMRule *rule;
421
422   rule = g_new (GstASMRule, 1);
423   rule->root = NULL;
424   rule->props = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
425
426   return rule;
427 }
428
429 static void
430 gst_asm_rule_free (GstASMRule * rule)
431 {
432   g_hash_table_destroy (rule->props);
433   if (rule->root)
434     gst_asm_node_free (rule->root);
435   g_free (rule);
436 }
437
438 static void
439 gst_asm_rule_add_property (GstASMRule * rule, gchar * key, gchar * val)
440 {
441   g_hash_table_insert (rule->props, key, val);
442 }
443
444 static GstASMNode *gst_asm_scan_parse_condition (GstASMScan * scan);
445
446 static GstASMNode *
447 gst_asm_scan_parse_operand (GstASMScan * scan)
448 {
449   GstASMNode *node;
450
451   switch (scan->token) {
452     case GST_ASM_TOKEN_DOLLAR:
453       gst_asm_scan_next_token (scan);
454
455       if (scan->token != GST_ASM_TOKEN_IDENTIFIER)
456         g_warning ("identifier expected");
457
458       node = gst_asm_node_new ();
459       node->type = GST_ASM_NODE_VARIABLE;
460       node->data.varname = g_strdup (scan->val);
461       break;
462     case GST_ASM_TOKEN_INT:
463       node = gst_asm_node_new ();
464       node->type = GST_ASM_NODE_INTEGER;
465       node->data.intval = (gfloat) atof (scan->val);
466       break;
467     case GST_ASM_TOKEN_FLOAT:
468       node = gst_asm_node_new ();
469       node->type = GST_ASM_NODE_FLOAT;
470       node->data.floatval = atoi (scan->val);
471       break;
472     case GST_ASM_TOKEN_LPAREN:
473       gst_asm_scan_next_token (scan);
474       node = gst_asm_scan_parse_condition (scan);
475       if (scan->token != GST_ASM_TOKEN_RPAREN)
476         g_warning (") expected");
477       break;
478     default:
479       g_warning ("$ <number> or ) expected");
480       node = NULL;
481       break;
482   }
483   gst_asm_scan_next_token (scan);
484
485   return node;
486 }
487
488 static GstASMNode *
489 gst_asm_scan_parse_expression (GstASMScan * scan)
490 {
491   GstASMNode *node, *left;
492
493   node = gst_asm_scan_parse_operand (scan);
494
495   while (IS_COND_TOKEN (scan->token)) {
496     left = node;
497
498     node = gst_asm_node_new ();
499     node->type = GST_ASM_NODE_OPERATOR;
500     node->data.optype = (GstASMOp) scan->token;
501
502     gst_asm_scan_next_token (scan);
503
504     node->right = gst_asm_scan_parse_operand (scan);
505     node->left = left;
506   }
507   return node;
508 }
509
510 static GstASMNode *
511 gst_asm_scan_parse_condition (GstASMScan * scan)
512 {
513   GstASMNode *node, *left;
514
515   node = gst_asm_scan_parse_expression (scan);
516
517   while (IS_OP_TOKEN (scan->token)) {
518     left = node;
519
520     node = gst_asm_node_new ();
521     node->type = GST_ASM_NODE_OPERATOR;
522     node->data.optype = (GstASMOp) scan->token;
523
524     gst_asm_scan_next_token (scan);
525
526     node->right = gst_asm_scan_parse_expression (scan);
527     node->left = left;
528   }
529   return node;
530 }
531
532 static void
533 gst_asm_scan_parse_property (GstASMRule * rule, GstASMScan * scan)
534 {
535   gchar *key, *val;
536
537   if (scan->token != GST_ASM_TOKEN_IDENTIFIER) {
538     g_warning ("identifier expected");
539     return;
540   }
541   key = g_strdup (scan->val);
542
543   gst_asm_scan_next_token (scan);
544   if (scan->token != GST_ASM_TOKEN_EQUAL) {
545     g_warning ("= expected");
546     g_free (key);
547     return;
548   }
549   gst_asm_scan_next_token (scan);
550   val = g_strdup (scan->val);
551
552   gst_asm_rule_add_property (rule, key, val);
553   gst_asm_scan_next_token (scan);
554 }
555
556 static GstASMRule *
557 gst_asm_scan_parse_rule (GstASMScan * scan)
558 {
559   GstASMRule *rule;
560
561   rule = gst_asm_rule_new ();
562
563   if (scan->token == GST_ASM_TOKEN_HASH) {
564     gst_asm_scan_next_token (scan);
565     rule->root = gst_asm_scan_parse_condition (scan);
566     if (scan->token == GST_ASM_TOKEN_COMMA)
567       gst_asm_scan_next_token (scan);
568   }
569
570   if (scan->token != GST_ASM_TOKEN_SEMICOLON) {
571     gst_asm_scan_parse_property (rule, scan);
572     while (scan->token == GST_ASM_TOKEN_COMMA) {
573       gst_asm_scan_next_token (scan);
574       gst_asm_scan_parse_property (rule, scan);
575     }
576     gst_asm_scan_next_token (scan);
577   }
578   return rule;
579 }
580
581 static gboolean
582 gst_asm_rule_evaluate (GstASMRule * rule, GHashTable * vars)
583 {
584   gboolean res;
585
586   if (rule->root) {
587     res = (gboolean) gst_asm_node_evaluate (rule->root, vars);
588   } else
589     res = TRUE;
590
591   return res;
592 }
593
594 GstASMRuleBook *
595 gst_asm_rule_book_new (const gchar * rulebook)
596 {
597   GstASMRuleBook *book;
598   GstASMRule *rule = NULL;
599   GstASMScan *scan;
600   GstASMToken token;
601
602   book = g_new0 (GstASMRuleBook, 1);
603   book->rulebook = rulebook;
604
605   scan = gst_asm_scan_new (book->rulebook);
606   gst_asm_scan_next_token (scan);
607
608   do {
609     rule = gst_asm_scan_parse_rule (scan);
610     if (rule) {
611       book->rules = g_list_append (book->rules, rule);
612       book->n_rules++;
613     }
614     token = scan->token;
615   } while (token != GST_ASM_TOKEN_EOF);
616
617   gst_asm_scan_free (scan);
618
619   return book;
620 }
621
622 void
623 gst_asm_rule_book_free (GstASMRuleBook * book)
624 {
625   GList *walk;
626
627   for (walk = book->rules; walk; walk = g_list_next (walk)) {
628     GstASMRule *rule = (GstASMRule *) walk->data;
629
630     gst_asm_rule_free (rule);
631   }
632   g_list_free (book->rules);
633   g_free (book);
634 }
635
636 gint
637 gst_asm_rule_book_match (GstASMRuleBook * book, GHashTable * vars,
638     gint * rulematches)
639 {
640   GList *walk;
641   gint i, n = 0;
642
643   for (walk = book->rules, i = 0; walk; walk = g_list_next (walk), i++) {
644     GstASMRule *rule = (GstASMRule *) walk->data;
645
646     if (gst_asm_rule_evaluate (rule, vars)) {
647       rulematches[n++] = i;
648     }
649   }
650   return n;
651 }
652
653 #ifdef TEST
654 gint
655 main (gint argc, gchar * argv[])
656 {
657   GstASMRuleBook *book;
658   gint rulematch[MAX_RULEMATCHES];
659   GHashTable *vars;
660   gint i, n;
661
662   static const gchar rules1[] =
663       "#($Bandwidth < 67959),TimestampDelivery=T,DropByN=T,"
664       "priority=9;#($Bandwidth >= 67959) && ($Bandwidth < 167959),"
665       "AverageBandwidth=67959,Priority=9;#($Bandwidth >= 67959) && ($Bandwidth"
666       " < 167959),AverageBandwidth=0,Priority=5,OnDepend=\\\"1\\\";#($Bandwidth >= 167959)"
667       " && ($Bandwidth < 267959),AverageBandwidth=167959,Priority=9;#($Bandwidth >= 167959)"
668       " && ($Bandwidth < 267959),AverageBandwidth=0,Priority=5,OnDepend=\\\"3\\\";"
669       "#($Bandwidth >= 267959),AverageBandwidth=267959,Priority=9;#($Bandwidth >= 267959)"
670       ",AverageBandwidth=0,Priority=5,OnDepend=\\\"5\\\";";
671   static const gchar rules2[] =
672       "AverageBandwidth=32041,Priority=5;AverageBandwidth=0,"
673       "Priority=5,OnDepend=\\\"0\\\", OffDepend=\\\"0\\\";";
674   static const gchar rules3[] =
675       "#(($Bandwidth >= 27500) && ($OldPNMPlayer)),AverageBandwidth=27500,priority=9,PNMKeyframeRule=T;#(($Bandwidth >= 27500) && ($OldPNMPlayer)),AverageBandwidth=0,priority=5,PNMNonKeyframeRule=T;#(($Bandwidth < 27500) && ($OldPNMPlayer)),TimestampDelivery=T,DropByN=T,priority=9,PNMThinningRule=T;#($Bandwidth < 13899),TimestampDelivery=T,DropByN=T,priority=9;#($Bandwidth >= 13899) && ($Bandwidth < 19000),AverageBandwidth=13899,Priority=9;#($Bandwidth >= 13899) && ($Bandwidth < 19000),AverageBandwidth=0,Priority=5,OnDepend=\\\"4\\\";#($Bandwidth >= 19000) && ($Bandwidth < 27500),AverageBandwidth=19000,Priority=9;#($Bandwidth >= 19000) && ($Bandwidth < 27500),AverageBandwidth=0,Priority=5,OnDepend=\\\"6\\\";#($Bandwidth >= 27500) && ($Bandwidth < 132958),AverageBandwidth=27500,Priority=9;#($Bandwidth >= 27500) && ($Bandwidth < 132958),AverageBandwidth=0,Priority=5,OnDepend=\\\"8\\\";#($Bandwidth >= 132958) && ($Bandwidth < 187958),AverageBandwidth=132958,Priority=9;#($Bandwidth >= 132958) && ($Bandwidth < 187958),AverageBandwidth=0,Priority=5,OnDepend=\\\"10\\\";#($Bandwidth >= 187958),AverageBandwidth=187958,Priority=9;#($Bandwidth >= 187958),AverageBandwidth=0,Priority=5,OnDepend=\\\"12\\\";";
676
677   vars = g_hash_table_new (g_str_hash, g_str_equal);
678   g_hash_table_insert (vars, (gchar *) "Bandwidth", (gchar *) "300000");
679
680   book = gst_asm_rule_book_new (rules1);
681   n = gst_asm_rule_book_match (book, vars, rulematch);
682   gst_asm_rule_book_free (book);
683
684   g_print ("%d rules matched\n", n);
685   for (i = 0; i < n; i++) {
686     g_print ("rule %d matched\n", rulematch[i]);
687   }
688
689   book = gst_asm_rule_book_new (rules2);
690   n = gst_asm_rule_book_match (book, vars, rulematch);
691   gst_asm_rule_book_free (book);
692
693   g_print ("%d rules matched\n", n);
694   for (i = 0; i < n; i++) {
695     g_print ("rule %d matched\n", rulematch[i]);
696   }
697
698   book = gst_asm_rule_book_new (rules3);
699   n = gst_asm_rule_book_match (book, vars, rulematch);
700   gst_asm_rule_book_free (book);
701
702
703   g_print ("%d rules matched\n", n);
704   for (i = 0; i < n; i++) {
705     g_print ("rule %d matched\n", rulematch[i]);
706   }
707
708   g_hash_table_destroy (vars);
709
710   return 0;
711 }
712 #endif