1#!/usr/local/bin/perl -w
2use strict;  # we at least try to ;)
3use Class::Struct;
4
5# This file is part of the wvWare 2 project
6# Copyright (C) 2001-2003 Werner Trobin <trobin@kde.org>
7
8# This library is free software; you can redistribute it and/or
9# modify it under the terms of the GNU Library General Public
10# License version 2 as published by the Free Software Foundation.
11
12# This library is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15# Library General Public License for more details.
16
17# You should have received a copy of the GNU Library General Public License
18# along with this library; see the file COPYING.LIB.  If not, write to
19# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20# Boston, MA 02111-1307, USA.
21
22
23# A small utility to generate the basic classes needed to
24# read an write primitive Word structures.
25# Usage: perl generate.pl input_file.html Word97
26# The input_file.html is the document we want to process,
27# 'Word97' is used for various things:
28#   - The namespace is called Word97 and all the generated
29#     code lives in there
30#   - word97_generated.cpp and word97_generated.h are the
31#     filenames (note the case)
32
33# A few notes about the form of the HTML document:
34# 1) We expect all seven fields in the tables:
35#    b10, b16, field, type, size, bitfield, comment
36#    If any of them is absent just add empty ones (<td></td>)
37# 2) If you want to set an initial value for a (plain!) variable
38#    you can add a <!-- initial="50" --> HTML comment to the
39#    "entry" (preferably after the "field" tag).
40#    Note: It has to be on a separate line, else it won't get
41#    picked up!
42#    Note 2: We don't check the value, we just assign it, so make
43#    sure that this is legal C++ (e.g. inital="true", initial="42+42")!
44#    Note 3: Everything else will be set to 0
45# 3) In some cases the </table> tag has to be right after the
46#    last </tr> tag, so better do that everywhere :)
47# 4) An array with a dynamic size can easily be created by editing
48#    the "type field." If you add, say "U8[foo]" then this means:
49#       - we create a dynamic array of size "foo", where "foo" is
50#         some variable of that structure we have already read.
51#         Note: We don't do any error checking here, so be careful
52#               not to use uninitialized values we didn't read at
53#               the time we create the array!
54#         Note2: You can even put plain expressions there, or a
55#                call to a function you include in the template!
56#                Just make sure that it's legal C++ and that it
57#                doesn't contain any '[' or ']' as it will probably
58#                confuse the parser.
59#      - if foo=="", i.e. if you just have "U32[]" then we will
60#        just create a plain pointer for you and initialize it with 0.
61#         Note: Plain destruction will work as we just delete [] it.
62#         Attention: Copy CTOR and assignment operator won't work!!!!
63#                    (as we can't know the length) What we do is what
64#                    C++ does by default - copy the pointer :}
65#   To allow proper comparsions (operator==) we have to know the length
66#   of the dynamich structure. Therefore you should add a HTML comment
67#   to such items, specifying a way to perform that check.
68#   (e.g. <!-- compareSizeLHS="lhs.cb" compareSizeRHS="rhs.cb" -->)
69#   Everything between the quotes will be copied verbatim to an if statement
70#   (e.g. if((lhs.cb)!=(rhs.cb)) ).
71#   If you decide to call a function please ensure it returns something
72#   useful we can compare :)
73# 5) For all structures which need a way to "apply" a grpprl (e.g. PAP, CHP)
74#    we provide a special method you can reimplement if you want to. In the
75#    header we simply add a declaration.
76# 6) If you need the possibility to share a structure (e.g. SEP, PAP, CHP,...)
77#    you can add it to the list in sub selectShared()
78# 7) In case you want to use the structure in any of the PL(C)F templates
79#    you have to add the "sizeof" comment to the .htm file between the name of
80#    the structure and the <table> (like for the DTTM struct)
81
82# If you want to ignore certain structures, please add them to
83# the 'cleanStructures' sub.
84# If you need the possibility to read a structure from a plain
85# pointer, too, please add it to the if statement in parseStructures
86
87# This structure holds one "variable"
88struct Item => {
89    name => '$',         # The name of this variable
90    type => '$',         # The type (e.g. U16, S32[42],...)
91    bits => '$',         # The amount of bits (e.g. 3), if any
92    comment => '$',      # The comment for this variable
93    initial => '$',      # The initial value of this field, if any
94    len => '$',          # If the item is a dynamic array we store its length
95                         # here. length can be a plain C++ expression.
96    compareSizeLHS => '$',  # If the item is a dynamic array we need to compare the
97                            # left-hand-side (lhs) and the rhs in their size. This
98                            # is a plain C++ expression returning the size of the LHS.
99    compareSizeRHS => '$',  # If the item is a dynamic array we need to compare the
100                            # left-hand-side (lhs) and the rhs in their size. This
101                            # is a plain C++ expression returning the size of the RHS.
102    startNew => '$',     # This field is used for debugging purposes. It
103                         # is set to 1 if this variable should start a new
104                         # bitfield (and close the last one). We simply
105                         # check whether we filled the last field completely here
106};
107
108struct Structure => {
109    name => '$',      # The name of the structure
110    comment => '$',   # The comment for this struct
111    items => '@',     # All the data members
112    hidden => '$',    # Set to "//" if we want to comment that structure out
113    dynamic => '$',   # Do we have dynamic memory? Then we need a Copy CTOR,
114                      # DTOR, assignment op, op==,...
115    readPtr => '$',   # Do we want to be able to construct/read from a pointer?
116    shared => '$',    # Whether this structure should be derived from wvWare::Shared'
117    sizeOf => '$',    # The size of the structure (not padded, as in the file!)
118    dumpCode => '$',      # Whether dumping code should be generated
119};
120
121
122# This array of strings contains the whole HTML
123# documentation file.
124# All the parsing subs will read/modify that global array
125# Note: All the tags we use are already converted to
126# uppercase.
127my @document;
128
129# The current index in the document-array (used during parsing)
130my $i;
131
132# This string holds the name of the namespace to create
133my $namespace;
134
135# This array holds all the structures we want to write out
136# It's filled during parsing and used heavily afterwards
137my @structs;
138
139# The current struct we're working on (only used during parsing)
140my $struct;
141# The current item we're working on (only used during parsing)
142my $item;
143
144# Parses all the structures
145sub parseStructures {
146    my ($tmp);
147
148    print "Parsing...\n";
149    $i=0;
150    while($i<=$#document) {
151        if($document[$i] =~ m,\</H3\>,) {
152            if($document[$i-1] =~ m/\<H3\>/) {  # Safe, as </H3> can't be in the first line
153                # looks okay
154                $struct=Structure->new();  # create a new structure element
155                $document[$i] =~ m,^(.*)\</H3\>,;
156                $struct->comment($1);
157            }
158            elsif($document[$i] =~ m/\<H3\>/) {
159                # looks okay, too
160                $struct=Structure->new();  # create a new structure element
161                $document[$i] =~ m,\<H3\>(.*)\</H3\>,;
162                $struct->comment($1);
163            }
164            else {
165                if($document[$i-1] !~ m/Algorithm/) {
166                    # huh? Shouldn't happen at all
167                    print "####### ERROR #######\n";
168                    print $document[$i-1], "\n", $document[$i], "\n";
169                }
170                $i++; # don't forget that one here :))
171                next;
172            }
173            $struct->comment =~ m,.*\((.*)\),;  # get the name of the structure
174            $tmp=$1;              # store it in a $tmp var as I'm too clueless :)
175            $tmp =~ s/\s/_/;      # replace the spaces with underscores
176            $struct->name($tmp);  # ...and set it as name
177            #print "found: name: '", $struct->name, "' comment: '", $struct->comment, "'\n";
178            $struct->hidden("");  # initialize that with a sane value
179
180            # We want that readPtr function :)
181            if($struct->name eq "BRC" || $struct->name eq "SHD" || $struct->name eq "DCS"
182               || $struct->name eq "DTTM" || $struct->name eq "PHE" || $struct->name eq "TLP"
183               || $struct->name eq "ANLD" || $struct->name eq "ANLV" || $struct->name eq "OLST"
184               || $struct->name eq "TC" || $struct->name eq "PCD" || $struct->name eq "PRM"
185               || $struct->name eq "NUMRM") {
186                $struct->readPtr(1);
187            }
188
189            #print "Checking for a <TABLE> ";
190            while($document[$i] !~ m,\<TABLE ,) {
191  	        if($document[$i] =~ m,\<\!--\s*sizeOf\s*=\s*\"(.*?)\"\s*--\>,) {
192		    #print "found a sizeOf tag for structure " . $struct->name . ": " . $1 . "\n";
193		    $struct->sizeOf($1);
194		}
195                $i++;
196                #print ".";
197            }
198            #print " found\n";
199            # parse the <TABLE> we found
200            if(parseStructure()) {
201                push(@structs, $struct); # append the new structure
202            }
203            else {
204                print "####### ERROR #######\n";
205                print "   name: '", $struct->name, "' comment: '", $struct->comment, "'\n";
206            }
207        }
208        $i++;
209    }
210    # print "Number of structures: ", $#structs+1, "\n";
211    print "Done.\n";
212}
213
214# Parses one structure (<table>...</table>)
215sub parseStructure {
216
217    # eat the first row (headline)
218    while($document[$i] !~ m,^\<TR\>$,) {
219        $i++;
220    }
221    while($document[$i] !~ m,^\</TR\>$,) {
222        $i++;
223    }
224
225    # parse all the variables till we encounter </TABLE>
226    while($document[$i] !~ m,^\</TABLE\>$,) {
227        if(parseItem()) {
228            push(@{$struct->items}, $item);
229            $i++;
230        }
231        else {
232            print "####### ERROR #######\n";
233            print "   Error while parsing an item!\n";
234            return 0; # uh-oh :}
235        }
236    }
237    #print "count: ", $#{$struct->items}+1, "\n";
238    return 1;  # success
239}
240
241# Parses one row of the table (<tr> ... </tr>) to get one
242# data item out of it. Does some trivial error checking
243sub parseItem {
244    my ($myState, $tmp);
245
246    $myState=0;
247    while($document[$i] !~ m,^\<TR\>$,) {
248        $i++;
249    }
250    $item=Item->new();
251    while($document[$i] !~ m,^\</TR\>$,) {
252        if($document[$i] =~ m,^\<TD\>(.*)\</TD\>$,) {
253            if($myState==0) {  # this is used for debugging/sanity checking
254                $item->startNew($1);
255                #print "   startNew: ", $1, "\n";
256            }
257            # yes, I left out $myState==1 on purpose
258            elsif($myState==2) {
259                $item->name($1);
260                #print "   name: ", $1, "\n";
261            }
262            elsif($myState==3) {
263                $item->type($1);
264                #print "   type: ", $1, "\n";
265            }
266            elsif($myState==4) {
267                $tmp=$1;
268                if($tmp =~ m/^:(.*)/) {
269                    $item->bits($1);
270                    #print "   bits: ", $1, "\n";
271                }
272                else {
273                    #print "   no bits but a plain size attribute!\n";
274                }
275            }
276            # yes, I left out $myState==5 on purpose
277            elsif($myState==6) {
278                $item->comment($1);
279                #print "   (short) comment: ", $1, "\n";
280            }
281            $myState++;
282        }
283        # The comment can expand across several lines
284        elsif($document[$i] =~ m,^\<TD\>(.*)$, && $myState==6) {
285            $tmp=$1;
286            # Insert a <BR> for "newlines" (consistency)
287            if($document[$i+1] !~ m,\<BR\>,) {
288                $tmp .= "<BR>";
289            }
290            $i++;
291            while($document[$i] !~ m,(.*)\</TD\>$,) {
292                $tmp .= $document[$i];
293                # Insert a <BR> for "newlines" (consistency)
294                if($document[$i+1] !~ m,\<BR\>,) {
295                    $tmp .= "<BR>";
296                }
297                $i++;
298            }
299            $document[$i] =~ m,(.*)\</TD\>$,;
300            $tmp .= $1;
301            $item->comment($tmp);
302            #print "  (long) comment: ", $tmp, "\n";
303            $myState++;
304        }
305        elsif($document[$i] =~ m,\<\!--\s*initial=\"(.*?)\"\s*--\>,) {
306            #print "initial found: ", $document[$i], " filtered: ", $1, "\n";
307            $item->initial($1);
308        }
309        elsif($document[$i] =~ m,\<\!--\s+compareSizeLHS=\"(.*?)\"\s+compareSizeRHS=\"(.*?)\"\s+--\>,) {
310            #print "compareSize found: ", $document[$i], " filtered: ", $1, ", ", $2, "\n";
311            $item->compareSizeLHS($1);
312            $item->compareSizeRHS($2);
313        }
314        elsif($document[$i] =~ m,^\</TABLE\>$,) {
315            print "Error: Found a table end where I didn't expect it!\n";
316            return 0;
317        }
318        $i++;
319    }
320    #print "$myState==7 ? ", $myState==7, "\n";
321    return $myState==7;
322}
323
324# Removes some structures we can't generate easily.
325# Note: We write out the struct in the header and just
326# comment it out (that you can copy it for a proper impl.).
327sub cleanStructures {
328    my($index, @clean, $done);
329
330    print "Cleaning up...\n";
331    # Feel free to add your "favorites" here
332    # The goal, however, should be to have as much as possible
333    # generated, so try to fix the HTML ;)
334    @clean=("PAPXFKP", "CHPXFKP",
335            "PAPX", "CHPX", "FLD", "PLCF", "STD", "FFN", "TBD");
336    foreach (@clean) {
337        $index=0;
338        $done=0;
339        while($index<=$#structs && $done==0) {
340            if($structs[$index]->name eq $_) {
341                print "Removing: ", $structs[$index]->name, "\n";
342                # Better not really remove, just comment it out by setting "hidden"
343                # That way you can copy the declaration for a real implementation
344                #splice @structs,$index,1;
345                $structs[$index]->hidden("//");
346                $done=1;
347            }
348            $index++;
349        }
350    }
351    print "Done.\n";
352}
353
354# Moves around some structures to resolve forward references
355# in the generated sources
356sub hoistStructures {
357    my($index, @hoist, $done);
358
359    print "Resolving forward references...\n";
360    # Feel free to add your "favorites" here
361    # Note: LIFO, at least kind of (the last element here is first afterwards)
362    @hoist=("TBD", "TAP", "DPPOLYLINE", "DPTXBX", "DPHEAD", "TC", "TLP", "BRC", "PHE",
363            "SHD", "PRM", "PRM2", "DOPTYPOGRAPHY", "DTTM");
364    foreach (@hoist) {
365        $index=0;
366        $done=0;
367        while($index<=$#structs && $done==0) {
368            if($structs[$index]->name eq $_) {
369                print "Moving: ", $structs[$index]->name, "\n";
370                #print "before: ", $#structs, "\n";
371                unshift @structs, $structs[$index];
372                $index++;
373                #print "afterwards: ", $#structs, "\n";
374                #print "delete: ", $structs[$index]->name, "\n";
375                splice @structs,$index,1;
376                #print "test: ", $structs[0]->name, "\n";
377                $done=1;
378            }
379            $index++;
380        }
381    }
382    print "Done.\n";
383}
384
385# Selects the structures we want to derive from wvWare::Shared.
386sub selectShared {
387    my($index, @shared, $done);
388
389    print "Selecting shared structures...\n";
390    @shared=("SEP", "TAP", "PAP", "CHP", "PICF");
391    foreach (@shared) {
392        $index=0;
393        $done=0;
394        while($index<=$#structs && $done==0) {
395            if($structs[$index]->name eq $_) {
396                print "Sharing: ", $structs[$index]->name, "\n";
397                $structs[$index]->shared(1);
398                $done=1;
399            }
400            $index++;
401        }
402    }
403    print "Done.\n";
404}
405
406# Selects the structures which should contain a dump() method
407sub selectDumped {
408    my($index, @dumped, $done);
409
410    print "Selecting structures with a dump() method...\n";
411    @dumped=("SEP", "TAP", "PAP", "CHP", "OLST", "BRC", "TLP",
412             "SHD", "DTTM", "PHE", "TC", "ANLV", "LSPD", "DCS",
413             "NUMRM", "ANLD", "PICF", "METAFILEPICT");
414    foreach (@dumped) {
415        $index=0;
416        $done=0;
417        while($index<=$#structs && $done==0) {
418            if($structs[$index]->name eq $_) {
419                print "Adding dump() to: ", $structs[$index]->name, "\n";
420                $structs[$index]->dumpCode(1);
421                $done=1;
422            }
423            $index++;
424        }
425    }
426    print "Done.\n";
427}
428
429# The "main" generator function for headers.
430sub generateHeader {
431    my($tmp, $license, $includes, $before, $after, $myState);
432
433    print "Generating the header file...\n";
434    $tmp=lc($namespace);
435    $tmp .= "_generated.h";
436    open(HEADER, ">$tmp") or die "Couldn't open the header for writing: " . $!;
437
438    ($license, $includes, $before, $after) = parseTemplate("template-$namespace.h");
439
440    $tmp =~ s/.h/_h/;
441    $tmp=uc($tmp);
442    # license section...
443    print HEADER $license;
444    print HEADER "\n#ifndef $tmp\n#define $tmp\n\n";
445    # include section...
446    print HEADER "#include \"global.h\"\n";
447    print HEADER "#include \"sharedptr.h\"\n";
448    print HEADER "#include \"utilities.h\"\n";
449    print HEADER $includes;
450    print HEADER "\nnamespace wvWare {\n\n";
451    print HEADER "class OLEStreamReader;\n";
452    print HEADER "class OLEStreamWriter;\n";
453    print HEADER "class StyleSheet;\n";
454    print HEADER "class Style;\n\n";
455
456    print HEADER "namespace $namespace {\n\n";
457
458    # pre
459    print HEADER $before . "\n";
460    # Fill the empty template
461    print HEADER generateHeaderStructs();
462    # post
463    print HEADER $after;
464
465    print HEADER "\n} // namespace $namespace\n\n";
466    print HEADER "} // namespace wvWare\n\n";
467    print HEADER "#endif // $tmp\n";
468    close(HEADER) or die $!;
469    print "Done.\n";
470}
471
472# This subroutine generates the header file's structures
473sub generateHeaderStructs {
474    my($index, $string, $n, $h, $tmp);
475
476    for($index=0; $index<=$#structs; $index++) {
477        $n=$structs[$index]->name;
478        $h=$structs[$index]->hidden;
479        $string .= "/**\n * " . $structs[$index]->comment . "\n */\n";
480        if($h ne "") {
481            $string .= "/* This structure has been commented out because we can't handle it correctly\n";
482            $string .= " * Please don't try to fix it here in this file, but rather copy this broken\n";
483            $string .= " * structure definition and fix it in some auxilliary file. If you want to\n";
484            $string .= " * include that aux. file here, please change the template file.\n */\n";
485        }
486        $string .= $h . "struct $n ";
487        if(defined($structs[$index]->shared)) {
488            $string .= ": public Shared ";
489        }
490        $string .= "{\n";
491        $string .= $h . "    /**\n";
492        $string .= $h . "     * Creates an empty $n structure and sets the defaults\n";
493        $string .= $h . "     */\n";
494        $string .= $h . "    $n();\n";
495        $string .= $h . "    /**\n";
496        $string .= $h . "     * Simply calls read(...)\n";
497        $string .= $h . "     */\n";
498        $string .= $h . "    $n(OLEStreamReader *stream, bool preservePos=false);\n";
499        if(defined($structs[$index]->readPtr)) {
500            $string .= $h . "    /**\n";
501            $string .= $h . "     * Simply calls readPtr(...)\n";
502            $string .= $h . "     */\n";
503            $string .= $h . "    $n(const U8 *ptr);\n";
504        }
505
506        # From here on we first put the text into a temporary variable, as
507        # we might have to insert some code at this place. The reason is
508        # that we need DTOR, Copy CTOR,... if we have pointers in our struct.
509        # Unfortunately we find that out in generateHeaderData and don't know
510        # it here.
511        $tmp = "\n" . $h . "    /**\n";
512        $tmp .= $h . "     * This method reads the $n structure from the stream.\n";
513        $tmp .= $h . "     * If  preservePos is true we push/pop the position of\n";
514        $tmp .= $h . "     * the stream to save the state. If it's false the state\n";
515        $tmp .= $h . "     * of stream will be changed!\n";
516        $tmp .= $h . "     */\n";
517        $tmp .= $h . "    bool read(OLEStreamReader *stream, bool preservePos=false);\n\n";
518        # Special readPtr() method for all the "ultra primitive" structs
519        # we sometimes have to read from memory (SPRM parameter,...)
520        if(defined($structs[$index]->readPtr)) {
521            $tmp .= $h . "    /**\n";
522            $tmp .= $h . "     * This method reads the struct from a pointer\n";
523            $tmp .= $h . "     */\n";
524            $tmp .= $h . "    void readPtr(const U8 *ptr);\n\n";
525        }
526        $tmp .= $h . "    /**\n";
527        $tmp .= $h . "     * Same as reading :)\n";
528        $tmp .= $h . "     */\n";
529        $tmp .= $h . "    bool write(OLEStreamWriter *stream, bool preservePos=false) const;\n\n";
530        $tmp .= $h . "    /**\n";
531        $tmp .= $h . "     * Set all the fields to the inital value (default is 0)\n";
532        $tmp .= $h . "     */\n";
533        $tmp .= $h . "    void clear();\n\n";
534
535        # Special apply() method for all the PAP, CHP,... structs
536        # Implement that in an auxilliary file
537        if(lc($namespace) eq "word97" && ($n eq "PAP" || $n eq "CHP" || $n eq "TAP" || $n eq "SEP" || $n eq "PICF")) {
538            $tmp .= $h . "    /**\n";
539            $tmp .= $h . "     * This method applies a grpprl with \@param count elements\n";
540            $tmp .= $h . "     */\n";
541            $tmp .= $h . "    void apply(const U8 *grpprl, U16 count, const Style* style, const StyleSheet* styleSheet, OLEStreamReader* dataStream, WordVersion version);\n\n";
542            $tmp .= $h . "    /**\n";
543            $tmp .= $h . "     * This method applies a whole " . $n . "X to the structure.\n";
544            $tmp .= $h . "     * The reason that we only pass a pointer to the start of the exception\n";
545            $tmp .= $h . "     * structure is, that we don't know the type in the FKP template :}\n";
546            $tmp .= $h . "     */\n";
547	    if($n eq "CHP") {  # More than just CHP?
548	        $tmp .= $h . "    void applyExceptions(const U8* exceptions, const Style* paragraphStyle, const StyleSheet* styleSheet, OLEStreamReader* dataStream, WordVersion version);\n\n";
549	    }
550	    else {
551	        $tmp .= $h . "    void applyExceptions(const U8 *exceptions, const StyleSheet *styleSheet, OLEStreamReader* dataStream, WordVersion version);\n\n";
552	    }
553            $tmp .= $h . "    /**\n";
554            $tmp .= $h . "     * This method applies one single SPRM. It returns -1 if it wasn't\n";
555            $tmp .= $h . "     * a " . $n . " SPRM and it returns the length of the applied SPRM\n";
556            $tmp .= $h . "     * if it was successful.\n";
557            $tmp .= $h . "     */\n";
558            $tmp .= $h . "    S16 apply" . $n . "SPRM(const U8* ptr, const Style* style, const StyleSheet* styleSheet, OLEStreamReader* dataStream, WordVersion version);\n\n";
559        }
560
561        # Special toPRM2 method for the PRM struct, implemented in word97_helper.cpp
562        # This method is neccessary as we don't want to rely on a "packed" layout of
563        # the structure so we can't just do evil casting ;)
564        if($n eq "PRM") {
565            $tmp .= $h . "    /**\n";
566            $tmp .= $h . "     * This method returns a PRM2 created from the current PRM\n";
567            $tmp .= $h . "     */\n";
568            $tmp .= $h . "    PRM2 toPRM2() const;\n\n";
569        }
570
571        if(defined($structs[$index]->dumpCode)) {
572	    $tmp .= $h . "    /**\n";
573	    $tmp .= $h . "     * Dumps all fields of this structure (for debugging)\n";
574	    $tmp .= $h . "     */\n";
575	    $tmp .= $h . "    void dump() const;\n\n";
576
577	    $tmp .= $h . "    /**\n";
578	    $tmp .= $h . "     * Converts the data structure to a string (for debugging)\n";
579	    $tmp .= $h . "     */\n";
580	    $tmp .= $h . "    std::string toString() const;\n\n";
581	}
582
583	if(defined($structs[$index]->sizeOf)) {
584	    $tmp .= $h . "    // Size of the structure\n";
585	    $tmp .= $h . "    static const unsigned int sizeOf;\n\n";
586	}
587
588        $tmp .= $h . "    // Data\n";
589        $tmp .= generateHeaderData($index);
590
591        if(defined($structs[$index]->dynamic)) {
592            # okay, now we already know what we need, so let's
593            # add that stuff (to $string, of course ;)
594            $string .= $h . "    /**\n";
595            $string .= $h . "     * Attention: This struct allocates memory on the heap\n";
596            $string .= $h . "     */\n";
597            $string .= $h . "    $n(const $n &rhs);\n";
598            $string .= $h . "    ~" . $n . "();\n\n";
599            $string .= $h . "    " . $n . " &operator=(const $n &rhs);\n";
600        }
601        # insert the stuff from above
602        $string .= $tmp;
603
604        # If we have dynamic structures we have to be careful
605        # with clear()! We simply define that clear() also
606        # delete []s all the arrays and clearInternal() just sets
607        # everything to 0
608        if(defined($structs[$index]->dynamic)) {
609            $string .= $h . "private:\n";
610            $string .= $h . "    void clearInternal();\n\n";
611        }
612        $string .= $h . "}; // $n\n";
613
614        # ...and add some more code "outside"
615        $string .= "\n" . $h . "bool operator==(const $n &lhs, const $n &rhs);\n";
616        $string .= $h . "bool operator!=(const $n &lhs, const $n &rhs);\n\n\n";
617    }
618    return $string;
619}
620
621# Takes one structure and generates all the fields for it.
622# Checks the bit-fields for missing bits and tries to detect
623# arrays with non-static size. We use that information all
624# over the place :)
625sub generateHeaderData {
626    my ($index)=@_;
627    my ($string, $tmp, $tmp2, $sum, $bits, $h);
628
629    $sum=0;  # no bits counted up to now :)
630    $bits=0; # make the first check work
631
632    # write out all the data
633    foreach (@{$structs[$index]->items}) {
634        $h=$structs[$index]->hidden;
635        $string .= prepareComment($_->comment, $h);
636        # Check the completeness of the bitfields...
637        if($_->startNew ne "") {
638            if($bits != $sum) {
639                print "   ERROR: Last bitfield incomplete. Current position: ";
640                print $structs[$index]->name . " - " . $_->name . "\n";
641            }
642            # set up a new check (sloppy, only for U8, U16, and U32 bitfields)
643            if($_->type =~ m/U(\d+)/ && defined($_->bits)) {
644                #print "bitfield..." . $_->name . "\n";
645                $bits=$1;
646            }
647            else {
648                $bits=0;
649            }
650            $sum=0;
651        }
652        # Handle XCHAR[32] by splitting it up properly
653        if($_->type =~ m/(.*)(\[.*\])/) {
654            $tmp = "    " . $1 . " " . $_->name . $2;
655            #print "Array: '" . $tmp . "'\n";
656            # Is it a fixed size array or not?
657            if($tmp !~ m/.*\[\d+\]/) {
658                $tmp =~ m/    (.+)\[(.*)\]/;
659                $tmp2=$1;
660                # get the "length" (or the C++ expression ;)
661                $_->len($2);
662                $tmp2 =~ s/ / \*/;
663                $tmp = "    " . $tmp2 . ";   //" . $tmp;
664                #print " --- Result: " . $tmp . "\n";
665                # okay, we found a dynamic array, so we need some additional
666                # code for that struct (Copy CTOR, DTOR,...)
667                $structs[$index]->dynamic(1);
668                #if(defined($_->len)) {
669                #    print "Dynamic: " . $structs[$index]->name . ", length: " . $_->len . "\n";
670                #}
671            }
672            $string .= $h . $tmp;
673        }
674        else {
675            $string .= $h . "    " . $_->type . " " . $_->name;
676        }
677        if(defined($_->bits)) {
678            $string .= ":" . $_->bits;
679            $sum += $_->bits;
680        }
681        $string .= ";\n\n";
682    }
683    return $string;
684}
685
686# This meathod gets a looong comment string. It takes the
687# string and splits it at the <BR>s and creates a nice
688# comment out of it (not longer than, say 90 cols, as found in
689# the HTML spec)
690sub prepareComment {
691    my($comment, $h)=@_;
692    my($string, @tmp);
693
694    if($comment eq "") {
695        return "";
696    }
697
698    $string = $h . "    /**\n";
699    # "unfold" the <BR>'ed comments
700    @tmp=split(/\<BR\>/, $comment);
701    foreach (@tmp) {
702        $string .= $h . "     * $_\n";
703    }
704    $string .= $h . "     */\n";
705    return $string;
706}
707
708# Parse the template file
709sub parseTemplate {
710    my($name) = @_;  # name of the template
711    my($license, $includes, $before, $after, $myState);
712
713    open(TEMPLATE, "<$name") or die "Couldn't open the template: " . $!;
714    # initialize all the template vars
715    $myState=0;
716    $license="";
717    $includes="";
718    $before="";
719    $after="";
720    # read in the information...
721    while(<TEMPLATE>) {
722        if(m/^\#\#\#/) {  # ignore comments
723            next;
724        }
725        if(m/^\@\@license-start\@\@$/) {  # license section
726            $myState=1;
727            next;
728        }
729        if(m/^\@\@license-end\@\@$/) {  # end of license sect.
730            $myState=0;
731            next;
732        }
733        if(m/^\@\@includes-start\@\@$/) {  # includes section
734            $myState=2;
735            next;
736        }
737        if(m/^\@\@includes-end\@\@$/) {  # end of includes sect.
738            $myState=0;
739            next;
740        }
741        if(m/^\@\@namespace-start\@\@$/) {  # namespace (before)
742            $myState=3;
743            next;
744        }
745        if(m/^\@\@generated-code\@\@$/) {  # namespace (after)
746            $myState=4;
747            next;
748        }
749        if(m/^\@\@namespace-end\@\@$/) {  # end of namespace
750            $myState=0;
751            next;
752        }
753
754        if($myState==1) {
755            $license .= $_;
756        }
757        elsif($myState==2) {
758            $includes .= $_;
759        }
760        elsif($myState==3) {
761            $before .= $_;
762        }
763        elsif($myState==4) {
764            $after .= $_;
765        }
766    }
767    close(TEMPLATE) or die $!;
768    return ($license, $includes, $before, $after);
769}
770
771# generate the source file
772sub generateImplementation {
773    my($tmp, $license, $includes, $before, $after, $myState);
774
775    print "Generating the source file...\n";
776    $tmp=lc($namespace);
777    $tmp .= "_generated.cpp";
778    open(SOURCE, ">$tmp") or die "Couldn't open the file for writing: " . $!;
779
780    ($license, $includes, $before, $after) = parseTemplate("template-$namespace.cpp");
781
782    # license section...
783    print SOURCE $license . "\n";
784    # include section...
785    $tmp =~ s/\.cpp/\.h/;
786    print SOURCE "#include <$tmp>\n";
787    print SOURCE "#include <olestream.h>\n";
788    print SOURCE "#include <string.h>  // memset(), memcpy()\n";
789    print SOURCE "#include \"wvlog.h\"\n";
790    print SOURCE $includes;
791    print SOURCE "\nnamespace wvWare {\n";
792    print SOURCE "\nnamespace $namespace {\n\n";
793
794    # pre
795    print SOURCE $before . "\n";
796    # Fill the empty template
797    print SOURCE generateImplStructs();
798    # post
799    print SOURCE $after;
800
801    print SOURCE "\n} // namespace $namespace\n";
802    print SOURCE "\n} // namespace wvWare\n";
803    close(SOURCE) or die $!;
804    print "Done.\n";
805}
806
807# Iterare over all structs and generte the necessary code
808sub generateImplStructs {
809    my($index, $string, $n);
810
811    for($index=0; $index<=$#structs; $index++) {
812        if($structs[$index]->hidden ne "") {
813            next; # Don't generate useless code
814        }
815        $n=$structs[$index]->name;
816        $string .= "// $n implementation\n\n";
817
818	# Size (if specified)
819	if(defined($structs[$index]->sizeOf)) {
820	    $string .= "const unsigned int " . $n . "::sizeOf = " . $structs[$index]->sizeOf . ";\n\n";
821	}
822
823        # default CTOR
824        $string .= $n . "::" . $n . "() ";
825        if(defined($structs[$index]->shared)) {
826            $string .= ": Shared() ";
827        }
828        $string .= "{\n";
829        if(defined($structs[$index]->dynamic)) {
830            $string .= "    clearInternal();\n";
831        }
832        else {
833            $string .= "    clear();\n";
834        }
835        $string .= "}\n\n";
836        # stream CTOR
837        $string .= $n . "::" . $n . "(OLEStreamReader *stream, bool preservePos) ";
838        if(defined($structs[$index]->shared)) {
839            $string .= ": Shared() ";
840        }
841        $string .= "{\n";
842        if(defined($structs[$index]->dynamic)) {
843            $string .= "    clearInternal();\n";
844        }
845        else {
846            $string .= "    clear();\n";
847        }
848        $string .= "    read(stream, preservePos);\n";
849        $string .= "}\n\n";
850        # readPtr CTOR
851        if(defined($structs[$index]->readPtr)) {
852            $string .= $n . "::" . $n . "(const U8 *ptr) ";
853            if(defined($structs[$index]->shared)) {
854                $string .= ": Shared() ";
855            }
856            $string .= "{\n";
857            if(defined($structs[$index]->dynamic)) {
858                $string .= "    clearInternal();\n";
859            }
860            else {
861                $string .= "    clear();\n";
862            }
863            $string .= "    readPtr(ptr);\n";
864            $string .= "}\n\n";
865        }
866        if(defined($structs[$index]->dynamic)) {
867            # Copy CTOR
868            $string .= $n . "::" . $n . "(const $n &rhs) ";
869            if(defined($structs[$index]->shared)) {
870                $string .= ": Shared() ";
871            }
872            $string .= "{\n";
873            $string .= generateCopyCTOR($index);
874            $string .= "}\n\n";
875            # DTOR
876            $string .= $n . "::~" . $n . "() {\n";
877            $string .= generateDTOR($index);
878            $string .= "}\n\n";
879            # assignement operator
880            $string .= $n . " &" . $n .  "::operator=(const $n &rhs) {\n";
881            $string .= generateAssignment($index);
882            $string .= "}\n\n";
883        }
884        # read()
885        $string .= "bool " . $n . "::read(OLEStreamReader *stream, bool preservePos) {\n";
886        $string .= generateRead($index);
887        $string .= "}\n\n";
888        # readPtr()?
889        if(defined($structs[$index]->readPtr)) {
890            $string .= "void " . $n . "::readPtr(const U8 *ptr) {\n";
891            $string .= generateReadPtr($index);
892            $string .= "}\n\n";
893        }
894        # write()
895        $string .= "bool " . $n . "::write(OLEStreamWriter *stream, bool preservePos) const {\n";
896        $string .= generateWrite($index);
897        $string .= "}\n\n";
898        # clear()
899        $string .= "void " . $n . "::clear() {\n";
900        $string .= generateClear($index);
901        $string .= "}\n\n";
902
903        if(defined($structs[$index]->dumpCode)) {
904            $string .= "void " . $n . "::dump() const\n{\n";
905            $string .= generateDump($index);
906            $string .= "}\n\n";
907
908            $string .= "std::string " . $n . "::toString() const\n{\n";
909            $string .= generateToString($index);
910            $string .= "}\n\n";
911        }
912
913        if(defined($structs[$index]->dynamic)) {
914            $string .= "void " . $n . "::clearInternal() {\n";
915            $string .= generateClearInternal($index);
916            $string .= "}\n\n";
917        }
918
919	# It's okay to initialize the const variable in the header
920	#if(defined($structs[$index]->sizeOf)) {
921	#    $string .= "const unsigned int " . $n . "::sizeOf = " . $structs[$index]->sizeOf . ";\n\n";
922	#}
923
924        # operator== and op!=
925        $string .= "bool operator==(const $n &lhs, const $n &rhs) {\n";
926        $string .= generateEqualityOp($index);
927        $string .= "}\n\n";
928        $string .= "bool operator!=(const $n &lhs, const $n &rhs) {\n";
929        $string .= "    return !(lhs==rhs);\n";
930        $string .= "}\n\n\n";
931    }
932    return $string;
933}
934
935# Generates a Copy Constructor
936sub generateCopyCTOR {
937    my($index)=@_;
938    my($string);
939
940    foreach (@{$structs[$index]->items}) {
941        # is it a dyn. array we know the size of?
942        if(defined($_->len) && $_->len ne "") {
943            $_->type =~ m/(.*)\[.*\]/;
944            $string .= "    " . $_->name . "=new " . $1 . "[" . $_->len . "];\n";
945            $string .= "    memcpy(" . $_->name . ", rhs." . $_->name . ", sizeof($1)*(" . $_->len . "));\n";
946        }
947        elsif($_->type =~ m/.*\[\d+\]/) {
948            $string .= "    memcpy(&" . $_->name . ", &rhs." . $_->name . ", sizeof(" . $_->name . "));\n";
949        }
950        else {
951            # "plain" members, no problem here
952            $string .= "    " . $_->name . "=rhs." . $_->name . ";\n";
953        }
954    }
955    return $string;
956}
957
958
959# Generates a Destructor
960sub generateDTOR {
961    my($index)=@_;
962    my($string);
963
964    foreach (@{$structs[$index]->items}) {
965        # is it a dyn. array (regardless whether we know the size!) ?
966        if(defined($_->len)) {
967            $string .= "    delete [] " . $_->name . ";\n";
968        }
969    }
970    return $string;
971}
972
973# Generates an assignment operator
974sub generateAssignment {
975    my($index)=@_;
976    my($string);
977
978    $string = "\n    // Check for assignment to self\n";
979    $string .= "    if(this==&rhs)\n";
980    $string .= "        return *this;\n\n";
981
982    foreach (@{$structs[$index]->items}) {
983        # is it a dyn. array we know the size of?
984        if(defined($_->len) && $_->len ne "") {
985            $string .= "    delete [] " . $_->name . ";\n";
986            $_->type =~ m/(.*)\[.*\]/;
987            $string .= "    " . $_->name . "=new " . $1 . "[" . $_->len . "];\n";
988            $string .= "    memcpy(" . $_->name . ", rhs." . $_->name . ", sizeof($1)*(" . $_->len . "));\n";
989        }
990        elsif($_->type =~ m/.*\[\d+\]/) {
991            $string .= "    memcpy(&" . $_->name . ", &rhs." . $_->name . ", sizeof(" . $_->name . "));\n";
992        }
993        else {
994            # "plain" members, no problem here
995            $string .= "    " . $_->name . "=rhs." . $_->name . ";\n";
996        }
997    }
998    $string .= "\n    return *this;\n";
999    return $string;
1000}
1001
1002# Generates the code to read from the stream
1003sub generateRead {
1004    my($index)=@_;
1005    my($needU8, $needU16, $needU32, $string, $sum, $limit, $vars);
1006
1007    $needU8=0;
1008    $needU16=0;
1009    $needU32=0;
1010
1011    $string = "    if(preservePos)\n";
1012    $string .= "        stream->push();\n\n";
1013
1014    foreach (@{$structs[$index]->items}) {
1015        if(defined($_->bits)) {
1016            if($_->type eq "U8") {
1017                $needU8=1;
1018                $limit=8;
1019            }
1020            elsif($_->type eq "U16") {
1021                $needU16=1;
1022                $limit=16;
1023            }
1024            elsif($_->type eq "U32") {
1025                $needU32=1;
1026                $limit=32;
1027            }
1028            else {
1029                print "   ERROR: Don't know how to handle a '" . $_->type . "' bitfield\n";
1030            }
1031            # first bit of a bitfield?
1032            if($_->startNew ne "") {
1033                $string .= "    shifter" . $_->type . "=stream->read" . $_->type . "();\n";
1034                $sum=0;
1035            }
1036            $string .= "    " . $_->name . "=shifter" . $_->type . ";\n";
1037            $sum+=$_->bits;
1038            if($sum<$limit) {
1039                $string .= "    shifter" . $_->type . ">>=" . $_->bits . ";\n";
1040            }
1041        }
1042        # okay, no bitfields from here on
1043        else {
1044            # Array?
1045            if($_->type =~ m/(.*)\[(.*)\]/) {
1046                #print "Array: " . $_->name . " -- type: " . $_->type . "\n";
1047                #print "   1: " . $1 . ", 2: " . $2 . "\n";
1048                if($2 eq "") {
1049                    #print "    empty! -> warning\n";
1050                    $string .= "    // Attention: I don't know how to read " . $_->name . " - " . $_->type . "\n";
1051                    $string .= "#ifdef __GNUC__\n";
1052                    $string .= "#warning \"Couldn't generate reading code for " . $structs[$index]->name . "::" . $_->name . "\"\n";
1053                    $string .= "#endif\n";
1054                }
1055                else {
1056                    # Do we have to allocate the memory first?
1057                    if(defined($_->len) && $_->len ne "") {
1058                        #print "   allocating...\n";
1059                        $_->type =~ m/(.*)\[.*\]/;
1060                        $string .= "    " . $_->name . "=new " . $1 . "[" . $_->len . "];\n";
1061                    }
1062                    $string .= "    for(int _i=0; _i<(" . $2 . "); ++_i)\n";
1063                    $string .= "    " . readVariable($_->name . "[_i]", $1);
1064                }
1065            }
1066            else {
1067                $string .= readVariable($_->name, $_->type);
1068            }
1069        }
1070    }
1071
1072    $vars="\n";
1073    if($needU8) {
1074        $vars .= "    U8 shifterU8;\n";
1075    }
1076    if($needU16) {
1077        $vars .= "    U16 shifterU16;\n";
1078    }
1079    if($needU32) {
1080        $vars .= "    U32 shifterU32;\n";
1081    }
1082
1083    # looks better IMVHO :)
1084    if($vars ne "\n") {
1085        $vars .= "\n";
1086    }
1087
1088    $string .= "\n    if(preservePos)\n";
1089    $string .= "        stream->pop();\n";
1090    $string .= "    return true;\n";
1091
1092    return $vars . $string;
1093}
1094
1095# Generates the code to read from a pointer
1096sub generateReadPtr {
1097    my($index)=@_;
1098    my($needU8, $needU16, $needU32, $string, $sum, $limit, $vars);
1099
1100    $needU8=0;
1101    $needU16=0;
1102    $needU32=0;
1103
1104    foreach (@{$structs[$index]->items}) {
1105        if(defined($_->bits)) {
1106            if($_->type eq "U8") {
1107                $needU8=1;
1108                $limit=8;
1109            }
1110            elsif($_->type eq "U16") {
1111                $needU16=1;
1112                $limit=16;
1113            }
1114            elsif($_->type eq "U32") {
1115                $needU32=1;
1116                $limit=32;
1117            }
1118            else {
1119                print "   ERROR: Don't know how to handle a '" . $_->type . "' bitfield\n";
1120            }
1121            # first bit of a bitfield?
1122            if($_->startNew ne "") {
1123                $string .= "    shifter" . $_->type . "=read" . $_->type . "(ptr);\n";
1124                $string .= "    ptr+=sizeof(" . $_->type . ");\n";
1125                $sum=0;
1126            }
1127            $string .= "    " . $_->name . "=shifter" . $_->type . ";\n";
1128            $sum+=$_->bits;
1129            if($sum<$limit) {
1130                $string .= "    shifter" . $_->type . ">>=" . $_->bits . ";\n";
1131            }
1132        }
1133        # okay, no bitfields from here on
1134        else {
1135            # Array?
1136            if($_->type =~ m/(.*)\[(.*)\]/) {
1137                #print "Array: " . $_->name . " -- type: " . $_->type . "\n";
1138                #print "   1: " . $1 . ", 2: " . $2 . "\n";
1139                if($2 eq "") {
1140                    #print "    empty! -> warning\n";
1141                    $string .= "    // Attention: I don't know how to read " . $_->name . " - " . $_->type . "\n";
1142                    $string .= "#ifdef __GNUC__\n";
1143                    $string .= "#warning \"Couldn't generate reading code for " . $structs[$index]->name . "::" . $_->name . "\"\n";
1144                    $string .= "#endif\n";
1145                }
1146                else {
1147                    # Do we have to allocate the memory first?
1148                    if(defined($_->len) && $_->len ne "") {
1149                        #print "   allocating...\n";
1150                        $_->type =~ m/(.*)\[.*\]/;
1151                        $string .= "    " . $_->name . "=new " . $1 . "[" . $_->len . "];\n";
1152                    }
1153                    $string .= "    for(int _i=0; _i<(" . $2 . "); ++_i) {\n";
1154                    $string .= readVariablePtr($_->name . "[_i]", $1, "        ");
1155                    $string .= "    }\n";
1156                }
1157            }
1158            else {
1159                $string .= readVariablePtr($_->name, $_->type, "    ");
1160            }
1161        }
1162    }
1163
1164    $vars="\n";
1165    if($needU8) {
1166        $vars .= "    U8 shifterU8;\n";
1167    }
1168    if($needU16) {
1169        $vars .= "    U16 shifterU16;\n";
1170    }
1171    if($needU32) {
1172        $vars .= "    U32 shifterU32;\n";
1173    }
1174
1175    # looks better IMVHO :)
1176    if($vars ne "\n") {
1177        $vars .= "\n";
1178    }
1179    return $vars . $string;
1180}
1181
1182# Is the passed name a known structure? (needed for read())
1183sub knownType {
1184    my($name)=@_;
1185
1186    foreach (@structs) {
1187        if($_->name eq $name) {
1188            return 1;
1189        }
1190    }
1191    return 0;
1192}
1193
1194# Generates code to read one plain variable (no arrays!)
1195sub readVariable {
1196    my($name, $type)=@_;
1197    my($string);
1198
1199    if($type =~ m/^[US]\d+$/) {
1200        $string = "    " . $name . "=stream->read" . $type . "();\n";
1201    }
1202    elsif($type eq "FC") {
1203        $string = "    " . $name . "=stream->readU32();\n";
1204    }
1205    elsif($type eq "XCHAR") {
1206        $string = "    " . $name . "=stream->readU16();\n";
1207    }
1208    elsif(knownType($type)) {
1209        #print "Known: " . $type . "\n";
1210        $string = "    " . $name . ".read(stream, false);\n";
1211    }
1212    elsif($type =~ m/std::vector/ ) {
1213        print "Found a std::vector, skipping it for reading. I hope you know what you're doing\n";
1214        $string = "    // skipping the std::vector " . $name . "\n";
1215    }
1216    else {
1217        print "Error: Can't read " . $name . ", " . $type . "\n";
1218        $string="";  # initialize
1219    }
1220    return $string;
1221}
1222
1223# Generates code to read one plain variable (no arrays!) from memory
1224sub readVariablePtr {
1225    my($name, $type, $indent)=@_;
1226    my($string);
1227
1228    if($type =~ m/^[US]\d+$/) {
1229        $string = $indent . $name . "=read" . $type . "(ptr);\n";
1230	$string .= $indent . "ptr+=sizeof(" . $type . ");\n";
1231    }
1232    elsif($type eq "FC") {
1233        $string = $indent . $name . "=readU32(ptr);\n";
1234	$string .= $indent . "ptr+=sizeof(U32);\n";
1235    }
1236    elsif($type eq "XCHAR") {
1237        $string = $indent . $name . "=readU16(ptr);\n";
1238	$string .= $indent . "ptr+=sizeof(U16);\n";
1239    }
1240    elsif(knownType($type)) {
1241        #print "Known: " . $type . "\n";
1242        $string = $indent . $name . ".readPtr(ptr);\n";
1243	$string .= $indent . "ptr+=" . $type . "::sizeOf;\n";
1244    }
1245    elsif($type =~ m/std::vector/ ) {
1246        print "Found a std::vector, skipping it for reading. I hope you know what you're doing\n";
1247        $string = "    // skipping the std::vector " . $name . "\n";
1248    }
1249    else {
1250        print "Error: Can't read " . $name . ", " . $type . "\n";
1251        $string="";  # initialize
1252    }
1253    return $string;
1254}
1255
1256# Generates code to add one plain variable (no arrays!) to the string
1257sub variableToString {
1258    my($name, $type)=@_;
1259    my($string, $output);
1260
1261    if($name =~ m/(.+)\[(.+)\]/) {
1262        $output = $1 . "[\" + int2string( " . $2 . " ) + \"]";
1263    }
1264    else {
1265        $output = $name;
1266    }
1267
1268    $string = "    s += \"\\n" . $output . "=\";\n";
1269    if($type =~ m/^U\d+$/ || $type eq "FC" || $type eq "XCHAR") {
1270        $string .= "    s += uint2string( " . $name . " );\n";
1271    }
1272    elsif($type =~ m/^S\d+$/) {
1273        $string .= "    s += int2string( " . $name . " );\n";
1274    }
1275    elsif(knownType($type)) {
1276        #print "Known: " . $type . "\n";
1277        $string .= "    s += \"\\n{\" + " . $name . ".toString() + \"}\\n\";\n";
1278    }
1279    elsif($type =~ m/std::vector/ ) {
1280        print "Found a std::vector, skipping it for reading. I hope you know what you're doing\n";
1281        $string .= "    // skipping the std::vector " . $name . "\n";
1282    }
1283    else {
1284        print "Error: Can't dump " . $name . ", " . $type . "\n";
1285    }
1286    return $string;
1287}
1288
1289# Generates the code to write to the stream
1290sub generateWrite {
1291    my($index)=@_;
1292    my($needU8, $needU16, $needU32, $string, $position, $limit, $vars);
1293
1294    $needU8=0;
1295    $needU16=0;
1296    $needU32=0;
1297    $position=0;
1298
1299    $string = "    if(preservePos)\n";
1300    $string .= "        stream->push();\n\n";
1301
1302    foreach (@{$structs[$index]->items}) {
1303        if(defined($_->bits)) {
1304            if($_->type eq "U8") {
1305                $needU8=1;
1306                $limit=8;
1307            }
1308            elsif($_->type eq "U16") {
1309                $needU16=1;
1310                $limit=16;
1311            }
1312            elsif($_->type eq "U32") {
1313                $needU32=1;
1314                $limit=32;
1315            }
1316            else {
1317                print "   ERROR: Don't know how to handle a '" . $_->type . "' bitfield\n";
1318            }
1319            # first bit of a bitfield?
1320            if($_->startNew ne "") {
1321                # Do we have to write out the last bitfield?
1322                if($position != 0) {
1323                    $string .= "    stream->write(shifter" . $_->type . ");\n";
1324                    $position=0;
1325                }
1326                $string .= "    shifter" . $_->type . "=" . $_->name . ";\n";
1327            }
1328            else {
1329                $string .= "    shifter" . $_->type . "|=" . $_->name . " << " . $position . ";\n";
1330            }
1331            $position+=$_->bits;
1332        }
1333        # Do we have to write out the last bitfield?
1334        if(defined($limit) && $position == $limit) {
1335            #print "reached the limit...\n";
1336            $string .= "    stream->write(shifter" . $_->type . ");\n";
1337            $position=0;
1338            undef($limit);
1339        }
1340        # okay, no bitfields from here on
1341        if(!defined($_->bits)) {
1342            # Array?
1343            if($_->type =~ m/(.*)\[(.*)\]/) {
1344                #print "Array: " . $_->name . " -- type: " . $_->type . "\n";
1345                #print "   1: " . $1 . ", 2: " . $2 . "\n";
1346                if($2 eq "") {
1347                    #print "    empty! -> warning\n";
1348                    $string .= "    // Attention: I don't know how to write " . $_->name . " - " . $_->type . "\n";
1349                    $string .= "#ifdef __GNUC__\n";
1350                    $string .= "#warning \"Couldn't generate writing code for " . $structs[$index]->name . "::" . $_->name . "\"\n";
1351                    $string .= "#endif\n";
1352                }
1353                else {
1354                    $string .= "    for(int _i=0; _i<(" . $2 . "); ++_i)\n";
1355                    $string .= "    " . writeVariable($_->name . "[_i]", $1);
1356                }
1357            }
1358            else {
1359                $string .= writeVariable($_->name, $_->type);
1360            }
1361        }
1362    }
1363
1364    $vars="\n";
1365    if($needU8) {
1366        $vars .= "    U8 shifterU8;\n";
1367    }
1368    if($needU16) {
1369        $vars .= "    U16 shifterU16;\n";
1370    }
1371    if($needU32) {
1372        $vars .= "    U32 shifterU32;\n";
1373    }
1374
1375    # looks better IMVHO :)
1376    if($vars ne "\n") {
1377        $vars .= "\n";
1378    }
1379
1380    $string .= "\n    if(preservePos)\n";
1381    $string .= "        stream->pop();\n";
1382    $string .= "    return true;\n";
1383
1384    return $vars . $string;
1385}
1386
1387# Generates code to write one plain variable (no arrays!)
1388sub writeVariable {
1389    my($name, $type)=@_;
1390    my($string);
1391
1392    if($type =~ m/^[US]\d+$/ || $type eq "FC" || $type eq "XCHAR") {
1393        $string = "    stream->write(" . $name . ");\n";
1394    }
1395    elsif(knownType($type)) {
1396        #print "Known: " . $type . "\n";
1397        $string = "    " . $name . ".write(stream, false);\n";
1398    }
1399    elsif($type =~ m/std::vector/ ) {
1400        print "Found a std::vector, skipping it for writing. I hope you know what you're doing\n";
1401        $string = "    // skipping the std::vector " . $name . "\n";
1402    }
1403    else {
1404        print "Error: Can't write " . $name . ", " . $type . "\n";
1405        $string="";  # initialize
1406    }
1407    return $string;
1408}
1409
1410# Generates the code to compare structs with dynamic members
1411sub generateEqualityOp {
1412    my($index)=@_;
1413    my($string, $first, $tmp);
1414
1415    # first check the arrays
1416    # Note: We don't check the 0-sized ones!
1417    foreach (@{$structs[$index]->items}) {
1418        if($_->type =~ m/.*\[(.*)\]/) {
1419            $tmp=$1;
1420            if($tmp eq "") {
1421                #print "    empty! -> warning\n";
1422                $string .= "    // Attention: I don't know how to compare " . $_->name . " - " . $_->type . "\n";
1423                $string .= "#ifdef __GNUC__\n";
1424                $string .= "#warning \"Can't compare " . $structs[$index]->name . "::" . $_->name . " items\"\n";
1425                $string .= "#endif\n";
1426                next;
1427            }
1428            # okay, not a plain array, so we have to compare the size, too :}
1429            if($_->type !~ m/.*\[(\d+)\]/) {
1430                if(defined($_->compareSizeLHS) && defined($_->compareSizeRHS)) {
1431                    #print " ---> compareSize: " . $_->compareSizeLHS . ", " . $_->compareSizeRHS . "\n";
1432                    $string .= "\n    if((" . $_->compareSizeLHS . ")!=(" . $_->compareSizeRHS . "))\n";
1433                    $string .= "        return false;\n";
1434                    $string .= "    for(int _i=0; _i<(" . $_->compareSizeLHS . "); ++_i) {\n";
1435                    $string .= "        if(lhs." . $_->name . "[_i]!=rhs." . $_->name . "[_i])\n";
1436                    $string .= "            return false;\n";
1437                    $string .= "    }\n";
1438                }
1439                else {
1440                    $string .= "#ifdef __GNUC__\n";
1441                    $string .= "#warning \"Please provide a compareSize* comment for " . $structs[$index]->name . "::" . $_->name . "\"\n";
1442                    $string .= "#endif\n";
1443                }
1444            }
1445            else {
1446                $string .= "\n    for(int _i=0; _i<(" . $tmp . "); ++_i) {\n";
1447                $string .= "        if(lhs." . $_->name . "[_i]!=rhs." . $_->name . "[_i])\n";
1448                $string .= "            return false;\n";
1449                $string .= "    }\n";
1450            }
1451        }
1452    }
1453    # ... then create a nice return statement ;)
1454    $string .= "\n    return";
1455    $first=1;  # first line?
1456    foreach (@{$structs[$index]->items}) {
1457        # don't forget to exclude the arrays!
1458        if($_->type !~ m/.*\[.*\]/) {
1459            # special case: first line
1460            if($first==1) {
1461                $string .= " lhs." . $_->name . "==rhs." . $_->name;
1462                $first=0;
1463            }
1464            else {
1465                $string .= " &&\n           lhs." . $_->name . "==rhs." . $_->name;
1466            }
1467        }
1468    }
1469    $string .= ";\n";
1470    return $string;
1471}
1472
1473# Generates a proper clear() method depending whether we have
1474# dynamic members of not
1475sub generateClear {
1476    my($index)=@_;
1477    my($string);
1478
1479    if(defined($structs[$index]->dynamic)) {
1480        # delete [] the dynamic memory!
1481        foreach (@{$structs[$index]->items}) {
1482            if(defined($_->len)) {
1483                $string .= "    delete [] " . $_->name . ";\n";
1484            }
1485        }
1486        $string .= "    clearInternal();\n";
1487    }
1488    else {
1489        $string=generateClearInternal($index);
1490    }
1491    return $string;
1492}
1493
1494# Generates the code to dump a structure
1495sub generateDump {
1496    my($index, $tmp) = @_;
1497    my($string);
1498
1499    $string .= "    wvlog << \"Dumping " . $structs[$index]->name . ":\" << std::endl;\n";
1500    $string .= "    wvlog << toString().c_str() << std::endl;\n";
1501    $string .= "    wvlog << \"\\nDumping " . $structs[$index]->name . " done.\" << std::endl;\n";
1502    return $string;
1503}
1504
1505# Generates the code to convert a structure to a string
1506sub generateToString {
1507    my($index, $tmp) = @_;
1508    my($string);
1509
1510    $string .= "    std::string s( \"" . $structs[$index]->name . ":\" );\n";
1511    foreach (@{$structs[$index]->items}) {
1512        # Array?
1513        if($_->type =~ m/(.*)\[(.*)\]/) {
1514	    #print "Array: " . $_->name . " -- type: " . $_->type . "\n";
1515	    #print "   1: " . $1 . ", 2: " . $2 . "\n";
1516	    if($2 eq "") {
1517	        #print "    empty! -> warning\n";
1518	        $string .= "    // Attention: I don't know how to turn " . $_->name . " - " . $_->type . " to a string\n";
1519		$string .= "#ifdef __GNUC__\n";
1520		$string .= "#warning \"Couldn't generate toString code for " . $structs[$index]->name . "::" . $_->name . "\"\n";
1521		$string .= "#endif\n";
1522	    }
1523	    else {
1524	        $string .= "    for(int _i=0; _i<(" . $2 . "); ++_i) {\n";
1525		$string .= "    " . variableToString($_->name . "[_i]", $1);
1526		$string .= "    }\n";
1527	    }
1528	}
1529	else {
1530	    $string .= variableToString($_->name, $_->type);
1531	}
1532    }
1533    $string .= "    s += \"\\n" . $structs[$index]->name . " Done.\";\n";
1534    $string .= "    return s;\n";
1535    return $string;
1536}
1537
1538# Generates the code to initialize all fields
1539sub generateClearInternal {
1540    my($index, $tmp) = @_;
1541    my($string);
1542
1543    foreach (@{$structs[$index]->items}) {
1544        if($_->type =~ m/.*\[(.*)\]/) {
1545            $tmp=$1;
1546            # fine, just a pointer
1547            if($tmp eq "") {
1548                $string .= "    " . $_->name . "=0;\n";
1549                next;
1550            }
1551            # okay, also a pointer
1552            if($_->type !~ m/.*\[(\d+)\]/) {
1553                $string .= "    " . $_->name . "=0;\n";
1554            }
1555            else {
1556                $string .= "    for(int _i=0; _i<(" . $tmp . "); ++_i)\n";
1557                $_->type =~ m/(.*)\[.*\]/;
1558                $string .= "        " . $_->name . "[_i]" . initValue($_, $1) . ";\n";
1559            }
1560        }
1561        else {
1562            $string .= "    " . $_->name . initValue($_, $_->type) . ";\n";
1563        }
1564    }
1565    return $string;
1566}
1567
1568sub initValue {
1569    my($item, $type)=@_;
1570    my($string);
1571
1572    if(defined($item->initial)) {
1573        $string = "=" . $item->initial;
1574    }
1575    else {
1576        if($type =~ m/^[US]\d+$/ || $type eq "FC" || $type eq "XCHAR") {
1577            $string = "=0";
1578        }
1579        elsif(knownType($type)) {
1580            #print "Known: " . $type . "\n";
1581            $string = ".clear()";
1582        }
1583	elsif($type =~ m/std::vector/ ) {
1584	    $string = ".clear()";
1585	}
1586        else {
1587            print "Error: Can't initialize " . $item->name . ", " . $type . "\n";
1588            $string="";  # initialize
1589        }
1590    }
1591    return $string;
1592}
1593
1594# Generates some testcases
1595sub generateTest {
1596    my($tmp);
1597
1598    print "Generating the testcases...\n";
1599    $tmp=lc($namespace);
1600    open(TEST, ">$tmp" . "_test.cpp") or die "Couldn't open the file for writing: " . $!;
1601
1602    print TEST "// This file contains generated testing code. We do some basic tests\n";
1603    print TEST "// like writing and reading from/to streams. Of course these tests are\n";
1604    print TEST "// neither complete nor very advanced, so watch out :)\n\n";
1605    print TEST "#include <" . $tmp . "_generated.h>\n";
1606    print TEST "#include <olestream.h>\n";
1607    print TEST "#include <iostream>\n";
1608    print TEST "#include <stdlib.h>   // rand(), srand()\n\n";
1609    print TEST "#include <time.h>   // time()\n\n";
1610
1611    print TEST "using namespace wvWare;\n";
1612    print TEST "using namespace " . $namespace .";\n\n";
1613
1614    print TEST "int main(int, char**) {\n\n";
1615    print TEST "    // First we have to create some infrastructure\n";
1616    print TEST "    system(\"rm " . $tmp . "_test.doc &> /dev/null\");\n";
1617    print TEST "    OLEStorage storage(\"" . $tmp . "_test.doc\");\n";
1618    print TEST "    if(!storage.open(OLEStorage::WriteOnly)) {\n";
1619    print TEST "        std::cout << \"Error: Couldn't open the storage!\" << std::endl;\n";
1620    print TEST "        ::exit(1);\n";
1621    print TEST "    }\n\n";
1622    print TEST "    OLEStreamWriter *writer=storage.createStreamWriter(\"TestStream\");\n";
1623    print TEST "    if(!writer || !writer->isValid()) {\n";
1624    print TEST "        std::cout << \"Error: Couldn't open a stream for writing!\" << std::endl;\n";
1625    print TEST "        ::exit(1);\n";
1626    print TEST "    }\n\n";
1627    print TEST "    // Initialize the random number generator\n";
1628    print TEST "    srand( time( 0 ) );\n\n";
1629
1630    print TEST "    // Some \"global\" variables...\n";
1631    print TEST "    int *ptr=0;  // used to \"initialize\" the structs\n";
1632    print TEST "    int tmp;\n";
1633
1634    print TEST "    std::cout << \"Testing the $namespace structures...\" << std::endl;\n";
1635
1636    print TEST generateTestStructures();
1637
1638    print TEST "    std::cout << \"Done.\" << std::endl;\n";
1639    print TEST "    // Clean up\n";
1640    print TEST "    storage.close();\n";
1641    print TEST "}\n";
1642
1643    close(TEST) or die $!;
1644    print "Done.\n";
1645}
1646
1647# Generates all the tests for every single structure
1648sub generateTestStructures {
1649    my($string, $index, $n);
1650
1651    # writing
1652    for($index=0; $index<=$#structs; $index++) {
1653        if($structs[$index]->hidden ||
1654	   $structs[$index]->name eq "DPCALLOUT" || # DPCALLOUT depends on DPPOLY -> dynamic
1655	   $structs[$index]->name eq "PAP" || # PAP uses std::vector<Foo>
1656	   $structs[$index]->name eq "SEP" || # SEP uses std::vector<Foo>
1657	   $structs[$index]->name eq "TAP") { # TAP uses std::vector<Foo>
1658            next;
1659        }
1660        $n=$structs[$index]->name;
1661        if($structs[$index]->dynamic) {
1662            $string .= "    std::cout << \"Testing writing for $n:\" << std::endl;\n";
1663            $string .= "    std::cout << \"  Sorry, testing dynamic structures isn't implemented,\"\n";
1664            $string .= "              << \" yet.\" << std::endl;\n";
1665        }
1666        else {
1667            $string .= "    // Begin of writing test for $n\n";
1668            $string .= "    std::cout << \"Testing writing for $n: \";\n";
1669            $string .= generateWritingTest($index);
1670            $string .= "    // End of writing test for $n\n\n";
1671        }
1672    }
1673
1674    # mess with the streams...
1675    $string .= "\n    // Okay, close the stream writer and open it for reading...\n";
1676    $string .= "    int position=writer->tell();  // store the position for a check\n";
1677    $string .= "    delete writer;\n";
1678    $string .= "    storage.close();\n";
1679    $string .= "    if(!storage.open(OLEStorage::ReadOnly)) {\n";
1680    $string .= "        std::cout << \"Error: Couldn't open the storage!\" << std::endl;\n";
1681    $string .= "        ::exit(1);\n";
1682    $string .= "    }\n";
1683    $string .= "    OLEStreamReader *reader=storage.createStreamReader(\"TestStream\");\n";
1684    $string .= "    if(!reader || !reader->isValid()) {\n";
1685    $string .= "        std::cout << \"Error: Couldn't open a stream for reading!\" << std::endl;\n";
1686    $string .= "        ::exit(1);\n";
1687    $string .= "    }\n\n";
1688
1689    # reading
1690    for($index=0; $index<=$#structs; $index++) {
1691        if($structs[$index]->hidden ||
1692	   $structs[$index]->name eq "DPCALLOUT" || # DPCALLOUT depends on DPPOLY -> dynamic
1693	   $structs[$index]->name eq "PAP" || # PAP uses std::vector<Foo>
1694	   $structs[$index]->name eq "SEP" || # SEP uses std::vector<Foo>
1695	   $structs[$index]->name eq "TAP") { # TAP uses std::vector<Foo>
1696            next;
1697        }
1698        $n=$structs[$index]->name;
1699        if($structs[$index]->dynamic) {
1700            $string .= "    std::cout << \"Testing reading for $n:\" << std::endl;\n";
1701            $string .= "    std::cout << \"  Sorry, testing dynamic structures isn't implemented,\"\n";
1702            $string .= "              << \" yet.\" << std::endl;\n";
1703        }
1704        else {
1705            $string .= "    // Begin of reading test for $n\n";
1706            $string .= "    std::cout << \"Testing reading for $n: \";\n";
1707            $string .= generateReadingTest($index);
1708            $string .= "    // End of reading test for $n\n\n";
1709        }
1710    }
1711
1712    # check the position in the stream
1713    $string .= "\n    if(position!=reader->tell())\n";
1714    $string .= "        std::cout << \"Error: Different amount of bytes read/written!\" << std::endl;\n";
1715    $string .= "    delete reader;\n\n";
1716    return $string;
1717}
1718
1719# Generates some code to initialize a struct and write it out.
1720sub generateWritingTest {
1721    my($index)=@_;
1722    my($string, $n, $var);
1723
1724    $n=$structs[$index]->name;
1725    $var=lc($n) . "1";
1726    $string .= "    $n " . $var . ";\n";
1727    $string .= "    // Initilaize the struct with random data\n";
1728    $string .= "    tmp=sizeof($n)/sizeof(int);\n";
1729    $string .= "    ptr=reinterpret_cast<int*>( &" . $var . " );\n";
1730    $string .= "    for(int _i=0; _i<tmp; ++_i)\n";
1731    $string .= "        *ptr++=rand();\n";
1732    $string .= "    *ptr |= rand() & (0x00ffffff >> (((sizeof(int)-1)-(sizeof($n) % sizeof(int)))*8));  // yay! :)\n";
1733    $string .= "    // and write it out...\n";
1734    $string .= "    if($var.write(writer, false))\n";
1735    $string .= "        std::cout << \"Passed.\" << std::endl;\n";
1736    $string .= "    else\n";
1737    $string .= "        std::cout << \"Failed.\" << std::endl;\n";
1738    if(defined($structs[$index]->dumpCode)) {
1739        $string .= "    " . $var . ".dump();\n";
1740    }
1741    return $string;
1742}
1743
1744sub generateReadingTest {
1745    my($index)=@_;
1746    my($string, $n, $var2, $var3);
1747
1748    $n=$structs[$index]->name;
1749    $var2=lc($n) . "2";
1750    $string .= "    $n " . $var2 . ";\n";
1751    $string .= "    // Read the data from the stream\n";
1752    $string .= "    if(!$var2.read(reader, false))\n";
1753    $string .= "        std::cout << \"Failed. \" << std::endl;\n";
1754    $string .= "    // Test the copy CTOR\n";
1755    $var3=lc($n) . "3";
1756    $string .= "    $n " . $var3 . "($var2);\n";
1757    $string .= "    if(" . lc($n) . "1==$var2 && $var2==$var3)\n";
1758    $string .= "        std::cout << \"Passed.\" << std::endl;\n";
1759    $string .= "    else\n";
1760    $string .= "        std::cout << \"Failed.\" << std::endl;\n";
1761
1762    return $string;
1763}
1764
1765# Reads the HTML file and converts the "interesting" tags
1766# to uppercase. It also cuts of areas we're not interested in
1767# from the begin and the end of the file.
1768sub main {
1769    # A flag which tells us what part of the HTML to ignore
1770    my $ignore=1;
1771
1772    open(INPUT, "<$ARGV[0]") or die $!;
1773    $namespace=$ARGV[1];
1774
1775    while(<INPUT>) {
1776
1777        # Detection of start for Word9x
1778        if(m,^Structure Definitions\</h[12]\>$,) {
1779            $ignore=0;
1780        }
1781        # Detection of end for Word97
1782        elsif(m,^Appendix A - Reading a Macintosh PICT Graphic\</h2\>$,) {
1783            $ignore=1;
1784        }
1785        # Detection of end for Word95
1786        elsif(m,^Appendix A - Changes from version 1\.x to 2\.0\</h1\>$,) {
1787            $ignore=1;
1788        }
1789
1790        if(!$ignore) {
1791            chomp;
1792            # convert the important tags we use to uppercase on the fly
1793            s,\<tr\>,\<TR\>,;
1794            s,\</tr\>,\</TR\>,;
1795            s,\<td\>,\<TD\>,;
1796            s,\</td\>,\</TD\>,;
1797            s,\<table ,\<TABLE ,;
1798            s,\</table\>,\</TABLE\>,;
1799            s,\<br\>,\<BR\>,;
1800            s,\<h3\>,\<H3\>,;
1801            s,\</h3\>,\</H3\>,;
1802            # get rid of that ugly &nbsp; thingies
1803            s/&nbsp;//g;
1804
1805            push(@document, $_);
1806        }
1807    }
1808    close(INPUT) or die $!;
1809    # print "Size of the array: ", $#document+1, "\n";
1810
1811    parseStructures();    # parse the document
1812    cleanStructures();    # get rid of stuff we don't want to use
1813    hoistStructures();    # resolve forward references
1814    selectShared();       # select the structures we wanted to share
1815    selectDumped();       # select the structures which should contain a dump() method
1816
1817    generateHeader();     # generate the header file
1818    generateImplementation(); # generate the source
1819    generateTest();       # generate the sythetic test cases
1820}
1821
1822# We start execution here
1823if($#ARGV != 1) {
1824    print "Script to generate C++ code to read Word structures from the HTML\n";
1825    print "documentation. Usage: perl generate.pl input_file.html Word9x\n";
1826    exit(1);
1827}
1828
1829main();
1830