#! /usr/bin/awk -f
# addcols.awk generates a tally.
# It reads input lines and looks at the end of each line for a real
# number preceded by an integer. The integer is optional and
# defaults to 1.
# The output line consists of:
# whatever preceded the numbers on the input line;
# the integer;
# the real number and
# the product of the two numbers.
# Input lines that are blank, contain only white space or that have "#"
# as the first non-whitespace character are printed as they are.
# The grand total of the products of each line is displayed at the end.
#
# Any of the following are valid input lines ( without the "#" ):
# 56
# 4.78
# .78
# 412 4.78
# 2 x 4 412 4.78
# two-by-four's 412 4.78
#######################################################################
# Display error message and set exit status.
function Error(a_Field, ai_Line, as_Message)
{
printf("\"%s\" in line %d %s\n\n", a_Field, ai_Line, as_Message)
EXIT_STATUS_ = EXIT_FAILURE_
}
# Form the first part of the output line.
function FormLineStart(ai_CurrentField,
ls_LineStart, ls_Tmp)
{
ls_LineStart = ""
ls_Tmp = ""
while (ai_CurrentField > 0)
{
ls_Tmp = $ai_CurrentField " " ls_LineStart
ls_LineStart = ls_Tmp
ai_CurrentField--
}
return (ls_LineStart)
}
# Check if argument is an integer.
function IsInteger(ai_Arg,
li_TestResults)
{
li_TestResults = TRUE_
if ( ai_Arg !~ /^[0-9][0-9]*$/ )
{
li_TestResults = FALSE_
}
return (li_TestResults)
}
# Check if arg is a real number.
function IsReal(af_Amt,
li_TestResults)
{
li_TestResults = TRUE_
# Test for:
# At least one digit followed by an optional decimal point and optional
# digits;
# Optional digits followed by a decimal point and digits;
if ( af_Amt !~ /^[0-9][0-9]*\.?[0-9]*$/ && af_Amt !~ /^[0-9]*\.[0-9][0-9]*$/ )
{
li_TestResults = FALSE_
}
return (li_TestResults)
}
# Check for blank lines, lines containing only white space and lines
# beginning with #.
function NonDataLine(as_Line,
li_TestResults)
{
li_TestResults = TRUE_
if ( $0 !~ /^[ \t]*$/ && $0 !~ /^[ \t]*#/ )
{
li_TestResults = FALSE_
}
return (li_TestResults)
}
# Print a separator line.
function Separator( \
li_Count)
{
for ( li_Count = 0; li_Count < WIDTH_LINE_; li_Count++)
{
printf("%s", FILL_CHAR_)
}
print ""
}
BEGIN {
# Widths of fields for printf
WIDTH_QTY_ = 5
WIDTH_AMT_ = 12
WIDTH_PRECISION_ = 2
WIDTH_LINE_ = 78
WIDTH_TOTAL_ = WIDTH_AMT_ + WIDTH_QTY_
WIDTH_GRANDTOTAL_ = WIDTH_TOTAL_ + 3
WIDTH_LINESTART_ = \
WIDTH_LINE_ - WIDTH_QTY_ - WIDTH_AMT_ - WIDTH_TOTAL_
WIDTH_LASTLINESTART_ = WIDTH_LINE_ - WIDTH_GRANDTOTAL_
TRUE_ = 1
FALSE_ = 0
FILL_CHAR_ = "-"
AMT_FIELD_ERR_ = " should be a real number."
EXIT_SUCCESS_ = 0
EXIT_FAILURE_ = 1
EXIT_STATUS_ = EXIT_SUCCESS_
lf_GrandTotal = 0.00
# Print header line.
printf("\n%-*s%*s%*s%*s\n", WIDTH_LINESTART_, "",
WIDTH_QTY_, "Qty", WIDTH_AMT_, "Amt", WIDTH_TOTAL_, "Total")
Separator()
}
# Main
{
lf_Amt = 0.00
li_Qty = 1
lf_Total = 0.00
ls_LineStart = ""
li_CurrentField = NF
if ( NonDataLine($0) )
{
print
next
}
if ( !IsReal($li_CurrentField) )
{
Error( $li_CurrentField, NR, AMT_FIELD_ERR_)
exit (EXIT_STATUS_)
}
lf_Amt = $li_CurrentField
if ( --li_CurrentField > 0 )
{
if ( IsInteger($li_CurrentField) )
{
li_Qty = $li_CurrentField
li_CurrentField--
}
if ( li_CurrentField > 0 )
{
ls_LineStart = FormLineStart(li_CurrentField)
}
}
lf_Total = li_Qty * lf_Amt
lf_GrandTotal += lf_Total
printf("%-*s%*d%*.*f%*.*f\n", WIDTH_LINESTART_, ls_LineStart,
WIDTH_QTY_, li_Qty, WIDTH_AMT_, WIDTH_PRECISION_, lf_Amt,
WIDTH_TOTAL_, WIDTH_PRECISION_, lf_Total)
}
END {
if (EXIT_STATUS_ == EXIT_SUCCESS_ )
{
Separator()
printf("%*s%*.*f\n\n", WIDTH_LASTLINESTART_, "Total",
WIDTH_GRANDTOTAL_, WIDTH_PRECISION_, lf_GrandTotal)
}
}