taking care of two hads

taking care of two hads

#! /bin/awk -f

#check back to back double occurrence of a word

BEGIN {
}

#body
FILENAME != prevfile {
  NR = 1
  prevfile = FILENAME
}
NF > 0{
  if ($1 == lastword && $1 != "had")
     printf "double %s, file %s, line %d\n", $1, FILENAME, NR
  for (i=2; i <= NF; i++)
      if ($i == $(i-1) && $i != "had")
         printf "double %s, file %s, line %d\n", $i, FILENAME, NR
  if (NF > 0){
     lastword = $NF
  }
}
END {
}

#! /bin/awk -f

#check back to back double occurrence of a word

BEGIN {
}

#body
FILENAME != prevfile {
  NR = 1
  prevfile = FILENAME
}
NF > 0{
  if ($1 == lastword)
     printf "double %s, file %s, line %d\n", $1, FILENAME, NR
  for (i=2; i <= NF; i++)
      if ($i == $(i-1))
         printf "double %s, file %s, line %d\n", $i, FILENAME, NR
  if (NF > 0){
     lastword = $NF
  }
}
END {
}


make TOC nicer, find paragraph starts

make TOC nicer, find paragraph starts

BEGIN {
    heading = 0
}

hunt && /class="section_anchor">/ {
    hier[heading] = $0
    sub(/<\/a>/, "", hier[heading])
    sub(/<\/a>/, "", hier[heading])
    here = hier[heading]

    if (heading == 2)
    {
      hier[heading] = "⋅ " hier[heading]
    }
    else if (heading == 3)
    {
      hier[heading] = "– " hier[heading]
    }
    else if (heading >= 4)
    {
      hier[heading] = "— " hier[heading]
    }

    if (slide) print "
"
    print "
"
    slide = 1
    if (heading > 1) {
      print "
" hier[heading] "
"
    }
    for (i=1; i < heading; ++i) {
      print "<h" i " style='color: #C0C0C0;'>" hier[i] "</h" i ">"
    }
    print "<h" heading ">" here
    hunt = 0
    next
}

/
/ {
    heading = 1
    hunt = 1
    next
}

/<\/h[1-9]>/ {
    heading = 0
    print
    next
}

/
/ && heading == 0 {
    heading = 2
    hunt = 1
    next
}

/
/ && heading == 0 {
    heading = 3
    hunt = 1
    next
}

/
/ && heading == 0 {
    heading = 4
    hunt = 1
    next
}

{
  gsub(/
/, "\n&\n")
  print
}

END {
    if (slide) print "
"
}

BEGIN {
    heading = 0
}

hunt && /class="section_anchor">/ {
    hier[heading] = $0
    sub(/<\/a>/, "", hier[heading])
    sub(/<\/a>/, "", hier[heading])
    if (slide) print "
"
    print "
"
    slide = 1
    if (heading > 1) {
	print "
" hier[heading] "
"
    }
    for (i=1; i < heading; ++i) {
	print "<h" i " style='color: #C0C0C0;'>" hier[i] "</h" i ">"
    }
    print "<h" heading ">" hier[heading]
    hunt = 0
    next
}

/
/ {
    heading = 1
    hunt = 1
    next
}

/<\/h[1-9]>/ {
    heading = 0
    print
    next
}

/
/ && heading == 0 {
    heading = 2
    hunt = 1
    next
}

/
/ && heading == 0 {
    heading = 3
    hunt = 1
    next
}

/
/ && heading == 0 {
    heading = 4
    hunt = 1
    next
}

{ print }

END {
    if (slide) print "
"
}


oops, only add ornaments in the TOC

oops, only add ornaments in the TOC

BEGIN {
    heading = 0
}

hunt && /class="section_anchor">/ {
    hier[heading] = $0
    sub(/<\/a>/, "", hier[heading])
    sub(/<\/a>/, "", hier[heading])

    if (heading == 2)
    {
      here = "⋅ " hier[heading]
    }
    else if (heading == 3)
    {
      here = "– " hier[heading]
    }
    else if (heading >= 4)
    {
      here = "— " hier[heading]
    }
    else
    {
      here = hier[heading]
    }

    if (slide) print "
"
    print "
"
    slide = 1
    if (heading > 1) {
      print "
" here "
"
    }
    for (i=1; i < heading; ++i) {
      print "<h" i " style='color: #C0C0C0;'>" hier[i] "</h" i ">"
    }
    print "<h" heading ">" hier[heading]
    hunt = 0
    next
}

/
/ {
    heading = 1
    hunt = 1
    next
}

/<\/h[1-9]>/ {
    heading = 0
    print
    next
}

/
/ && heading == 0 {
    heading = 2
    hunt = 1
    next
}

/
/ && heading == 0 {
    heading = 3
    hunt = 1
    next
}

/
/ && heading == 0 {
    heading = 4
    hunt = 1
    next
}

{
  gsub(/
/, "\n&\n")
  print
}

END {
    if (slide) print "
"
}

BEGIN {
    heading = 0
}

hunt && /class="section_anchor">/ {
    hier[heading] = $0
    sub(/<\/a>/, "", hier[heading])
    sub(/<\/a>/, "", hier[heading])
    here = hier[heading]

    if (heading == 2)
    {
      hier[heading] = "⋅ " hier[heading]
    }
    else if (heading == 3)
    {
      hier[heading] = "– " hier[heading]
    }
    else if (heading >= 4)
    {
      hier[heading] = "— " hier[heading]
    }

    if (slide) print "
"
    print "
"
    slide = 1
    if (heading > 1) {
      print "
" hier[heading] "
"
    }
    for (i=1; i < heading; ++i) {
      print "<h" i " style='color: #C0C0C0;'>" hier[i] "</h" i ">"
    }
    print "<h" heading ">" here
    hunt = 0
    next
}

/
/ {
    heading = 1
    hunt = 1
    next
}

/<\/h[1-9]>/ {
    heading = 0
    print
    next
}

/
/ && heading == 0 {
    heading = 2
    hunt = 1
    next
}

/
/ && heading == 0 {
    heading = 3
    hunt = 1
    next
}

/
/ && heading == 0 {
    heading = 4
    hunt = 1
    next
}

{
  gsub(/
/, "\n&\n")
  print
}

END {
    if (slide) print "
"
}


parse_blast.awk deprecated

parse_blast.awk deprecated

#!/usr/bin/awk -f

function is_repetitive_hit(start,end,threshold,threshold2)
{
  overlap[0]= -1;
  overlap[1]= -10;

  if (region[0]>=start && region[0]<end) {
    overlap[0]=region[0];
    extent[0]=start;
  }
  else if (start>=region[0] && start<region[1]) {
    overlap[0]=start;
    extent[0]=region[0];
  }
  else { # WE HAVE NO OVERLAP, SO JUST RESET AND RETURN */
    region[0]= start; # SAVE THE CURRENT REGION FOR CHECKING THE NEXT ONE*/
    region[1]= end;
    return 0;  # NO REPETITIVE OVERLAP */
  }
  if (region[1]>=start && region[1]<end) {
    overlap[1]=region[1];
    extent[1]=end;
  }
  else {
    overlap[1]=end;
    extent[1]=region[1];
  }
  if (overlap[0]>=0) { # GOT OVERLAP */
    my_length=end-start;
    if (overlap[1]-overlap[0]>threshold*my_length && overlap[1]-overlap[0]>threshold2*(region[1]-region[0])) {
#      printf("REPEAT [%d,%d]: overlaps [%d,%d]\n",start,end,region[0],region[1]);
      region[0]=extent[0]; # SAVE THE UNION OF THE OVERLAPPING REGIONS*/
      region[1]=extent[1];
      return 1; # THIS IS A REPEAT!*/
    }
  }

  region[0]= start; # SAVE THE CURRENT REGION FOR CHECKING THE NEXT ONE*/
  region[1]= end;
  return 0;  # NO REPETITIVE OVERLAP */
}

function print_interval()
{
    if (mode=="all") {
	if (IDENTITY_PERCENT+0<identity_min) {
	    return;
	}
	if (BLAST_FRAME+0>=0) {
	    printf("MATCH_INTERVAL\t%d\t%s\t%s\t%d\t%s\t%s\t%s\t%d\t%d\t%d\n",hit_id,SEQ_ID,SUBJ_ID,BLAST_SCORE+0,E_VALUE,IDENTITY_PERCENT,BLAST_FRAME,q_interval_start,i_query-q_interval_start,s_interval_start);
	}
	else {
	    printf("MATCH_INTERVAL\t%d\t%s\t%s\t%d\t%s\t%s\t%s\t%d\t%d\t%d\n",hit_id,SEQ_ID,SUBJ_ID,BLAST_SCORE+0,E_VALUE,IDENTITY_PERCENT,BLAST_FRAME,q_interval_start,i_query-q_interval_start,i_subj-BLAST_FRAME);
	}
    }
    else if (mode=="detail") {
	printf("%d\t%d\t%d\t%d\t%d\n",hit_id,q_interval_start,i_query-1,s_interval_start,i_subj-BLAST_FRAME);
    }
}

function print_hit()
{
  if (remove_repeats) {
    if (is_repetitive_hit(QUERY_START,QUERY_END,0.9,0.8)) {
      if (nrepeat++ > 20) {
	printf("REMOVING REPEAT %d-%d\n",QUERY_START,QUERY_END);
        return 0;
      }
    }
    else {
      nrepeat=0;
    }
  }
    hit_id++;
    query_len=length(QUERY_SEQ);
    q_interval_start= -1;
    i_query=QUERY_START;
    if (BLAST_FRAME+0>0) {
	i_subj=SUBJ_START;
    }
    else {
	i_subj=SUBJ_END;
    }
    for (i=1;i<=query_len;i++) {
	if (substr(QUERY_SEQ,i,1)=="-") {
	    if (q_interval_start>0) {
		print_interval();
	    }
	    q_interval_start= -1;
	    i_subj+= BLAST_FRAME;
	}
	else {
	    if (substr(SUBJ_SEQ,i,1)=="-") {
		if (q_interval_start>0) {
		    print_interval();
		}
		q_interval_start= -1;
	    }
	    else {
		if (q_interval_start<0) {
		    q_interval_start=i_query;
		    s_interval_start=i_subj;
		}
		i_subj+= BLAST_FRAME;
	    }
	    i_query++;
	}
    }
    if (q_interval_start>0) {
	print_interval();
    }
    gsub("-","",QUERY_SEQ);
    gsub("-","",SUBJ_SEQ);
    if (mode=="summary") {
	printf("%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",hit_id,SEQ_ID,SUBJ_ID,BLAST_SCORE,E_VALUE,IDENTITY_PERCENT,BLAST_FRAME,QUERY_START,QUERY_END,SUBJ_START,SUBJ_END);
    }
    QUERY_START=9999999999;
    QUERY_END= -1;
    SUBJ_START=9999999999;
    SUBJ_END= -1;
}

/^Query=/ {
    SEQ_ID=$2;
    TITLE=substr($0,index($0,$3));
    QUERY_START=9999999999;
    QUERY_END= -1;
}

/No hits/ {
}

/^>/ {
    if (QUERY_START<QUERY_END) {
	print_hit();
    }
    SUBJ_ID=substr($1,2);
    SUBJ_TITLE=substr($0,index($0,$2));
    BLAST_FRAME="+1"
}

/^ Score =/ {
    if (QUERY_START<QUERY_END) {
	print_hit();
    }
    BLAST_SCORE=$3;
    E_VALUE=$8;
    if (substr(E_VALUE,1,1)=="e") {
	E_VALUE="1" E_VALUE;
    }
    if (E_VALUE+0.0 == 0.0) {
	E_VALUE=300;
    }
    else {
	E_VALUE= -log(E_VALUE+0.0)/log(10.0);
	if (E_VALUE=="inf") {
	    E_VALUE=300;
	}
    }
    QUERY_SEQ="";
    SUBJ_SEQ="";
    SUBJ_START=9999999999;
    SUBJ_END= -1;
}

/^  Database:/ {
    if (QUERY_START<QUERY_END) {
	print_hit();
    }
}

/Identities =/ {
    IDENTITY_PERCENT=substr($4,2);
    IDENTITY_PERCENT=substr(IDENTITY_PERCENT,1,index(IDENTITY_PERCENT,"%")-1);
}

/^ Frame =/ {
    BLAST_FRAME=$3;
}

/^ Strand = / {
    if ($5=="Plus") {
	BLAST_FRAME="+1";
    }
    else if ($5=="Minus") {
	BLAST_FRAME="-1";
    }
}

/^Query:/ {
    if ($2<QUERY_START) {
	QUERY_START=$2;
    }
    if ($4<QUERY_START) {
	QUERY_START=$4;
    }
    if ($2>QUERY_END) {
	QUERY_END=$2;
    }
    if ($4>QUERY_END) {
	QUERY_END=$4;
    }
    QUERY_SEQ=QUERY_SEQ $3;
}

/^Sbjct:/ {
    if ($2<SUBJ_START) {
	SUBJ_START=$2;
    }
    if ($4<SUBJ_START) {
	SUBJ_START=$4;
    }
    if ($2>SUBJ_END) {
	SUBJ_END=$2;
    }
    if ($4>SUBJ_END) {
	SUBJ_END=$4;
    }
    SUBJ_SEQ=SUBJ_SEQ $3;
}


Use SListRef instead of UnsafeMutablePointer<GSList>!

