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 thingies 1803 s/ //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