# QTAwk Utility To Mimic QTGrep Program -- Only
# Slower, but only a little
#
# Scan Specified Files For Matches To Multiple
# Regular Expressions And Keywords And Print
# Matching Lines With Line Number And Number Of
# Match
#
# Have Explicitly  Written Regular Expressions  To
# Match In 'BEGIN' Action And Must Change  'BEGIN'
# Action For Differing Searches.
#
# This version searches for matches in C language source
# files for:
#  1: function definitions using the original K&R style
#  2: function definitions using the newer ANSI standard style
#  3: variable definitions and initializations. Two kinds
#     of variables are found:
#     1. global variables
#     2. function parameter variable definition folllowing
#        the original K&R style
#  4: function prototype statements
#  5: braces opening and closing functions, '{' and '}'
#     braces MUST be in column 1 to distingush from other
#     block statements. Used to highlight function definitions
#     from other lines in listing.
#
# Use FILE_SEARCH And FILE_SEARCH_PAT Built-In
# Variables To Speed Search  - Process ONLY  Those
# Lines   Which    Match   An    Element   In    The
# FILE_SEARCH_PAT Array.
#
# Set SPAN_RECORDS To:
#   TRUE  - Allow Matches To Span Multiple Records
#   FALSE - Confine Matches To A Single Record
# Have Set Span_records To TRUE In This Version.
#
# Set DELAY_INPUT_PARSE to TRUE since individual fields are
# not used. Thus save the time used in splitting
#
BEGIN {
      # Expressions To Find All C Function Definitions And Variable Definitions
      #  C function definitions are assumed to be in the following format:
      #
      #  K&R style function definition - each function parameter defined
      #       on line following function declaration
      #   Column 1
      #   V
      #   unsigned char *strchr(string, chr) /* optional comment */
      #   const unsigned char *string;    /* optional comment */
      #   const unsigned char chr;        /* optional comment */
      #   {
      #    .
      #    .
      #    .
      #   }
      #
      #  ANSI C style function definition - each function parameter defined
      #       on line following function declaration
      #   Column 1
      #   V
      #   unsigned char *strchr(          /* optional comment */
      #   const unsigned char *string,    /* optional comment */
      #   const unsigned char chr)        /* optional comment */
      #   {
      #    .
      #    .
      #    .
      #   }
      #
      # Define Sub-expressions For Use In Main Expressions:
      #
      # Define C Name Expression
    c_n = /[A-Za-z_][A-Za-z0-9_]*/;
      #
      # Define C Name Expression with braces
    c_nb = /[A-Za-z_][A-Za-z0-9_\[\]]*/;
      #
      # Define C Comment Expression
      # Note: Does NOT Allow Comment To Span Lines
    c_c = /(\/\*.*\*\/)/;
      #
      # Define Single Line Comment
    c_slc = /({_w}*{c_c}{_w}*)?/;
      #
      # Define C Name With Pointer
    c_np = /\**{c_nb}/;
      #
      # Define C Name With Pointer Or Address
    c_ni = /[\*&]*{c_nb}/;
      #
      # Define C Function Type And Name Declaration
    c_fname = /{c_n}({_w}+{c_np})+/;
      #
      # K&R Style Function Declaration:
      #
      # Define Expression For First Argument In Function List
    c_first_arg = /({_w}*{c_ni}{_w}*)+/;
      #
      # Define Expression For Remaining Argument In Function List
    c_rem_arg = /(,{c_first_arg})*/;
      #
      # Define C Function Argument List
    c_arg_list = /\(({c_first_arg}{c_rem_arg})?\)/;
      #
      # ANSI Style Function Declaration:
      #
      # Define C Arguments - Type And Name
    c_argn = /{_w}*{c_n}({_w}+{c_np})*/;
      # Define expression for argument in function list
    c_arg_ns = /({c_slc}\n{c_argn})/;
      # Define C function argument list
    c_arg_list_ns = /\(({c_arg_ns}(,{_w}*{c_arg_ns})*)?\)/;
      #
      # define initializers for global variables
    gv_init = /({_w}*={_w}*({c_n}|"[!"]*"|{_i}|{_r}|{[!{}]+}))?/;
      #
      #
      # Now define the full patterns to search for in C source files
      #
      # -1-
      # Expression To Find All C Function Definitions - K&R Style Definition
    FILE_SEARCH_PAT[1] = /^{c_fname}{c_arg_list}{c_slc}$/;
      # description of match for output
    comment_exp[1] = "K&R Style Definition";
      #
      # -2-
      # Expression To Find All C Function Definitions - ANSI C Style Definition
    FILE_SEARCH_PAT[2] = /^{c_fname}{c_arg_list_ns}{c_slc}$/;
      # description of match for output
    comment_exp[2] = "ANSI C Style Definition";
      #
      # -3-
      # Expression To Find C Variable Declarations/Initializers
    FILE_SEARCH_PAT[3] = /^{c_fname}{gv_init};{c_slc}$/;
      # description of match for output
    comment_exp[3] = "Variable Declarations/Initializers";
      #
      # -4-
      # Expression to find function prototypes - only difference from pattern #1, ending ';'
    FILE_SEARCH_PAT[4] = /^{c_fname}{c_arg_list};$/;
      # description of match for output
    comment_exp[4] = "Function Prototypes";
      #
      # -5-
      # Tack Function Block Delimitors After Functions And Argument List To
      # Distinguish From Global Variables
      # Expression To Find Function Block Delimitors -
      # allow comment contained entirely on same line
    FILE_SEARCH_PAT[5] = /^[{}]{c_slc}$/;
      # description of match for output
    comment_exp[5] = "Function block delimiters";

    # set built-in variable to enable faster scan
    FILE_SEARCH = TRUE;

    # set built-in variable to enable multi-record pattern match
    # needed to match newer ANSI style function definitions
    SPAN_RECORDS = TRUE;

    # Delay Parsing the input record into fields - fields not used so do not parse
    DELAY_INPUT_PARSE = TRUE;
}

# accumulate statistics on matches and print match
# when printing match, indicate multi-record match by preceding
# record number with '0's
    {
    local mi = MATCH_INDEX + 0;
    local rf;
    local rn;

    # count for each pattern matched
    file_pat_count[mi]++;

    # total match count
    file_accum_pat_count++;

    # count newlines in $0
    rn = gsub(/\n/,"\n");

    # Count of records matched
    file_record_count += rn + 1;

    # format match header - precede multi-record match record numbers with '0's
    if ( rn ) {
        $0 = sprintf("%-12s: %6uE: %06uR: ",FILENAME,mi,FNR - rn) >< $0;
      } else {
        $0 = sprintf("%-12s: %6uE: %6uR: ",FILENAME,mi,FNR - rn) >< $0;
    } ## endif

    # replace newlines with header string
    if ( rn ) {
        gsub(/\n/,sprintf("\n%-12s: %6uE: %06uR: ",FILENAME,mi,FNR - --rn));
    } ## endif

    print $0;
}

# print statistics for individual file
FINAL {
    local i;

    printf("%s%s\n",FILEPATH,FILENAME);

    printf("Match           Count: %6u\n",file_accum_pat_count);
    tot_accum_pat_count += file_accum_pat_count;
    file_accum_pat_count = 0;

    for ( i in file_pat_count ) {
        printf("  Pattern %5u Count:   %6u - %s\n",i,file_pat_count[i],comment_exp[i]);
        tot_pat_count[i] += file_pat_count[i];
    }
    deletea file_pat_count;

    printf("Matching Record Count: %6u\n",file_record_count);
    tot_record_count += file_record_count;
    file_record_count = 0;;

    printf("Records       Scanned: %6u\n",FNR);
}

# print statistics for all files
END{
    printf("\n\nMatch           Count: %6u\n",tot_accum_pat_count);

    for ( i in tot_pat_count ) {
        printf("  Pattern %5u Count:   %6u - %s\n",i,tot_pat_count[i],comment_exp[i]);
    }

    printf("Matching Record Count: %6u\n",tot_record_count);

    printf("Records       Scanned: %6u\n",NR);
}