#!/usr/bin/awk
#
# Patch the generated wrapper Swift code to handle special cases
#
BEGIN { depr_init = 0 ; comment = 0 ; slist = 0 }
/open .* ColorSelection/ { depr_init = 1 }
/public .* ColorSelection/ { depr_init = 1 }
/public .* HSV/ { depr_init = 1 }
/open .* HSV/ { depr_init = 1 }
/open .* HSV/ { depr_init = 1 }
/func getColumnHeaderCells/ { comment = 1 }
/func getRowHeaderCells/ { comment = 1 }
/^[^ ]/ { slist = 0 }
/ UnsafeMutablePointer<GSList>! {/ {
	slist = 1
	gsub("UnsafeMutablePointer.GSList..", "SListRef?")
}
/return cast.rv.$/ {
	if (slist) {
		gsub("cast.rv.", "rv.map { SListRef($0) }")
	}
}
/cast.newValue./ {
	if (slist) {
		gsub("cast.newValue.", "newValue.map { $0.ptr }")
		slist = 0
	}
}
/^    }$/ { slist = 0 }
/ init.. {/ {
	if (depr_init) {
		printf("@available(*, deprecated) ")
		depr_init = 0
	}
}
/ init. title:/ {
	if (depr_init) {
		printf("@available(*, deprecated) ")
		depr_init = 0
	}
}
/^open class RadioButton:/ {
	print
	print "    /// Convenience constructor for creating a RadioButton with a text label,"
	print "    /// creating a new group."
	print "    ///"
	print "    /// - Parameter label: the label to use for the button"
	print "    convenience init(label: UnsafePointer<gchar>) {"
	print "        let rv = gtk_radio_button_new_with_label(nil, label)"
	print "        self.init(cast(rv))"
	print "    }"
	print ""
	print "    /// Convenience constructor for creating a RadioButton with a text label"
	print "    /// that contains menomics creates a new group.  Underscores in `label`"
	print "    /// indicate the mnemonic for the button."
	print "    ///"
	print "    /// - Parameter label: the label (including mnemonic) to use for the button"
	print "    convenience init(mnemonic label: UnsafePointer<gchar>) {"
	print "        let rv = gtk_radio_button_new_with_mnemonic(nil, label)"
	print "        self.init(cast(rv))"
	print "    }"
	print ""
	next
}
/^open class RadioMenuItem:/ {
	print
	print "    /// Convenience constructor for creating a RadioMenuItem with a text label,"
	print "    /// creating a new group."
	print "    ///"
	print "    /// - Parameter label: the label to use for the button"
	print "    convenience init(label: UnsafePointer<gchar>) {"
	print "        let rv = gtk_radio_menu_item_new_with_label(nil, label)"
	print "        self.init(cast(rv))"
	print "    }"
	print ""
	print "    /// Convenience constructor for creating a RadioMenuItem with a text label"
	print "    /// that contains menomics and creates a new group.  Underscores in `label`"
	print "    /// indicate the mnemonic for the button."
	print "    ///"
	print "    /// - Parameter label: the label (including mnemonic) to use for the button"
	print "    convenience init(mnemonic label: UnsafePointer<gchar>) {"
	print "        let rv = gtk_radio_menu_item_new_with_mnemonic(nil, label)"
	print "        self.init(cast(rv))"
	print "    }"
	print ""
	next
}
/^open class RadioToolButton:/ {
	print
	print "    /// Convenience constructor for creating a RadioToolButton group."
	print "    convenience init() {"
	print "        let rv = gtk_radio_tool_button_new(nil)"
	print "        self.init(cast(rv))"
	print "    }"
	print ""
	next
}
// {
	if (comment) {
		printf("// ")
	}
	print
}
/^    }$/ { comment = 0 }

#!/usr/bin/awk
#
# Patch the generated wrapper Swift code to handle special cases
#
BEGIN { depr_init = 0 ; comment = 0 }
/open .* ColorSelection/ { depr_init = 1 }
/public .* ColorSelection/ { depr_init = 1 }
/public .* HSV/ { depr_init = 1 }
/open .* HSV/ { depr_init = 1 }
/open .* HSV/ { depr_init = 1 }
/func getColumnHeaderCells/ { comment = 1 }
/func getRowHeaderCells/ { comment = 1 }
/ init.. {/ {
	if (depr_init) {
		printf("@available(*, deprecated) ")
		depr_init = 0
	}
}
/ init. title:/ {
	if (depr_init) {
		printf("@available(*, deprecated) ")
		depr_init = 0
	}
}
/^open class RadioButton:/ {
	print
	print "    /// Convenience constructor for creating a RadioButton with a text label,"
	print "    /// creating a new group."
	print "    ///"
	print "    /// - Parameter label: the label to use for the button"
	print "    convenience init(label: UnsafePointer<gchar>) {"
	print "        let rv = gtk_radio_button_new_with_label(nil, label)"
	print "        self.init(cast(rv))"
	print "    }"
	print ""
	print "    /// Convenience constructor for creating a RadioButton with a text label"
	print "    /// that contains menomics creates a new group.  Underscores in `label`"
	print "    /// indicate the mnemonic for the button."
	print "    ///"
	print "    /// - Parameter label: the label (including mnemonic) to use for the button"
	print "    convenience init(mnemonic label: UnsafePointer<gchar>) {"
	print "        let rv = gtk_radio_button_new_with_mnemonic(nil, label)"
	print "        self.init(cast(rv))"
	print "    }"
	print ""
	next
}
/^open class RadioMenuItem:/ {
	print
	print "    /// Convenience constructor for creating a RadioMenuItem with a text label,"
	print "    /// creating a new group."
	print "    ///"
	print "    /// - Parameter label: the label to use for the button"
	print "    convenience init(label: UnsafePointer<gchar>) {"
	print "        let rv = gtk_radio_menu_item_new_with_label(nil, label)"
	print "        self.init(cast(rv))"
	print "    }"
	print ""
	print "    /// Convenience constructor for creating a RadioMenuItem with a text label"
	print "    /// that contains menomics and creates a new group.  Underscores in `label`"
	print "    /// indicate the mnemonic for the button."
	print "    ///"
	print "    /// - Parameter label: the label (including mnemonic) to use for the button"
	print "    convenience init(mnemonic label: UnsafePointer<gchar>) {"
	print "        let rv = gtk_radio_menu_item_new_with_mnemonic(nil, label)"
	print "        self.init(cast(rv))"
	print "    }"
	print ""
	next
}
/^open class RadioToolButton:/ {
	print
	print "    /// Convenience constructor for creating a RadioToolButton group."
	print "    convenience init() {"
	print "        let rv = gtk_radio_tool_button_new(nil)"
	print "        self.init(cast(rv))"
	print "    }"
	print ""
	next
}
// {
	if (comment) {
		printf("// ")
	}
	print
}
/^    }$/ { comment = 0 }


Initial cut at processing all BritBox programs

BEGIN {
    FS="\t"
    print "Sortkey\tTitle\tSeasons\tDuration\tYear\tRating\tDescription"
}

# if needed for debugging record placment, replace "/nosuchrecord/" below
/nosuchrecord/ {
    print ""
    print NR " - " $0
    for ( i = 1; i <= NF; i++ ) {
        print "field " i " = " $i
    }
}

{
    for ( i = 1; i <= NF; i++ ) {
        if ($i == "null")
            $i = ""
    }
}

/\/us\/movie\/|\/us\/show\//{
    URL = $3
    showTitle = $4
    Year = $5
    NumSeasons = $6
    Duration = $7
    Rating = $8
    Description = $9

    sub (/ Seasons?/,"",NumSeasons)
    sub( / min/,"",Duration)

    # Convert duration from minutes to HMS
    secs = 0
    mins = Duration % 60
    hrs = int(Duration / 60)
    HMS = sprintf ("%02d:%02d:%02d", hrs, mins, secs)
    if (HMS == "00:00:00")
        HMS = ""

    # Titles starting with "The" should not sort based on "The"
    if (match (showTitle, /^The /))
        showTitle = substr(showTitle, 5) ", The"

    # Indicate different types of programs
    showtype = "S"
    if (URL ~ /^\/us\/movie\//)
        showtype = "M"

    # Extract sortkey from URL
    nflds = split (URL,fld,"_")
    if (URL ~ /_[[:digit:]]*$/) {
        sortkey = sprintf ("%s%05d", showtype, fld[nflds])
        printf \
            ("%s - %s\t=HYPERLINK(\"https://www.britbox.com%s\";\"%s\"\)\t%s\t%s\t%s\t%s\t%s\n",\
             showTitle, sortkey, URL, showTitle, NumSeasons, HMS, Year, Rating, Description)
    }
}

BEGIN {
    FS="\t"
    print "Sortkey\tTitle\tSeasons\tDuration\tYear\tRating\tDescription"
}

# if needed for debugging record placment, replace "/nosuchrecord/" below
/nosuchrecord/ {
    print ""
    print NR " - " $0
    for ( i = 1; i <= NF; i++ ) {
        print "field " i " = " $i
    }
}

{
    for ( i = 1; i <= NF; i++ ) {
        if ($i == "null")
            $i = ""
    }
}

/\/us\/movie\/|\/us\/show\//{
    URL = $3
    showTitle = $4
    Year = $5
    NumSeasons = $6
    Duration = $7
    Rating = $8
    Description = $9

    sub (/ Season[s]/,"",NumSeasons)
    sub( / min/,"",Duration)

    # Convert duration from minutes to HMS
    secs = 0
    mins = Duration % 60
    hrs = int(Duration / 60)
    HMS = sprintf ("%02d:%02d:%02d", hrs, mins, secs)

    # Titles starting with "The" should not sort based on "The"
    if (match (showTitle, /^The /)) {
        showTitle = substr(showTitle, 5) ", The"
    }
}

/\/us\/movie\// {
    # Extract movie sortkey from URL
    nflds = split (URL,fld,"_")
    if (URL ~ /_[[:digit:]]*$/) {
        sortkey = sprintf ("M%05d", fld[nflds])
        printf \
            ("%s %s - mv\t=HYPERLINK(\"https://www.britbox.com%s\";\"%s, %s, %s\"\)\t\t%s\t%s\t%s\t%s\n", \
             showTitle, sortkey, URL, showTitle, sortkey, \
             Title, HMS, Year, Rating, Description)
    }
    next
}

/\/us\/show\// {
    # Extract show sortkey from URL
    nflds = split (URL,fld,"_")
    if (URL ~ /_[[:digit:]]*$/) {
        sortkey = sprintf ("S%05d", fld[nflds])
        printf \
            ("%s %s - sh\t=HYPERLINK(\"https://www.britbox.com%s\";\"%s, %s, %s\"\)\t\t%s\t%s\t%s\t%s\n", \
             showTitle, sortkey, URL, showTitle, sortkey, \
             Title, HMS, Year, Rating, Description)
    }
    next
}


minor

minor

function obsForm()
{

  htmlHeader("Make an observation", 400);

  print "
Make an observation
";
  print "
for tree with tag " f["tag"] "
 (Please double check the tag code)
";
  print "<form enctype=\"multipart/form-data\" method=\"post\" action=\"map?method=obsD\">" ;
  print "
<input type=\"hidden\" name=\"tag\" value=\"" f["tag"] "\" />Type of observation:
<select name=\"type\"><option value=\"bud\">Spring buds just breaking open</option><option value=\"flower\">In flower (any stage)</option><option value=\"fruit\">In fruit (any stage)</option><option value=\"fall\">Outstanding fall color</option></select>
";
  # <option value=\"damage\">Report plant damage</option></select>
";
  print "
Upload a photo (optional)
(small please: <2 MB; NB: iPhone does not support file uploads)
<input type=\"file\" name=\"photo\" />
";
  print "
Your notes (optional):
<textarea name=\"notes\" cols=\"30\" rows=\"2\"></textarea>
";
  print "
Your email (optional)
<input type=\"text\" name=\"email\" size=\"30\"/>
";

  print "
<input type=\"submit\" value=\"Send observation\" />
";
  print "</form>";

  htmlFooter();

}

