#! /usr/bin/perl

# $Id: sc,v 1.1.1.1 1996/02/13 13:17:35 dag Exp $

$Prog = "sc";
$Progx = " " x (length($Prog)+1);	# Blanks for diagnostics.
$Version = "3.18";

# Directories

$Cim_lib      = "/local/lib/cim";
$Lund_lib     = "/local/lib/simula";
$Source_lib   = "/local/src/simula/simtools";
$Xite_atr_lib = "/local/blab/xite/atr/$OS";
$Xite_lib     = "/local/blab/xite/lib/$OS";
$X_local      = "/local/x11/lib";
$Xpm_lib      = "/local/src/X11R5/ifi/lib/xpm-3.4e/lib";

# Programs

$Assembler     = "/usr/bin/as";
$C_compiler    = "/usr/bin/cc";
$Cim_compiler  = "/local/bin/cim";
$Linker        = "/usr/bin/ld";
$Lund_compiler = "/local/bin/simula";
$Uname         = "/bin/uname";

# Current architecture and operating system:
$OSname = `$Uname -s`; chop($OSname);
$OS = $OSname;

if ($OSname eq "Linux") {
    $C_compiler = "/usr/bin/gcc";
    $Assembler = "/usr/bin/as";
    $Linker = "/usr/bin/ld";
    $Cim_lib      = "/usr/local/lib";
    $Lund_lib     = "/local/lib/simula";
    $Source_lib   = "/local/src/simula/simtools";
    $Xite_atr_lib = "/local/blab/xite/atr/$OS";
    $Xite_lib     = "/local/blab/xite/lib/$OS";
    $X_local      = "/usr/X11R6/lib";
    $Xpm_lib      = "/usr/X11R6/lib";
    $Cim_compiler  = "/usr/local/bin/cim";
}


#if ($OSname eq "SunOS") {
#    $OSname = `$Uname -s -r`;  chop($OSname);
#}
#
## Operating system peculiarities:
#if ($OSname =~ /SunOS 5\./) {
#    $C_compiler = "/local/bin/cc";
#    $Assembler  = "/usr/ccs/bin/as";
#    $Linker     = "/usr/ccs/bin/ld";
#}



# Useful constants:

$False = "";
$True  = 1;

# Global variables:
$Errors = 0;
$Verbosity = 1;



# And now: the main program:

&Scan_params;  &Done if $Errors;

print "$Prog: OSname=$OSname, OS=$OS.\n" if $Verbosity >= 3;
print "Sim_files=", @Sim_files, ", O_files=", @O_files, 
    ", Other_files=", @Other_files, ".\n" if $Verbosity >= 3;

if (!$Compiler) {
    $Compiler = ($Num_cim_files>0 || ! -x $Lund_compiler ? "cim" : "lund");
}

&Make_links;

&Compile;  &Done if $Errors || $Compile_only;

$Sim_prog = &basename((@Sim_files,@Other_files,@O_files)[0]) unless $Sim_prog;

&Link;  &Done if $Errors;
push(@Temp_files, $Sim_prog) if $Run_opt;

&Check_system_prog;
&Run if $Run_opt;  

&Done;




# Warning ($Line1, $Line2)
# -------
# Print a warning message.
sub Warning {
    local($Line1, $Line2) = @_;

    print "$Prog: Warning: $Line1\n";
    if ($Line2) { print "$Progx          $Line2\n"; };
}


# Scan_params ()
# -----------
# Scan the parameter list, and place the parameters and options in
# suitable global variables.
#
sub Scan_params {
PARAM:
    while (@ARGV) {
	$_ = shift @ARGV;

	/^-blab$/ && do {
	    print "$Prog: Option -blab is obsolete; use -xite instead.\n";
	    $Link_xite = $True;  next PARAM; };
	/^-C$/ && do {
	    $Check_compile = $True;  next PARAM; };
	/^-c$/ && do {
	    $Compile_only = $True;  next PARAM; };
	/^-cim$/ && do { 
	    $Compiler = "cim";  next PARAM; };
	/^-d/ && do {
	    $Debug = $True;  next PARAM; };
	/^-help$/ && do { 
	    &Give_help;  next PARAM; };
	/^-in105$/ && do {
	    print "$Prog: Option -in105 is obsolete; use -simutil instead.\n";
	    $Link_simutil = $True;  next PARAM; };
	/^-in105w$/ && do {
	    print "$Prog: Option -in105w is obsolete; use -simwin instead.\n";
	    $Link_simwin = $True;  next PARAM; };
	/^-L$/ && do {
	    push(@Extra_links, shift(@ARGV));  next PARAM; };
	/^-lund$/ && do { 
	    if (-x $Lund_compiler) { 
		$Compiler = "lund";
	    } else { 
		print "$Prog: No Lund compiler available;\n";
		print "$Progx option -lund ignored.\n"; 
	    };
	    next PARAM; };
	/^-o$/ && do {
	    $Sim_prog = shift @ARGV;  next PARAM; };
	/^-o/ && do {
	    s/^-o//;  $Sim_prog = $_;  next PARAM; };
	/^-q/ && do { 
	    $Verbosity = 0;  next PARAM; };
	/^-r$/ && do {
	    $Run_opt = $True;  next PARAM; };
	/^-simlib$/ && do {
	    $Link_simlib = $True;  next PARAM; };
	/^-simutil$/ && do {
	    $Link_simwin = $True;  next PARAM; };
	/^-simwin$/ && do {
	    $Link_simwin = $True;  next PARAM; };
	/^-test$/ && do {
	    &Warning("Running test version of libraries.", "");
	    $Use_test_lib = $True;  next PARAM; };
	/^-v$/ && do { 
	    print "This is $Prog (version $Version).\n";
	    $Verbosity = 2;  next PARAM; };
	/^-vv$/ && do { 
	    print "This is $Prog (version $Version).\n";
	    $Verbosity = 3;  next PARAM; };
	/^-xite$/ && do {
	    $Link_xite = $True;  next PARAM; };
	/^-/ && do { 
	    print "$Prog: Unknown option $_ ignored.\n";
	    next PARAM; };

	&Note_filename($_); 
    }; #while

    if (@Sim_files + @Other_files + @O_files == 0) { 
	$Errors++;  &Give_usage;
	print "Try `$Prog -help' for more information.\n"; 
    }
} # Scan_params


# Note_filename (File_name)
# -------------
# Check that File_name (or File_name.sim or File_name.cim) exists,
# and save name in the proper list.
#
sub Note_filename {
    if (/\.cim$/) { 
	if (-r) { 
	    push(@Sim_files, $_);  $Num_cim_files++;
	} else { 
	    print "$Prog: Unable to read file `$_'.\n";  $Errors++; 
	}
    } elsif (/\.sim$/) { 
	if (-r) { 
	    push(@Sim_files, $_); 
	} else { 
	    print "$Prog: Unable to read file `$_'.\n";  $Errors++; 
	}
    } elsif (/\.o$/) {
	if (-r) {
	    push(@O_files, $_);
	} else {
	    print "$Prog: Unable to read file `$_'.\n";  $Errors++;
	}
    } elsif (-r ($_ . ".sim")) { 
	push(@Sim_files, $_ . ".sim"); 
    } elsif (-r ($_ . ".cim")) { 
	push(@Sim_files, $_ . ".cim");  $Num_cim_files++;
    } elsif (-r $_) { 
	push (@Other_files, $_); 
    } else { 
	print "$Prog: Unable to read file `$_'.\n";  $Errors++; 
    }
} # Note_filename


# basename (File_name)
# --------
# Find the base part of the file name (no /-s and no final .xxx).
#
sub basename {
    local($_) = $_[0];

    m|/$| && chop;
    s|.*/||;
    s|(.+)\..*|\1|;
    $_;
}


# Give_help ()
# ---------
# Give the user information on how to use this program.
# Exit afterwards.
#
sub Give_help {
    &Give_usage;

    print "The following options are available:
  -C        Compile only the files that really need recompiling (Lund only).
  -c        Omit linking.
  -cim      Use the Cim compiler.
  -d        Include the debugger (Lund only).
  -help     Print this help message and exit.
  -L x      Include option `x' when linking.
  -lund     Use the Lund compiler.
  -o file   Name of compiled program file.
  -q        Make $Prog more quiet.
  -r        Run the program.
  -simlib   Include the SimLib library (Lund only).
  -simutil  Include the SimUtil library.
  -simwin   Include the SimWin library.
  -test     Use test version of libraries (developers only).
  -v        State version and make $Prog more verbose.
  -xite     Include the Xite library (Lund only).
";

    &Done;
} # Give_help


# Give_usage ()
# ----------
# Give a brief explanation on how to use this program.
#
sub Give_usage {
    print "Usage: $Prog [option...] file-name...\n\n";
} # Give_usage


# Compile ()
# -------
# Compile the various files.
sub Compile {
    local($F);

    if (@Sim_files) {
	if ($Compiler eq "cim") {
	    &Compile_cim;
	} else {
	    &Compile_lund;
	}

	foreach $F (@Sim_files) {
	    push(@O_files, &basename($F) . ".o");
	}
    }

    &Compile_others if @Other_files;
} # Compile


# Compile_cim ()
# -----------
# Compile the Simula files using the Cim compiler.
sub Compile_cim {
    local(@Options);
    local($Must_compile) = ! $Check_compile;
    local($F, $FC, $Fo, $FoC, $Fatr, $FatrC, $Atr_lib);

    foreach $F (reverse @Sim_files) {
	if (! $Must_compile) {
	    $Fo   = &basename($F) . ".o";
	    $Fatr = &basename($F) . ".atr";
	    $Must_compile = $True if (! -e $Fo);
	    $Must_compile = $True if (! -e $Fatr);
	    if (! $Must_compile) {
		$FC = -C $F;  $FoC = -C $Fo;  $FatrC = -C $Fatr;
		$Must_compile = $True if ($FC < $FoC || $FC < $FatrC);
		printf("%s: %s:%.4f, %s:%.4f, %s:%.4f\n", 
		       $Prog, $F, $FC, $Fo, $FoC, $Fatr, $FatrC)
		    if $Verbosity >= 3;
	    };
	};

	if ($Must_compile) {
	    @Options = ("-c");
	    if ($Link_simutil || $Link_simwin) {
		$Atr_lib = $Cim_lib;
		$Atr_lib = "$Source_lib/cim" if $Use_test_lib;
		push(@Options, "-L$Atr_lib", "-lsimtools-atr");
	    };
	    push(@Options, $F);
	    print "$Prog: Compiling: cim @Options\n" if $Verbosity>0;
	    system("$Cim_compiler", @Options);
	    push(@Temp_files, &basename($F) . ".o", &basename($F) . ".atr")
		unless $Compile_only || $Check_compile;
	} else {
	    print "$Prog: No compilation of $F necessary\n" if $Verbosity>0;
	};
    };
} # Compile_cim


# Compile_lund ()
# ------------
# Compile the Simula files using the Lund comnpiler.
sub Compile_lund {
    local($Use_ass) = "";
    local($Must_compile) = ! $Check_compile;
    local($F, $FC, $Fo, $FoC, $Fatr, $FatrC, @As_opts);

    if ($Debug && $OSname eq "IRIX") {
	&Warning("Option -d not yet recognized by the IRIX implementation;",
		 "option ignored.");
	$Debug = $False;
    }

    $Use_ass = "-s " if $OSname eq "ULTRIX" || $OSname eq "IRIX" ||
	                $OSname =~ /SunOS 5\./;

    foreach $F (reverse @Sim_files) {
	if (! $Must_compile) {
	    $Fo   = &basename($F) . ".o";
	    $Fatr = &basename($F) . ".atr";
	    $Must_compile = $True if (! -e $Fo);
	    $Must_compile = $True if (! -e $Fatr);
	    if (! $Must_compile) {
		$FC = -C $F;  $FoC = -C $Fo;  $FatrC = -C $Fatr;
		$Must_compile = $True if ($FC < $FoC || $FC < $FatrC);
		printf("%s: %s:%.4f, %s:%.4f, %s:%.4f\n", 
		       $Prog, $F, $FC, $Fo, $FoC, $Fatr, $FatrC)
		    if $Verbosity >= 3;
	    };
	};

	if ($Must_compile) {
	    print "$Prog: Compiling: simula $Use_ass$F\n" if $Verbosity>0;
	    open (Compiler, "$Lund_compiler $Use_ass$F 2>&1|");
	    while (<Compiler>) {
		s/\x00//g;	## The compiler adds superfluous NUL bytes!

		if (/^\s*(\d+)\s{2,}(.*)/) { ## Fix appearance of error lines.
		    print "\"$F\", line $1: $2\n";  $Errors++;
		} else {
		    print;
		}
	    }
	    push(@Temp_files, &basename($F) . ".o")
		unless $Compile_only || $Check_compile;
	    push(@Temp_files, &basename($F) . ".atr")
		unless $Compile_only || $Check_compile || $Debug;
	    push(@Temp_files, &basename($F) . ".s") 
		if $Use_ass ne "";

	    if ($Use_ass ne "" && $Errors == 0) {
		@As_opts = ("$OSname" eq "ULTRIX" ? "-L." : "-L");
		push(@As_opts, 
		     "-o", &basename($F) . ".o", &basename($F) . ".s");

		print "$Prog: Assembling: as @As_opts\n" if $Verbosity>0;
		system($Assembler, @As_opts);
	    };
	} else {
	    print "$Prog: No compilation of $F necessary\n" if $Verbosity>0;
	};
    };
} # Compile_lund


# Compile_others ()
# --------------
# Compile all the non-Simula files.
sub Compile_others {
    local($F);

    print "$Prog: Compiling: cc -c @Other_files\n" if $Verbosity>0;
    system($C_compiler, "-c", @Other_files);

    foreach $F (@Other_files) {
	push(@O_files, &basename($F) . ".o");
	push(@Temp_files, &basename($F) . ".o")
	    unless $Compile_only || $Check_compile;
    };
} # Compile_others


# Make_links ()
# ----------
# Make the necessary links to external libraries of .atr files.
sub Make_links {
    local($Atr_lib);

    if ($Compiler eq "cim") {
	&Remove_link("utilities.atr");
	&Remove_link("settools.atr");
	&Remove_link("windowtools.atr");
    } else {
	$Atr_lib = $Lund_lib;
	$Atr_lib = $Source_lib . "/lund" if $Use_test_lib;

	if ($Link_simutil || $Link_simwin) {
	    &Check_link($Atr_lib . "/", "utilities.atr");
	    &Check_link($Atr_lib . "/", "settools.atr");
	}
	if ($Link_simwin) {
	    &Check_link($Atr_lib . "/", "windowtools.atr");
	}
	if ($Link_xite) {
	    &Check_link($Xite_atr_lib . "/", "image.atr");
	}
    }
}


# Check_link (Dir, File)
# ----------
# Check that a link to Dir/File exists, and check its contents.
# If no link or wrong one, create a proper link.
sub Check_link {
    local($Dir, $File) = @_;
    local($Full_name, $New_link) = ($Dir . $File, $False);

    if (-l "$File") {
	# Check existing link:
	if (! &Same_file($File, $Full_name)) {
	    print "$Prog: Removing link: rm $File\n" if $Verbosity > 0;
	    unlink $File || &Error("Could not remove $File.");
	    $New_link = $True;
	}
    }
    elsif (-f "$File") {
	# Checking existing file:
	if (! &Same_file($File, $Full_name)) {
	    &Warning("Wrong ATR file $File.",
		     "Delete that file and compile again.");
	}
    }
    else {
	$New_link = $True;
    }

    if ($New_link) {
	if (symlink($Full_name, $File)) {
	    print "$Prog: Making link: ln -s $Full_name $File\n"
		if $Verbosity > 0;
	} else {
	    print "$Prog: Unable to make link $File!\n";
	    $Errors++;
	}
    }
}


# Remove_link (File)
# -----------
# Remove the link named File in the current directory (if it exists).
sub Remove_link {
    local($File) = @_;

    if (-l "$File") {
	print "$Prog: Removing link: rm $File\n" if $Verbosity > 0;
	unlink $File || &Error("Could not remove $File.");
    };
}


# Same_file (Fn1, Fn2)
# ---------
# Check if the contents of two files Fn1 and Fn2 is identical.
sub Same_file {
    local($Fn1, $Fn2) = @_;
    local($L1, $L2);

    open(F1, $Fn1);  open(F2, $Fn2);
    while ($L1 = <F1>) {
	if ($L2 = <F2>) {
	    return $False if $L1 ne $L2;
	} else {
	    return $False;
	}
    }
    if (<F2>) { return $False; };

    return $True;
}


# Link ()
# ----
# Link the files.
sub Link {
    if ($Compiler eq "cim") {
	if ($Debug) {
	    &Warning("Cim compiler does not recognize -d option;",
		     "option ignored.");
	}
	&Link_cim;
    } else {
	&Link_lund;
    }
}


# Link_cim ()
# --------
# Link the files produced by the Cim compiler.
sub Link_cim {
    local(@Options);

    @Options = ("-o", "$Sim_prog");
    push(@Options, @O_files, @Extra_links);
    push(@Options, "-L$Source_lib/cim") if $Use_test_lib;
    push(@Options, "-L$Cim_lib");

    if ($Link_xite) {
	&Warning("Cim compiler does not recognize -xite option.");
	$Errors++;
    }
    if ($Link_simlib) {
	&Warning("Cim compiler does not recognize -simlib option.");
	$Errors++;
    }
    if ($Link_simutil || $Link_simwin) {
	push(@Options, "-lsimtools");
	if ($Link_simwin) {
	    push(@Options, "-L/usr/openwin/lib") if $OSname =~ /SunOS 5\./;
	    push(@Options, "-L$X_local -L$Xpm_lib -lXpm -lXext");
	    push(@Options, "-lX11");
	}
    }

    push(@Options, "-lcim -lm");

    print "$Prog: Linking: cc @Options\n" if $Verbosity>0;
    system("$C_compiler @Options");
}


# Link_lund ()
# ---------
# Link the files produced by the Lund compiler.
sub Link_lund {
    local (@Options);

    if ($OSname eq "ULTRIX") {
	@Options = ("-s", "/lib/crt0.o", "-o", "$Sim_prog");
    } elsif ($OSname eq "IRIX") {
	@Options = ("-elf", "-_SYSTYPE_SVR4", "-require_dynamic_link",
		    "_rld_new_interface", "-no_unresolved", "-Wx,-G", "0",
		    "-call_shared", "-g0", "-KPIC", "-nocount",
		    "/usr/lib/crt1.o", "-o", "$Sim_prog", "-count"); 
    } elsif ($OSname =~ /SunOS 5\./) {
	@Options = ("-dy", "/opt/SUNWspro/SC2.0.1/crt1.o", 
		    "-o", "$Sim_prog");
    } else {
	@Options = ("-dc", "-dp", "-e", "start", "-X", "/lib/crt0.o", 
		    "-o", "$Sim_prog");
    }
    push(@Options, @O_files);
    push(@Options, "-nocount") if $OSname eq "IRIX";
    push(@Options, @Extra_links);

    if ($Link_xite) {
	push(@Options, "-L$Xite_lib", "-lxite");
    }

    push(@Options, "-L$Source_lib/lund") if $Use_test_lib;
    push(@Options, "-L$Lund_lib");

    if ($Link_simlib || $Link_simutil || $Link_simwin) {
	if ($Link_simutil || $Link_simwin) {
	    push(@Options, "-lsimtools");
	}
	if ($Link_simwin) {
	    push(@Options, "-L/usr/openwin/lib") if $OSname =~ /SunOS 5\./;
	    push(@Options, "-L$X_local -L$Xpm_lib -lXpm -lXext");
	    push(@Options, "-lX11");
	}
	if ($Link_simlib) {
	    if ($OSname eq "ULTRIX") {
		&Warning("Option -simlib does not work under Ultrix.");
		$Errors++;
	    } elsif ($OSname eq "IRIX") {
		&Warning("Option -simlib does not work under IRIX.");
		$Errors++;
	    } else {
		push(@Options, "-lsimlib");
	    }
	}
    }

    push(@Options, "-lsimdeb") if $Debug;
    push(@Options, "-lsimula");
    push(@Options, "/usr/ucblib/libucb.a") if $OSname =~ /SunOS 5\./;
    push(@Options, "-lc");
    push(@Options, "/usr/lib/crtn.o") if $OSname eq "IRIX";

    if (! $Errors) {
	print "$Prog: Linking: ld @Options\n" if $Verbosity>0;
	system("$Linker @Options") && $Errors++;
    }
}


# Run ()
# ---
# Run the compiled and linked program.
sub Run {
    local(@Command) = ("./$Sim_prog");

    if ($Debug && $Compiler ne "cim") {
	push(@Command, "-d");
    }

    print "$Prog: Running: @Command\n" if $Verbosity > 0;
    system(@Command);
}


# Check_system_prog ()
# -----------------
# Check if the user has chosen a program name that is also a UNIX program.
sub Check_system_prog {
    local($Fullname);

    foreach $F (split(':',$ENV{"PATH"})) {
	$Fullname = "$F" ? "$F/$Sim_prog" : "$Sim_prog";
	print "$Prog: Checking $Fullname...\n" if $Verbosity>=3;
	if (-x "$Fullname") {
	    return if "$F" eq "" || "$F" eq ".";
	    print "$Prog: Warning: Your program name `$Sim_prog' is also the name of a UNIX program;\n";
	    print "$Progx to run it, write `./$Sim_prog'.\n";
	    return;
	}
    }
}


# Done ()
# ----
# Delete all temporary files and exit with status value.
sub Done {
    if (@Temp_files) {
	print "$Prog: Deleting: rm @Temp_files\n" if $Verbosity > 0;
	unlink(@Temp_files);
    }

    exit $Errors;
}
