#! /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) } }