function obsCapture()
{

# Script to parse return from observation form

# Form template:

# -----------------------------1928954284948244595549659756
# Content-Disposition: form-data; name="type"
#
# fall
# -----------------------------1928954284948244595549659756
# Content-Disposition: form-data; name="notes"
#
# Notes
# -----------------------------1928954284948244595549659756
# Content-Disposition: form-data; name="photo"; filename="img.png"
# Content-Type: image/png
#
# <image>
# -----------------------------1928954284948244595549659756--

  htmlHeader("Data stored",400);

  # View form data during testing with:
  #  RS = "\x04"; getline; print "
" $0;
  #  print $0 > "tmp";
  #  print "
"; exit;

  RS = "\r\n"; getline;
  # Use the first line as the multipart form delimiter
  # escape regexp chars (prob just `-'):
  gsub(/\-/,"\\-",$0); gsub(/\*/,"\\*",$0);
  gsub(/\./,"\\.",$0); gsub(/\?/,"\\?",$0);
  RS = $0 "[^\r]*" "\r?\n" ;
  recdate = strftime("%Y-%m-%d"); rectime= strftime("%H%M%S") ;
  # had to add the extra regex return because webkit and mozilla use a
  #   different last delimiter ( ----XYZ-- )
  while (getline > 0)
  	{
	  # print "
" $0 "
";
	  # split the dataparts
	  lines = split($0, line, "\r\n");
	  # if the media:
	  if ((line[2] ~ /Content\-Type/) && \
	      ("png|jpeg|jpg" ~ gensub(/[^/]*\/([a-z]+)/,"\\1","G",line[2])))
		{
		  ORS = "";
		  split (line[1], lineTmp2, ";");
		  filename = gensub(/ filename="([^"]*)"$/,"\\1","G",lineTmp2[3]);
		  mediatype = gensub(/[^/]*\/([a-z]+)/,"\\1","G",line[2]);
		  outfile = recdate "T" rectime "_" tag "." mediatype ;
		  # likely all to be in line 4, but there may be a \r\n in the
		  #  file, so better to concatenate lines
		  for (x = 4 ; x < lines ; x++)
			{
			  # could add a rand component: srand(); substr(rand(),3,5)
			  # but chance of two uploads in same second very tiny
			  print line[x] "\r\n" >> "obsdata/" outfile;
			}
		  ORS = "\n";
		}
	  else if (line[1] ~ /Content\-Disposition/)
		{
		  split (line[1], lineTmp, ";");
		  name = gensub(/\ name="([^"]*)"$/,"\\1","G",lineTmp[2]);
		  if (name == "tag")  { tag = line[3] }
		  if (name == "type") { obstype = line[3] }
		  if (name == "email") { email = line[3] }
		  if (name == "notes")
			{
			  for (x = 3 ; x < lines ; x++)
				{
				  notes = notes line[x] "\\n" ;
				}
			}
		}
	}

  gsub(/|/,"",notes) ; gsub(/|/,"",filename); # just in case

  # WRITE TO FILE
  print "<date>" recdate \
	"</date><time>" rectime \
	"</time><tag>" tag \
	"</tag><obstype>" obstype \
	"</obstype><notes>" notes \
	"</notes><email>" email \
	"</email><oriname>"	filename \
	"</oriname><outfile>" outfile \
	"</outfile>" >> "obsdata/data.csv" ;

  # OUTPUT

  print "
Data stored
" ;
  print "
Thanks for your observation on plant " tag "
";
  print "
[ home ]
";

  # make thumb
  if (outfile != "")
  {
      system("convert 'obsdata/" outfile "' -resize 50x50 'obsdata/sm_" outfile "'");
  }

}

function obsReport()
{

  htmlHeader("Observations report", 420);

  stages["bud"] = "budding out";
  stages["flower"] = "in flower";
  stages["fruit"] = "in fruit";
  stages["fall"] = "Fall color";

  FS="|";

  while ((getline < "data/aa-tree.csv") > 0)
  {
	sp[$1] = $4 ;
  }

  while ((getline < "obsdata/data.csv") > 0)
	{
	  parseXML(++n,$0);
	  if (XML[n,"tag"] == f["tag"]) XML[n, "match"] = 1; tagDataFound = 1;
	}

  if ((f["tag"] != "") && (tagDataFound != 1))
	{
	  print "
Sorry, no citizen science records for this plant.
  Why not make one?
" ;
	}
  else
	{
	    print "
Recent observations
" ;
	  print "
";
	  print "
Tag	Taxon	Info	Date	Notes	Photo
";
	  if (n > 10) start = n-10 ;
	  else start = 1;
	  for (i = start; i <= n ; i++)
		{
		  print "
" XML[i,"tag"] "	" ;
		  print "" sp[XML[i,"tag"]] "	" ;

		  print "" stages[XML[i,"obstype"]] "	" ;
		  print "" XML[i,"date"] "	" ;
		  print "" gensub(/\\n/,"\\ ", "G", XML[i,"notes"]) "	" ;
		  if (XML[i,"outfile"] != "")
			{
			  print "
" ;
			}
		  else print "
" ;
		}
	  print "
";
	}
}

function parseXML(indx, string,    i)
{
  # parse simple XML
  i = 1;
  while (i <= length(string))
	{
	  # skip leading blanks and check in case there are no `<' in line
	  while ((substr(string,i,1) != "<") && (i <= length(string))) i++ ;
	  tag = ""; i++;
	  # read tag name
	  while ((substr(string,i,1) != ">") && (i <= length(string)))
		{
		  tag = tag substr(string,i,1) ; i++;
		}
	  # clean tag name of blanks
	  gsub(/\ /,"",tag);  data = ""; i++;
	  # read data, terminated by `</'
	  while ((substr(string,i,2) != "</") && (i <= length(string)))
		{
		  data = data substr(string,i,1) ; i++;
		}
	  # record
	  XML[indx, tag] = data ; i++;
	  # print tag, data;
	  # move to next tag, or finish
	  while ((substr(string,i+1,1) != "<") && (i <= length(string))) i++;
	}
}

function obsForm()
{

  htmlHeader("Make an observation", 400);

  print "
Make an observation
";
  print "
for tree with tag " f["tag"] "
 (Please double check the tag code)
";
  print "<form enctype=\"multipart/form-data\" method=\"post\" action=\"map?method=obsD\">" ;
  print "
<input type=\"hidden\" name=\"tag\" value=\"" f["tag"] "\" />Type of observation:
<select name=\"type\"><option value=\"bud\">Spring buds just breaking open</option><option value=\"flower\">In flower (any stage)</option><option value=\"fruit\">In fruit (any stage)</option><option value=\"fall\">Outstanding fall color</option><option value=\"damage\">Report plant damage</option></select>
";
  print "
Upload a photo (optional)
(small please: <2 MB; NB: iPhone does not support file uploads)
<input type=\"file\" name=\"photo\" />
";
  print "
Your notes (optional):
<textarea name=\"notes\" cols=\"30\" rows=\"2\"></textarea>
";
  print "
Your email (optional)
<input type=\"text\" name=\"email\" size=\"30\"/>
";

  print "
<input type=\"submit\" value=\"Send observation\" />
";
  print "</form>";

  htmlFooter();

}

function obsCapture()
{

# Script to parse return from observation form

# Form template:

# -----------------------------1928954284948244595549659756
# Content-Disposition: form-data; name="type"
#
# fall
# -----------------------------1928954284948244595549659756
# Content-Disposition: form-data; name="notes"
#
# Notes
# -----------------------------1928954284948244595549659756
# Content-Disposition: form-data; name="photo"; filename="img.png"
# Content-Type: image/png
#
# <image>
# -----------------------------1928954284948244595549659756--

  htmlHeader("Data stored",400);

  # View form data during testing with:
  #  RS = "\x04"; getline; print "
" $0;
  #  print $0 > "tmp";
  #  print "
"; exit;

  RS = "\r\n"; getline;
  # Use the first line as the multipart form delimiter
  # escape regexp chars (prob just `-'):
  gsub(/\-/,"\\-",$0); gsub(/\*/,"\\*",$0);
  gsub(/\./,"\\.",$0); gsub(/\?/,"\\?",$0);
  RS = $0 "[^\r]*" "\r?\n" ;
  recdate = strftime("%Y-%m-%d"); rectime= strftime("%H%M%S") ;
  # had to add the extra regex return because webkit and mozilla use a
  #   different last delimiter ( ----XYZ-- )
  while (getline > 0)
  	{
	  # print "
" $0 "
";
	  # split the dataparts
	  lines = split($0, line, "\r\n");
	  # if the media:
	  if ((line[2] ~ /Content\-Type/) && \
	      ("png|jpeg|jpg" ~ gensub(/[^/]*\/([a-z]+)/,"\\1","G",line[2])))
		{
		  ORS = "";
		  split (line[1], lineTmp2, ";");
		  filename = gensub(/ filename="([^"]*)"$/,"\\1","G",lineTmp2[3]);
		  mediatype = gensub(/[^/]*\/([a-z]+)/,"\\1","G",line[2]);
		  outfile = recdate "T" rectime "_" tag "." mediatype ;
		  # likely all to be in line 4, but there may be a \r\n in the
		  #  file, so better to concatenate lines
		  for (x = 4 ; x < lines ; x++)
			{
			  # could add a rand component: srand(); substr(rand(),3,5)
			  # but chance of two uploads in same second very tiny
			  print line[x] "\r\n" >> "obsdata/" outfile;
			}
		  ORS = "\n";
		}
	  else if (line[1] ~ /Content\-Disposition/)
		{
		  split (line[1], lineTmp, ";");
		  name = gensub(/\ name="([^"]*)"$/,"\\1","G",lineTmp[2]);
		  if (name == "tag")  { tag = line[3] }
		  if (name == "type") { obstype = line[3] }
		  if (name == "email") { email = line[3] }
		  if (name == "notes")
			{
			  for (x = 3 ; x < lines ; x++)
				{
				  notes = notes line[x] "\\n" ;
				}
			}
		}
	}

  gsub(/|/,"",notes) ; gsub(/|/,"",filename); # just in case

  # WRITE TO FILE
  print "<date>" recdate \
	"</date><time>" rectime \
	"</time><tag>" tag \
	"</tag><obstype>" obstype \
	"</obstype><notes>" notes \
	"</notes><email>" email \
	"</email><oriname>"	filename \
	"</oriname><outfile>" outfile \
	"</outfile>" >> "obsdata/data.csv" ;

  # OUTPUT

  print "
Data stored
" ;
  print "
Thanks for your observation on plant " tag "
";
  print "
[ home ]
";

  # make thumb
  if (outfile != "")
  {
      system("convert 'obsdata/" outfile "' -resize 50x50 'obsdata/sm_" outfile "'");
  }

}

function obsReport()
{

  htmlHeader("Observations report", 420);

  stages["bud"] = "budding out";
  stages["flower"] = "in flower";
  stages["fruit"] = "in fruit";
  stages["fall"] = "Fall color";

  FS="|";

  while ((getline < "data/aa-tree.csv") > 0)
  {
	sp[$1] = $4 ;
  }

  while ((getline < "obsdata/data.csv") > 0)
	{
	  parseXML(++n,$0);
	  if (XML[n,"tag"] == f["tag"]) XML[n, "match"] = 1; tagDataFound = 1;
	}

  if ((f["tag"] != "") && (tagDataFound != 1))
	{
	  print "
Sorry, no citizen science records for this plant.
  Why not make one?
" ;
	}
  else
	{
	    print "
Recent observations
" ;
	  print "
";
	  print "
Tag	Taxon	Info	Date	Notes	Photo
";
	  if (n > 10) start = n-10 ;
	  else start = 1;
	  for (i = start; i <= n ; i++)
		{
		  print "
" XML[i,"tag"] "	" ;
		  print "" sp[XML[i,"tag"]] "	" ;

		  print "" stages[XML[i,"obstype"]] "	" ;
		  print "" XML[i,"date"] "	" ;
		  print "" gensub(/\\n/,"\\ ", "G", XML[i,"notes"]) "	" ;
		  if (XML[i,"outfile"] != "")
			{
			  print "
" ;
			}
		  else print "
" ;
		}
	  print "
";
	}
}

function parseXML(indx, string,    i)
{
  # parse simple XML
  i = 1;
  while (i <= length(string))
	{
	  # skip leading blanks and check in case there are no `<' in line
	  while ((substr(string,i,1) != "<") && (i <= length(string))) i++ ;
	  tag = ""; i++;
	  # read tag name
	  while ((substr(string,i,1) != ">") && (i <= length(string)))
		{
		  tag = tag substr(string,i,1) ; i++;
		}
	  # clean tag name of blanks
	  gsub(/\ /,"",tag);  data = ""; i++;
	  # read data, terminated by `</'
	  while ((substr(string,i,2) != "</") && (i <= length(string)))
		{
		  data = data substr(string,i,1) ; i++;
		}
	  # record
	  XML[indx, tag] = data ; i++;
	  # print tag, data;
	  # move to next tag, or finish
	  while ((substr(string,i+1,1) != "<") && (i <= length(string))) i++;
	}
}


makeDistortosConfiguration.awk: modify shebang

Use more generic "/bin/awk" instead of "/usr/bin/awk".

#!/bin/awk -f

#
# file: makeDistortosConfiguration.awk
#
# author: Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
#
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
# distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# date: 2015-09-25
#

BEGIN {
	configPattern = "CONFIG_[A-Za-z0-9_]+"
	booleanTrueConfigPattern = "^" configPattern "=y$"
	booleanFalseConfigPattern = "^" configPattern "=n$"
	notSetConfigPattern = "^# " configPattern " is not set$"
	numericConfigPattern = "^" configPattern "=(([[:digit:]]+)|(0x[[:xdigit:]]+))$"
	stringConfigPattern = "^" configPattern "=\"([^\"\\\\]|\\\\.)*\"$"

	print "/**"
	print " * \\file"
	print " * \\brief distortos configuration"
	print " *"
	print " * \\warning"
	print " * Automatically generated file - do not edit!"
	print " *"
	print " * \\date " strftime("%Y-%m-%d %H:%M:%S")
	print " */"
	print ""
	print "#ifndef INCLUDE_DISTORTOS_DISTORTOSCONFIGURATION_H_"
	print "#define INCLUDE_DISTORTOS_DISTORTOSCONFIGURATION_H_"
	print ""
}

$0 ~ booleanTrueConfigPattern {
	split($0, array, "=")
	print "#define " array[1]
}

$0 ~ booleanFalseConfigPattern {
	split($0, array, "=")
	print "#undef " array[1]
}

$0 ~ notSetConfigPattern {
	print "#undef " $2
}

$0 ~ numericConfigPattern {
	split($0, array, "=")
	print "#define " array[1] " " array[2]
}

$0 ~ stringConfigPattern {
	split($0, array, "=")
	print "#define " array[1] " " array[2]
}

END {
	print ""
	print "#endif	/* INCLUDE_DISTORTOS_DISTORTOSCONFIGURATION_H_ */"
}

