00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "dbus-gparser.h"
00024 #include "dbus/dbus-glib-lowlevel.h"
00025 #include "dbus-gidl.h"
00026 #include "dbus-gobject.h"
00027 #include "dbus/dbus-signature.h"
00028 #include <string.h>
00029
00030 #include <libintl.h>
00031 #define _(x) gettext ((x))
00032 #define N_(x) x
00033
00034 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00035
00036 #define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
00037
00038 typedef struct
00039 {
00040 const char *name;
00041 const char **retloc;
00042 } LocateAttr;
00043
00044 static gboolean
00045 locate_attributes (const char *element_name,
00046 const char **attribute_names,
00047 const char **attribute_values,
00048 GError **error,
00049 const char *first_attribute_name,
00050 const char **first_attribute_retloc,
00051 ...)
00052 {
00053 va_list args;
00054 const char *name;
00055 const char **retloc;
00056 int n_attrs;
00057 #define MAX_ATTRS 24
00058 LocateAttr attrs[MAX_ATTRS];
00059 gboolean retval;
00060 int i;
00061
00062 g_return_val_if_fail (first_attribute_name != NULL, FALSE);
00063 g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
00064
00065 retval = TRUE;
00066
00067 n_attrs = 1;
00068 attrs[0].name = first_attribute_name;
00069 attrs[0].retloc = first_attribute_retloc;
00070 *first_attribute_retloc = NULL;
00071
00072 va_start (args, first_attribute_retloc);
00073
00074 name = va_arg (args, const char*);
00075 retloc = va_arg (args, const char**);
00076
00077 while (name != NULL)
00078 {
00079 if (retloc == NULL)
00080 {
00081 va_end (args);
00082 return FALSE;
00083 }
00084
00085 g_assert (n_attrs < MAX_ATTRS);
00086
00087 attrs[n_attrs].name = name;
00088 attrs[n_attrs].retloc = retloc;
00089 n_attrs += 1;
00090 *retloc = NULL;
00091
00092 name = va_arg (args, const char*);
00093 retloc = va_arg (args, const char**);
00094 }
00095
00096 va_end (args);
00097
00098 if (!retval)
00099 return retval;
00100
00101 i = 0;
00102 while (attribute_names[i])
00103 {
00104 int j;
00105 gboolean found;
00106
00107 found = FALSE;
00108 j = 0;
00109 while (j < n_attrs)
00110 {
00111 if (strcmp (attrs[j].name, attribute_names[i]) == 0)
00112 {
00113 retloc = attrs[j].retloc;
00114
00115 if (*retloc != NULL)
00116 {
00117 g_set_error (error,
00118 G_MARKUP_ERROR,
00119 G_MARKUP_ERROR_PARSE,
00120 _("Attribute \"%s\" repeated twice on the same <%s> element"),
00121 attrs[j].name, element_name);
00122 retval = FALSE;
00123 goto out;
00124 }
00125
00126 *retloc = attribute_values[i];
00127 found = TRUE;
00128 }
00129
00130 ++j;
00131 }
00132
00133 if (!found)
00134 {
00135
00136 if (strchr (attribute_names[i], ':') == NULL)
00137 {
00138 g_set_error (error,
00139 G_MARKUP_ERROR,
00140 G_MARKUP_ERROR_PARSE,
00141 _("Attribute \"%s\" is invalid on <%s> element in this context"),
00142 attribute_names[i], element_name);
00143 retval = FALSE;
00144 goto out;
00145 }
00146 }
00147
00148 ++i;
00149 }
00150
00151 out:
00152 return retval;
00153 }
00154
00155 #if 0
00156 static gboolean
00157 check_no_attributes (const char *element_name,
00158 const char **attribute_names,
00159 const char **attribute_values,
00160 GError **error)
00161 {
00162 if (attribute_names[0] != NULL)
00163 {
00164 g_set_error (error,
00165 G_MARKUP_ERROR,
00166 G_MARKUP_ERROR_PARSE,
00167 _("Attribute \"%s\" is invalid on <%s> element in this context"),
00168 attribute_names[0], element_name);
00169 return FALSE;
00170 }
00171
00172 return TRUE;
00173 }
00174 #endif
00175
00176 struct Parser
00177 {
00178 int refcount;
00179
00180 NodeInfo *result;
00181 GSList *node_stack;
00182 InterfaceInfo *interface;
00183 MethodInfo *method;
00184 SignalInfo *signal;
00185 PropertyInfo *property;
00186 ArgInfo *arg;
00187 gboolean in_annotation;
00188 guint unknown_namespaced_depth;
00189 };
00190
00191 Parser*
00192 parser_new (void)
00193 {
00194 Parser *parser;
00195
00196 parser = g_new0 (Parser, 1);
00197
00198 parser->refcount = 1;
00199
00200 return parser;
00201 }
00202
00203 Parser *
00204 parser_ref (Parser *parser)
00205 {
00206 parser->refcount += 1;
00207
00208 return parser;
00209 }
00210
00211 void
00212 parser_unref (Parser *parser)
00213 {
00214 parser->refcount -= 1;
00215 if (parser->refcount == 0)
00216 {
00217 if (parser->result)
00218 node_info_unref (parser->result);
00219
00220 g_free (parser);
00221 }
00222 }
00223
00224 gboolean
00225 parser_check_doctype (Parser *parser,
00226 const char *doctype,
00227 GError **error)
00228 {
00229 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
00230
00231 if (strcmp (doctype, "node") != 0)
00232 {
00233 g_set_error (error,
00234 G_MARKUP_ERROR,
00235 G_MARKUP_ERROR_PARSE,
00236 "D-BUS description file has the wrong document type %s, use node or interface",
00237 doctype);
00238 return FALSE;
00239 }
00240 else
00241 return TRUE;
00242 }
00243
00244 static gboolean
00245 parse_node (Parser *parser,
00246 const char *element_name,
00247 const char **attribute_names,
00248 const char **attribute_values,
00249 GError **error)
00250 {
00251 const char *name;
00252 NodeInfo *node;
00253
00254 if (parser->interface ||
00255 parser->method ||
00256 parser->signal ||
00257 parser->property ||
00258 parser->arg ||
00259 parser->in_annotation)
00260 {
00261 g_set_error (error, G_MARKUP_ERROR,
00262 G_MARKUP_ERROR_PARSE,
00263 _("Can't put <%s> element here"),
00264 element_name);
00265 return FALSE;
00266 }
00267
00268 name = NULL;
00269 if (!locate_attributes (element_name, attribute_names,
00270 attribute_values, error,
00271 "name", &name,
00272 NULL))
00273 return FALSE;
00274
00275
00276 if (parser->node_stack != NULL && name == NULL)
00277 {
00278 g_set_error (error, G_MARKUP_ERROR,
00279 G_MARKUP_ERROR_PARSE,
00280 _("\"%s\" attribute required on <%s> element "),
00281 "name", element_name);
00282 return FALSE;
00283 }
00284
00285
00286 if (parser->node_stack == NULL && name && *name != '/')
00287 {
00288 g_set_error (error, G_MARKUP_ERROR,
00289 G_MARKUP_ERROR_PARSE,
00290 _("\"%s\" attribute on <%s> element must be an absolute object path, \"%s\" not OK"),
00291 "name", element_name, name);
00292 return FALSE;
00293 }
00294
00295
00296 if (parser->node_stack != NULL && name && *name == '/')
00297 {
00298 g_set_error (error, G_MARKUP_ERROR,
00299 G_MARKUP_ERROR_PARSE,
00300 _("\"%s\" attribute on <%s> element must not be an absolute object path, \"%s\" starts with /"),
00301 "name", element_name, name);
00302 return FALSE;
00303 }
00304
00305 node = node_info_new (name);
00306
00307 if (parser->node_stack != NULL)
00308 {
00309 node_info_add_node (parser->node_stack->data,
00310 node);
00311 }
00312
00313 parser->node_stack = g_slist_prepend (parser->node_stack,
00314 node);
00315
00316 return TRUE;
00317 }
00318
00319 static gboolean
00320 parse_interface (Parser *parser,
00321 const char *element_name,
00322 const char **attribute_names,
00323 const char **attribute_values,
00324 GError **error)
00325 {
00326 const char *name;
00327 InterfaceInfo *iface;
00328 NodeInfo *top;
00329
00330 if (parser->interface ||
00331 parser->method ||
00332 parser->signal ||
00333 parser->property ||
00334 parser->arg ||
00335 parser->in_annotation ||
00336 (parser->node_stack == NULL))
00337 {
00338 g_set_error (error, G_MARKUP_ERROR,
00339 G_MARKUP_ERROR_PARSE,
00340 _("Can't put <%s> element here"),
00341 element_name);
00342 return FALSE;
00343 }
00344
00345 name = NULL;
00346 if (!locate_attributes (element_name, attribute_names,
00347 attribute_values, error,
00348 "name", &name,
00349 NULL))
00350 return FALSE;
00351
00352 if (name == NULL)
00353 {
00354 g_set_error (error, G_MARKUP_ERROR,
00355 G_MARKUP_ERROR_PARSE,
00356 _("\"%s\" attribute required on <%s> element "),
00357 "name", element_name);
00358 return FALSE;
00359 }
00360
00361 top = parser->node_stack->data;
00362
00363 iface = interface_info_new (name);
00364 node_info_add_interface (top, iface);
00365 interface_info_unref (iface);
00366
00367 parser->interface = iface;
00368
00369 return TRUE;
00370 }
00371
00372 static gboolean
00373 parse_method (Parser *parser,
00374 const char *element_name,
00375 const char **attribute_names,
00376 const char **attribute_values,
00377 GError **error)
00378 {
00379 const char *name;
00380 MethodInfo *method;
00381
00382 if (parser->interface == NULL ||
00383 parser->node_stack == NULL ||
00384 parser->method ||
00385 parser->signal ||
00386 parser->property ||
00387 parser->in_annotation ||
00388 parser->arg)
00389 {
00390 g_set_error (error, G_MARKUP_ERROR,
00391 G_MARKUP_ERROR_PARSE,
00392 _("Can't put <%s> element here"),
00393 element_name);
00394 return FALSE;
00395 }
00396
00397 name = NULL;
00398 if (!locate_attributes (element_name, attribute_names,
00399 attribute_values, error,
00400 "name", &name,
00401 NULL))
00402 return FALSE;
00403
00404 if (name == NULL)
00405 {
00406 g_set_error (error, G_MARKUP_ERROR,
00407 G_MARKUP_ERROR_PARSE,
00408 _("\"%s\" attribute required on <%s> element "),
00409 "name", element_name);
00410 return FALSE;
00411 }
00412
00413 method = method_info_new (name);
00414 interface_info_add_method (parser->interface, method);
00415 method_info_unref (method);
00416
00417 parser->method = method;
00418
00419 return TRUE;
00420 }
00421
00422 static gboolean
00423 parse_signal (Parser *parser,
00424 const char *element_name,
00425 const char **attribute_names,
00426 const char **attribute_values,
00427 GError **error)
00428 {
00429 const char *name;
00430 SignalInfo *signal;
00431
00432 if (parser->interface == NULL ||
00433 parser->node_stack == NULL ||
00434 parser->signal ||
00435 parser->method ||
00436 parser->property ||
00437 parser->in_annotation ||
00438 parser->arg)
00439 {
00440 g_set_error (error, G_MARKUP_ERROR,
00441 G_MARKUP_ERROR_PARSE,
00442 _("Can't put <%s> element here"),
00443 element_name);
00444 return FALSE;
00445 }
00446
00447 name = NULL;
00448 if (!locate_attributes (element_name, attribute_names,
00449 attribute_values, error,
00450 "name", &name,
00451 NULL))
00452 return FALSE;
00453
00454 if (name == NULL)
00455 {
00456 g_set_error (error, G_MARKUP_ERROR,
00457 G_MARKUP_ERROR_PARSE,
00458 _("\"%s\" attribute required on <%s> element "),
00459 "name", element_name);
00460 return FALSE;
00461 }
00462
00463 signal = signal_info_new (name);
00464 interface_info_add_signal (parser->interface, signal);
00465 signal_info_unref (signal);
00466
00467 parser->signal = signal;
00468
00469 return TRUE;
00470 }
00471
00472 static gboolean
00473 validate_signature (const char *str,
00474 const char *element_name,
00475 GError **error)
00476 {
00477 DBusError derror;
00478
00479 dbus_error_init (&derror);
00480
00481 if (!dbus_signature_validate (str, &derror))
00482 {
00483 dbus_set_g_error (error, &derror);
00484 return FALSE;
00485 }
00486 return TRUE;
00487 }
00488
00489 static gboolean
00490 parse_property (Parser *parser,
00491 const char *element_name,
00492 const char **attribute_names,
00493 const char **attribute_values,
00494 GError **error)
00495 {
00496 const char *name;
00497 const char *access;
00498 const char *type;
00499 PropertyInfo *property;
00500 PropertyAccessFlags access_flags;
00501
00502 if (parser->interface == NULL ||
00503 parser->node_stack == NULL ||
00504 parser->signal ||
00505 parser->method ||
00506 parser->property ||
00507 parser->in_annotation ||
00508 parser->arg)
00509 {
00510 g_set_error (error, G_MARKUP_ERROR,
00511 G_MARKUP_ERROR_PARSE,
00512 _("Can't put <%s> element here"),
00513 element_name);
00514 return FALSE;
00515 }
00516
00517 name = NULL;
00518 if (!locate_attributes (element_name, attribute_names,
00519 attribute_values, error,
00520 "name", &name,
00521 "access", &access,
00522 "type", &type,
00523 NULL))
00524 return FALSE;
00525
00526 if (name == NULL)
00527 {
00528 g_set_error (error, G_MARKUP_ERROR,
00529 G_MARKUP_ERROR_PARSE,
00530 _("\"%s\" attribute required on <%s> element "),
00531 "name", element_name);
00532 return FALSE;
00533 }
00534
00535 if (access == NULL)
00536 {
00537 g_set_error (error, G_MARKUP_ERROR,
00538 G_MARKUP_ERROR_PARSE,
00539 _("\"%s\" attribute required on <%s> element "),
00540 "access", element_name);
00541 return FALSE;
00542 }
00543
00544 if (type == NULL)
00545 {
00546 g_set_error (error, G_MARKUP_ERROR,
00547 G_MARKUP_ERROR_PARSE,
00548 _("\"%s\" attribute required on <%s> element "),
00549 "type", element_name);
00550 return FALSE;
00551 }
00552
00553 if (!validate_signature (type, element_name, error))
00554 return FALSE;
00555
00556 access_flags = 0;
00557 if (strcmp (access, "readwrite") == 0)
00558 access_flags = PROPERTY_READ | PROPERTY_WRITE;
00559 else if (strcmp (access, "read") == 0)
00560 access_flags = PROPERTY_READ;
00561 else if (strcmp (access, "write") == 0)
00562 access_flags = PROPERTY_WRITE;
00563 else
00564 {
00565 g_set_error (error, G_MARKUP_ERROR,
00566 G_MARKUP_ERROR_PARSE,
00567 _("access=\"%s\" must have value readwrite, read, or write on %s\n"),
00568 access, element_name);
00569 return FALSE;
00570 }
00571
00572 property = property_info_new (name, type, access_flags);
00573 interface_info_add_property (parser->interface, property);
00574 property_info_unref (property);
00575
00576 parser->property = property;
00577
00578 return TRUE;
00579 }
00580
00581 static gboolean
00582 parse_arg (Parser *parser,
00583 const char *element_name,
00584 const char **attribute_names,
00585 const char **attribute_values,
00586 GError **error)
00587 {
00588 const char *name;
00589 const char *type;
00590 const char *direction;
00591 ArgDirection dir;
00592 ArgInfo *arg;
00593 char *generated_name;
00594
00595 if (!(parser->method || parser->signal) ||
00596 parser->node_stack == NULL ||
00597 parser->property ||
00598 parser->in_annotation ||
00599 parser->arg)
00600 {
00601 g_set_error (error, G_MARKUP_ERROR,
00602 G_MARKUP_ERROR_PARSE,
00603 _("Can't put <%s> element here"),
00604 element_name);
00605 return FALSE;
00606 }
00607
00608 name = NULL;
00609 if (!locate_attributes (element_name, attribute_names,
00610 attribute_values, error,
00611 "name", &name,
00612 "type", &type,
00613 "direction", &direction,
00614 NULL))
00615 return FALSE;
00616
00617
00618
00619 if (type == NULL)
00620 {
00621 g_set_error (error, G_MARKUP_ERROR,
00622 G_MARKUP_ERROR_PARSE,
00623 _("\"%s\" attribute required on <%s> element "),
00624 "type", element_name);
00625 return FALSE;
00626 }
00627
00628 if (direction == NULL)
00629 {
00630
00631 if (parser->method)
00632 direction = "in";
00633 else if (parser->signal)
00634 direction = "out";
00635 else
00636 g_assert_not_reached ();
00637 }
00638
00639 dir = ARG_INVALID;
00640
00641 if (strcmp (direction, "in") == 0)
00642 dir = ARG_IN;
00643 else if (strcmp (direction, "out") == 0)
00644 dir = ARG_OUT;
00645
00646 if (dir == ARG_INVALID ||
00647 (parser->signal && dir == ARG_IN))
00648 {
00649 if (parser->signal)
00650 g_set_error (error, G_MARKUP_ERROR,
00651 G_MARKUP_ERROR_PARSE,
00652 _("Signals must have direction=\"out\" (just omit the direction attribute)"));
00653 else
00654 g_set_error (error, G_MARKUP_ERROR,
00655 G_MARKUP_ERROR_PARSE,
00656 _("\"%s\" attribute on <%s> has value \"in\" or \"out\""),
00657 "direction", element_name);
00658 return FALSE;
00659 }
00660
00661 if (!validate_signature (type, element_name, error))
00662 return FALSE;
00663
00664 generated_name = NULL;
00665 if (name == NULL)
00666 generated_name = g_strdup_printf ("arg%d",
00667 parser->method ?
00668 method_info_get_n_args (parser->method) :
00669 signal_info_get_n_args (parser->signal));
00670
00671 arg = arg_info_new (name ? name : generated_name, dir, type);
00672 if (parser->method)
00673 method_info_add_arg (parser->method, arg);
00674 else if (parser->signal)
00675 signal_info_add_arg (parser->signal, arg);
00676 else
00677 g_assert_not_reached ();
00678
00679 g_free (generated_name);
00680
00681 arg_info_unref (arg);
00682
00683 parser->arg = arg;
00684
00685 return TRUE;
00686 }
00687
00688 static gboolean
00689 parse_annotation (Parser *parser,
00690 const char *element_name,
00691 const char **attribute_names,
00692 const char **attribute_values,
00693 GError **error)
00694 {
00695 const char *name;
00696 const char *value;
00697
00698 if (!(parser->method || parser->interface || parser->arg) ||
00699 parser->node_stack == NULL ||
00700 parser->signal ||
00701 parser->property ||
00702 parser->in_annotation)
00703 {
00704 g_set_error (error, G_MARKUP_ERROR,
00705 G_MARKUP_ERROR_PARSE,
00706 _("Can't put <%s> element here"),
00707 element_name);
00708 return FALSE;
00709 }
00710
00711 name = NULL;
00712 if (!locate_attributes (element_name, attribute_names,
00713 attribute_values, error,
00714 "name", &name,
00715 "value", &value,
00716 NULL))
00717 return FALSE;
00718
00719 if (name == NULL)
00720 {
00721 g_set_error (error, G_MARKUP_ERROR,
00722 G_MARKUP_ERROR_PARSE,
00723 _("\"%s\" attribute required on <%s> element "),
00724 "name", element_name);
00725 return FALSE;
00726 }
00727 if (value == NULL)
00728 {
00729 g_set_error (error, G_MARKUP_ERROR,
00730 G_MARKUP_ERROR_PARSE,
00731 _("\"%s\" attribute required on <%s> element "),
00732 "value", element_name);
00733 return FALSE;
00734 }
00735
00736 if (parser->arg)
00737 arg_info_add_annotation (parser->arg, name, value);
00738 else if (parser->method)
00739 method_info_add_annotation (parser->method, name, value);
00740 else if (parser->interface)
00741 interface_info_add_annotation (parser->interface, name, value);
00742 else
00743 g_assert_not_reached ();
00744
00745 parser->in_annotation = TRUE;
00746
00747 return TRUE;
00748 }
00749
00750 gboolean
00751 parser_start_element (Parser *parser,
00752 const char *element_name,
00753 const char **attribute_names,
00754 const char **attribute_values,
00755 GError **error)
00756 {
00757 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
00758
00759 if (ELEMENT_IS ("node"))
00760 {
00761 if (!parse_node (parser, element_name, attribute_names,
00762 attribute_values, error))
00763 return FALSE;
00764 }
00765 else if (ELEMENT_IS ("interface"))
00766 {
00767 if (!parse_interface (parser, element_name, attribute_names,
00768 attribute_values, error))
00769 return FALSE;
00770 }
00771 else if (ELEMENT_IS ("method"))
00772 {
00773 if (!parse_method (parser, element_name, attribute_names,
00774 attribute_values, error))
00775 return FALSE;
00776 }
00777 else if (ELEMENT_IS ("signal"))
00778 {
00779 if (!parse_signal (parser, element_name, attribute_names,
00780 attribute_values, error))
00781 return FALSE;
00782 }
00783 else if (ELEMENT_IS ("property"))
00784 {
00785 if (!parse_property (parser, element_name, attribute_names,
00786 attribute_values, error))
00787 return FALSE;
00788 }
00789 else if (ELEMENT_IS ("arg"))
00790 {
00791 if (!parse_arg (parser, element_name, attribute_names,
00792 attribute_values, error))
00793 return FALSE;
00794 }
00795 else if (ELEMENT_IS ("annotation"))
00796 {
00797 if (!parse_annotation (parser, element_name, attribute_names,
00798 attribute_values, error))
00799 return FALSE;
00800 }
00801 else
00802 {
00803 if (strchr (element_name, ':') != NULL)
00804
00805 parser->unknown_namespaced_depth += 1;
00806 else if (parser->unknown_namespaced_depth == 0)
00807 {
00808 g_set_error (error, G_MARKUP_ERROR,
00809 G_MARKUP_ERROR_PARSE,
00810 _("Element <%s> not recognized"),
00811 element_name);
00812 return FALSE;
00813 }
00814 }
00815
00816 return TRUE;
00817 }
00818
00819 gboolean
00820 parser_end_element (Parser *parser,
00821 const char *element_name,
00822 GError **error)
00823 {
00824 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
00825
00826 if (ELEMENT_IS ("interface"))
00827 {
00828 parser->interface = NULL;
00829 }
00830 else if (ELEMENT_IS ("method"))
00831 {
00832 parser->method = NULL;
00833 }
00834 else if (ELEMENT_IS ("signal"))
00835 {
00836 parser->signal = NULL;
00837 }
00838 else if (ELEMENT_IS ("property"))
00839 {
00840 parser->property = NULL;
00841 }
00842 else if (ELEMENT_IS ("arg"))
00843 {
00844 parser->arg = NULL;
00845 }
00846 else if (ELEMENT_IS ("annotation"))
00847 {
00848 parser->in_annotation = FALSE;
00849 }
00850 else if (ELEMENT_IS ("node"))
00851 {
00852 NodeInfo *top;
00853
00854 g_assert (parser->node_stack != NULL);
00855 top = parser->node_stack->data;
00856
00857 parser->node_stack = g_slist_remove (parser->node_stack,
00858 top);
00859
00860 if (parser->node_stack == NULL)
00861 parser->result = top;
00862 }
00863 else if (strchr (element_name, ':') != NULL)
00864 {
00865
00866 parser->unknown_namespaced_depth -= 1;
00867 }
00868 else if (parser->unknown_namespaced_depth > 0)
00869 {
00870
00871 }
00872 else
00873 g_assert_not_reached ();
00874
00875 return TRUE;
00876 }
00877
00878 gboolean
00879 parser_content (Parser *parser,
00880 const char *content,
00881 int len,
00882 GError **error)
00883 {
00884 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
00885
00886
00887
00888 return TRUE;
00889 }
00890
00891 gboolean
00892 parser_finished (Parser *parser,
00893 GError **error)
00894 {
00895 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
00896
00897 return TRUE;
00898 }
00899
00900 NodeInfo*
00901 parser_get_nodes (Parser *parser)
00902 {
00903 return parser->result;
00904 }
00905
00906 #endif