#!/usr/bin/perl

$EHELPER_PATH = "./";

unless ((-d "./libname") and (-d "./libname/name")) {
    die "$0 must be run from the ./ehelper directory of the tools module tree";
}

if ($ARGV[0] =~ m/create/) {
    shift(@ARGV);
    ext_create(ext_name(shift(@ARGV)), @ARGV);
    exit(0);
}

if ($ARGV[0] =~ m/add2struct/) {
    shift (@ARGV);
    ext_add2struct(ext_name(shift(@ARGV)), @ARGV);
    exit(0);
}

if ($ARGV[0] =~ m/addop/) {
    ext_addop(ext_name($ARGV[1]), ext_name($ARGV[2]), $ARGV[3]);
    exit(0);
}

if ($ARGV[0] =~ m/addtarg/) {
    shift(@ARGV);
    $name = shift(@ARGV);
    $prefix = $targ = shift(@ARGV);
    $targ =~ s/.*\-//g;
    $prefix =~ s/\-.*//g or $prefix = 'display';
    ext_addtarg(ext_name($name), $prefix, ext_name($targ), @ARGV);
    exit(0);
}

if ($ARGV[0] =~ m/add2targstruct/) {
    shift (@ARGV);
    ext_add2targstruct(ext_name(shift(@ARGV)), ext_name(shift(@ARGV)), @ARGV);
    exit(0);
}

ext_usage();

sub ext_usage {
    print STDERR <<EOUSAGE ;
usage:
$0 create <extname> [<authname> [<copyright> [<authemail>]]] <prereqs>
$0 add2struct <extname> <type> <name> <ival> [<type> <name> <ival> ...]
$0 addop <extname> <opname> "<opargs>"
$0 addtarg <extname> <targname> [compact] <prereqs>
$0 add2targstruct <extname> <targname> <type> <name> <ival>

<prereqs> == [ (-i<header.h> | -l<lib>,<func>) ... ]
EOUSAGE
    exit(0);
}

sub ext_name {
    my ($origName) = shift;
    die "Need a name that's alphanumeric and starts with a letter."
	unless $origName =~ /^[A-Za-z]\w*$/;
    my ($Name) = $origName;
    substr($Name,0,1) =~ y/a-z/A-Z/;
    my ($NAME) = $origName;
    $NAME =~ y/a-z/A-Z/;
    my ($name) = $origName;
    $name =~ y/A-Z/a-z/;
    return($origName, $Name, $NAME, $name);
}

sub ext_pubinfo {
    my ($author, $copyright, $authmail) = @_;
    my($ddmmyy, $Authors, $AuthorEmails, 
       $AuthorsWithEmail, $CopyNotice, $Copyright, $CopyByWhoWhen);
    my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
	localtime(time);
    my $ctime = localtime(time);
    $ddmmyy = sprintf("%2.2d%2.2d%2.2d", $mday, $mon + 1, $year % 100);
    if (not ref($author)) {
	my(@authors) = split(",",$author);
	$Authors = join(" , ", @authors);
	$Authors =~ s/ , ([^,]+\Z)/ and $1/;
	my(@authmails) = split(",",$authmail);
	$AuthorEmails = join(" , ", @authormails);
	for $author (@authors) {
	    $AuthorsWithEmail .= "\t$author\t\t" . shift(@authmails) . "\n";
	}
	$ctime =~ s/[0-9]+:[0-9]+:[0-9]+ //;
	$CopyByWhoWhen = 
	    sprintf("Copyright (c) %s by: \n%s", $ctime, $AuthorsWithEmail);
	if (length($copyright)) {
	    if (-f "/usr/doc/copyright/$copyright") {
		open(COPYRIGHT, "</usr/doc/copyright/$copyright");
		$Copyright = join("", <COPYRIGHT>);
		close(COPYRIGHT);
	    }
	    elsif (-f "${EHELPER_PATH}/copyrights/$copyright") {
		open(COPYRIGHT, "<${EHELPER_PATH}/copyrights/$copyright");
		$Copyright = join("", <COPYRIGHT>);
		close(COPYRIGHT);
	    }
	    else {
		$Copyright = $copyright;
	    }
	    if (-f "${EHELPER_PATH}/copyrights/${copyright}.notice") {
		open(COPYRIGHT, "<${EHELPER_PATH}/copyrights/${copyright}.notice");
		$CopyNotice = join("", <COPYRIGHT>);
		close(COPYRIGHT);
	    }
	    else {
		$CopyNotice = $Copyright;
	    }
	}
	else {
	    open (COPYRIGHT, "<${EHELPER_PATH}/copyrights/none");
	    $Copyright = join("", <COPYRIGHT>);
	    close(COPYRIGHT);
	    $CopyNotice = $Copyright;
	    $Copyright .= $CopyByWhoWhen;
	}
    }
    else { # Kinda lazy -- doesn't adjust date 
	$copyfh = shift();
	$copyfile = join('', <$copyfh>);
	$copyfile =~ m/(Copyright.*?)\*\*\*\*/s;
	$CopyNotice = $1;
	$CopyByWhoWhen = "";
    }
    return($ddmmyy, $Authors, $AuthorEmails, 
	   $AuthorsWithEmail, $CopyNotice, $Copyright, $CopyByWhoWhen);
}