#!/usr/bin/awk -f

#
# file: makeDistortosConfiguration.awk
#
# author: Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
#
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
# distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# date: 2015-08-25
#

BEGIN {
	configPattern = "CONFIG_[A-Za-z0-9_]+"
	booleanTrueConfigPattern = "^" configPattern "=y$"
	booleanFalseConfigPattern = "^" configPattern "=n$"
	notSetConfigPattern = "^# " configPattern " is not set$"
	numericConfigPattern = "^" configPattern "=(([[:digit:]]+)|(0x[[:xdigit:]]+))$"
	stringConfigPattern = "^" configPattern "=\"([^\"\\\\]|\\\\.)*\"$"

	print "/**"
	print " * \\file"
	print " * \\brief distortos configuration"
	print " *"
	print " * \\warning"
	print " * Automatically generated file - do not edit!"
	print " *"
	print " * \\date " strftime("%Y-%m-%d %H:%M:%S")
	print " */"
	print ""
	print "#ifndef INCLUDE_DISTORTOS_DISTORTOSCONFIGURATION_H_"
	print "#define INCLUDE_DISTORTOS_DISTORTOSCONFIGURATION_H_"
	print ""
}

$0 ~ booleanTrueConfigPattern {
	split($0, array, "=")
	print "#define " array[1]
}

$0 ~ booleanFalseConfigPattern {
	split($0, array, "=")
	print "#undef " array[1]
}

$0 ~ notSetConfigPattern {
	print "#undef " $2
}

$0 ~ numericConfigPattern {
	split($0, array, "=")
	print "#define " array[1] " " array[2]
}

$0 ~ stringConfigPattern {
	split($0, array, "=")
	print "#define " array[1] " " array[2]
}

END {
	print ""
	print "#endif	/* INCLUDE_DISTORTOS_DISTORTOSCONFIGURATION_H_ */"
}


[CLEANUP] - update year in checkFile.awk

#!/bin/gawk
# Checks that a file is valid.

function error(fname, linum, msg) {
    printf "%s:%d: %s\n", fname, linum, msg;
    if (0) print; # for debug
}
function _matchFile(fname) {
    return fname ~ "/mondrian/" \
       || fname ~ "/org/olap4j/" \
       || fname ~ "/aspen/" \
       || fname ~ "/farrago/" \
       || fname ~ "/fennel/" \
       || fname ~ "/extensions/" \
       || fname ~ "/com/sqlstream/" \
       || fname ~ "/linq4j/" \
       || fname ~ "/lambda/" \
       || fname ~ "/optiq/" \
       || strict > 0;
}
function _isCpp(fname) {
    return fname ~ /\.(cpp|h)$/;
}
function _isJava(fname) {
    return fname ~ /\.(java|jj)$/;
}
function _isMondrian(fname) {
    return fname ~ /mondrian/;
}
function _isOlap4j(fname) {
    return fname ~ "/org/olap4j/";
}
function push(val) {
    switchStack[switchStackLen++] = val;
}
function pop() {
    --switchStackLen
    val = switchStack[switchStackLen];
    delete switchStack[switchStackLen];
    return val;
}
function afterFile() {
    # Compute basename. If fname="/foo/bar/baz.txt" then basename="baz.txt".
    basename = fname;
    gsub(".*/", "", basename);
    gsub(lf, "", lastNonEmptyLine);
    terminator = "// End " basename;
    if (matchFile && (lastNonEmptyLine != terminator)) {
        error(fname, FNR, sprintf("Last line should be %c%s%c", 39, terminator, 39));
    }
}
# Returns whether there are unmatched open parentheses.
# unmatchedOpenParens("if ()") returns false.
# unmatchedOpenParens("if (") returns true.
# unmatchedOpenParens("if (foo) bar(") returns false
function unmatchedOpenParens(s) {
    i = index(s, "(");
    if (i == 0) {
        return 0;
    }
    openCount = 1;
    while (++i <= length(s)) {
        c = substr(s, i, 1);
        if (c == "(") {
            ++openCount;
        }
        if (c == ")") {
            if (--openCount == 0) {
                return 0;
            }
        }
    }
    return 1;
}

function countLeadingSpaces(str) {
    i = 0;
    while (i < length(str) && substr(str, i + 1, 1) == " ") {
        ++i;
    }
    return i;
}

function checkIndent(str) {
    return str % indent == 0;
}

function startsWith(s, p) {
    return length(s) > length(p) \
        && substr(s, 1, length(p)) == p;
}

