#!/usr/bin/perl
#
#   subst [IS] v=vaRiaBle V=SYMBOL Y=Re_Cord F=Field t=typ <proto.F >foo.F
#
# This is a program that transforms the "generic" chunks  of  text  that  are
# scattered  throughout  the  SNMP  agent  code,  replacing  several embedded
# strings with our command-line args.  The chunks are shown  in  the  generic
# text  as letter triplets, such as "vvv" or "TTT"; the command-line argument
# "x=fuBar" means to replace the "xxx" chunks with the string "fuBar".
#
# Thus for the command:
#      subst S v=swsfmptBar V=XSWSFMPTSBP7BAR Y=4_SFM_Perf_progress \
#          F=SBBar7 o=28 l=4 t=sfm i=1
# the transformations that we make are:
#   vvv -> swsfmptBar          (Symbolic name of SNMP variable)
#   VVV -> XSWSFMPTSBP7BAR     (Symbolic name of variable's "magic" constant)
#   YYY -> 4_SFM_Perf_progress (Record's name, minus initial "CM" or "CT")
#   FFF -> SBBar7      (Field within record)
#   ooo -> 28          (Field offset)
#   lll -> 4           (Field length)
#   ttt -> sfm         (Device type for pointers)
#   TTT -> SFM         (Device type for messages)
#   iii -> 1           (Difference between DeviceID and SNMP index)
# where vvv is the SNMP MIB variable name  (which  must  also  conform  to  C
# variable-name  rules), VVV is the #defined constant in mibtbl.h, and FFF is
# the field name in the cm4_record.h header file  for  the  record  Rec_name.
# Note  that  the  file  "icd.t" (produced as a side-effect of using icd-c to
# make cm4record.h) contains a list of many of the fields you need for  calls
# on this program.
#
# The one-char field 'B', 'I' or 'S', if present, tells subst whether this is
# is an Byte, Integer or String variable.  This is used for a special kludge:
# when the "RVAR;" command is encountered, it is expanded to the  lines  that
# build  the  return variable.  This code is different for integer and string
# variables, and no template is feasible, so it  is  just  generated  in  its
# entirety.   For  integer  variables,  the  value  is loaded into the global
# "rval"; for strings, the "bp" parameter is pointed directly at the value.
#
# The difference between 'B' and 'S' is that 'S'  includes  "[0]"  after  the
# field  name,  while 'B' doesn't; this is solely to handle stupid C compiler
# complaints about misuse of '&' before arrays.  Depending on the version  of
# the ICD (and your compiler), 'B' may or may not be needed.
#
# Input lines with '|' in col 1 are dropped, so that we have a way to comment
# the prototype code without the comments appearing in the  output.   C-style
# comments are processed as code.
#
# The s, S, T and V chunks are special cases which, if omitted, will be built
# from other chunks, as described below.

require "cmagic.pl";