sub ext_create {
    my($origName, $Name, $NAME, $name, @rest) = @_;
    my(%reqlibs, $reqlibs, %reqheaders, $includes, @credits);
    my($arg);
    foreach $arg (@rest) {
	($reqlibs{$1} = 1), next if $arg =~ m/\-l(.+)/;
	($reqheaders{$1} = 1), next if $arg =~ m/\-h(.+)/;
	push(@credits, $arg);
    }
    $reqlibs = join(" -l", '', keys %reqlibs);
    $reqlibs =~ s/,[^\s]*//g;
    ($author, $copyright, $authmail) = @credits;
    my($dir, $newdir, $mode, $file, $newfile);
    my(%coredirs) = &ext_coredirs;
    die "Not overwriting existing file/directory!" if (-e "lib${name}");
    mkdir("lib${name}", 0775);
    foreach my $depth (1,2,3,4) {
  	while (($dir, $mode) = each(%coredirs)) {
	    $newdir = ext_translate_filename($origName, $Name, $NAME, $name, 
					     undef, undef, undef, undef, $dir);
	    mkdir($newdir, $mode) if ($newdir =~ s|/|/|g) == $depth;
	}
    }
    my(%corefiles) = &ext_corefiles;
    while (($file, $mode) = each(%corefiles)) {
	$newfile = ext_translate_filename($origName, $Name, $NAME, $name, 
					  undef, undef, undef, undef, $file);
	open (TEMPLATE, "<${EHELPER_PATH}/${file}");
	open (NEWFILE, ">$newfile");
	if ($newfile =~ m/ltmain\.sh/) {
	    print NEWFILE join('', <TEMPLATE>);
	}
	else {
	    ext_translate_template($origName, $Name, $NAME, $name, 
				   undef, undef, undef, undef,
				   ext_pubinfo($author, $copyright, $authmail),
				   \*TEMPLATE, \*NEWFILE);
	}
	close (TEMPLATE);
	close (NEWFILE);
	chmod $mode, $newfile;
    }
    if (0 + keys(%reqheaders)) {
	$fn = "./lib${name}/configure.in";
	$confin = ext_readin_or_die($fn);
	$confin =~ m:(dnl - Check for other headers\s+AC_CHECK_HEADERS\([^\)]+)\):m
	    or die ("$fn didn't parse as expected..\n");
	$confin = $` . "$1";
	$saverest = $';
	$confin .= " " . join(" ", keys(%reqheaders)) . ")" . $saverest;
	$confin =~ m:(dnl - Must have these extra headers to build.):m
	    or die ("$fn didn't parse as expected...\n");
	$confin = $` . "$1 ";
	$saverest = $';
	foreach $header (keys(%reqheaders)) {
	    $header =~ s:[\/\.]:_:g;
	    $checkheaders .= qq(
if test "\$ac_cv_header_$header" != "yes"; then
  extension_missing_part=yes
fi
);
	}
	$confin .= $checkheaders . $saverest;
	print "Adding check for required headers to $fn.\n";
	ext_writeout_or_die($confin, $fn);
    }
    if (0 + keys(%reqlibs)) {
	$fn = "./lib${name}/configure.in";
	$confin = ext_readin_or_die($fn);
	$confin =~ m:(dnl - Checks for library functions.):m
	    or die ("$fn didn't parse as expected.\n");
	$do_x = 0;
	$confin = $` . "$1";
	$saverest = $';
	foreach $lib (keys(%reqlibs)) {
	    if ($lib =~ m/(X11|Xext),/ and not $do_x) {
		foreach (keys (%reqlibs)) {
		    next if m/(X11|Xext),/;
		    $newreqlibs{$_} = 1;
		}
		$reqlibs = join(" -l", '', keys %newreqlibs);
		$reqlibs =~ s/,[^\s]*//g;
		$reqlibs .= ' $(X_LIBS) -lX11 -lXext $(X_EXTRA_LIBS)';
		$includes = ' $(X_CFLAGS)';
		$do_x = 1;
		next;
	    }
	    $checklibs .= "\n\nAC_CHECK_LIB($lib,foo=bar,extension_missing_part=yes)";
	}
	if ($do_x) {
	    $checklibs .= '

if test "x$no_x" = "xyes"; then
  extension_missing_part="yes"
fi
';
	}
	$confin .= "$checklibs" . $saverest;
	print "Adding checks for library functions to $fn.\n";
	ext_writeout_or_die($confin, $fn);
    }

    if (length($includes)) {
	$fn = "./lib${name}/${name}/Makefile.am";
	$makefileam = ext_readin_or_die($fn);
        $makefileam =~ m:(INCLUDES .*?)\n:m;
        $makefileam = $` . "$1 $includes\n" . $';
	print "Adding flags for libraries to $fn\n";
	ext_writeout_or_die($makefileam, $fn);
    }
    if (length($reqlibs)) {
	$fn = "./lib${name}/${name}/Makefile.am";
	$makefileam = ext_readin_or_die($fn);
        $makefileam =~ m:(_la_LIBADD = .*?)\n:m;
        $makefileam = $` . "$1 $reqlibs\n" . $';
	print "Adding flags for includes to $fn\n";
	ext_writeout_or_die($makefileam, $fn);
    }


}

sub ext_add2struct {
    my($origName, $Name, $NAME, $name, @vars) = @_;
    ext_usage if (@vars + 0) % 3;
    my($type, $vname, $ival, @vnames, %types, %ivals, $varlist, $initstr);
    while (@vars + 0) {
	$type = shift(@vars);
	$vname = shift(@vars);
	$ival = shift(@vars);
	push (@vnames, $vname); # preserves order 
	$types{$vname} = $type;
	$ivals{$vname} = $ival;
	$varlist .= "\n\t$type $vname;";
	$initstr .= "\n\t\tLIBGGI_${NAME}EXT(vis)->${vname} = ${ival};";
    }
    foreach $vname (@vnames) {
	die "Bad C variable name" unless
	    $vname =~ m/\A[A-Za-z]\w*\Z/;
    }

    $fn = "./lib${name}/include/ggi/internal/${name}.h";
    $inth = ext_readin_or_die($fn);
    $inth =~ m:/\*-\* Other structure members \*/:m 
	or die ("$fn didn't parse as expected\n");
    $inth = $` . "/*-* Other structure members */" . $varlist . $';
    print "Adding structure members in $fn\n";
    ext_writeout_or_die($inth,$fn);
    
    $fn = "./lib${name}/${name}/init.c";
    $initc = ext_readin_or_die($fn);
    $initc =~ m:/\*-\* Apply initial values \*/:m 
	or die ("$fn didn't parse as expected\n");
    $initc = $` . "/*-* Apply initial values */" . $initstr . $';
    print "Adding structure data init to $fn\n";
    ext_writeout_or_die($initc,$fn);
}

sub ext_addop {
    my($origName, $Name, $NAME, $name, $opName, $Op, $OP, $op, $opargs) = @_;
    die "Bad function name $opName" unless $opName =~ m/\A\w*\Z/;
    my $opstrip = $opargs . ",";
    $opargs = ", " . $opargs if length($opargs);
    # Cross yer fingers and hope it works -- strip the types out 
    @opstrip = $opstrip =~ m/([A-Za-z]\w*)\s*,/g;
    $opstrip = ", " . join (", ", @opstrip);

    $fn = "./lib${name}/include/ggi/internal/${name}.h";
    $inth = ext_readin_or_die($fn);
    $inth =~ m:(/\*-\* Data types for the API functions \*/):m 
	or die ("$fn didn't parse as expected.\n");
    $inth = $` . 
	"$1\ntypedef int (${origName}func_${opName})(ggi_visual *vis${opargs});" . $';
    $inth =~ m:(/\*-\* API function pointers \*/):m 
	or die ("$fn didn't parse as expected.\n");
    $inth = $` . "$1\n\t${origName}func_${opName} *${op};" . $';
    print "Adding operation to $fn.\n";
    ext_writeout_or_die($inth,$fn); 
    
    $fn = "./lib${name}/include/ggi/${name}.h";
    $nameh = ext_readin_or_die($fn);
    $nameh =~ m:(/\*-\* API functions\s+\*/):m
	or die ("$fn didn't parse as expected.\n");
    $nameh = $` . "$1\nint ${Name}${Op}(ggi_visual_t vis${opargs});" . $';
    print "Adding API function to $fn.\n";
    ext_writeout_or_die($nameh, $fn);
    
    $fn = "./lib${name}/${name}/init.c";
    $initc = ext_readin_or_die($fn);
    $initc =~ 
      m:(/\*-\* Reset all function pointers to dummyfunc \*/[^\{]+?\{):m 
	  or die ("$fn didn't parse as expected.\n");
    $initc = $` . "$1\n\tpriv->${op} = (void *) dummyfunc;" . $';
    print "Adding failops to $fn.\n";
    ext_writeout_or_die($initc, $fn);

    $fn = "./lib${name}/${name}/stubs.c";
    $stubsc = ext_readin_or_die($fn);
    $stubsc =~ m:(/\*-\* Stub function code. \*/):m 
	or die ("$fn didn't parse as expected.\n");
    $stubsc = $` . "$1
int ${Name}${Op}(ggi_visual_t vis${opargs})
\{       return LIBGGI_${NAME}EXT(vis)->${op}(vis${opstrip}); }
"  . $'; # '\{' is for emacs
    print "Adding stub function to $fn\n";
    ext_writeout_or_die($stubsc,$fn);

    $fn = "./lib${name}/demos/${name}test.c";
    $demo = ext_readin_or_die($fn);
    $demo =~ m:(/\*-\* Edit and uncomment the calls to test \*/):m
	or die ("$fn didn't parse as expected.\n");
    $demo = $` . "$1\n/* ${Name}${Op}(vis, ...);*/" . $';
    print "Adding function test to $fn\n";
    ext_writeout_or_die($demo,$fn);

    $fn = "lib${name}/${name}/EXPSYMS";
    open (OFILE, ">>$fn") or die("cannot append to $fn\n");
    print OFILE "\n${Name}${Op}";
    close (OFILE);

    # Get target names from configure.in
    $fn = "lib${name}/configure.in";
    $confin = ext_readin_or_die($fn);
    @add2targs = $confin =~ m/don't build the ([^\s]+) target\],/g;

    foreach $t (@add2targs) {
        my($origTarg, $Targ, $TARG, $targ) = ext_name($t);
	$imptxt = qq[
int ${NAME}_${Targ}_${opName}(ggi_visual_t vis${opargs}) {
\tprintf("Function ${NAME}_${Targ}_${opName} called.\\n");
\treturn(0);
}\n\n];
	$protos = "\n${origName}func_${opName} ${NAME}_${Targ}_${opName};";
	$hooks = "\n\tLIBGGI_${NAME}EXT(vis)->${op} = ${NAME}_${Targ}_${Op};";
	$funcs_exists = 1;
	open (IFILE, "<./lib${name}/display/${targ}/funcs.c") 
	    or ($funcs_exists = 0)
		or open (IFILE, "<./lib${name}/display/${targ}/init.c")
		    or die("cannot read ./lib${name}/display/${targ}/" .
			   ($funcs_exists ? "funcs.c\n" : "init.c\n"));
	$imp = join('', <IFILE>);
	close (IFILE);
	$imp =~ m:(/\*-\* API Implementation \*/):s
	    or die("./lib${name}/display/${targ}/" .
		   ($funcs_exists ? "funcs.c" : "init.c") .
		   " didn't parse as expected.");    
	$imp = $` . "$1\n$imptxt" . $';
	print "Adding function definition to ./lib${name}/display/${targ}/" . 
	    ($funcs_exists ? "funcs.c\n" : "init.c\n");
	open (OFILE, ">./lib${name}/display/${targ}/" . 
	      ($funcs_exists ? "funcs.c" : "init.c"))
	    or die("cannot write ./lib${name}/display/${targ}/" .
		   ($funcs_exists ? "funcs.c\n" : "init.c\n"));
	print OFILE $imp;
	close OFILE;
	
	$fn = "./lib${name}/display/${targ}/${targ}${name}.h";
	if (open(IFILE, "<$fn")) {
	    $targinth = join('', <IFILE>);
	    close(IFILE);
	    $targinth =~ m:(/\*-\* Function prototypes \*/):m
		or die ("$fn didn't parse as expected.\n");
	    $targinth = $` . "$1\n$protos" . $';
	    print "Adding prototype to $fn\n";
	    ext_writeout_or_die($targinth, $fn);
	}
	else {
	    print "Cannot read $fn ... assuming compact format target source.\n";
	}
	
	$fn = "./lib${name}/display/${targ}/init.c";
	$initc = ext_readin_or_die($fn);
	$initc =~ m:(/\*-\* Hook in target functions \*/):m
	    or die ("$fn didn't parse as expected.");
	$initc = $` . "$1\n$hooks" . $';
	print "Adding target function hook to $fn.\n";
	ext_writeout_or_die($initc, $fn);
        
    }
    
}

sub ext_addtarg {
    my($origName, $Name, $NAME, $name, 
       $prefix, $origTarg, $Targ, $TARG, $targ, @rest) = @_;
    my(%reqlibs, $reqlibs, %reqheaders, $includes, $type);
    my($arg);
    foreach $arg (@rest) {
	($reqlibs{$1} = 1), next if $arg =~ m/\-l(.+)/;
	($reqheaders{$1} = 1), next if $arg =~ m/\-h(.+)/;
	$type = "compact" if $arg eq "compact";
    }
    $reqlibs = join(" -l", '', keys %reqlibs);
    $reqlibs =~ s/,[^\s]*//g;
    my($mode, $file, $newfile);
    my($coredir) = "lib${name}/display/$targ";
    die "Not overwriting existing file/directory!" if -e $coredir;
    mkdir $coredir, 0775;
    print "Taking copyright from ./lib${name}/${name}/stubs.c, you may wish to edit.\n";
    my(%corefiles) = $type eq "compact" ? 
	&targ_compact_corefiles : &targ_full_corefiles;
    while (($file, $mode) = each(%corefiles)) {
	$newfile = ext_translate_filename($origName, $Name, $NAME, $name, 
					  $origTarg, $Targ, $TARG, $targ,
					  $file);
	open (TEMPLATE, "<${EHELPER_PATH}/${file}");
	open (NEWFILE, ">$newfile");
	open (COPYFROM, "<./lib${name}/${name}/stubs.c"); # copyright notice
	ext_translate_template($origName, $Name, $NAME, $name, 
			       $origTarg, $Targ, $TARG, $targ,
			       ext_pubinfo(*COPYFROM{IO}),
			       \*TEMPLATE, \*NEWFILE);
	close (COPYFROM);
	close (TEMPLATE);
	close (NEWFILE);
	chmod $mode, $newfile;
    }

    $fn = "./lib${name}/configure.in";
    $confin = ext_readin_or_die($fn);
    $confin =~ m/(dnl - Targets)/m or die ("$fn didn't parse as expected.\n");
    $confin = $` . "$1\nbuild_${targ}_target=\"auto\"" . $';
    $confin =~ m/(dnl - TARGETS:)/m or die ("$fn didn't parse as expected.\n");
    $confin = $` . "$1
AC_ARG_ENABLE(${targ},
\[  --disable-${targ}             don't build the ${origTarg} target],
build_${targ}_target=\$enableval)

" . $';   # '\[' is for emacs
    if ($targ =~ m/\A(x|xlib)\Z/) { # Assume they need X includes
	foreach (keys (%reqlibs)) {
	    next if m/(X11|Xext),/;
	    $newreqlibs{$_} = 1;
	}
	$reqlibs{"X11"} = 1; # flag to cause entry into next block
	$reqlibs = join(" -l", '', keys %newreqlibs);
	$reqlibs =~ s/,[^\s]*//g;
	$reqlibs .= ' $(X_LIBS) -lX11 -lXext $(X_EXTRA_LIBS)';
	$includes = ' $(X_CFLAGS)';
    }

    if (0 + keys(%reqlibs)) {
	$confin =~ m/(DISPLAYSUBDIRS="")/m
	    or warn ("$fn didn't parse as expected. (nonfatal)\n");
	$confin = $` . "$1\n";
	$saverest = $';
	$do_x = 0;
	foreach $lib (keys(%reqlibs)) {
	    ($do_x = 1), next if ($lib eq "X11" or $lib eq "Xext");
	    $lib =~ m/([^,]+),([^,]+)/;
	    $libname = $1;  $libfunc = $2;
	    $confin .= qq(
dnl Check for the function '$libfunc' in the '$libname' library.
dnl If not found we disable the "$targ" target.
AC_CHECK_LIB($lib, foo=bar, build_${targ}_target="no")
); 
       }
       if ($do_x) {
	   $confin .= qq[
dnl Use AC_PATH_XTRA if you want to build something that uses X
if test "x\$build_${targ}_target" != "xno"; then
  AC_PATH_XTRA
    if test "x\$no_x" = "xyes"; then
    build_${targ}_target="no"
  fi
fi
];
        }
        $confin .= $saverest;
    }

    if (0 + keys(%reqheaders)) {
	$confin =~ m:(dnl - Check for other headers\s+AC_CHECK_HEADERS\([^\)]+)\):m
	    or die ("$fn didn't parse as expected..\n");
	$confin = $` . "$1";
	$saverest = $';
	$confin .= " " . join(" ", keys(%reqheaders)) . ")" . $saverest;
	$confin =~ m/(DISPLAYSUBDIRS="")/m
	    or warn ("$fn didn't parse as expected. (nonfatal)\n");
	$confin = $` . "$1\n";
	$saverest = $';
	foreach $hdr (keys(%reqheaders)) {
	    $hdr_h = $hdr;
	    $hdr_h =~ s:[\./]:_:g;
	    $hdr_h =~ tr/A-Z/a-z/;
	    $confin .= qq[
dnl If $hdr was not found above we disable the "$targ" target.
if test "x\$ac_cv_header_$hdr_h" != "xyes"; then
  build_${targ}_target="no"
fi
]; 
       };
       $confin .= $saverest;
    }

    $confin =~ m/(dnl - Check for targets)/m
	or die ("./lib${name}/configure.in didn't parse as expected.\n");
    $confin = $` . qq[$1

AC_MSG_CHECKING(if we should build ${targ}        target)
if test "x\$build_${targ}_target" = "xno"; then
  AC_MSG_RESULT(no)
else
  DISPLAYSUBDIRS="\$DISPLAYSUBDIRS ${targ}"
  build_${targ}_target="yes"
  AC_MSG_RESULT(yes)
fi
] . $';
    $confin =~ m/(AC_OUTPUT\([^\)]+?)\)/m
	or die ("$fn didn't parse as expected.\n");
    $confin = $` . "${1}display/${targ}/Makefile\n)" . $';

    print "Adding target build control to to configure.in\n";
    ext_writeout_or_die($confin, $fn);

    $fn = "./lib${name}/display/${targ}/Makefile.am";
    $makefileam = ext_readin_or_die($fn);
    if (length($includes)) {
        $makefileam =~ m:(INCLUDES .*?)\n:m;
        $makefileam = $` . "$1 $includes\n" . $';
    }
    if (length($reqlibs)) {
        $makefileam =~ m:(_la_LIBADD = .*?)\n:m;
        $makefileam = $` . "$1 $reqlibs\n" . $';
    }
    print "Adding flags for includes/libraries to $fn.\n";
    ext_writeout_or_die($makefileam, $fn);

    $fn = "./lib${name}/lib${name}.conf.in";
    $nameconfin = ext_readin_or_die($fn);
    $sstarg = $targ;
    $sstarg =~ s/_/-/g;
    $nameconfin =~ m:(\#-\# target strings):m # '\#' is for emacs
	or die ("$fn didn't parse as expected\n");
    $nameconfin = $` . 
       "$1\n${prefix}-${sstarg}-${name}\t\tdisplay/${targ}_${name}.\@DLLEXT\@" 
         . $';
    print "Adding suggest string to $fn\n";
    ext_writeout_or_die($nameconfin, $fn);

    $fn = "./lib${name}/display/Makefile.am";
    $namedmfam = ext_readin_or_die($fn);
    $namedmfam =~ m:(DIST_SUBDIRS = ):m # '\#' is for emacs
	or die ("$fn didn't parse as expected\n");
    $namedmfam = $` . "DIST_SUBDIRS = ${targ} " . $';
    print "Adding target to DIST_SUBDIRS in $fn\n";
    ext_writeout_or_die($namedmfam, $fn);
    
    # Get the current API from the typedefs.
    $fn = "./lib${name}/include/ggi/internal/${name}.h";
    $nameint = ext_readin_or_die($fn);
    $nameint =~ m:/\*-\* Data types for the API functions \*/(.*?)typedef struct:s
	or die("./lib${name}/include/ggi/internal/${name}.h" .
	       " didn't parse as expected.");
    $typedefs = $1;
    $typedefs =~ s/\s+/ /sg;
    %funcs = $typedefs =~ m/typedef [^_]+?_([^\(]+?)\)(\([^\(]+?\));/g;
    while (($fname,$fargs) = each %funcs) {
	$imptxt .= qq[
int ${NAME}_${Targ}_${fname}${fargs} {
\tprintf("Function ${NAME}_${Targ}_${fname}${fargs} called.\\n");
\treturn(0);\n}\n\n];
	$flcase = $fname;
	$flcase =~ tr/A-Z/a-z/;
	$protos .= "\n${origName}func_${fname} ${NAME}_${Targ}_${fname};";
	$hooks .= 
	    "\n\tLIBGGI_${NAME}EXT(vis)->${flcase} = ${NAME}_${Targ}_${fname};";
    }
    
    
    $funcs_exists = 1;
    open (IFILE, "<./lib${name}/display/${targ}/funcs.c") 
	or ($funcs_exists = 0)
	    or open (IFILE, "<./lib${name}/display/${targ}/init.c")
		or die("cannot read ./lib${name}/display/${targ}/" .
		       ($funcs_exists ? "funcs.c\n" : "init.c\n"));
    $imp = join('', <IFILE>);
    close (IFILE);
    $imp =~ m:(/\*-\* API Implementation \*/):s
	or die("./lib${name}/display/${targ}/" .
	       ($funcs_exists ? "funcs.c" : "init.c") .
	       " didn't parse as expected.");    
    $imp = $` . "$1\n$imptxt" . $';
    print "Adding function definitions to ./lib${name}/display/${targ}/" . 
	($funcs_exists ? "funcs.c\n" : "init.c\n");
    open (OFILE, ">./lib${name}/display/${targ}/" . 
	  ($funcs_exists ? "funcs.c" : "init.c"))
	or die("cannot write ./lib${name}/display/${targ}/" .
	       ($funcs_exists ? "funcs.c\n" : "init.c\n"));
    print OFILE $imp;
    close OFILE;
    
    $fn = "./lib${name}/display/${targ}/${targ}${name}.h";
    if (open(IFILE, "<$fn")) {
	$targinth = join('', <IFILE>);
	close(IFILE);
	$targinth =~ m:(/\*-\* Function prototypes \*/):m
	    or die ("$fn didn't parse as expected.\n");
	$targinth = $` . "$1\n$protos" . $';
	print "Adding prototypes to $fn\n";
	ext_writeout_or_die($targinth, $fn);
    }
    else {
	print "Cannot read $fn ... assuming compact format target source.\n";
    }
    
    $fn = "./lib${name}/display/${targ}/init.c";
    $initc = ext_readin_or_die($fn);
    $initc =~ m:(/\*-\* Hook in target functions \*/):m
	or die ("$fn didn't parse as expected.");
    $initc = $` . "$1\n$hooks" . $';
    print "Adding target function hooks to $fn.\n";
    ext_writeout_or_die($initc, $fn);
        
}

sub ext_add2targstruct {
    my($origName, $Name, $NAME, $name, 
       $origTarg, $Targ, $TARG, $targ, @vars) = @_;
    ext_usage if (@vars + 0) % 3;
    my($type, $vname, $ival, @vnames, %types, %ivals, $varlist, $initstr);
    while (@vars + 0) {
	$type = shift(@vars);
	$vname = shift(@vars);
	$ival = shift(@vars);
	push (@vnames, $vname); # preserves order 
	$types{$vname} = $type;
	$ivals{$vname} = $ival;
	$varlist .= "\n\t$type $vname;";
	$initstr .= "\n\t${TARG}${NAME}_PRIV(vis)->${vname} = ${ival};";
    }
    foreach $vname (@vnames) {
	die "Bad C variable name" unless
	    $vname =~ m/\A[A-Za-z]\w*\Z/;
    }

    $fn = "./lib${name}/display/${targ}/${targ}${name}.h";
    $inth = ext_readin_or_die($fn, "Compact format target (no data structure permitted),\n...or missing $fn!\n");
    if ($inth =~ m:/\*-\* Structure holding private data. \*/:m) {
	$inth = $` . "struct ${targ}${name}_priv {
\t/*-* Private structure members */
};
#define ${TARG}${NAME}_PRIV(vis) ((struct ${targ}${name}_priv*) (LIBGGI_${NAME}EXT(vis)->priv))\n" . $';
	print "Adding target private structure and macro to $fn.\n";
	print "** Did you add2struct a void* priv NULL ??? you should. **\n";
	ext_writeout_or_die($inth, $fn);

	$fn = "./lib${name}/display/${targ}/init.c";
	$initc = ext_readin_or_die($fn);
	$initc =~ m:(/\*-\* Initialize target-private data structure \*/):m 
	    or die ("$fn didn't parse as expected\n");
	$initc = $` . "$1
\t${TARG}${NAME}_PRIV(vis) = malloc(sizeof(struct ${targ}${name}_priv));
\tif (${TARG}${NAME}_PRIV(vis) == NULL) return GGI_DL_ERROR;
" . $';
	print "Adding structure alloc to $fn\n";
	ext_writeout_or_die($initc,$fn);

	$initc = ext_readin_or_die($fn);
	$initc =~ m:(/\*-\* Free target-private structure \*/):m 
	    or die ("$fn didn't parse as expected\n");
	$initc = $` . "$1\n\tfree(${TARG}${NAME}_PRIV(vis));
\n\t${TARG}${NAME}_PRIV(vis) = NULL;" . $';
	print "Adding structure free() to $fn\n";
	ext_writeout_or_die($initc,$fn);

	$fn = "./lib${name}/display/${targ}/${targ}${name}.h";
	$inth = ext_readin_or_die($fn);
    }

    $inth =~ m:(/\*-\* Private structure members \*/):m 
	or die ("$fn didn't parse as expected\n");
    $inth = $` . "$1$varlist" . $';
    print "Adding private structure members in $fn\n";
    ext_writeout_or_die($inth,$fn);
    
    $fn = "./lib${name}/display/${targ}/init.c";
    $initc = ext_readin_or_die($fn);
    $initc =~ m:(/\*-\* Apply initial values \*/):m 
	or die ("$fn didn't parse as expected\n");
    $initc = $` . "$1$initstr" . $';
    print "Adding structure data init to $fn\n";
    ext_writeout_or_die($initc,$fn);
}

sub ext_coredirs {
    return (
	    "libname/demos" =>				0775,
	    "libname/display" =>			0775,
	    "libname/doc" =>				0775,
	    "libname/include" =>			0775,
	    "libname/include/ggi" =>			0775,
	    "libname/include/ggi/internal" =>		0775,
	    "libname/name" => 				0775,
	    "libname/m4" =>				0775
	    );
}

sub ext_corefiles {
    return (
	    "libname/demos/.cvsignore" =>		0664,
	    "libname/demos/Makefile.am" =>		0664,
	    "libname/demos/nametest.c" =>		0664,
	    "libname/.cvsignore" =>			0664,
	    "libname/COPYING" =>			0664,
	    "libname/Makefile.am" =>			0664,
	    "libname/README" =>				0664,
	    "libname/acconfig.h" =>			0664,
	    "libname/autogen.sh" =>			0755,
	    "libname/config.guess" =>			0755,
	    "libname/config.sub" =>			0755,
	    "libname/configure.in" =>			0664,
	    "libname/include/Makefile.am" =>		0664,
	    "libname/include/ggi/Makefile.am" =>	0664,
	    "libname/include/ggi/name.h" =>		0664,
	    "libname/include/ggi/internal/Makefile.am" => 0664,
	    "libname/include/ggi/internal/name.h" =>	0664,
	    "libname/install-sh" =>			0755,
	    "libname/libname.conf.in" =>		0664,
	    "libname/ltconfig" =>			0664,
	    "libname/ltmain.sh" =>			0664,
	    "libname/missing" =>			0755,
	    "libname/mkinstalldirs" =>			0755,
	    "libname/display/.cvsignore" =>		0664,
	    "libname/display/Makefile.am" =>		0664,
	    "libname/doc/.cvsignore" =>			0664,
	    "libname/doc/Makefile.am" =>		0664,
	    "libname/doc/name.txt" =>			0664,
	    "libname/name/.cvsignore" =>		0664,
	    "libname/name/EXPSYMS" =>			0664,
	    "libname/name/Makefile.am" =>		0664,
	    "libname/name/init.c" =>			0664,
	    "libname/name/stubs.c" =>			0664,
	    "libname/m4/.cvsignore" =>			0664,
	    "libname/m4/Makefile.am" =>			0664,
	    "libname/m4/dllext.m4" =>			0664,
	    "libname/m4/extrapaths.m4" =>		0664,
	    "libname/m4/libtool.m4" =>			0664
	    );
}

sub targ_full_corefiles {
    return (
	    "libname/display/full/.cvsignore" =>	0664,
	    "libname/display/full/EXPSYMS" =>		0664,
	    "libname/display/full/Makefile.am" =>	0664,
	    "libname/display/full/funcs.c" =>		0664,
	    "libname/display/full/init.c" =>		0664,
	    "libname/display/full/targname.h" =>	0664
	    );
}

sub targ_compact_corefiles {
    return (
	    "libname/display/compact/.cvsignore" =>	0664,
	    "libname/display/compact/EXPSYMS" =>	0664,
	    "libname/display/compact/Makefile.am" =>	0664,
	    "libname/display/compact/init.c" =>		0664
	    );
}

sub ext_translate_filename {
    my(%trans);
    my($fname) = pop();
    @trans{"origName", "Name", "NAME", "name", 
	   "origTarg", "Targ", "TARG", "targ"} = @_;
    $fname =~ s:/compact/|/full/:/$trans{targ}/:;
    $fname =~ 
	s/(origName|Name|NAME|name|origTarg|Targ|TARG|targ)/$trans{$1}/ge;
    return($fname);
}


sub ext_translate_template {
    my(%trans);
    my($nfile) = pop();
    my($tfile) = pop();
    @trans{"origName", "Name", "NAME", "name", 
	   "origTarg", "Targ", "TARG", "targ",
	   "ddmmyy", "Authors", "AuthorEmails", 
	   "AuthorsWithEmail", "CopyNotice", 
	   "Copyright", "CopyByWhoWhen"} = @_;
    my($template) = join("",<$tfile>);
    $template =~ 
	s"\$\{(${\ (join('|', keys(%trans)))})\}"$trans{$1}"ge;
    print $nfile $template;
}

sub ext_readin_or_die {
    my($filename) = shift();
    my($message) = (@_ + 0) ? shift : "Cannot read %s\n";
    open (IFILE, "<$filename") or die(sprintf($message, $filename));
    my($res) = join('',<IFILE>);
    close (IFILE);
    return $res;
}

sub ext_writeout_or_die {
    my($content) = shift();
    my($filename) = shift();
    my($message) = (@_ + 0) ? shift : "Cannot write %s\n";
    open (OFILE, ">$filename") or die(sprintf($message, $filename));
    print OFILE $content;
    close (OFILE);
}
