#!/usr/bin/perl

$EOK = 0;
$EINVAL = 1;

$ENV{PATH} = "/bin:/sbin";

select (STDOUT); $| = 1;

$kfile = "/unix";
$kmem = "/dev/kmem";
$dbxinp = "dbx.inp";
$dbxout = "dbx.out";
$dbxres = "dbx.res";
$dbxcmd = "/usr/bin/dbx";

&usage() if $#ARGV != 1;

$structfile = $ARGV[0];
$headerfile = $ARGV[1];

%exclude = ();
@include = ();

open (F, $structfile) || &bye($ESYSTEM, "Cannot open $structfile: $!\n");
while (<F>) {
  /^include\s+(\S+)/ && push(@include, $1);
  /^exclude\s+(\S+)/ && ($exclude{$1} = 1);
}
close(F);

open (F, ">$dbxinp") || &bye($ESYSTEM, "Cannot open $dbxinp: $!\n");
print F "set \$page=0\n";
print F "record output $dbxres\n";
foreach $i (@include) {
  print F "whatis $i\n";
}
print F "quit\n";
close(F);

unlink $dbxres;

print "Running dbx...\n";
open (L, ">$dbxout") || &bye($ESYSTEM, "Cannot open $dbxout: $!\n");
open (F, "$dbxcmd -c $dbxinp -k $kfile $kmem |") || &bye($ESYSTEM, "Cannot run $dbxcmd: $!\n");
while (<F>) {
  print L;
}
close(F);
close(L);

%headers = ();

open (F, "$dbxres") || &bye($ESYSTEM, "Cannot open $dbxres: $!\n");
while (<F>) {
  /struct (\S+) {/ && &struct($1);
}
close(F);

open (F, ">$headerfile") || &bye($ESYSTEM, "Cannot open $headerfile: $!\n");
foreach $i (keys %headers) {
  if ($exclude{$i}) {
    print F "/* excluding $i\n";
    print F "$headers{$i}";
    print F "*/\n\n";
  } else {
    print F "$headers{$i}\n";
  }
};
close (F);

sub struct {
  local $struct = $_[0];
  local $body = "struct $_[0] {\n";
  local $kcount = 1;
  local $sline = "";
  local $svalue = "";
  while (<F>) {
    if (/struct (\S+) {/) {
      chomp;
      $sline = $_;
      $svalue = &struct($1);
      $svalue =~ s/ .*} //;
      $sline =~ s/{/$svalue/;
      $body .= $sline;
      next;
    }
    /{/ && ($kcount++);
    /}/ && ($kcount--);
    if ($kcount == 0) {
      $body .= "};\n";
      $headers{$struct} = $body;
      return $_;
    }
    $body .= $_;
  }
}

exit $EOK;

sub usage {
  print STDERR "$_[0] \n" if $_[0];
  print STDERR "usage: $0 file header\n";
  exit $EINVAL;
}

sub bye {
  print STDERR $_[1];
  exit $_[0];
}