for $a (@ARGV) {
	if ($a =~ /^.$/) {
		$type = $a;
	} elsif ($a =~ /^(\w)=(.*)/) {
		$x{$1} = $2;
	} else {
		print STDERR "$0: Unknown arg \"$a\" ignored.\n"; 
	}
}
$D = $x{'D'};
$d = $x{'d'};
$F = $x{'F'};
$i = $x{'i'};
$Y = $x{'Y'};
$o = $x{'o'};
$l = $x{'l'};
$S = $x{'S'};
$s = $x{'s'};
$T = $x{'T'};
$t = $x{'t'};
$V = $x{'V'};
$v = $x{'v'};
# The following variables can be calculated if not given on the
# command line:
if (!$D) {
	$D = $d;
	substr($D,0,1) =~ tr/a-z/A-Z/;
	$x{'D'} = $D;
}
if (!$V) {
	@V = &cmagic($v);	# Magic number and (ignored) code.
	$x{'V'} = $V[0];
}
if (!$T){
	($T =  "$t") =~ tr/a-z/A-Z/;	# The type of device this is for.
	$x{'T'} = $T;
}
if ($o eq 'P' || $o eq 'R') {	# Shorthand for the offset in cm4_fields.h
	$o = 'CO4_' . $o . '_' . $v;
	$x{'o'} = $o;
}
if ($l eq 'P' || $l eq 'R') {	# Shorthand for the length in cm4_fields.h
	$l = 'CL4_' . $l . '_' . $v;
	$x{'l'} = $l;
}
if (!$s) {
	$s = ($type eq 'S') ? "MinSS(p->$F)" : "sizeof(p->$F)";	# The size of the datum.
	$x{'s'} = $s;
}
if (!$S) {
	$S = "CS4_$Y";		# The size of the record.
	$x{'S'} = $S;
}
if (!$x{'i'}) {	# Add to index to get DeviceID.
	$x{'i'} = '0';
}
if (!$x{'p'}) {	# PortID value for lookups.
	$x{'p'} = '0';
}
for (<STDIN>) {
	next if /^\|/;		# Comment that doesn't appear in the C code.
	if (/^(\s+)RVAL;/){	# Generate return-val code depending on type.
		$ind = $1;		# Preserve the indentation for readability.
		if (!$type) {	# Complain if type arg omitted.
			print STDERR "Can't expand RVAL; need 1-char type arg.\n";
		} elsif ($type eq 'I') {	# Integer.
			print "${ind}rval = htonl(p->$F);\n";
			print "${ind}rp = bp;\n";
			print "${ind}rp->v =  (BP) \&rval;\n";
			print "${ind}rp->l = sizeof(rval);\n";
			print "${ind}D5(\"Fld type I is offset %d size %d $Y.$F\"\n";
			print "${ind}\t,Pdiff(&p->$F,p),sizeof(p->$F));\n";
		} elsif ($type eq 'S') {	# String.
			print "${ind}rp = bp;\n";
			print "${ind}rp->v = (BP)\&(p->$F[0]);\n";
			print "${ind}rp->l = $s;\n";
			print "${ind}D5(\"Fld type S is offset %d size %d $Y.$F[0]\"\n";
			print "${ind}\t,Pdiff(rp->v,p),rp->l);\n";
		} elsif ($type eq 'B') {	# Byte vector.
			print "${ind}rp = bp;\n";
			print "${ind}rp->v = (BP) (p->$F);\n";
			print "${ind}rp->l = $s;\n";
			print "${ind}D5(\"Fld type B is offset %d size %d $Y.$F\"\n";
			print "${ind}\t,Pdiff(rp->v,p),rp->l);\n";
		} else {
			print STDERR "$0: RVAL type unknown; need [BSI] option.\n";
		}
	} elsif (/^(\s+)RBAD;/){	# Generate bad--val code depending on type.
		$ind = $1;		# Preserve the indentation for readability.
		if (!$type) {	# Complain if type arg omitted.
			print STDERR "Can't expand RVAL; need 1-char type arg.\n";
		} elsif ($type eq 'I') {	# Integer.
			print "${ind}rval = htonl(f_value);\n";
			print "${ind}rp = bp;\n";
			print "${ind}rp->v =  (BP) \&rval;\n";
			print "${ind}rp->l = sizeof(rval);\n";
		} elsif ($type eq 'S') {	# String.
			print "${ind}rp = bp;\n";
			print "${ind}rp->v = (BP)unknown;\n";
			print "${ind}rp->l = Strlen(unknown);\n";
		} elsif ($type eq 'B') {	# Byte vector.
			print "${ind}rp = bp;\n";
			print "${ind}rp->v = (BP)unknown;\n";
			print "${ind}rp->l = Strlen(unknown);\n";
		} else {
			print STDERR "$0: RBAD type unknown; need [BSI] option.\n";
		}
	} elsif (/^(\s+)SVAL;/){	# Generate set-val code depending on type.
		$ind = $1;		# Preserve the indentation for readability.
		$var = "${t}_${v}";
		if (!$type) {	# Complain if type arg omitted.
			print STDERR "Can't expand RVAL; need 1-char type arg.\n";
		} elsif ($type eq 'I') {	# Integer.
			print "${ind}$var = htonl(ap->v.i);\n";
			print "${ind}D4(\"$var=%d.\",$var);\n";
		} elsif ($type eq 'B') {	# String.
			print "${ind}MakBlkM(&$var,ap->p.s.v,ap->p.s.l,\"$var\");\n";
			print "${ind}D4(\"$var=%s len=%d.\",DspSV($var),ap->p.s.l);\n";
		} elsif ($type eq 'S') {	# String.
			print "${ind}MakBlkM(&$var,ap->p.s.v,ap->p.s.l,\"$var\");\n";
			print "${ind}D4(\"$var=%s len=%d.\",DspSV($var),ap->p.s.l);\n";
		} else {
			print STDERR "$0: SVAL type unknown; need [BSI] option.\n";
		}
	} else {	# Make all possible substitutions in the line.
		for $x (keys x) {
			$pat = "$x$x$x";
			$rpl = "$x{$x}";
			s/$pat/$rpl/g;
		}
		print;
	}
}
