tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

convert_wiki.pl (20532B)


      1 #!/usr/bin/perl
      2 #
      3 #  convert_wiki.pl - Transform an old-style wiki into the new format
      4 #
      5 #  This script assumes that a wiki has testable statement entries
      6 #  with varying lemgth lines. Those lines will be converted into
      7 #  the format described by the specification at
      8 #  https://spec-ops.github.io/atta-api/index.html
      9 #
     10 #  usage: convert_wiki.pl -f file | -w wiki_title -o outFile
     11 
     12 use strict;
     13 
     14 use IO::String ;
     15 use JSON ;
     16 use MediaWiki::API ;
     17 use Getopt::Long;
     18 
     19 my @apiNames = qw(UIA MSAA ATK IAccessible2 AXAPI);
     20 
     21 # dir is determined based upon the short name of the spec and is defined
     22 # by the input or on the command line
     23 
     24 my $file = undef ;
     25 my $spec = undef ;
     26 my $wiki_title = undef ;
     27 my $dir = undef;
     28 my $outFile = undef;
     29 
     30 my $result = GetOptions(
     31    "f|file=s"   => \$file,
     32    "w|wiki=s"   => \$wiki_title,
     33    "s|spec=s"   => \$spec,
     34    "o|output=s"   => \$outFile);
     35 
     36 my $wiki_config = {
     37  "api_url" => "https://www.w3.org/wiki/api.php"
     38 };
     39 
     40 my %specs = (
     41    "aria11" => {
     42      title => "ARIA_1.1_Testable_Statements",
     43      specURL => "https://www.w3.org/TR/wai-aria11"
     44    },
     45    "svg" => {
     46      title => "SVG_Accessibility/Testing/Test_Assertions_with_Tables_for_ATTA",
     47      specURL => "https://www.w3.org/TR/svg-aam-1.0/"
     48    }
     49 );
     50 
     51 my $io ;
     52 our $theSpecURL = "";
     53 
     54 if ($spec) {
     55  $wiki_title = $specs{$spec}->{title};
     56  $theSpecURL = $specs{$spec}->{specURL};
     57 }
     58 
     59 if ($wiki_title) {
     60  my $MW = MediaWiki::API->new( $wiki_config );
     61  my $page = $MW->get_page( { title => $wiki_title } );
     62  my $theContent = $page->{'*'};
     63  $io = IO::String->new($theContent);
     64 } elsif ($file) {
     65  open($io, "<", $file) || die("Failed to open $file: " . $@);
     66 } else {
     67  usage() ;
     68 }
     69 
     70 my $outH ;
     71 if (defined $outFile) {
     72  open($outH, ">", $outFile) || die("Failed to create file $outFile: $@");
     73 } else {
     74  $outH = new IO::Handle;
     75  $outH->fdopen(fileno(STDOUT), "w");
     76 }
     77 
     78 
     79 # Now let's walk through the content and spit it back out
     80 # transformed
     81 #
     82 
     83 # iterate over the content
     84 
     85 # my $io ;
     86 # open($io, "<", "raw") ;
     87 
     88 my $state = 0;   # between items
     89 my $theCode = "";
     90 my $theAttributes = {};
     91 my $theAsserts = {} ;
     92 my $theAssertCount = 0;
     93 my $theAPI = "";
     94 my $typeRows = 0;
     95 my $theType = "";
     96 my $theName = "";
     97 my $theRef = "";
     98 
     99 my $before = "" ;
    100 my $after = "" ;
    101 
    102 my @errors = () ;
    103 my $linecount = 0;
    104 
    105 while (<$io>) {
    106  $linecount++;
    107  # look for state
    108  if ($state == 0) {
    109    if (scalar(keys(%$theAsserts))) {
    110      # we were in an item; dump it
    111      print $outH  dump_table($theAsserts) ;
    112      $theAsserts = {};
    113    }
    114    print $outH $_;
    115  }
    116  if (m/^\{\|/) {
    117    # table started
    118    $state = 4;
    119    $theAPI = "";
    120  }
    121  if ($state == 4) {
    122    if (m/^\|-/) {
    123      if ($theAPI
    124          && exists($theAsserts->{$theAPI}->[$theAssertCount])
    125          && scalar(@{$theAsserts->{$theAPI}->[$theAssertCount]})) {
    126        $theAssertCount++;
    127      }
    128      # start of a table row
    129      if ($theType ne "" && $typeRows) {
    130        # we are still processing items for a type
    131        $typeRows--;
    132        # populate the first cell
    133        $theAsserts->{$theAPI}->[$theAssertCount] = [ $theType ] ;
    134      } else {
    135        $theType = "";
    136      }
    137    } elsif (m/^\|\}/) {
    138      # ran out of table
    139      $state = 0;
    140    } elsif (m/^\|rowspan="*([0-9])"*\|(.*)$/) {
    141      # someone put a rowspan in here..  is ht an API?
    142      my $rows = $1;
    143      my $theString = $2;
    144      $theString =~ s/ +$//;
    145      $theString =~ s/^ +//;
    146      $theString = "IAccessible2" if ($theString eq "IA2") ;
    147      if (grep { $_ eq $theString } @apiNames) {
    148        $theAssertCount = 0;
    149        # this is a new API section
    150        $theAPI = $theString ;
    151        $theAsserts->{$theAPI} = [ [] ] ;
    152        $theType = "";
    153      } else {
    154        # nope, this is a multi-row type
    155        $theType = $theString;
    156        $typeRows = $rows;
    157        $typeRows--;
    158        # populate the first cell
    159        if ($theAPI
    160            && exists($theAsserts->{$theAPI}->[$theAssertCount])
    161            && scalar(@{$theAsserts->{$theAPI}->[$theAssertCount]})) {
    162          $theAssertCount++;
    163        }
    164        $theAsserts->{$theAPI}->[$theAssertCount] = [ $theType ] ;
    165      }
    166    } elsif (m/^\|note/) {
    167      # there is a note in the table...  throw it out
    168      # and the next line too
    169      my $l = <$io>;
    170    } elsif (m/^\|(MSAA|UIA|IA2|IAccessible2|ATK|AXAPI) *$/) {
    171      # they FORGOT a rowspan on an API.  That means there is only 1
    172      # row
    173      my $theString = $1;
    174      $theString =~ s/ +$//;
    175      $theString =~ s/^ +//;
    176      $theString = "IAccessible2" if ($theString eq "IA2") ;
    177      if (grep { $_ eq $theString } @apiNames) {
    178        $theAssertCount = 0;
    179        # this is a new API section
    180        $theAPI = $theString ;
    181        $theAsserts->{$theAPI} = [ [] ] ;
    182        $theType = "";
    183      } else {
    184        push(@errors, "Bad API Name at $linecount: $theString");
    185      }
    186    } elsif (m/^\|(.*)$/) {
    187      my $item = $1;
    188      $item =~ s/^ *//;
    189      $item =~ s/ *$//;
    190      $item =~ s/^['"]//;
    191      $item =~ s/['"]$//;
    192      # add into the data structure for the API
    193      if (!exists $theAsserts->{$theAPI}->[$theAssertCount]) {
    194        $theAsserts->{$theAPI}->[$theAssertCount] = [ $item ] ;
    195      } else {
    196        push(@{$theAsserts->{$theAPI}->[$theAssertCount]}, $item);
    197      }
    198    }
    199    next;
    200  }
    201 };
    202 
    203 if ($state == 0) {
    204  if (scalar(keys(%$theAsserts))) {
    205    # we were in an item; dump it
    206    print $outH  dump_table($theAsserts) ;
    207  }
    208 }
    209 
    210 if (@errors) {
    211  print "There were the following errors:\n";
    212  foreach my $err (@errors) {
    213    print $err . "\n";
    214  }
    215 }
    216 
    217 exit 0;
    218 
    219 
    220 sub dump_table() {
    221  my $asserts = shift;
    222 
    223  if (!scalar(keys(%$asserts)) )  {
    224    # no actual assertions
    225    return "";
    226  }
    227 
    228  my $output = "" ;
    229 
    230  my @keywords = qw(property result event);
    231 
    232  foreach my $API (sort(keys(%$asserts))) {
    233    # looking at each API in turn
    234    my $ref = $asserts->{$API};
    235    my $rowcount = scalar(@$ref) ;
    236    # $output .= "|-\n|rowspan=$count|$API\n" ;
    237    # now we are in the assertions; special case each API
    238    my @conditions = @$ref;
    239    for (my $i = 0; $i < scalar(@conditions); $i++) {
    240      my (@new, @additional) ;
    241      if ($i) {
    242        $output .= "|-\n";
    243      }
    244      if ($API eq "ATK") {
    245        my $start = 0;
    246        my $assert = "is";
    247        if ($conditions[$i]->[0] =~ m/^NOT/) {
    248          $start = 1;
    249          $assert = "isNot";
    250        }
    251 
    252        if ($conditions[$i]->[$start] =~ m/^ROLE_/) {
    253          $new[0] = "property";
    254          $new[1] = "role";
    255          $new[2] = $assert;
    256          $new[3] = $conditions[$i]->[$start];
    257        } elsif ($conditions[$i]->[$start] =~ m/^xml-roles/) {
    258          $new[0] = "property";
    259          $new[1] = "role";
    260          $new[2] = $assert;
    261          $new[3] = $conditions[$i]->[$start+1];
    262        } elsif ($conditions[$i]->[$start] =~ m/^description/) {
    263          my $id = $conditions[$i]->[$start+1];
    264          $new[0] = "property";
    265          $new[1] = "description";
    266          $new[2] = $assert;
    267          $new[3] = $id;
    268          push(@{$additional[0]}, ("relation", "RELATION_DESCRIBED_BY", $assert, $id));
    269          push(@{$additional[1]}, ("relation", "RELATION_DESCRIPTION_FOR", $assert, "test"));
    270        } elsif ($conditions[$i]->[$start] =~ m/not in accessibility tree/i) {
    271          @new = qw(property accessible exists false);
    272        } elsif ($conditions[$i]->[$start] =~ m/^RELATION/) {
    273          $new[0] = "relation";
    274          $new[1] = $conditions[$i]->[$start];
    275          $new[2] = $assert;
    276          $new[3] = $conditions[$i]->[$start+1];
    277        } elsif ($conditions[$i]->[$start] =~ m/(.*) interface/i) {
    278          $new[0] = "property";
    279          $new[1] = "interfaces";
    280          $new[3] = $1;
    281          if ($conditions[$i]->[$start+1] ne '<shown>'
    282            && $conditions[$i]->[$start+1] !~ m/true/i ) {
    283            $assert = "doesNotContain";
    284          } else {
    285            $assert = "contains";
    286          }
    287          $new[2] = $assert;
    288        } elsif ($conditions[$i]->[$start] eq "object" || $conditions[$i]->[$start] eq "attribute" ) {
    289          $new[0] = "property";
    290          $new[1] = "objectAttributes";
    291          my $val = $conditions[$i]->[2];
    292          $val =~ s/"//g;
    293          $new[3] = $conditions[$i]->[1] . ":" . $val;
    294          if ($conditions[$i]->[1] eq "not exposed"
    295            || $conditions[$i]->[2] eq "false") {
    296            $new[2] = "doesNotContain";
    297          } else {
    298            $new[2] = "contains";
    299          }
    300        } elsif ($conditions[$i]->[$start] =~ m/^STATE_/) {
    301          $new[0] = "property";
    302          $new[1] = "states";
    303          $new[3] = $conditions[$i]->[$start];
    304          if ($assert eq "true") {
    305            $new[2] = "contains";
    306          } else {
    307            $new[2] = "doesNotContain";
    308          }
    309        } elsif ($conditions[$i]->[$start] =~ m/^object attribute (.*)/) {
    310          my $name = $1;
    311          $new[0] = "property";
    312          $new[0] = "objectAttributes";
    313          my $val = $conditions[$i]->[1];
    314          $val =~ s/"//g;
    315          if ($val eq "not exposed" || $val eq "not mapped") {
    316            $new[3] = $name;
    317            $new[2] = "doesNotContain";
    318          } else {
    319            $new[3] = $name . ":" . $val;
    320            $new[2] = "contains";
    321          }
    322        } elsif ($conditions[$i]->[$start] =~ m/^name/) {
    323          my $name = $conditions[$i]->[1];
    324          my $cond = "is" ;
    325          if ($name eq "<empty>" ) {
    326            $cond = "empty";
    327            $name = "true"
    328          } elsif ($name eq "<not empty>") {
    329            $cond = "empty";
    330            $name = "false";
    331          }
    332          $new[0] = "property";
    333          $new[1] = "name";
    334          $new[2] = $cond;
    335          $new[3] = $name;
    336        } else {
    337          @new = @{$conditions[$i]};
    338          if ($conditions[$i]->[2] eq '<shown>') {
    339            $new[2] = "contains";
    340          }
    341        }
    342        $conditions[$i] = \@new;
    343      } elsif ($API eq "UIA") {
    344        my $start = 0;
    345        my $assert = "is";
    346        if ($conditions[$i]->[$start] =~ m/\./) {
    347          my $val = $conditions[$i]->[$start+1];
    348          $val =~ s/"//g;
    349          $val =~ s/'//g;
    350          $new[0] = "result";
    351          $new[1] = $conditions[$i]->[$start];
    352          $new[2] = $assert;
    353          $new[3] = $conditions[$i]->[$start+1];
    354        } elsif ($conditions[$i]->[$start] =~ m/not in accessibility tree/i) {
    355          @new = qw(property accessible exists false);
    356        } elsif ($conditions[$i]->[$start] =~ m/^(AriaProperties|Toggle|ExpandCollapse)/) {
    357          my $name = $conditions[$i]->[1];
    358          $new[0] = "property";
    359          $new[1] = $1;
    360          my $val = $conditions[$i]->[2];
    361          $val =~ s/"//g;
    362          if ($val eq "not exposed" || $val eq "not mapped") {
    363            $new[3] = $name;
    364            $new[2] = "doesNotContain";
    365          } else {
    366            $new[3] = $name . ":" . $val;
    367            $new[2] = "contains";
    368          }
    369        } elsif ($conditions[$i]->[$start] =~ m/^LabeledBy/i) {
    370          $new[0] = "property";
    371          $new[1] = $conditions[$i]->[$start];
    372          $new[2] = $assert;
    373          $new[3] = $conditions[$i]->[$start+1];
    374        } elsif ($conditions[$i]->[$start] =~ m/^Name/) {
    375          my $name = $conditions[$i]->[1];
    376          my $cond = "is" ;
    377          if ($name eq "<empty>" ) {
    378            $cond = "empty";
    379            $name = "true"
    380          } elsif ($name eq "<not empty>") {
    381            $cond = "empty";
    382            $name = "false";
    383          }
    384          $new[0] = "property";
    385          $new[1] = "Name";
    386          $new[2] = $cond;
    387          $new[3] = $name;
    388        } elsif ($conditions[$i]->[$start] =~ m/^TBD/) {
    389          $new[0] = "TBD";
    390          $new[1] = $new[2] = $new[3] = "";
    391        } else {
    392          if ($conditions[$i]->[1] ne '<shown>'
    393            && $conditions[$i]->[1] !~ m/true/i ) {
    394            $assert = "isNot";
    395          } else {
    396            $assert = "is";
    397          }
    398          $new[0] = "property";
    399          $new[1] = $conditions[$i]->[$start];
    400          $new[2] = $assert;
    401          $new[3] = $conditions[$i]->[$start+1];
    402        }
    403      } elsif ($API eq "MSAA") {
    404        my $start = 0;
    405        my $assert = "is";
    406        if ($conditions[$i]->[0] =~ m/^NOT/) {
    407          $start = 1;
    408          $assert = "isNot";
    409        }
    410 
    411        if ($conditions[$i]->[$start] =~ m/^role/) {
    412          $new[0] = "property";
    413          $new[1] = "role";
    414          $new[2] = $assert;
    415          $new[3] = $conditions[$i]->[$start+1];
    416        } elsif ($conditions[$i]->[$start] =~ m/^xml-roles/) {
    417          $new[0] = "property";
    418          $new[1] = "role";
    419          $new[2] = $assert;
    420          $new[3] = $conditions[$i]->[$start+1];
    421        } elsif ($conditions[$i]->[$start] =~ m/not in accessibility tree/i) {
    422          @new = qw(property accessible exists false);
    423        } elsif ($conditions[$i]->[$start] =~ m/^(accName|accDescription)/) {
    424          my $name = $conditions[$i]->[$start+1];
    425          my $cond = "is" ;
    426          if ($name eq "<empty>" ) {
    427            $cond = "empty";
    428            $name = "true"
    429          } elsif ($name eq "<not empty>") {
    430            $cond = "empty";
    431            $name = "false";
    432          }
    433          $new[0] = "property";
    434          $new[1] = $conditions[$i]->[$start];
    435          $new[2] = $cond;
    436          $new[3] = $name;
    437        } elsif ($conditions[$i]->[$start] =~ m/^ROLE_/) {
    438          $new[0] = "property";
    439          $new[1] = "role";
    440          $new[2] = $assert;
    441          $new[3] = $conditions[$i]->[$start];
    442        } elsif ($conditions[$i]->[$start] =~ m/^(STATE_.*) *([^ ]*)/) {
    443          $new[0] = "property";
    444          $new[1] = "states";
    445          $new[3] = $1;
    446          if ($2 && $2 eq "cleared") {
    447            print "MATCHED $1, $2\n";
    448            $new[2] = "doesNotContain";
    449          } else {
    450            $new[2] = "contains";
    451          }
    452        } elsif ($conditions[$i]->[$start] =~ m/^TBD/) {
    453          $new[0] = "TBD";
    454          $new[1] = $new[2] = $new[3] = "";
    455        }
    456      } elsif ($API eq "IAccessible2") {
    457        my $start = 0;
    458        my $assert = "is";
    459        if ($conditions[$i]->[0] =~ m/^NOT/) {
    460          $start = 1;
    461          $assert = "isNot";
    462        }
    463        if ($conditions[$i]->[$start] =~ m/^IA2_ROLE_/) {
    464          $new[0] = "property";
    465          $new[1] = "role";
    466          $new[2] = $assert;
    467          $new[3] = $conditions[$i]->[$start];
    468        } elsif ($conditions[$i]->[$start] =~ m/not in accessibility tree/i) {
    469          @new = qw(property accessible exists false);
    470        } elsif ($conditions[$i]->[$start] =~ m/^IA2_RELATION_/) {
    471          $new[0] = "relation";
    472          $new[1] = $conditions[$i]->[$start];
    473          $new[2] = $assert;
    474          $new[3] = $conditions[$i]->[$start+1];
    475        } elsif ($conditions[$i]->[$start] =~ m/^IA2_STATE_/) {
    476          $new[0] = "property";
    477          $new[1] = "states";
    478          $new[3] = $conditions[$i]->[$start];
    479          if ($assert eq "true") {
    480            $new[2] = "contains";
    481          } else {
    482            $new[2] = "doesNotContain";
    483          }
    484        } elsif ($conditions[$i]->[$start] =~ m/^IA2_/) {
    485          $new[0] = "property";
    486          $new[1] = "states";
    487          $new[3] = $conditions[$i]->[$start];
    488          if ($assert eq "true") {
    489            $new[2] = "contains";
    490          } else {
    491            $new[2] = "doesNotContain";
    492          }
    493        } elsif ($conditions[$i]->[$start] =~ m/(IAccessibleTable2)/i) {
    494          $new[0] = "property";
    495          $new[1] = "interfaces";
    496          $new[3] = $1;
    497          if ($conditions[$i]->[$start+1] ne '<shown>'
    498            && $conditions[$i]->[$start+1] !~ m/true/i ) {
    499            $assert = "doesNotContain";
    500          } else {
    501            $assert = "contains";
    502          }
    503          $new[2] = $assert;
    504        } elsif ($conditions[$i]->[$start] =~ m/(.*) interface/i) {
    505          $new[0] = "property";
    506          $new[1] = "interfaces";
    507          $new[3] = $1;
    508          if ($conditions[$i]->[$start+1] ne '<shown>'
    509            && $conditions[$i]->[$start+1] !~ m/true/i ) {
    510            $assert = "doesNotContain";
    511          } else {
    512            $assert = "contains";
    513          }
    514          $new[2] = $assert;
    515        } elsif ($conditions[$i]->[$start] =~ m/(.*)\(\)/) {
    516          $new[0] = "result";
    517          $new[1] = $conditions[$i]->[$start];
    518          my $val = $conditions[$i]->[2];
    519          $val =~ s/"//g;
    520          $new[3] = $conditions[$i]->[1] . ":" . $val;
    521          if ($conditions[$i]->[1] eq "not exposed"
    522            || $conditions[$i]->[2] eq "false") {
    523            $new[2] = "doesNotContain";
    524          } else {
    525            $new[2] = "contains";
    526          }
    527        } elsif ($conditions[$i]->[$start] =~ m/(.*localizedExtendedRole)/) {
    528          $new[0] = "result";
    529          $new[1] = $conditions[$i]->[$start];
    530          my $val = $conditions[$i]->[2];
    531          $val =~ s/"//g;
    532          $new[3] = $conditions[$i]->[1] . ":" . $val;
    533          if ($conditions[$i]->[1] eq "not exposed"
    534            || $conditions[$i]->[2] eq "false") {
    535            $new[2] = "doesNotContain";
    536          } else {
    537            $new[2] = "contains";
    538          }
    539        } elsif ($conditions[$i]->[$start] eq "object" || $conditions[$i]->[$start] eq "attribute" ) {
    540          $new[0] = "property";
    541          $new[1] = "objectAttributes";
    542          my $val = $conditions[$i]->[2];
    543          $val =~ s/"//g;
    544          $new[3] = $conditions[$i]->[1] . ":" . $val;
    545          if ($conditions[$i]->[1] eq "not exposed"
    546            || $conditions[$i]->[2] eq "false") {
    547            $new[2] = "doesNotContain";
    548          } else {
    549            $new[2] = "contains";
    550          }
    551        } elsif ($conditions[$i]->[$start] =~ m/^object attribute (.*)/) {
    552          my $name = $1;
    553          $new[0] = "property";
    554          $new[1] = "objectAttributes";
    555          my $val = $conditions[$i]->[1];
    556          $val =~ s/"//g;
    557          if ($val eq "not exposed" || $val eq "not mapped") {
    558            $new[3] = $name;
    559            $new[2] = "doesNotContain";
    560          } else {
    561            $new[3] = $name . ":" . $val;
    562            $new[2] = "contains";
    563          }
    564        } else {
    565          @new = @{$conditions[$i]};
    566          if ($conditions[$i]->[2] eq '<shown>') {
    567            $new[2] = "contains";
    568          }
    569        }
    570        $conditions[$i] = \@new;
    571      } elsif ($API eq "AXAPI") {
    572        my $start = 0;
    573        my $assert = "is";
    574        if ($conditions[$i]->[0] =~ m/^NOT/) {
    575          $start = 1;
    576          $assert = "isNot";
    577        }
    578        if ($conditions[$i]->[$start] =~ m/^AXElementBusy/) {
    579          if ($conditions[$i]->[$start+1] =~ m/yes/i) {
    580            $new[3] = "true";
    581          } else {
    582            $new[3] = "false";
    583          }
    584          $new[0] = "property";
    585          $new[1] = $conditions[$i]->[$start];
    586          $new[2] = $assert;
    587        } elsif ($conditions[$i]->[$start] =~ m/not in accessibility tree/i) {
    588          @new = qw(property accessible exists false);
    589        } elsif ($conditions[$i]->[$start] =~ m/^AX/) {
    590          $new[0] = "property";
    591          $new[1] = $conditions[$i]->[$start];
    592          $new[2] = $assert;
    593          $new[3] = $conditions[$i]->[$start+1];
    594        } elsif ($conditions[$i]->[$start] =~ m/^TBD/) {
    595          $new[0] = "TBD";
    596          $new[1] = $new[2] = $new[3] = "";
    597        } else {
    598          if ($conditions[$i]->[1] ne '<shown>'
    599            && $conditions[$i]->[1] !~ m/true/i ) {
    600            $assert = "isNot";
    601          } else {
    602            $assert = "is";
    603          }
    604          $new[0] = "result";
    605          $new[1] = $conditions[$i]->[0];
    606          $new[2] = $assert;
    607          $new[3] = "true";
    608        }
    609      }
    610      if ($i == 0) {
    611        if (scalar(@additional)) {
    612          $rowcount += scalar(@additional);
    613        }
    614        $output .= "|-\n|rowspan=$rowcount|$API\n";
    615      }
    616      foreach my $row (@new) {
    617        $output .= "|$row\n";
    618      }
    619      if (scalar(@additional)) {
    620        foreach my $arow (@additional) {
    621          $output .= "|-\n" ;
    622          foreach my $aItem (@$arow) {
    623            $output .= "|$aItem\n";
    624          }
    625        }
    626      }
    627    }
    628  }
    629  $output .= "|}\n";
    630 
    631  return $output;
    632 }
    633 
    634 sub usage() {
    635  print STDERR q(usage: make_tests.pl -f file | -w wiki_title | -s spec [-n -v -d dir ]
    636 
    637  -s specname   - the name of a spec known to the system
    638  -w wiki_title - the TITLE of a wiki page with testable statements
    639  -f file       - the file from which to read
    640  -o outFile    - the file to fill with  the converted wiki; defaults to STDOUT
    641 
    642  -n            - do nothing
    643  -v            - be verbose
    644  );
    645  exit 1;
    646 }
    647 
    648 # vim: ts=2 sw=2 ai: