#! /bin/sh
#
# Copyright (C) 2000 by Kevin L. Mitchell <klmitch@mit.edu>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# @(#)$Id: comp_et-sh.in,v 1.2 2000/12/13 04:48:13 klmitch Exp $

com_err=true
c_file=
c_name=
c_dir=
h_file=
h_name=
h_dir=
dir=
in_file=

ce_prev=
for ce_option
do
    # if the previous option needed an argument, assign it...
    if test -n "$ce_prev"; then
	eval "$ce_prev=\$ce_option"
	ce_prev=
	continue
    fi

    case "$ce_option" in
    -*=*) ce_optarg=`echo "$ce_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
    *) ce_optarg= ;;
    esac

    case "$ce_option" in

     -c_f   |  -c_fi   |  -c_fil   |  -c_file   | \
     -c-f   |  -c-fi   |  -c-fil   |  -c-file   | \
    --c_f   | --c_fi   | --c_fil   | --c_file   | \
    --c-f   | --c-fi   | --c-fil   | --c-file   )
	ce_prev=c_file ;;
     -c_f=* |  -c_fi=* |  -c_fil=* |  -c_file=* | \
     -c-f=* |  -c-fi=* |  -c-fil=* |  -c-file=* | \
    --c_f=* | --c_fi=* | --c_fil=* | --c_file=* | \
    --c-f=* | --c-fi=* | --c-fil=* | --c-file=* )
	c_file="$ce_optarg" ;;

     -c_d   |  -c_di   |  -c_dir   | \
     -c-d   |  -c-di   |  -c-dir   | \
    --c_d   | --c_di   | --c_dir   | \
    --c-d   | --c-di   | --c-dir   )
	ce_prev=c_dir ;;
     -c_d=* |  -c_di=* |  -c_dir=* | \
     -c-d=* |  -c-di=* |  -c-dir=* | \
    --c_d=* | --c_di=* | --c_dir=* | \
    --c-d=* | --c-di=* | --c-dir=* )
	c_dir="$ce_optarg" ;;

     -h_f   |  -h_fi   |  -h_fil   |  -h_file   | \
     -h-f   |  -h-fi   |  -h-fil   |  -h-file   | \
    --h_f   | --h_fi   | --h_fil   | --h_file   | \
    --h-f   | --h-fi   | --h-fil   | --h-file   )
	ce_prev=h_file ;;
     -h_f=* |  -h_fi=* |  -h_fil=* |  -h_file=* | \
     -h-f=* |  -h-fi=* |  -h-fil=* |  -h-file=* | \
    --h_f=* | --h_fi=* | --h_fil=* | --h_file=* | \
    --h-f=* | --h-fi=* | --h-fil=* | --h-file=* )
	h_file="$ce_optarg" ;;

     -h_d   |  -h_di   |  -h_dir   | \
     -h-d   |  -h-di   |  -h-dir   | \
    --h_d   | --h_di   | --h_dir   | \
    --h-d   | --h-di   | --h-dir   )
	ce_prev=h_dir ;;	   
     -h_d=* |  -h_di=* |  -h_dir=* | \
     -h-d=* |  -h-di=* |  -h-dir=* | \
    --h_d=* | --h_di=* | --h_dir=* | \
    --h-d=* | --h-di=* | --h-dir=* )
	h_dir="$ce_optarg" ;;

     -d   |  -di   |  -dir   | \
    --d   | --di   | --dir   )
	ce_prev=dir ;;
     -d=* |  -di=* |  -dir=* | \
    --d=* | --di=* | --dir=* )
	dir="$ce_optarg" ;;

     -com   |  -com_   |  -com_e   |  -com_er   |  -com_err   | \
	       -com-   |  -com-e   |  -com-er   |  -com-err   | \
    --com   | --com_   | --com_e   | --com_er   | --com_err   | \
	      --com-   | --com-e   | --com-er   | --com-err   )
	ce_prev=com_err ;;
     -com=* |  -com_=* |  -com_e=* |  -com_er=* |  -com_err=* | \
	       -com-=* |  -com-e=* |  -com-er=* |  -com-err=* | \
    --com=* | --com_=* | --com_e=* | --com_er=* | --com_err=* | \
	      --com-=* | --com-e=* | --com-er=* | --com-err=* )
	com_err="$ce_optarg" ;;

     -he |  -hel |  -help | \
    --he | --hel | --help )
	cat <<EOF
Usage: $0 [options] <infile>
Options:
  --c-file=FILE              Set the output .c file to FILE
  --h-file=FILE              Set the output .h file to FILE
  --com-err=OPTION           Selects whether support for com_err should be
                             used; OPTION may be either true or false
  --help                     Print this message
EOF
	exit 0 ;;

    -*)
	echo "$0: Invalid option $ce_option; use --help to show usage" 1>&2
	exit 1 ;;

    *)
	if test -n "$in_file"; then
	    echo "$0: Can only process one file at a time" 1>&2
	    exit 1
	fi
	in_file="$ce_option" ;;
    esac
done

if test -n "$ce_prev"; then
    echo "$0: Missing argument for option; use --help to show usage" 1>&2
    exit 1
fi

if test -z "$in_file"; then
    echo "$0: No input file specified; use --help to show usage" 1>&2
    exit 1
fi

if test ! -r "$in_file"; then
    echo "$0: Unable to read input file $in_file" 1>&2
    exit 1
fi

com_err=`echo "$com_err" | tr 'A-Z' 'a-z'`

case "$com_err" in
t | tr | tru | true | y | ye | yes)
    com_err=true ;;

f | fa | fal | fals | false | n | no)
    com_err=false ;;

*)
    echo "$0: Invalid value for --com-err; use --help to show usage" 1>&2
    exit 1 ;;
esac

in_dir=`echo "$in_file" | sed 's%/[^/]*$%%'`
in_name=`echo "$in_file" | sed -e 's%.*/%%' -e 's/\.et$//'`

if test -z "$dir"; then
    dir="$in_dir"
fi

if test -z "$c_dir"; then
    c_dir="$dir"
fi

if test -z "$h_dir"; then
    h_dir="$dir"
fi

if test "$com_err" = false; then
    c_file="/dev/null"
    c_dir=
elif test -z "$c_file"; then
    c_file="$c_dir/$in_name.c"
fi

c_name=`echo "$c_file" | sed 's%.*/%%'`

if test -z "$h_file"; then
    h_file="$h_dir/$in_name.h"
fi

h_name=`echo "$h_file" | sed 's%.*/%%'`

@AWK@ -f - "com_err=$com_err" "c_file=$c_file" "c_name=$c_name" \
			    "h_file=$h_file" "h_name=$h_name" "$in_file" <<\EOF
BEGIN {
    char_shift = 64;

    c2n["A"] = 1;   c2n["B"] = 2;   c2n["C"] = 3;   c2n["D"] = 4;
    c2n["E"] = 5;   c2n["F"] = 6;   c2n["G"] = 7;   c2n["H"] = 8;
    c2n["I"] = 9;   c2n["J"] = 10;  c2n["K"] = 11;  c2n["L"] = 12;
    c2n["M"] = 13;  c2n["N"] = 14;  c2n["O"] = 15;  c2n["P"] = 16;
    c2n["Q"] = 17;  c2n["R"] = 18;  c2n["S"] = 19;  c2n["T"] = 20;
    c2n["U"] = 21;  c2n["V"] = 22;  c2n["W"] = 23;  c2n["X"] = 24;
    c2n["Y"] = 25;  c2n["Z"] = 26;  c2n["a"] = 27;  c2n["b"] = 28;
    c2n["c"] = 29;  c2n["d"] = 30;  c2n["e"] = 31;  c2n["f"] = 32;
    c2n["g"] = 33;  c2n["h"] = 34;  c2n["i"] = 35;  c2n["j"] = 36;
    c2n["k"] = 37;  c2n["l"] = 38;  c2n["m"] = 39;  c2n["n"] = 40;
    c2n["o"] = 41;  c2n["p"] = 42;  c2n["q"] = 43;  c2n["r"] = 44;
    c2n["s"] = 45;  c2n["t"] = 46;  c2n["u"] = 47;  c2n["v"] = 48;
    c2n["w"] = 49;  c2n["x"] = 50;  c2n["y"] = 51;  c2n["z"] = 52;
    c2n["0"] = 53;  c2n["1"] = 54;  c2n["2"] = 55;  c2n["3"] = 56;
    c2n["4"] = 57;  c2n["5"] = 58;  c2n["6"] = 59;  c2n["7"] = 60;
    c2n["8"] = 61;  c2n["9"] = 62;  c2n["_"] = 63;
}

/^#/ { next }

/^[ \t]*(error_table|et)[ \t]+[a-zA-Z][a-zA-Z0-9_]+/ {
    tab_num = 0;
    tab_name = $2;
    mod_base = 1000000;

    for (i = 1; i <= length(tab_name); i++) {
	tab_num = (tab_num * char_shift) + c2n[substr(tab_name, i, 1)];
    }

    # Some awk programs don't have enough precision, so we have to play
    # some games here...
    base_low = tab_num % mod_base;
    base_high = int(tab_num / mod_base);
    base_sign = 1;

    # now shift it over by 8 bits...
    base_low = base_low * 256;
    base_high = (base_high * 256) + int(base_low / mod_base);
    base_low = base_low % mod_base;

    # Next, figure out the sign...
    if (tab_num > (128 * 256 * 256)) {
	# subtract 256 * 256 * 256 * 256...
	sub_low = 256 * 256 * 256 % mod_base;
	sub_high = int(256 * 256 * 256 / mod_base);

	sub_low = sub_low * 256;
	sub_high = (sub_high * 256) + int(sub_low / mod_base);
	sub_low = sub_low % mod_base;

	base_low = sub_low - base_low;
	base_high = sub_high - base_high;
	base_sign = -1;
	# Now correct for base_low being negative...
	if (base_low < 0) {
	    base_low = base_low + mod_base;
	    base_high--;
	}
    }

    curr_low = base_low;
    curr_high = base_high;
    curr_sign = base_sign;

    tab_count = 0;

    print "/*" > h_file;
    print " * " h_name ":" > h_file;
    print " * This file is automatically generated; please do not edit it." > h_file;
    print " */" > h_file;

    print "/*" > c_file;
    print " * " c_name ":" > c_file;
    print " * This file is automatically generated; please do not edit it." > c_file;
    print " */" > c_file;
    print "#if defined(__STDC__) || defined(_MSDOS) || defined(_WIN32)" > c_file;
    print "#define NOARGS void" > c_file;
    print "#else" > c_file;
    print "#define NOARGS" > c_file;
    print "#define const" > c_file;
    print "#endif" > c_file;
    print "" > c_file;
    print "static const char * const text[] = {" > c_file;
}

(cont == 1) && ($0 ~ /\\[ \t]*$/) {
    text = substr($0, 1, length($0) - 1);
    cont_buf = cont_buf text;
}

(cont == 1) && ($0 ~ /"[ \t]*$/) {
    printf "\t%s,\n", cont_buf $0 > c_file;
    cont = 0;
}

/^[ \t]*(error_code|ec)[ \t]+[A-Z_0-9]+,[ \t]*$/ {
    tag = substr($2, 1, length($2) - 1);
    if (curr_high == 0) {
	printf "#define %-40s (%dL)\n", tag, curr_sign * curr_low > h_file;
    } else {
	printf "#define %-40s (%d%06dL)\n", tag, curr_sign * curr_high , curr_low > h_file;
    }
    curr_low += curr_sign;
    if (curr_low >= mod_base) {
	curr_low -= mod_base;
	curr_high++;
    }
    if (curr_low < 0) {
	curr_low += mod_base;
	curr_high--;
    }

    tab_count++;
    skipone = 1;
    next;
}

/^[ \t]*(error_code|ec)[ \t]+[A-Z_0-9]+,[ \t]*".*"[ \t]*$/ {
    tag = substr($2, 1, length($2) - 1);
    if (curr_high == 0) {
	printf "#define %-40s (%dL)\n", tag, curr_sign * curr_low > h_file;
    } else {
	printf "#define %-40s (%d%06dL)\n", tag, curr_sign * curr_high , curr_low > h_file;
    }
    curr_low += curr_sign;
    if (curr_low >= mod_base) {
	curr_low -= mod_base;
	curr_high++;
    }
    if (curr_low < 0) {
	curr_low += mod_base;
	curr_high--;
    }

    text = "";
    for (i = 3; i <= NF; i++) {
	text = text FS $i;
    }

    text = substr(text, 2, length(text) - 1);
    printf "\t%s,\n", text > c_file;
    tab_count++;
}

/^[ \t]*(error_code|ec)[ \t]+[A-Z_0-9]+,[ \t]*".*\\[ \t]*$/ {
    tag = substr($2, 1, length($2) - 1);
    if (curr_high == 0) {
	printf "#define %-40s (%dL)\n", tag, curr_sign * curr_low > h_file;
    } else {
	printf "#define %-40s (%d%06dL)\n", tag, curr_sign * curr_high , curr_low > h_file;
    }
    curr_low += curr_sign;
    if (curr_low >= mod_base) {
	curr_low -= mod_base;
	curr_high++;
    }
    if (curr_low < 0) {
	curr_low += mod_base;
	curr_high--;
    }

    text = "";
    for (i = 3; i <= NF; i++) {
	text = text FS $i;
    }

    text = substr(text, 2, length(text) - 2);
    cont_buf = text;
    cont++;
}

/^[ \t]*".*\\[ \t]*$/ {
    if (skipone) {
	text = substr($0, 1, length($0) - 1);
	cont_buf = text;
	cont++;
    }
    skipone = 0;
}

{
    if (skipone) {
	printf "\t%s,\n", $0 > c_file;
    }
    skipone = 0;
}

END {
    if (com_err == "false") {
	print "#define initialize_" tab_name "_error_table()" > h_file;
    } else {
	print "extern void initialize_" tab_name "_error_table ();" > h_file;
    }

    if (base_high == 0) {
	printf "#define ERROR_TABLE_BASE_%s (%dL)\n", tab_name, base_sign * base_low > h_file;
    } else {
	printf "#define ERROR_TABLE_BASE_%s (%d%06dL)\n", tab_name, base_sign * base_high, base_low > h_file;
    }
    print "" > h_file;
    print "/* for compatibility with older versions... */" > h_file;
    print "#define init_" tab_name "_err_tbl initialize_" tab_name "_error_table" > h_file;
    print "#define " tab_name "_err_base ERROR_TABLE_BASE_" tab_name > h_file;

    print "    0" > c_file;
    print "};" > c_file;
    print "" > c_file;
    print "struct error_table {" > c_file;
    print "    char const * const * msgs;" > c_file;
    print "    long base;" > c_file;
    print "    int n_msgs;" > c_file;
    print "};" > c_file;
    print "struct et_list {" > c_file;
    print "    struct et_list *next;" > c_file;
    print "    const struct error_table * table;" > c_file;
    print "};" > c_file;
    print "extern struct et_list *_et_list;" > c_file;
    print "" > c_file;
    if (base_high == 0) {
	printf "static const struct error_table et = { text, %dL, %d };\n", base_sign * base_low, tab_count > c_file;
    } else {
	printf "static const struct error_table et = { text, %d%06dL, %d };\n", base_sign * base_high, base_low, tab_count > c_file;
    }
    print "" > c_file;
    print "static struct et_list link = { 0, 0 };" > c_file;
    print "" > c_file;
    print "void initialize_" tab_name "_error_table (NOARGS) {" > c_file;
    print "    if (!link.table) {" > c_file;
    print "        link.next = _et_list;" > c_file;
    print "        link.table = &et;" > c_file;
    print "        _et_list = &link;" > c_file;
    print "    }" > c_file;
    print "}"> c_file;
}
EOF