BEGIN {
    # pre-compute regexp for quotes, linefeed
    apos = sprintf("%c", 39);
    quot = sprintf("%c", 34);
    lf = sprintf("%c", 13);
    pattern = apos "(\\" apos "|[^" apos "])" apos;
    if (0) printf "maxLineLength=%s strict=%s\n", maxLineLength, strict;
}
FNR == 1 {
    if (fname) {
        afterFile();
    }
    fname = FILENAME;
    matchFile = _matchFile(fname);
    isCpp = _isCpp(fname);
    isJava = _isJava(fname);
    mondrian = _isMondrian(fname);
    prevImport = "";
    prevImportGroup = "";
    indent = (fname ~ /lambda/ || fname ~ /linq4j/) ? 2 : 4;
    cindent = 4;

    delete headers;
    headerCount = 0;
    if ($0 ~ /Generated By:JavaCC/ \
        || $0 ~ /This class is generated/) {
        # generated; skip whole file
        nextfile
    } else if (fname ~ /XmlaOlap4jDriverVersion.java/ \
        || fname ~ /package-info.java/ \
        || fname ~ /MondrianOlap4jDriverVersion.java/ \
        || fname ~ /MondrianTestRunner.java/ \
        || fname ~ /MondrianResultPrinter.java/) {
        # generated; does not contain standard header
    } else if (mondrian) {
        headers[headerCount++] = "/\\*$";
        headers[headerCount++] = "// This software is subject to the terms of the Eclipse Public License v1.0$";
        headers[headerCount++] = "// Agreement, available at the following URL:$";
        headers[headerCount++] = "// http://www.eclipse.org/legal/epl-v10.html.$";
        headers[headerCount++] = "// You must accept the terms of that agreement to use this software.$";
        headers[headerCount++] = "//$";
        headers[headerCount++] = "// Copyright (C) [0-9]*-[0-9]* Pentaho$";
    } else if (1) {
        headers[headerCount++] = "/\\*$";
        if (_isOlap4j(fname)) {
            headers[headerCount++] = /^\/\/ \$Id: /;
            headers[headerCount++] = "//";
        }
        headers[headerCount++] = "// Licensed to Julian Hyde under one or more contributor license$";
        headers[headerCount++] = "// agreements. See the NOTICE file distributed with this work for$";
        headers[headerCount++] = "// additional information regarding copyright ownership.$";
        headers[headerCount++] = "//$";
        headers[headerCount++] = "// Julian Hyde licenses this file to you under the Apache License,$";
        headers[headerCount++] = "// Version 2.0 \\(the \"License\"\\); you may not use this file except in$";
        headers[headerCount++] = "// compliance with the License. You may obtain a copy of the License at:$";
        headers[headerCount++] = "//$";
        headers[headerCount++] = "// http://www.apache.org/licenses/LICENSE-2.0$";
        headers[headerCount++] = "//$";
        headers[headerCount++] = "// Unless required by applicable law or agreed to in writing, software$";
        headers[headerCount++] = "// distributed under the License is distributed on an \"AS IS\" BASIS,$";
        headers[headerCount++] = "// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.$";
        headers[headerCount++] = "// See the License for the specific language governing permissions and$";
        headers[headerCount++] = "// limitations under the License.$";
        headers[headerCount++] = "*/$";
    }
}
FNR < headerCount {
    if ($0 !~ headers[FNR - 1]) {
        error(fname, FNR, "Does not match expected header line: " headers[FNR - 1] "\n");
        headerCount = 0; # prevent further errors from this file
    }
}
{
    if (previousLineEndedInCloseBrace > 0) {
        --previousLineEndedInCloseBrace;
    }
    if (previousLineEndedInOpenBrace > 0) {
        --previousLineEndedInOpenBrace;
    }
    if (previousLineWasEmpty > 0) {
        --previousLineWasEmpty;
    }
    s = $0;
    # remove DOS linefeeds
    gsub(lf, "", s);
    # replace strings
    gsub(/"(\\"|[^"\\]|\\[^"])*"/, "string", s);
    # replace single-quoted strings
    gsub(pattern, "string", s);
    # replace {: and :} in .cup files
    if (fname ~ /\.cup$/) {
        gsub(/{:/, "{", s);
        gsub(/:}/, "}", s);
        gsub(/:/, " : ", s);
    }
    if (inComment && s ~ /\*\//) {
        # end of multiline comment "*/"
        inComment = 0;
        gsub(/^.*\*\//, "/* comment */", s);
    } else if (inComment) {
        s = "/* comment */";
    } else if (s ~ /\/\*/ && s !~ /\/\*.*\*\//) {
        # beginning of multiline comment "/*"
        inComment = 1;
        if (strict > 1 && FNR > 1 && s !~ /\/\*\*/ && s !~ /^\/\*/) {
            error(fname, FNR, "Multi-line c-style comment not allowed" s);
        }
        gsub(/\/\*.*$/, "/* comment */", s);
    } else {
        # mask out /* */ comments
        gsub(/\/\*.*\*\//, "/* comment */", s);
    }
    if (mondrian && s ~ /\/\/\$NON-NLS/) {
        error(fname, FNR, "NON-NLS not allowed");
    }
    if (s ~ /\/\/[A-Za-z]/ && strict > 1) {
        if (s ~ /noinspection/) {} # e.g. '//noinspection unchecked'
        else if (s ~ /;$/) {} # e.g. '//int dummy = 0;'
        else if (s ~ /:\/\//) {} # e.g. '// http://'
        else error(fname, FNR, "Need space after //");
    }
    # mask out // comments
    gsub(/\/\/.*$/, "// comment", s);
    # line starts with string or plus?
    if (s ~ /^ *string/ \
        && s !~ /)/)
    {
        stringCol = index(s, "string");
    } else if (s ~ /^ *[+] string/) {
        if (stringCol != 0 && index(s, "+") != stringCol) {
            error(fname, FNR, "String '+' must be aligned with string on line above");
        }
    } else if (s ~ /comment/) {
        # in comment; string target carries forward
    } else {
        stringCol = 0;
    }

    # Is the line indented as expected?
    if (nextIndent > 0) {
        x = countLeadingSpaces(s);
        if (x != nextIndent) {
            error(fname, FNR, "Incorrect indent for first line of arg list");
        }
    }
    nextIndent = -1;
}
/ $/ {
    error(fname, FNR, "Line ends in space");
}
/[\t]/ {
    if (matchFile) {
        error(fname, FNR, "Tab character");
    }
}
/[\r]/ {
    if (matchFile) {
        error(fname, FNR, "Carriage return character (file is in DOS format?)");
    }
}
/./ {
    lastNonEmptyLine = $0;
}
{
    # Rules beyond this point only apply to Java and C++.
    if (!isCpp && !isJava) {
        next;
    }
}

/^package / {
    thisPackage = $2;
}
/^package / && previousLineWasEmpty {
    error(fname, FNR, "'package' declaration must not occur after empty line");
}
/^import / {
    if (previousLineWasEmpty) {
        prevImport = "";
    } else {
        if (!prevImport) {
            error(fname, FNR, "Expected blank line before first import");
        }
    }
    thisImport = $2;
    gsub(/;/, "", thisImport);
    gsub(/\*/, "", thisImport);
    if (thisPackage ~ /^mondrian.*/ && thisImport ~ /^mondrian.*/) {
        importGroup = "a:mondrian";
    } else if (thisPackage ~ /^net.hydromatic.*/ && thisImport ~ /^net.hydromatic.*/) {
        importGroup = "a:net.hydromatic";
    } else if (thisPackage ~ /^org.olap4j.*/ && thisImport ~ /^org.olap4j.*/) {
        importGroup = "a:org.olap4j";
    } else if (thisImport ~ /^static/) {
        importGroup = "z:static";
    } else if (thisImport ~ /^java.*/) {
        if (thisPackage ~ /^org.eigenbase.*/) {
            importGroup = "aa:java";
        } else {
            importGroup = "y:java";
        }
    } else if (thisImport ~ /^junit.*/) {
        importGroup = "b:junit";
    } else if (thisImport ~ /^mondrian.*/) {
        importGroup = "bb:mondrian";
    } else if (thisImport ~ /^org.olap4j.xmla.server.*/) {
        importGroup = "bc:org.olap4j.xmla.server";
    } else if (thisImport ~ /^openjava.*/) {
        importGroup = "cc:openjava";
    } else if (thisImport ~ /^org.apache.*/) {
        importGroup = "c:org.apache";
    } else if (thisImport ~ /^org.eigenbase.*/) {
        importGroup = "d:org.eigenbase";
    } else if (thisImport ~ /^org.olap4j.*/) {
        importGroup = "e:org.olap4j";
    } else {
        importGroup = "f:other";
    }
    if (importGroup != prevImportGroup \
        && prevImportGroup)
    {
        if (!previousLineWasEmpty) {
            error(fname, FNR, "Expected blank line between import groups");
        } else if (prevImportGroup > importGroup) {
            error(fname, FNR, "Import group out of sequence (should precede " \
                substr(prevImportGroup, index(prevImportGroup, ":") + 1) ")");
        }
    } else if (prevImport \
        && prevImport > thisImport \
        && !startsWith(prevImport, thisImport) \
        && !startsWith(thisImport, prevImport))
    {
        error(fname, FNR, "Import out of sorted order");
    }
    prevImport = thisImport;
    prevImportGroup = importGroup;
}
/^\/\/ Copyright .* Pentaho/ && strict > 1 {
    # We assume that '--strict' is only invoked on files currently being
    # edited. Therefore we would expect the copyright to be current.
    if ($0 !~ /-2016/) {
        error(fname, FNR, "copyright is not current");
    }
}
/(static|public|private|protected|final|abstract)/ && !/import/ && strict > 1 {
    # Order of qualifiers: "public/private/protected static final abstract class ..."
    s2 = s;
    gsub(/\(.*$/, "", s2);
    if (s2 ~ /abstract .*final /) {
        error(fname, FNR, "'final' must come before 'abstract'");
    }
    if (s2 ~ /final .*static /) {
        error(fname, FNR, "'static' must come before 'final'");
    }
    if (s2 ~ /abstract .*static /) {
        error(fname, FNR, "'static' must come before 'abstract'");
    }
    if (s2 ~ /static .*(public|protected|private) /) {
        error(fname, FNR, "'public/private/protected' must come before 'static'");
    }
    if (s2 ~ /final .*(public|protected|private) /) {
        error(fname, FNR, "'public/private/protected' must come before 'final'");
    }
    if (s2 ~ /abstract .*(public|protected|private) /) {
        error(fname, FNR, "'public/private/protected' must come before 'abstract'");
    }
}
/^$/ {
    if (matchFile && previousLineEndedInOpenBrace) {
        error(fname, FNR, "Empty line following open brace");
    }
}
/^ +}( catch| finally| while|[;,)])/ ||
/^ +}$/ {
    if (matchFile && previousLineWasEmpty) {
        error(fname, FNR - 1, "Empty line before close brace");
    }
}
s ~ /\<if\>.*;$/ {
    if (!matchFile) {}
    else {
        error(fname, FNR, "if followed by statement on same line");
    }
}
s ~ /\<(if) *\(/ {
    if (!matchFile) {
    } else if (s !~ /\<(if) /) {
        error(fname, FNR, "if must be followed by space");
    } else if (s ~ / else if /) {
    } else if (s ~ /^#if /) {
    } else if (!checkIndent(s)) {
        error(fname, FNR, "if must be correctly indented");
    }
}
s ~ /\<(while) *\(/ {
    if (!matchFile) {
    } else if (s !~ /\<(while) /) {
        error(fname, FNR, "while must be followed by space");
    } else if (s ~ /} while /) {
    } else if (!checkIndent(s)) {
        error(fname, FNR, "while must be correctly indented");
    }
}
s ~ /\<(for|switch|synchronized|} catch) *\(/ {
    if (!matchFile) {}
    else if (!checkIndent(s)) {
        error(fname, FNR, "for/switch/synchronized/catch must be correctly indented");
    } else if (s !~ /\<(for|switch|synchronized|} catch) /) {
        error(fname, FNR, "for/switch/synchronized/catch must be followed by space");
    }
}
s ~ /\<(if|while|for|switch|catch)\>/ {
    # Check single-line if statements, such as
    #   if (condition) return;
    # We recognize such statements because there are equal numbers of open and
    # close parentheses.
    opens = s;
    gsub(/[^(]/, "", opens);
    closes = s;
    gsub(/[^)]/, "", closes);
    if (!matchFile) {
    } else if (s ~ /{( *\/\/ comment)?$/) {
        # lines which end with { and optional comment are ok
    } else if (s ~ /{.*\\$/ && isCpp) {
        # lines which end with backslash are ok in c++ macros
    } else if (s ~ /} while/) {
        # lines like "} while (foo);" are ok
    } else if (s ~ /^#/) {
        # lines like "#if 0" are ok
    } else if (s ~ /if \(true|false\)/) {
        # allow "if (true)" and "if (false)" because they are
        # used for commenting
    } else if (!unmatchedOpenParens(s)  \
               && length($0) != 79      \
               && length($0) != 80)
    {
        error(fname, FNR, "single-line if/while/for/switch/catch must end in {");
    }
}
s ~ /[[:alnum:]]\(/ &&
s !~ /\<(if|while|for|switch|assert)\>/ {
    ss = s;
    while (match(ss, /[[:alnum:]]\(/)) {
        ss = substr(ss, RSTART + RLENGTH - 1);
        parens = ss;
        gsub(/[^()]/, "", parens);
        while (substr(parens, 1, 2) == "()") {
            parens = substr(parens, 3);
        }
        opens = parens;
        gsub(/[^(]/, "", opens);
        closes = parens;
        gsub(/[^)]/, "", closes);
        if (length(opens) > length(closes)) {
            if (ss ~ /,$/) {
                bras = ss;
                gsub(/[^<]/, "", bras);
                kets = ss;
                gsub(/->/, "", kets);
                gsub(/[^>]/, "", kets);
                if (length(bras) > length(kets)) {
                    # Ignore case like 'for (Map.Entry<Foo,{nl} Bar> entry : ...'
                } else if (s ~ / for /) {
                    # Ignore case like 'for (int i = 1,{nl} j = 2; i < j; ...'
                } else if (indent == cindent) {
                    error(                                              \
                        fname, FNR,                                     \
                        "multi-line parameter list should start with newline");
                    break;
                }
            } else if (s ~ /[;(]( *\\)?$/) {
                # If open paren is at end of line (with optional backslash
                # for macros), we're fine.
            } else if (s ~ /@.*\({/) {
                # Ignore Java annotations.
            } else if (indent == cindent) {
                error(                                                  \
                    fname, FNR,                                         \
                    "Open parenthesis should be at end of line (function call spans several lines)");
                break;
            }
        }
        ss = substr(ss, 2); # remove initial "("
    }
}
s ~ /\<switch\>/ {
    push(switchCol);
    switchCol = index($0, "switch");
}
s ~ /{/ {
    braceCol = index($0, "{");
    if (braceCol == switchCol) {
        push(switchCol);
    }
}
s ~ /}/ {
    braceCol = index($0, "}");
    if (braceCol == switchCol) {
        switchCol = pop();
    }
}
s ~ /\<(case|default)\>/ {
    caseDefaultCol = match($0, /case|default/);
    if (!matchFile) {}
    else if (caseDefaultCol != switchCol) {
        error(fname, FNR, "case/default must be aligned with switch");
    }
}
s ~ /\<assert\>/ {
    if (!matchFile) {}
    else if (isCpp) {} # rule only applies to java
    else if (!checkIndent(s)) {
        error(fname, FNR, "assert must be correctly indented");
    } else if (s !~ /\<assert /) {
        error(fname, FNR, "assert must be followed by space");
    }
}
s ~ /\<return\>/ {
    if (!matchFile) {}
    else if (isCpp && s ~ /^#/) {
        # ignore macros
    } else if (!checkIndent(s)) {
        error(fname, FNR, "return must be correctly indented");
    } else if (s !~ /\<return[ ;]/ && s !~ /\<return$/) {
        error(fname, FNR, "return must be followed by space or ;");
    }
}
s ~ /\<throw\>/ {
    if (!matchFile) {}
    else if (isCpp) {
        # cannot yet handle C++ cases like 'void foo() throw(int)'
    } else if (!checkIndent(s)) {
        error(fname, FNR, "throw must be correctly indented");
    } else if (s !~ /\<throw / && s !~ /\<throw$/) {
        error(fname, FNR, "throw must be followed by space");
    }
}
s ~ /\<else\>/ {
    if (!matchFile) {}
    else if (isCpp && s ~ /^# *else$/) {} # ignore "#else"
    else if (!checkIndent(s)) {
        error(fname, FNR, "else must be correctly indented");
    } else if (s !~ /^ +} else (if |{$|{ *\/\/|{ *\/\*)/) {
        error(fname, FNR, "else must be preceded by } and followed by { or if");
    }
}
s ~ /\<do\>/ {
    if (!matchFile) {}
    else if (!checkIndent(s)) {
        error(fname, FNR, "do must be correctly indented");
    } else if (s !~ /^ *do {/) {
        error(fname, FNR, "do must be followed by space {");
    }
}
s ~ /\<try\>/ {
    if (!matchFile) {}
    else if (!checkIndent(s)) {
        error(fname, FNR, "try must be correctly indented");
    } else if (s !~ /^ +try {/) {
        error(fname, FNR, "try must be followed by space {");
    }
}
s ~ /\<catch\>/ {
    if (!matchFile) {}
    else if (!checkIndent(s)) {
        error(fname, FNR, "catch must be correctly indented");
    } else if (s !~ /^ +} catch /) {
        error(fname, FNR, "catch must be preceded by } and followed by space");
    }
}
s ~ /\<finally\>/ {
    if (!matchFile) {}
    else if (!checkIndent(s)) {
        error(fname, FNR, "finally must be correctly indented");
    } else if (s !~ /^ +} finally {/) {
        error(fname, FNR, "finally must be preceded by } and followed by space {");
    }
}
s ~ /\($/ {
    nextIndent = countLeadingSpaces(s) + cindent;
    if (s ~ / (if|while) .*\(.*\(/ && indent == cindent) {
        nextIndent += indent;
    }
}
match(s, /([]A-Za-z0-9()])(+|-|\*|\^|\/|%|=|==|+=|-=|\*=|\/=|>=|<=|!=|&|&&|\||\|\||^|\?|:) *[A-Za-z0-9(]/, a) {
    # < and > are not handled here - they have special treatment below
    if (!matchFile) {}
#    else if (s ~ /<.*>/) {} # ignore templates
    else if (a[2] == "-" && s ~ /\(-/) {} # ignore case "foo(-1)"
    else if (a[2] == "-" && s ~ /[eE][+-][0-9]/) {} # ignore e.g. 1e-5
    else if (a[2] == "+" && s ~ /[eE][+-][0-9]/) {} # ignore e.g. 1e+5
    else if (a[2] == ":" && s ~ /(case.*|default):$/) {} # ignore e.g. "case 5:"
    else if (isCpp && s ~ /[^ ][*&]/) {} # ignore e.g. "Foo* p;" in c++ - debatable
    else if (isCpp && s ~ /\<operator.*\(/) {} # ignore e.g. "operator++()" in c++
    else if (isCpp && a[2] == "/" && s ~ /#include/) {} # ignore e.g. "#include <x/y.hpp>" in c++
    else {
        error(fname, FNR, "operator '" a[2] "' must be preceded by space");
    }
}
match(s, /([]A-Za-z0-9() ] *)(+|-|\*|\^|\/|%|=|==|+=|-=|\*=|\/=|>=|<=|!=|&|&&|\||\|\||^|\?|:|,)[A-Za-z0-9(]/, a) {
    if (!matchFile) {}
#    else if (s ~ /<.*>/) {} # ignore templates
    else if (a[2] == "-" && s ~ /(\(|return |case |= )-/) {} # ignore prefix -
    else if (a[2] == ":" && s ~ /(case.*|default):$/) {} # ignore e.g. "case 5:"
    else if (s ~ /, *-/) {} # ignore case "foo(x, -1)"
    else if (s ~ /-[^ ]/ && s ~ /[^A-Za-z0-9] -/) {} # ignore case "x + -1" but not "x -1" or "3 -1"
    else if (a[2] == "-" && s ~ /[eE][+-][0-9]/) {} # ignore e.g. 1e-5
    else if (a[2] == "+" && s ~ /[eE][+-][0-9]/) {} # ignore e.g. 1e+5
    else if (a[2] == "*" && isCpp && s ~ /\*[^ ]/) {} # ignore e.g. "Foo *p;" in c++
    else if (a[2] == "&" && isCpp && s ~ /&[^ ]/) {} # ignore case "foo(&x)" in c++
    else if (isCpp && s ~ /\<operator[^ ]+\(/) {} # ignore e.g. "operator++()" in c++
    else if (isCpp && a[2] == "/" && s ~ /#include/) {} # ignore e.g. "#include <x/y.hpp>" in c++
    else if (strict < 2 && fname ~ /(fennel)/ && a[1] = ",") {} # not enabled yet
    else {
        error(fname, FNR, "operator '" a[2] "' must be followed by space");
    }
}
match(s, /( )(,)/, a) {
    # (, < and > are not handled here - they have special treatment below
    if (!matchFile) {}
    else {
        error(fname, FNR, "operator '" a[2] "' must not be preceded by space");
    }
}
match(s, / (+|-|\*|\/|==|>=|<=|!=|<<|<<<|>>|&|&&|\|\||\?|:)$/, a) || \
match(s, /(\.|->)$/, a) {
    if (strict < 2 && fname ~ /(aspen)/ && a[1] != ":") {} # not enabled yet
    else if (strict < 2 && fname ~ /(fennel|farrago|aspen)/ && a[1] = "+") {} # not enabled yet
    else if (a[1] == ":" && s ~ /(case.*|default):$/) {
        # ignore e.g. "case 5:"
    } else if ((a[1] == "*" || a[1] == "&") && isCpp && s ~ /^[[:alnum:]:_ ]* [*&]$/) {
        # ignore e.g. "const int *\nClass::Subclass2::method(int x)"
    } else {
        error(fname, FNR, "operator '" a[1] "' must not be at end of line");
    }
}
match(s, /^ *(=) /, a) {
    error(fname, FNR, "operator '" a[1] "' must not be at start of line");
}
match(s, /([[:alnum:]~]+)( )([(])/, a) {
    # (, < and > are not handled here - they have special treatment below
    if (!matchFile) {}
    else if (isJava && a[1] ~ /\<(if|while|for|catch|switch|case|return|throw|synchronized|assert)\>/) {}
    else if (isCpp && a[1] ~ /\<(if|while|for|catch|switch|case|return|throw|operator|void|PBuffer)\>/) {}
    else if (isCpp && s ~ /^#define /) {}
    else {
        error(fname, FNR, "there must be no space before '" a[3] "' in fun call or fun decl");
    }
}
s ~ /\<[[:digit:][:lower:]][[:alnum:]_]*</ {
    # E.g. "p<" but not "Map<"
    if (!matchFile) {}
    else if (isCpp) {} # in C++ 'xyz<5>' could be a template
    else {
        error(fname, FNR, "operator '<' must be preceded by space");
    }
}
s ~ /\<[[:digit:][:lower:]][[:alnum:]_]*>/ {
    # E.g. "g>" but not "String>" as in "List<String>"
    if (!matchFile) {}
    else if (isCpp) {} # in C++ 'xyz<int>' could be a template
    else {
        error(fname, FNR, "operator '>' must be preceded by space");
    }
}
match(s, /<([[:digit:][:lower:]][[:alnum:].]*)\>/, a) {
    if (!matchFile) {}
    else if (isCpp) {
        # in C++, template and include generate too many false positives
    } else if (isJava && a[1] ~ /(int|char|long|boolean|byte|double|float)/) {
        # Allow e.g. 'List<int[]>'
    } else if (isJava && a[1] ~ /^[[:lower:]]+\./) {
        # Allow e.g. 'List<java.lang.String>'
    } else {
        error(fname, FNR, "operator '<' must be followed by space");
    }
}
match(s, /^(.*[^-])>([[:digit:][:lower:]][[:alnum:]]*)\>/, a) {
    if (!matchFile) {}
    else if (isJava && a[1] ~ /.*\.<.*/) {
        # Ignore 'Collections.<Type>member'
    } else {
        error(fname, FNR, "operator '>' must be followed by space");
    }
}
s ~ /[[(] / {
    if (!matchFile) {}
    else if (s ~ /[[(] +\\$/) {} # ignore '#define foo(   \'
    else {
        error(fname, FNR, "( or [ must not be followed by space");
    }
}
s ~ / [])]/ {
    if (!matchFile) {}
    else if (s ~ /^ *\)/ && previousLineEndedInCloseBrace) {} # ignore "bar(new Foo() { } );"
    else {
        error(fname, FNR, ") or ] must not be followed by space");
    }
}
s ~ /}/ {
    if (!matchFile) {}
    else if (s !~ /}( |;|,|$|\)|\.)/) {
        error(fname, FNR, "} must be followed by space");
    } else if (s !~ /^ *}/) {
        # not at start of line - ignore
    } else if (!checkIndent(s)) {
        error(fname, FNR, "} must be correctly indented");
    }
}
$0 ~ /\* @param [A-Za-z0-9_]+$/ && strict > 1 {
    error(fname, FNR, "Empty javadoc param");
}
$0 ~ /\* @return *$/ && strict > 1 {
    error(fname, FNR, "Empty javadoc return");
}
s ~ /{/ {
    if (!matchFile) {}
    else if (s ~ /(\]\)?|=) *{/) {} # ignore e.g. "(int[]) {1, 2}" or "int[] x = {1, 2}"
    else if (s ~ /\({/) {} # ignore e.g. @SuppressWarnings({"unchecked"})
    else if (s ~ /{ *(\/\/|\/\*)/) {} # ignore e.g. "do { // a comment"
    else if (s ~ / \{\}$/) {} # ignore e.g. "Constructor() {}"
    else if (s ~ / },$/) {} # ignore e.g. "{ yada },"
    else if (s ~ / };$/) {} # ignore e.g. "{ yada };"
    else if (s ~ / \{\};$/) {} # ignore e.g. "template <> class Foo<int> {};"
    else if (s ~ / },? *\/\/.*$/) {} # ignore e.g. "{ yada }, // comment"
    else if (s ~ /\\$/) {} # ignore multiline macros
    else if (s ~ /{}/) { # e.g. "Constructor(){}"
        error(fname, FNR, "{} must be preceded by space and at end of line");
    } else if (isCpp && s ~ /{ *\\$/) {
        # ignore - "{" can be followed by "\" in c macro
    } else if (s !~ /{$/) {
        error(fname, FNR, "{ must be at end of line");
    } else if (s !~ /(^| ){/) {
        error(fname, FNR, "{ must be preceded by space or at start of line");
    } else {
        opens = s;
        gsub(/[^(]/, "", opens);
        closes = s;
        gsub(/[^)]/, "", closes);
        if (0 && strict < 2 && fname ~ /aspen/) {} # not enabled
        else if (length(closes) > length(opens) && indent == cindent) {
            error(fname, FNR, "Open brace should be on new line (function call/decl spans several lines)");
        }
    }
}
s ~ /(^| )(class|interface|enum) / ||
s ~ /(^| )namespace / && isCpp {
    if (isCpp && s ~ /;$/) {} # ignore type declaration
    else {
        classDeclStartLine = FNR;
        t = s;
        gsub(/.*(class|interface|enum|namespace) /, "", t);
        gsub(/ .*$/, "", t);
        if (s ~ /template/) {
            # ignore case "template <class INSTCLASS> static void foo()"
            classDeclStartLine = 0;
        } else if (t ~ /[[:upper:]][[:upper:]][[:upper:]][[:upper:]]/     \
            && t !~ /LRU/ \
            && t !~ /WAL/ \
            && t !~ /classUUID/ \
            && t !~ /classSQLException/ \
            && t !~ /BBRC/ \
            && t !~ /_/ \
            && t !~ /EncodedSqlInterval/)
        {
            error(fname, FNR, "Class name " t " has consecutive uppercase letters");
        }
    }
}
s ~ / throws\>/ {
    if (s ~ /\(/) {
        funDeclStartLine = FNR;
    } else {
        funDeclStartLine = FNR - 1;
    }
}
length($0) > maxLineLength                      \
&& $0 !~ /@(throws|see|link)/                   \
&& $0 !~ /\$Id: /                               \
&& $0 !~ /^import /                             \
&& $0 !~ /http:/                                \
&& $0 !~ /https:/                               \
&& $0 !~ /\/\/ Expect "/                        \
&& s !~ /^ *(\+ |<< |: |\?)?string\)*[;,]?$/ {
    error( \
        fname, \
        FNR, \
        "Line length (" length($0) ") exceeds " maxLineLength " chars");
}
/}$/ {
    previousLineEndedInCloseBrace = 2;
}
/;$/ {
    funDeclStartLine = 0;
}
/{$/ {
    # Ignore open brace if it is part of class or interface declaration.
    if (classDeclStartLine) {
        if (classDeclStartLine < FNR \
            && $0 !~ /^ *{$/ \
            && indent == cindent)
        {
            error(fname, FNR, "Open brace should be on new line (class decl spans several lines)");
        }
        classDeclStartLine = 0;
    } else {
        previousLineEndedInOpenBrace = 2;
    }
    if (funDeclStartLine) {
        if (funDeclStartLine < FNR \
            && $0 !~ /^ *{$/)
        {
            if (strict < 2 && fname ~ /aspen/) {} # not enabled
            else if (cindent != indent) {}
            else error(fname, FNR, "Open brace should be on new line (function decl spans several lines)");
        }
        funDeclStartLine = 0;
    }
}
/^$/ {
    previousLineWasEmpty = 2;
}
{
    next;
}
END {
    afterFile();
}

# End checkFile.awk

#!/bin/gawk
# Checks that a file is valid.

function error(fname, linum, msg) {
    printf "%s:%d: %s\n", fname, linum, msg;
    if (0) print; # for debug
}
function _matchFile(fname) {
    return fname ~ "/mondrian/" \
       || fname ~ "/org/olap4j/" \
       || fname ~ "/aspen/" \
       || fname ~ "/farrago/" \
       || fname ~ "/fennel/" \
       || fname ~ "/extensions/" \
       || fname ~ "/com/sqlstream/" \
       || fname ~ "/linq4j/" \
       || fname ~ "/lambda/" \
       || fname ~ "/optiq/" \
       || strict > 0;
}
function _isCpp(fname) {
    return fname ~ /\.(cpp|h)$/;
}
function _isJava(fname) {
    return fname ~ /\.(java|jj)$/;
}
function _isMondrian(fname) {
    return fname ~ /mondrian/;
}
function _isOlap4j(fname) {
    return fname ~ "/org/olap4j/";
}
function push(val) {
    switchStack[switchStackLen++] = val;
}
function pop() {
    --switchStackLen
    val = switchStack[switchStackLen];
    delete switchStack[switchStackLen];
    return val;
}
function afterFile() {
    # Compute basename. If fname="/foo/bar/baz.txt" then basename="baz.txt".
    basename = fname;
    gsub(".*/", "", basename);
    gsub(lf, "", lastNonEmptyLine);
    terminator = "// End " basename;
    if (matchFile && (lastNonEmptyLine != terminator)) {
        error(fname, FNR, sprintf("Last line should be %c%s%c", 39, terminator, 39));
    }
}
# Returns whether there are unmatched open parentheses.
# unmatchedOpenParens("if ()") returns false.
# unmatchedOpenParens("if (") returns true.
# unmatchedOpenParens("if (foo) bar(") returns false
function unmatchedOpenParens(s) {
    i = index(s, "(");
    if (i == 0) {
        return 0;
    }
    openCount = 1;
    while (++i <= length(s)) {
        c = substr(s, i, 1);
        if (c == "(") {
            ++openCount;
        }
        if (c == ")") {
            if (--openCount == 0) {
                return 0;
            }
        }
    }
    return 1;
}

function countLeadingSpaces(str) {
    i = 0;
    while (i < length(str) && substr(str, i + 1, 1) == " ") {
        ++i;
    }
    return i;
}

function checkIndent(str) {
    return str % indent == 0;
}

function startsWith(s, p) {
    return length(s) > length(p) \
        && substr(s, 1, length(p)) == p;
}

BEGIN {
    # pre-compute regexp for quotes, linefeed
    apos = sprintf("%c", 39);
    quot = sprintf("%c", 34);
    lf = sprintf("%c", 13);
    pattern = apos "(\\" apos "|[^" apos "])" apos;
    if (0) printf "maxLineLength=%s strict=%s\n", maxLineLength, strict;
}
FNR == 1 {
    if (fname) {
        afterFile();
    }
    fname = FILENAME;
    matchFile = _matchFile(fname);
    isCpp = _isCpp(fname);
    isJava = _isJava(fname);
    mondrian = _isMondrian(fname);
    prevImport = "";
    prevImportGroup = "";
    indent = (fname ~ /lambda/ || fname ~ /linq4j/) ? 2 : 4;
    cindent = 4;

    delete headers;
    headerCount = 0;
    if ($0 ~ /Generated By:JavaCC/ \
        || $0 ~ /This class is generated/) {
        # generated; skip whole file
        nextfile
    } else if (fname ~ /XmlaOlap4jDriverVersion.java/ \
        || fname ~ /package-info.java/ \
        || fname ~ /MondrianOlap4jDriverVersion.java/ \
        || fname ~ /MondrianTestRunner.java/ \
        || fname ~ /MondrianResultPrinter.java/) {
        # generated; does not contain standard header
    } else if (mondrian) {
        headers[headerCount++] = "/\\*$";
        headers[headerCount++] = "// This software is subject to the terms of the Eclipse Public License v1.0$";
        headers[headerCount++] = "// Agreement, available at the following URL:$";
        headers[headerCount++] = "// http://www.eclipse.org/legal/epl-v10.html.$";
        headers[headerCount++] = "// You must accept the terms of that agreement to use this software.$";
        headers[headerCount++] = "//$";
        headers[headerCount++] = "// Copyright (C) [0-9]*-[0-9]* Pentaho$";
    } else if (1) {
        headers[headerCount++] = "/\\*$";
        if (_isOlap4j(fname)) {
            headers[headerCount++] = /^\/\/ \$Id: /;
            headers[headerCount++] = "//";
        }
        headers[headerCount++] = "// Licensed to Julian Hyde under one or more contributor license$";
        headers[headerCount++] = "// agreements. See the NOTICE file distributed with this work for$";
        headers[headerCount++] = "// additional information regarding copyright ownership.$";
        headers[headerCount++] = "//$";
        headers[headerCount++] = "// Julian Hyde licenses this file to you under the Apache License,$";
        headers[headerCount++] = "// Version 2.0 \\(the \"License\"\\); you may not use this file except in$";
        headers[headerCount++] = "// compliance with the License. You may obtain a copy of the License at:$";
        headers[headerCount++] = "//$";
        headers[headerCount++] = "// http://www.apache.org/licenses/LICENSE-2.0$";
        headers[headerCount++] = "//$";
        headers[headerCount++] = "// Unless required by applicable law or agreed to in writing, software$";
        headers[headerCount++] = "// distributed under the License is distributed on an \"AS IS\" BASIS,$";
        headers[headerCount++] = "// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.$";
        headers[headerCount++] = "// See the License for the specific language governing permissions and$";
        headers[headerCount++] = "// limitations under the License.$";
        headers[headerCount++] = "*/$";
    }
}
FNR < headerCount {
    if ($0 !~ headers[FNR - 1]) {
        error(fname, FNR, "Does not match expected header line: " headers[FNR - 1] "\n");
        headerCount = 0; # prevent further errors from this file
    }
}
{
    if (previousLineEndedInCloseBrace > 0) {
        --previousLineEndedInCloseBrace;
    }
    if (previousLineEndedInOpenBrace > 0) {
        --previousLineEndedInOpenBrace;
    }
    if (previousLineWasEmpty > 0) {
        --previousLineWasEmpty;
    }
    s = $0;
    # remove DOS linefeeds
    gsub(lf, "", s);
    # replace strings
    gsub(/"(\\"|[^"\\]|\\[^"])*"/, "string", s);
    # replace single-quoted strings
    gsub(pattern, "string", s);
    # replace {: and :} in .cup files
    if (fname ~ /\.cup$/) {
        gsub(/{:/, "{", s);
        gsub(/:}/, "}", s);
        gsub(/:/, " : ", s);
    }
    if (inComment && s ~ /\*\//) {
        # end of multiline comment "*/"
        inComment = 0;
        gsub(/^.*\*\//, "/* comment */", s);
    } else if (inComment) {
        s = "/* comment */";
    } else if (s ~ /\/\*/ && s !~ /\/\*.*\*\//) {
        # beginning of multiline comment "/*"
        inComment = 1;
        if (strict > 1 && FNR > 1 && s !~ /\/\*\*/ && s !~ /^\/\*/) {
            error(fname, FNR, "Multi-line c-style comment not allowed" s);
        }
        gsub(/\/\*.*$/, "/* comment */", s);
    } else {
        # mask out /* */ comments
        gsub(/\/\*.*\*\//, "/* comment */", s);
    }
    if (mondrian && s ~ /\/\/\$NON-NLS/) {
        error(fname, FNR, "NON-NLS not allowed");
    }
    if (s ~ /\/\/[A-Za-z]/ && strict > 1) {
        if (s ~ /noinspection/) {} # e.g. '//noinspection unchecked'
        else if (s ~ /;$/) {} # e.g. '//int dummy = 0;'
        else if (s ~ /:\/\//) {} # e.g. '// http://'
        else error(fname, FNR, "Need space after //");
    }
    # mask out // comments
    gsub(/\/\/.*$/, "// comment", s);
    # line starts with string or plus?
    if (s ~ /^ *string/ \
        && s !~ /)/)
    {
        stringCol = index(s, "string");
    } else if (s ~ /^ *[+] string/) {
        if (stringCol != 0 && index(s, "+") != stringCol) {
            error(fname, FNR, "String '+' must be aligned with string on line above");
        }
    } else if (s ~ /comment/) {
        # in comment; string target carries forward
    } else {
        stringCol = 0;
    }

    # Is the line indented as expected?
    if (nextIndent > 0) {
        x = countLeadingSpaces(s);
        if (x != nextIndent) {
            error(fname, FNR, "Incorrect indent for first line of arg list");
        }
    }
    nextIndent = -1;
}
/ $/ {
    error(fname, FNR, "Line ends in space");
}
/[\t]/ {
    if (matchFile) {
        error(fname, FNR, "Tab character");
    }
}
/[\r]/ {
    if (matchFile) {
        error(fname, FNR, "Carriage return character (file is in DOS format?)");
    }
}
/./ {
    lastNonEmptyLine = $0;
}
{
    # Rules beyond this point only apply to Java and C++.
    if (!isCpp && !isJava) {
        next;
    }
}

/^package / {
    thisPackage = $2;
}
/^package / && previousLineWasEmpty {
    error(fname, FNR, "'package' declaration must not occur after empty line");
}
/^import / {
    if (previousLineWasEmpty) {
        prevImport = "";
    } else {
        if (!prevImport) {
            error(fname, FNR, "Expected blank line before first import");
        }
    }
    thisImport = $2;
    gsub(/;/, "", thisImport);
    gsub(/\*/, "", thisImport);
    if (thisPackage ~ /^mondrian.*/ && thisImport ~ /^mondrian.*/) {
        importGroup = "a:mondrian";
    } else if (thisPackage ~ /^net.hydromatic.*/ && thisImport ~ /^net.hydromatic.*/) {
        importGroup = "a:net.hydromatic";
    } else if (thisPackage ~ /^org.olap4j.*/ && thisImport ~ /^org.olap4j.*/) {
        importGroup = "a:org.olap4j";
    } else if (thisImport ~ /^static/) {
        importGroup = "z:static";
    } else if (thisImport ~ /^java.*/) {
        if (thisPackage ~ /^org.eigenbase.*/) {
            importGroup = "aa:java";
        } else {
            importGroup = "y:java";
        }
    } else if (thisImport ~ /^junit.*/) {
        importGroup = "b:junit";
    } else if (thisImport ~ /^mondrian.*/) {
        importGroup = "bb:mondrian";
    } else if (thisImport ~ /^org.olap4j.xmla.server.*/) {
        importGroup = "bc:org.olap4j.xmla.server";
    } else if (thisImport ~ /^openjava.*/) {
        importGroup = "cc:openjava";
    } else if (thisImport ~ /^org.apache.*/) {
        importGroup = "c:org.apache";
    } else if (thisImport ~ /^org.eigenbase.*/) {
        importGroup = "d:org.eigenbase";
    } else if (thisImport ~ /^org.olap4j.*/) {
        importGroup = "e:org.olap4j";
    } else {
        importGroup = "f:other";
    }
    if (importGroup != prevImportGroup \
        && prevImportGroup)
    {
        if (!previousLineWasEmpty) {
            error(fname, FNR, "Expected blank line between import groups");
        } else if (prevImportGroup > importGroup) {
            error(fname, FNR, "Import group out of sequence (should precede " \
                substr(prevImportGroup, index(prevImportGroup, ":") + 1) ")");
        }
    } else if (prevImport \
        && prevImport > thisImport \
        && !startsWith(prevImport, thisImport) \
        && !startsWith(thisImport, prevImport))
    {
        error(fname, FNR, "Import out of sorted order");
    }
    prevImport = thisImport;
    prevImportGroup = importGroup;
}
/^\/\/ Copyright .* Pentaho/ && strict > 1 {
    # We assume that '--strict' is only invoked on files currently being
    # edited. Therefore we would expect the copyright to be current.
    if ($0 !~ /-2015/) {
        error(fname, FNR, "copyright is not current");
    }
}
/(static|public|private|protected|final|abstract)/ && !/import/ && strict > 1 {
    # Order of qualifiers: "public/private/protected static final abstract class ..."
    s2 = s;
    gsub(/\(.*$/, "", s2);
    if (s2 ~ /abstract .*final /) {
        error(fname, FNR, "'final' must come before 'abstract'");
    }
    if (s2 ~ /final .*static /) {
        error(fname, FNR, "'static' must come before 'final'");
    }
    if (s2 ~ /abstract .*static /) {
        error(fname, FNR, "'static' must come before 'abstract'");
    }
    if (s2 ~ /static .*(public|protected|private) /) {
        error(fname, FNR, "'public/private/protected' must come before 'static'");
    }
    if (s2 ~ /final .*(public|protected|private) /) {
        error(fname, FNR, "'public/private/protected' must come before 'final'");
    }
    if (s2 ~ /abstract .*(public|protected|private) /) {
        error(fname, FNR, "'public/private/protected' must come before 'abstract'");
    }
}
/^$/ {
    if (matchFile && previousLineEndedInOpenBrace) {
        error(fname, FNR, "Empty line following open brace");
    }
}
/^ +}( catch| finally| while|[;,)])/ ||
/^ +}$/ {
    if (matchFile && previousLineWasEmpty) {
        error(fname, FNR - 1, "Empty line before close brace");
    }
}
s ~ /\<if\>.*;$/ {
    if (!matchFile) {}
    else {
        error(fname, FNR, "if followed by statement on same line");
    }
}
s ~ /\<(if) *\(/ {
    if (!matchFile) {
    } else if (s !~ /\<(if) /) {
        error(fname, FNR, "if must be followed by space");
    } else if (s ~ / else if /) {
    } else if (s ~ /^#if /) {
    } else if (!checkIndent(s)) {
        error(fname, FNR, "if must be correctly indented");
    }
}
s ~ /\<(while) *\(/ {
    if (!matchFile) {
    } else if (s !~ /\<(while) /) {
        error(fname, FNR, "while must be followed by space");
    } else if (s ~ /} while /) {
    } else if (!checkIndent(s)) {
        error(fname, FNR, "while must be correctly indented");
    }
}
s ~ /\<(for|switch|synchronized|} catch) *\(/ {
    if (!matchFile) {}
    else if (!checkIndent(s)) {
        error(fname, FNR, "for/switch/synchronized/catch must be correctly indented");
    } else if (s !~ /\<(for|switch|synchronized|} catch) /) {
        error(fname, FNR, "for/switch/synchronized/catch must be followed by space");
    }
}
s ~ /\<(if|while|for|switch|catch)\>/ {
    # Check single-line if statements, such as
    #   if (condition) return;
    # We recognize such statements because there are equal numbers of open and
    # close parentheses.
    opens = s;
    gsub(/[^(]/, "", opens);
    closes = s;
    gsub(/[^)]/, "", closes);
    if (!matchFile) {
    } else if (s ~ /{( *\/\/ comment)?$/) {
        # lines which end with { and optional comment are ok
    } else if (s ~ /{.*\\$/ && isCpp) {
        # lines which end with backslash are ok in c++ macros
    } else if (s ~ /} while/) {
        # lines like "} while (foo);" are ok
    } else if (s ~ /^#/) {
        # lines like "#if 0" are ok
    } else if (s ~ /if \(true|false\)/) {
        # allow "if (true)" and "if (false)" because they are
        # used for commenting
    } else if (!unmatchedOpenParens(s)  \
               && length($0) != 79      \
               && length($0) != 80)
    {
        error(fname, FNR, "single-line if/while/for/switch/catch must end in {");
    }
}
s ~ /[[:alnum:]]\(/ &&
s !~ /\<(if|while|for|switch|assert)\>/ {
    ss = s;
    while (match(ss, /[[:alnum:]]\(/)) {
        ss = substr(ss, RSTART + RLENGTH - 1);
        parens = ss;
        gsub(/[^()]/, "", parens);
        while (substr(parens, 1, 2) == "()") {
            parens = substr(parens, 3);
        }
        opens = parens;
        gsub(/[^(]/, "", opens);
        closes = parens;
        gsub(/[^)]/, "", closes);
        if (length(opens) > length(closes)) {
            if (ss ~ /,$/) {
                bras = ss;
                gsub(/[^<]/, "", bras);
                kets = ss;
                gsub(/->/, "", kets);
                gsub(/[^>]/, "", kets);
                if (length(bras) > length(kets)) {
                    # Ignore case like 'for (Map.Entry<Foo,{nl} Bar> entry : ...'
                } else if (s ~ / for /) {
                    # Ignore case like 'for (int i = 1,{nl} j = 2; i < j; ...'
                } else if (indent == cindent) {
                    error(                                              \
                        fname, FNR,                                     \
                        "multi-line parameter list should start with newline");
                    break;
                }
            } else if (s ~ /[;(]( *\\)?$/) {
                # If open paren is at end of line (with optional backslash
                # for macros), we're fine.
            } else if (s ~ /@.*\({/) {
                # Ignore Java annotations.
            } else if (indent == cindent) {
                error(                                                  \
                    fname, FNR,                                         \
                    "Open parenthesis should be at end of line (function call spans several lines)");
                break;
            }
        }
        ss = substr(ss, 2); # remove initial "("
    }
}
s ~ /\<switch\>/ {
    push(switchCol);
    switchCol = index($0, "switch");
}
s ~ /{/ {
    braceCol = index($0, "{");
    if (braceCol == switchCol) {
        push(switchCol);
    }
}
s ~ /}/ {
    braceCol = index($0, "}");
    if (braceCol == switchCol) {
        switchCol = pop();
    }
}
s ~ /\<(case|default)\>/ {
    caseDefaultCol = match($0, /case|default/);
    if (!matchFile) {}
    else if (caseDefaultCol != switchCol) {
        error(fname, FNR, "case/default must be aligned with switch");
    }
}
s ~ /\<assert\>/ {
    if (!matchFile) {}
    else if (isCpp) {} # rule only applies to java
    else if (!checkIndent(s)) {
        error(fname, FNR, "assert must be correctly indented");
    } else if (s !~ /\<assert /) {
        error(fname, FNR, "assert must be followed by space");
    }
}
s ~ /\<return\>/ {
    if (!matchFile) {}
    else if (isCpp && s ~ /^#/) {
        # ignore macros
    } else if (!checkIndent(s)) {
        error(fname, FNR, "return must be correctly indented");
    } else if (s !~ /\<return[ ;]/ && s !~ /\<return$/) {
        error(fname, FNR, "return must be followed by space or ;");
    }
}
s ~ /\<throw\>/ {
    if (!matchFile) {}
    else if (isCpp) {
        # cannot yet handle C++ cases like 'void foo() throw(int)'
    } else if (!checkIndent(s)) {
        error(fname, FNR, "throw must be correctly indented");
    } else if (s !~ /\<throw / && s !~ /\<throw$/) {
        error(fname, FNR, "throw must be followed by space");
    }
}
s ~ /\<else\>/ {
    if (!matchFile) {}
    else if (isCpp && s ~ /^# *else$/) {} # ignore "#else"
    else if (!checkIndent(s)) {
        error(fname, FNR, "else must be correctly indented");
    } else if (s !~ /^ +} else (if |{$|{ *\/\/|{ *\/\*)/) {
        error(fname, FNR, "else must be preceded by } and followed by { or if");
    }
}
s ~ /\<do\>/ {
    if (!matchFile) {}
    else if (!checkIndent(s)) {
        error(fname, FNR, "do must be correctly indented");
    } else if (s !~ /^ *do {/) {
        error(fname, FNR, "do must be followed by space {");
    }
}
s ~ /\<try\>/ {
    if (!matchFile) {}
    else if (!checkIndent(s)) {
        error(fname, FNR, "try must be correctly indented");
    } else if (s !~ /^ +try {/) {
        error(fname, FNR, "try must be followed by space {");
    }
}
s ~ /\<catch\>/ {
    if (!matchFile) {}
    else if (!checkIndent(s)) {
        error(fname, FNR, "catch must be correctly indented");
    } else if (s !~ /^ +} catch /) {
        error(fname, FNR, "catch must be preceded by } and followed by space");
    }
}
s ~ /\<finally\>/ {
    if (!matchFile) {}
    else if (!checkIndent(s)) {
        error(fname, FNR, "finally must be correctly indented");
    } else if (s !~ /^ +} finally {/) {
        error(fname, FNR, "finally must be preceded by } and followed by space {");
    }
}
s ~ /\($/ {
    nextIndent = countLeadingSpaces(s) + cindent;
    if (s ~ / (if|while) .*\(.*\(/ && indent == cindent) {
        nextIndent += indent;
    }
}
match(s, /([]A-Za-z0-9()])(+|-|\*|\^|\/|%|=|==|+=|-=|\*=|\/=|>=|<=|!=|&|&&|\||\|\||^|\?|:) *[A-Za-z0-9(]/, a) {
    # < and > are not handled here - they have special treatment below
    if (!matchFile) {}
#    else if (s ~ /<.*>/) {} # ignore templates
    else if (a[2] == "-" && s ~ /\(-/) {} # ignore case "foo(-1)"
    else if (a[2] == "-" && s ~ /[eE][+-][0-9]/) {} # ignore e.g. 1e-5
    else if (a[2] == "+" && s ~ /[eE][+-][0-9]/) {} # ignore e.g. 1e+5
    else if (a[2] == ":" && s ~ /(case.*|default):$/) {} # ignore e.g. "case 5:"
    else if (isCpp && s ~ /[^ ][*&]/) {} # ignore e.g. "Foo* p;" in c++ - debatable
    else if (isCpp && s ~ /\<operator.*\(/) {} # ignore e.g. "operator++()" in c++
    else if (isCpp && a[2] == "/" && s ~ /#include/) {} # ignore e.g. "#include <x/y.hpp>" in c++
    else {
        error(fname, FNR, "operator '" a[2] "' must be preceded by space");
    }
}
match(s, /([]A-Za-z0-9() ] *)(+|-|\*|\^|\/|%|=|==|+=|-=|\*=|\/=|>=|<=|!=|&|&&|\||\|\||^|\?|:|,)[A-Za-z0-9(]/, a) {
    if (!matchFile) {}
#    else if (s ~ /<.*>/) {} # ignore templates
    else if (a[2] == "-" && s ~ /(\(|return |case |= )-/) {} # ignore prefix -
    else if (a[2] == ":" && s ~ /(case.*|default):$/) {} # ignore e.g. "case 5:"
    else if (s ~ /, *-/) {} # ignore case "foo(x, -1)"
    else if (s ~ /-[^ ]/ && s ~ /[^A-Za-z0-9] -/) {} # ignore case "x + -1" but not "x -1" or "3 -1"
    else if (a[2] == "-" && s ~ /[eE][+-][0-9]/) {} # ignore e.g. 1e-5
    else if (a[2] == "+" && s ~ /[eE][+-][0-9]/) {} # ignore e.g. 1e+5
    else if (a[2] == "*" && isCpp && s ~ /\*[^ ]/) {} # ignore e.g. "Foo *p;" in c++
    else if (a[2] == "&" && isCpp && s ~ /&[^ ]/) {} # ignore case "foo(&x)" in c++
    else if (isCpp && s ~ /\<operator[^ ]+\(/) {} # ignore e.g. "operator++()" in c++
    else if (isCpp && a[2] == "/" && s ~ /#include/) {} # ignore e.g. "#include <x/y.hpp>" in c++
    else if (strict < 2 && fname ~ /(fennel)/ && a[1] = ",") {} # not enabled yet
    else {
        error(fname, FNR, "operator '" a[2] "' must be followed by space");
    }
}
match(s, /( )(,)/, a) {
    # (, < and > are not handled here - they have special treatment below
    if (!matchFile) {}
    else {
        error(fname, FNR, "operator '" a[2] "' must not be preceded by space");
    }
}
match(s, / (+|-|\*|\/|==|>=|<=|!=|<<|<<<|>>|&|&&|\|\||\?|:)$/, a) || \
match(s, /(\.|->)$/, a) {
    if (strict < 2 && fname ~ /(aspen)/ && a[1] != ":") {} # not enabled yet
    else if (strict < 2 && fname ~ /(fennel|farrago|aspen)/ && a[1] = "+") {} # not enabled yet
    else if (a[1] == ":" && s ~ /(case.*|default):$/) {
        # ignore e.g. "case 5:"
    } else if ((a[1] == "*" || a[1] == "&") && isCpp && s ~ /^[[:alnum:]:_ ]* [*&]$/) {
        # ignore e.g. "const int *\nClass::Subclass2::method(int x)"
    } else {
        error(fname, FNR, "operator '" a[1] "' must not be at end of line");
    }
}
match(s, /^ *(=) /, a) {
    error(fname, FNR, "operator '" a[1] "' must not be at start of line");
}
match(s, /([[:alnum:]~]+)( )([(])/, a) {
    # (, < and > are not handled here - they have special treatment below
    if (!matchFile) {}
    else if (isJava && a[1] ~ /\<(if|while|for|catch|switch|case|return|throw|synchronized|assert)\>/) {}
    else if (isCpp && a[1] ~ /\<(if|while|for|catch|switch|case|return|throw|operator|void|PBuffer)\>/) {}
    else if (isCpp && s ~ /^#define /) {}
    else {
        error(fname, FNR, "there must be no space before '" a[3] "' in fun call or fun decl");
    }
}
s ~ /\<[[:digit:][:lower:]][[:alnum:]_]*</ {
    # E.g. "p<" but not "Map<"
    if (!matchFile) {}
    else if (isCpp) {} # in C++ 'xyz<5>' could be a template
    else {
        error(fname, FNR, "operator '<' must be preceded by space");
    }
}
s ~ /\<[[:digit:][:lower:]][[:alnum:]_]*>/ {
    # E.g. "g>" but not "String>" as in "List<String>"
    if (!matchFile) {}
    else if (isCpp) {} # in C++ 'xyz<int>' could be a template
    else {
        error(fname, FNR, "operator '>' must be preceded by space");
    }
}
match(s, /<([[:digit:][:lower:]][[:alnum:].]*)\>/, a) {
    if (!matchFile) {}
    else if (isCpp) {
        # in C++, template and include generate too many false positives
    } else if (isJava && a[1] ~ /(int|char|long|boolean|byte|double|float)/) {
        # Allow e.g. 'List<int[]>'
    } else if (isJava && a[1] ~ /^[[:lower:]]+\./) {
        # Allow e.g. 'List<java.lang.String>'
    } else {
        error(fname, FNR, "operator '<' must be followed by space");
    }
}
match(s, /^(.*[^-])>([[:digit:][:lower:]][[:alnum:]]*)\>/, a) {
    if (!matchFile) {}
    else if (isJava && a[1] ~ /.*\.<.*/) {
        # Ignore 'Collections.<Type>member'
    } else {
        error(fname, FNR, "operator '>' must be followed by space");
    }
}
s ~ /[[(] / {
    if (!matchFile) {}
    else if (s ~ /[[(] +\\$/) {} # ignore '#define foo(   \'
    else {
        error(fname, FNR, "( or [ must not be followed by space");
    }
}
s ~ / [])]/ {
    if (!matchFile) {}
    else if (s ~ /^ *\)/ && previousLineEndedInCloseBrace) {} # ignore "bar(new Foo() { } );"
    else {
        error(fname, FNR, ") or ] must not be followed by space");
    }
}
s ~ /}/ {
    if (!matchFile) {}
    else if (s !~ /}( |;|,|$|\)|\.)/) {
        error(fname, FNR, "} must be followed by space");
    } else if (s !~ /^ *}/) {
        # not at start of line - ignore
    } else if (!checkIndent(s)) {
        error(fname, FNR, "} must be correctly indented");
    }
}
$0 ~ /\* @param [A-Za-z0-9_]+$/ && strict > 1 {
    error(fname, FNR, "Empty javadoc param");
}
$0 ~ /\* @return *$/ && strict > 1 {
    error(fname, FNR, "Empty javadoc return");
}
s ~ /{/ {
    if (!matchFile) {}
    else if (s ~ /(\]\)?|=) *{/) {} # ignore e.g. "(int[]) {1, 2}" or "int[] x = {1, 2}"
    else if (s ~ /\({/) {} # ignore e.g. @SuppressWarnings({"unchecked"})
    else if (s ~ /{ *(\/\/|\/\*)/) {} # ignore e.g. "do { // a comment"
    else if (s ~ / \{\}$/) {} # ignore e.g. "Constructor() {}"
    else if (s ~ / },$/) {} # ignore e.g. "{ yada },"
    else if (s ~ / };$/) {} # ignore e.g. "{ yada };"
    else if (s ~ / \{\};$/) {} # ignore e.g. "template <> class Foo<int> {};"
    else if (s ~ / },? *\/\/.*$/) {} # ignore e.g. "{ yada }, // comment"
    else if (s ~ /\\$/) {} # ignore multiline macros
    else if (s ~ /{}/) { # e.g. "Constructor(){}"
        error(fname, FNR, "{} must be preceded by space and at end of line");
    } else if (isCpp && s ~ /{ *\\$/) {
        # ignore - "{" can be followed by "\" in c macro
    } else if (s !~ /{$/) {
        error(fname, FNR, "{ must be at end of line");
    } else if (s !~ /(^| ){/) {
        error(fname, FNR, "{ must be preceded by space or at start of line");
    } else {
        opens = s;
        gsub(/[^(]/, "", opens);
        closes = s;
        gsub(/[^)]/, "", closes);
        if (0 && strict < 2 && fname ~ /aspen/) {} # not enabled
        else if (length(closes) > length(opens) && indent == cindent) {
            error(fname, FNR, "Open brace should be on new line (function call/decl spans several lines)");
        }
    }
}
s ~ /(^| )(class|interface|enum) / ||
s ~ /(^| )namespace / && isCpp {
    if (isCpp && s ~ /;$/) {} # ignore type declaration
    else {
        classDeclStartLine = FNR;
        t = s;
        gsub(/.*(class|interface|enum|namespace) /, "", t);
        gsub(/ .*$/, "", t);
        if (s ~ /template/) {
            # ignore case "template <class INSTCLASS> static void foo()"
            classDeclStartLine = 0;
        } else if (t ~ /[[:upper:]][[:upper:]][[:upper:]][[:upper:]]/     \
            && t !~ /LRU/ \
            && t !~ /WAL/ \
            && t !~ /classUUID/ \
            && t !~ /classSQLException/ \
            && t !~ /BBRC/ \
            && t !~ /_/ \
            && t !~ /EncodedSqlInterval/)
        {
            error(fname, FNR, "Class name " t " has consecutive uppercase letters");
        }
    }
}
s ~ / throws\>/ {
    if (s ~ /\(/) {
        funDeclStartLine = FNR;
    } else {
        funDeclStartLine = FNR - 1;
    }
}
length($0) > maxLineLength                      \
&& $0 !~ /@(throws|see|link)/                   \
&& $0 !~ /\$Id: /                               \
&& $0 !~ /^import /                             \
&& $0 !~ /http:/                                \
&& $0 !~ /https:/                               \
&& $0 !~ /\/\/ Expect "/                        \
&& s !~ /^ *(\+ |<< |: |\?)?string\)*[;,]?$/ {
    error( \
        fname, \
        FNR, \
        "Line length (" length($0) ") exceeds " maxLineLength " chars");
}
/}$/ {
    previousLineEndedInCloseBrace = 2;
}
/;$/ {
    funDeclStartLine = 0;
}
/{$/ {
    # Ignore open brace if it is part of class or interface declaration.
    if (classDeclStartLine) {
        if (classDeclStartLine < FNR \
            && $0 !~ /^ *{$/ \
            && indent == cindent)
        {
            error(fname, FNR, "Open brace should be on new line (class decl spans several lines)");
        }
        classDeclStartLine = 0;
    } else {
        previousLineEndedInOpenBrace = 2;
    }
    if (funDeclStartLine) {
        if (funDeclStartLine < FNR \
            && $0 !~ /^ *{$/)
        {
            if (strict < 2 && fname ~ /aspen/) {} # not enabled
            else if (cindent != indent) {}
            else error(fname, FNR, "Open brace should be on new line (function decl spans several lines)");
        }
        funDeclStartLine = 0;
    }
}
/^$/ {
    previousLineWasEmpty = 2;
}
{
    next;
}
END {
    afterFile();
}

# End checkFile.awk