[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[6131] parsetexi start on use of perl api
From: |
Gavin D. Smith |
Subject: |
[6131] parsetexi start on use of perl api |
Date: |
Mon, 16 Feb 2015 21:30:29 +0000 |
Revision: 6131
http://svn.sv.gnu.org/viewvc/?view=rev&root=texinfo&revision=6131
Author: gavin
Date: 2015-02-16 21:30:27 +0000 (Mon, 16 Feb 2015)
Log Message:
-----------
parsetexi start on use of perl api
Modified Paths:
--------------
trunk/parsetexi/ChangeLog
trunk/parsetexi/Makefile.am
trunk/parsetexi/Parsetexi/Parsetexi.xs
trunk/parsetexi/Parsetexi/lib/Parsetexi.pm
trunk/parsetexi/api.c
trunk/parsetexi/api.h
trunk/parsetexi/dump_perl.c
trunk/parsetexi/end_line.c
trunk/parsetexi/main.c
trunk/parsetexi/tree_types.h
Modified: trunk/parsetexi/ChangeLog
===================================================================
--- trunk/parsetexi/ChangeLog 2015-02-15 23:57:38 UTC (rev 6130)
+++ trunk/parsetexi/ChangeLog 2015-02-16 21:30:27 UTC (rev 6131)
@@ -1,3 +1,15 @@
+2015-02-16 Gavin Smith <address@hidden>
+
+ * api.c (build_texinfo_tree, build_label_list): New functions,
+ to construct Perl objects directly in memory, as a possible
+ replacement for using "eval" in Perl.
+ * Makefile.am (AM_CFLAGS): Add flags for linking with Perl
+ libraries.
+
+ * Parsetexi/lib/Parsetexi.pm (parse_texi_file): Add another
+ way to get data from C program (disabled). (Indices not done
+ yet.)
+
2015-02-14 Gavin Smith <address@hidden>
* tree.c (insert_into_contents): Update parent field of inserted
Modified: trunk/parsetexi/Makefile.am
===================================================================
--- trunk/parsetexi/Makefile.am 2015-02-15 23:57:38 UTC (rev 6130)
+++ trunk/parsetexi/Makefile.am 2015-02-16 21:30:27 UTC (rev 6131)
@@ -6,6 +6,14 @@
lib_LIBRARIES=libparsetexi.a
+# See "perlembed" man page.
+# (I expected the ldopts flags to have to occur at the end of the cc
+# command line, but this seems to work fine.
+# Otherwise LDADD would have to be set, but automake complains if you do
+# LDADD=`perl -MExtUtils::Embed -e ldopts`)
+
+AM_CFLAGS=`perl -MExtUtils::Embed -e ccopts -e ldopts`
+
libparsetexi_a_SOURCES=api.c api.h \
parser.c parser.h tree_types.h \
element_types.c element_types.h \
Modified: trunk/parsetexi/Parsetexi/Parsetexi.xs
===================================================================
--- trunk/parsetexi/Parsetexi/Parsetexi.xs 2015-02-15 23:57:38 UTC (rev
6130)
+++ trunk/parsetexi/Parsetexi/Parsetexi.xs 2015-02-16 21:30:27 UTC (rev
6131)
@@ -10,6 +10,9 @@
#include "../api.h"
#include "../errors.h"
+HV *build_texinfo_tree (void);
+HV *build_label_list (void);
+
MODULE = Parsetexi PACKAGE = Parsetexi
TYPEMAP: <<END
@@ -25,6 +28,9 @@
dump_tree_to_string_2 ()
char *
+dump_tree_to_string_25 ()
+
+char *
dump_tree_to_string_3 ()
char *dump_root_element_1 ()
@@ -61,3 +67,9 @@
void
add_include_directory (filename)
char *filename
+
+HV *
+build_texinfo_tree ()
+
+HV *
+build_label_list ()
Modified: trunk/parsetexi/Parsetexi/lib/Parsetexi.pm
===================================================================
--- trunk/parsetexi/Parsetexi/lib/Parsetexi.pm 2015-02-15 23:57:38 UTC (rev
6130)
+++ trunk/parsetexi/Parsetexi/lib/Parsetexi.pm 2015-02-16 21:30:27 UTC (rev
6131)
@@ -238,7 +238,22 @@
#print "Getting tree...\n";
my ($TREE, $LABELS, $INDEX_NAMES, $ERRORS);
- if (1) {
+ if (0) {
+ # This is our third way of passing the data: construct it using
+ # Perl api directly.
+ print "Parsing file...\n";
+ parse_file ($file_name);
+ print "Fetching data..\n";
+ $TREE = build_texinfo_tree ();
+ print "Got tree...\n";
+ #print Texinfo::Parser::_print_tree ($TREE);
+ print ref($TREE->{'contents'}[1]{'extra'}{'node_content'}), "\n";
+
+ $LABELS = build_label_list ();
+
+ # TODO: Get $INDEX_NAMES as well
+
+ } elsif (1) {
# $| = 1; # Flush after each print
print "Parsing file...\n";
parse_file ($file_name);
@@ -256,11 +271,14 @@
$tree_stream = dump_tree_to_string_2 ();
#print "tree stream is $tree_stream\n";
eval $tree_stream;
+ $tree_stream = dump_tree_to_string_25 ();
+ #print "tree stream is $tree_stream\n";
+ eval $tree_stream;
$tree_stream = dump_tree_to_string_3 ();
#print "tree stream is $tree_stream\n";
eval $tree_stream;
print "Got data.\n";
- } else {
+ } elsif (0) {
# This calls a separate executable instead of using the code
# compliled into Parsetexi.pm as a library.
Modified: trunk/parsetexi/api.c
===================================================================
--- trunk/parsetexi/api.c 2015-02-15 23:57:38 UTC (rev 6130)
+++ trunk/parsetexi/api.c 2015-02-16 21:30:27 UTC (rev 6131)
@@ -14,11 +14,22 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* Avoid namespace conflicts. */
+#define context perl_context
+
+#define PERL_NO_GET_CONTEXT
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+#undef context
+
#include <stdlib.h>
#include <stdio.h>
#include "parser.h"
#include "input.h"
+#include "labels.h"
ELEMENT *Root;
@@ -55,3 +66,322 @@
return e->args.number;
}
+static void element_to_perl_hash (ELEMENT *e);
+
+/* Return reference to Perl array built from e. If any of
+ the elements in E don't have 'hv' set, set it to an empty
+ hash table, or create it if route_not_in_tree. */
+static SV *
+build_perl_array (ELEMENT_LIST *e)
+{
+ SV *sv;
+ AV *av;
+ int i;
+
+ dTHX;
+
+ av = newAV ();
+ sv = newRV_inc ((SV *) av);
+ for (i = 0; i < e->number; i++)
+ {
+ if (!e->list[i]->hv)
+ {
+ if (e->list[i]->parent_type != route_not_in_tree)
+ e->list[i]->hv = newHV ();
+ else
+ {
+ /* WARNING: This is possibly recursive. */
+ element_to_perl_hash (e->list[i]);
+ }
+ }
+ av_push (av, newRV_inc ((SV *) e->list[i]->hv));
+ }
+ return sv;
+}
+
+/* Return reference to hash corresponding to VALUE. */
+static SV *
+build_node_spec (NODE_SPEC_EXTRA *value)
+{
+ HV *hv;
+
+ dTHX;
+
+ hv = newHV ();
+
+ if (value->manual_content)
+ {
+ hv_store (hv, "manual_content", strlen ("manual_content"),
+ build_perl_array (&value->manual_content->contents), 0);
+ }
+
+ if (value->node_content)
+ {
+ hv_store (hv, "node_content", strlen ("node_content"),
+ build_perl_array (&value->node_content->contents), 0);
+ }
+
+ if (value->normalized)
+ {
+ hv_store (hv, "normalized", strlen ("normalized"),
+ newSVpv (value->normalized, 0), 0);
+ }
+ return newRV_inc ((SV *)hv);
+}
+
+/* Set E->hv and 'hv' on E's descendants. e->parent->hv is assumed
+ to already exist. */
+static void
+element_to_perl_hash (ELEMENT *e)
+{
+ SV *sv;
+
+ dTHX;
+
+ /* e->hv may already exist if there was an extra value elsewhere
+ referring to e. */
+ if (!e->hv)
+ {
+ e->hv = newHV ();
+ }
+
+ if (e->parent && e->parent_type != route_not_in_tree)
+ {
+ sv = newRV_inc ((SV *) e->parent->hv);
+ hv_store (e->hv, "parent", strlen ("parent"), sv, 0);
+ }
+ /* FIXME: this assumes we don't have nested out-of-tree subtrees,
+ i.e. the only out-of-tree elements are simple text elements
+ (or other elements with no children) - otherwise we shall fail
+ to set "parent" properly. */
+
+ if (e->type)
+ {
+ sv = newSVpv (element_type_names[e->type], 0);
+ hv_store (e->hv, "type", strlen ("type"), sv, 0);
+
+ /* TODO: Could precompute hash of "type", and also reuse
+ the same SV for a single type? */
+ }
+
+ if (e->cmd)
+ {
+ sv = newSVpv (command_data(e->cmd).cmdname, 0);
+ hv_store (e->hv, "cmdname", strlen ("cmdname"), sv, 0);
+
+ /* TODO: Same optimizations as for 'type'. */
+ }
+
+ if (e->contents.number > 0)
+ {
+ AV *av;
+ int i;
+
+ av = newAV ();
+ sv = newRV_inc ((SV *) av);
+ hv_store (e->hv, "contents", strlen ("contents"), sv, 0);
+ for (i = 0; i < e->contents.number; i++)
+ {
+ element_to_perl_hash (e->contents.list[i]);
+ sv = newRV_inc ((SV *) e->contents.list[i]->hv);
+ av_push (av, sv);
+ }
+ }
+
+ if (e->args.number > 0)
+ {
+ AV *av;
+ int i;
+
+ av = newAV ();
+ sv = newRV_inc ((SV *) av);
+ hv_store (e->hv, "args", strlen ("args"), sv, 0);
+ for (i = 0; i < e->args.number; i++)
+ {
+ element_to_perl_hash (e->args.list[i]);
+ sv = newRV_inc ((SV *) e->args.list[i]->hv);
+ av_push (av, sv);
+ }
+ }
+
+ if (e->text.space > 0)
+ {
+ sv = newSVpv (e->text.text, e->text.end);
+ hv_store (e->hv, "text", strlen ("text"), sv, 0);
+ }
+
+ if (e->extra_number > 0)
+ {
+ HV *extra;
+ int i;
+ extra = newHV ();
+ hv_store (e->hv, "extra", strlen ("extra"),
+ newRV_inc((SV *)extra), 0);
+
+ for (i = 0; i < e->extra_number; i++)
+ {
+#define STORE(sv) hv_store (extra, key, strlen (key), sv, 0)
+ char *key = e->extra[i].key;
+ ELEMENT *f = e->extra[i].value;
+
+ switch (e->extra[i].type)
+ {
+ case extra_element:
+ /* For references to other parts of the tree, create the hash so
+ we can point to it. */
+ if (!f->hv && f->parent_type != route_not_in_tree)
+ {
+ /* TODO: Are there any extra values which are
+ extra_element that are route_not_in_tree? Consider
+ eliminating use of 'parent_type' to differentiate types
+ of extra value. */
+ f->hv = newHV ();
+ }
+ STORE(newRV_inc ((SV *)f->hv));
+ break;
+ case extra_element_contents:
+ {
+ int j;
+ fprintf (stderr, "extra element key is %s\n", key);
+ STORE(build_perl_array (&f->contents));
+#if 0
+ AV *av;
+ av = newAV ();
+ STORE(newRV_inc ((SV *)av));
+ for (j = 0; j < f->contents.number; j++)
+ {
+ /* TODO: Check if any of the elements in the array
+ are not in the main tree - if so, we will have to
+ create them. */
+ ELEMENT *g = f->contents.list[j];
+ if (!g->hv)
+ g->hv = newHV ();
+ av_push (av, f->hv);
+ }
+#endif
+ break;
+ }
+ case extra_element_contents_array:
+ {
+ /* Like extra_element_contents, but this time output an array
+ of arrays (instead of an array). */
+ int j, k;
+ AV *av;
+ av = newAV ();
+ STORE(newRV_inc ((SV *)av));
+ for (j = 0; j < f->contents.number; j++)
+ {
+ AV *av2;
+ ELEMENT *g;
+
+ g = f->contents.list[j];
+ av2 = newAV ();
+ av_push (av, newRV_inc ((SV *)av2));
+
+ for (k = 0; k < g->contents.number; k++)
+ {
+ ELEMENT *h;
+ h = g->contents.list[k];
+ /* TODO: Check if any of the elements in the array
+ are not in the main tree - if so, we will have to
+ create them. */
+ if (!h->hv)
+ h->hv = newHV ();
+ av_push (av2, g->hv);
+ }
+ }
+ break;
+ }
+ case extra_string:
+ { /* A simple string. */
+ char *value = (char *) f;
+ STORE(newSVpv (value, 0));
+ break;
+ }
+ case extra_misc_args:
+ {
+ int j;
+ AV *av;
+ av = newAV ();
+ STORE(newRV_inc ((SV *)av));
+ /* An array of strings. */
+ for (j = 0; j < f->contents.number; j++)
+ {
+ if (f->contents.list[j]->text.end > 0)
+ {
+ av_push (av,
+ newSVpv (f->contents.list[j]->text.text,
+ f->contents.list[j]->text.end));
+ }
+ /* else an error? */
+ }
+ break;
+ }
+ case extra_node_spec:
+ /* A complex structure - see "parse_node_manual" function
+ in end_line.c */
+ STORE(build_node_spec ((NODE_SPEC_EXTRA *) f));
+ break;
+ case extra_node_spec_array:
+ {
+ int j;
+ AV *av;
+ NODE_SPEC_EXTRA **array;
+ av = newAV ();
+ STORE(newRV_inc ((SV *)av));
+ array = (NODE_SPEC_EXTRA **) f;
+ while (*array)
+ {
+ av_push (av, build_node_spec (*array));
+ array++;
+ }
+ break;
+ }
+ case extra_index_entry:
+ /* A "index_entry" extra key on a command defining an index
+ entry. Unlike the other keys, the value is not in the
+ main parse tree, but in the indices_information. It would
+ be much nicer if we could get rid of the need for this key. */
+ break;
+ default:
+ abort ();
+ break;
+ }
+ }
+#undef STORE
+ }
+
+ /* TODO: line_nr. */
+}
+
+HV *
+build_texinfo_tree (void)
+{
+ element_to_perl_hash (Root);
+ return Root->hv;
+}
+
+/* Return hash object from label names to target elements. build_texinfo_tree
+ must be called first. */
+HV *
+build_label_list (void)
+{
+ HV *label_hash;
+ SV *sv;
+ int i;
+
+ dTHX;
+
+ label_hash = newHV ();
+
+ for (i = 0; i < labels_number; i++)
+ {
+ sv = newRV_inc (labels_list[i].target->hv);
+ hv_store (label_hash,
+ labels_list[i].label, strlen (labels_list[i].label),
+ sv, 0);
+ }
+
+ return label_hash;
+}
+
Modified: trunk/parsetexi/api.h
===================================================================
--- trunk/parsetexi/api.h 2015-02-15 23:57:38 UTC (rev 6130)
+++ trunk/parsetexi/api.h 2015-02-16 21:30:27 UTC (rev 6131)
@@ -10,6 +10,7 @@
/* Defined in dump_perl.c */
char *dump_tree_to_string_1 (void);
char *dump_tree_to_string_2 (void);
+char *dump_tree_to_string_25 (void);
char *dump_tree_to_string_3 (void);
char *dump_root_element_1 (void);
char *dump_root_element_2 (void);
Modified: trunk/parsetexi/dump_perl.c
===================================================================
--- trunk/parsetexi/dump_perl.c 2015-02-15 23:57:38 UTC (rev 6130)
+++ trunk/parsetexi/dump_perl.c 2015-02-16 21:30:27 UTC (rev 6131)
@@ -33,6 +33,9 @@
/* A dump to fill in references from one part of the tree to another. */
static TEXT fixup_dump;
+/* A dump for information about the indices. */
+static TEXT indices_dump;
+
/* A dump to fill in references from the parse tree to the indices
information. */
static TEXT tree_to_indices_dump;
@@ -517,73 +520,73 @@
int i;
INDEX_ENTRY *e;
- text_printf (&fixup_dump, "\n'index_entries' => [");
+ text_printf (&indices_dump, "\n'index_entries' => [");
for (i = 0; i < idx->index_number; i++)
{
e = &idx->index_entries[i];
- text_printf (&fixup_dump, "\n{");
- text_printf (&fixup_dump, "'index_name' => '%s',", e->index_name);
- text_printf (&fixup_dump, "'index_prefix' => '%s',", e->index_prefix);
+ text_printf (&indices_dump, "\n{");
+ text_printf (&indices_dump, "'index_name' => '%s',", e->index_name);
+ text_printf (&indices_dump, "'index_prefix' => '%s',", e->index_prefix);
- text_printf (&fixup_dump, "\n");
- text_printf (&fixup_dump, "'index_at_command' => '%s',",
+ text_printf (&indices_dump, "\n");
+ text_printf (&indices_dump, "'index_at_command' => '%s',",
command_data(e->index_at_command).cmdname);
- text_printf (&fixup_dump, "'index_type_command' => '%s',\n",
+ text_printf (&indices_dump, "'index_type_command' => '%s',\n",
command_data(e->index_type_command).cmdname);
- text_printf (&fixup_dump, "'command' => ");
- dump_route_to_element (e->command, &fixup_dump);
- text_printf (&fixup_dump, ",\n");
+ text_printf (&indices_dump, "'command' => ");
+ dump_route_to_element (e->command, &indices_dump);
+ text_printf (&indices_dump, ",\n");
- text_printf (&fixup_dump, "'number' => %d,", e->number);
+ text_printf (&indices_dump, "'number' => %d,", e->number);
if (e->content)
{
- text_printf (&fixup_dump, "'content' => ");
- dump_route_to_element (e->content, &fixup_dump);
- text_printf (&fixup_dump, "{'contents'}");
- text_printf (&fixup_dump, ",\n");
+ text_printf (&indices_dump, "'content' => ");
+ dump_route_to_element (e->content, &indices_dump);
+ text_printf (&indices_dump, "{'contents'}");
+ text_printf (&indices_dump, ",\n");
}
if (e->node)
{
- text_printf (&fixup_dump, "'node' => ");
- dump_route_to_element (e->node, &fixup_dump);
- text_printf (&fixup_dump, ",\n");
+ text_printf (&indices_dump, "'node' => ");
+ dump_route_to_element (e->node, &indices_dump);
+ text_printf (&indices_dump, ",\n");
}
- text_printf (&fixup_dump, "},");
+ text_printf (&indices_dump, "},");
}
- text_printf (&fixup_dump, "],\n");
+ text_printf (&indices_dump, "],\n");
}
-/* Append to FIXUP_DUMP information about the indices. */
+/* Append to INDICES_DUMP information about the indices. */
static void
dump_indices_information (void)
{
INDEX *i;
- text_append (&fixup_dump, "\n$INDEX_NAMES = {\n");
+ text_append (&indices_dump, "\n$INDEX_NAMES = {\n");
for (i = index_names; i->name; i++)
{
- text_printf (&fixup_dump, "'%s' => {", i->name);
- text_printf (&fixup_dump, "'name' => '%s',", i->name);
- text_printf (&fixup_dump, "'in_code' => 0,");
+ text_printf (&indices_dump, "'%s' => {", i->name);
+ text_printf (&indices_dump, "'name' => '%s',", i->name);
+ text_printf (&indices_dump, "'in_code' => 0,");
/* TODO: This is a list of recognized prefixes for the index. */
- text_printf (&fixup_dump, "'prefix' => ['%c', '%s'],",
+ text_printf (&indices_dump, "'prefix' => ['%c', '%s'],",
*i->name, i->name);
/* TODO: Handle index merging. */
- text_printf (&fixup_dump, "'contained_indices' => {'%s'=>1},",
+ text_printf (&indices_dump, "'contained_indices' => {'%s'=>1},",
i->name);
dump_entries_of_index (i);
- text_printf (&fixup_dump, "},\n");
+ text_printf (&indices_dump, "},\n");
}
- text_append (&fixup_dump, "};\n");
+ text_append (&indices_dump, "};\n");
}
void
@@ -611,6 +614,9 @@
if (fixup_dump.end > 0)
printf ("%s", fixup_dump.text);
+ if (indices_dump.end > 0)
+ printf ("%s", indices_dump.text);
+
/* This must be output at the end so that both the tree and the indices
will exist by the time this is read. */
if (tree_to_indices_dump.end > 0)
@@ -711,6 +717,7 @@
dump_tree_to_string_1 (void)
{
text_init (&fixup_dump);
+ text_init (&indices_dump);
text_init (&tree_to_indices_dump);
return "";
@@ -721,14 +728,22 @@
{
dump_labels_information ();
- dump_indices_information ();
-
if (fixup_dump.end > 0)
return fixup_dump.text;
return "";
}
char *
+dump_tree_to_string_25 (void)
+{
+ dump_indices_information ();
+
+ if (indices_dump.end > 0)
+ return indices_dump.text;
+ return "";
+}
+
+char *
dump_tree_to_string_3 (void)
{
/* This must be output at the end so that both the tree and the indices
Modified: trunk/parsetexi/end_line.c
===================================================================
--- trunk/parsetexi/end_line.c 2015-02-15 23:57:38 UTC (rev 6130)
+++ trunk/parsetexi/end_line.c 2015-02-16 21:30:27 UTC (rev 6131)
@@ -507,8 +507,12 @@
{
/* Replace the first element with another element with the leading
"(" removed. */
+ /* TODO: Would it be simpler to split the text element
+ in node->contents as well, to avoid having out-of-tree
+ elements? */
ELEMENT *first;
first = malloc (sizeof (ELEMENT));
+ first->parent_type = route_not_in_tree;
memcpy (first, trimmed->contents.list[0], sizeof (ELEMENT));
first->text.text = malloc (first->text.space);
memcpy (first->text.text,
@@ -521,7 +525,7 @@
{
(void) remove_from_contents (trimmed, 0);
/* Note the removed element still is present in the original
- 'node' argument. */
+ node->contents in the main tree. */
}
while (trimmed->contents.number > 0)
@@ -539,9 +543,11 @@
/* Split the element in two, putting the part before the ")"
in the manual name, leaving the part afterwards for the
node name. */
+ /* TODO: Same as above re route_not_in_tree. */
ELEMENT *before, *after;
before = new_element (ET_NONE);
+ before->parent_type = route_not_in_tree;
text_append_n (&before->text, e->text.text,
closing_bracket - e->text.text);
add_to_element_contents (manual, before);
Modified: trunk/parsetexi/main.c
===================================================================
--- trunk/parsetexi/main.c 2015-02-15 23:57:38 UTC (rev 6130)
+++ trunk/parsetexi/main.c 2015-02-16 21:30:27 UTC (rev 6131)
@@ -24,6 +24,8 @@
int
main (int argc, char **argv)
{
+ //extern int element_counter;
+
if (argc <= 1)
{
fprintf (stderr, "Please give the name of a file to process.\n");
@@ -33,6 +35,9 @@
add_include_directory (".");
parse_texi_file (argv[1]);
dump_tree_to_perl (Root);
+ //build_texinfo_tree ();
+ /* ^ doesn't work because there's no active perl instance */
+ //printf ("About %d elements in tree\n", element_counter);
exit (0);
}
Modified: trunk/parsetexi/tree_types.h
===================================================================
--- trunk/parsetexi/tree_types.h 2015-02-15 23:57:38 UTC (rev 6130)
+++ trunk/parsetexi/tree_types.h 2015-02-16 21:30:27 UTC (rev 6131)
@@ -15,6 +15,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <stdlib.h>
+
#include "command_ids.h"
#include "element_types.h"
@@ -82,14 +83,22 @@
/* Not used in final output. */
int remaining_args; /* Could be a stack instead. */
+ /* Set to route_not_in_tree if element not in main tree. Also
+ used for routing information along with 'index_in_parent' when
+ dumping to a text stream. */
+ enum route_element_type parent_type;
+
/********* Used when dumping to a text stream only. ************/
int index_in_parent;
- enum route_element_type parent_type;
PENDING_REFERENCE *pending_references;
size_t pending_number;
size_t pending_space;
+
+ /********* Used when building Perl tree only ********************/
+ /* should be HV *hv; */
+ void *hv;
} ELEMENT;
typedef struct GLOBAL_INFO {
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [6131] parsetexi start on use of perl api,
Gavin D. Smith <=