Made callouts in docs prettier and copy+paste friendly.

#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#

BEGIN {
  firstMatch=1
  styled=0
}

/Licensed to the Apache Software Foundation/ {
  isHeader=1
}

/<\/style>/ {
  if (!styled) {
    print ".invisible {position: absolute; left: 9999px}"
    styled=1
  }
}

!// {
  if (firstMatch || !isHeader) {
    print gensub(/()\(([0-9]+)\)(<\/b>)/,
                 "//\\1\\2\\3", "g")
  }
}

// {
  if (firstMatch || !isHeader) {
    print gensub(/\/\/<\/span>()\(([0-9]+)\)(<\/b>)/,
                 "//\\1\\2\\3\\\\<\/span>", "g")
  }
}

/under the License\./ {
  firstMatch=0
  isHeader=0
}

#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#

BEGIN {
  firstMatch=1
}

/Licensed to the Apache Software Foundation/ {
  isHeader=1
}

firstMatch || !isHeader

/under the License\./ {
  firstMatch=0
  isHeader=0
}


repeated matching of backquoted stuff

repeated matching of backquoted stuff

BEGIN {
  print "%TCIDATA{Version=5.00.0.2606}";
  print "%TCIDATA{LaTeXparent=0,0,classes.tex}";
  print "                      ";
  print "";
  print "%%%%% BEGINNING OF DOCUMENT BODY %%%%%";
  print "% html: Beginning of file: `clean.html'";
  print "% DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"";
  print "%  This is a (PRE) block.  Make sure it's left aligned or your toc title will be off. ";
  print "";
  quoting = 0;
  itemizing = 0;
  coding = 0;
}

END {
  print "% html: End of file: `clean.html'";
  print "";
  print "%%%%% END OF DOCUMENT BODY %%%%%";
  print "% In the future, we might want to put some additional data here, such";
  print "% as when the documentation was converted from wiki to TeX.";
  print "%";
}

{ origLine = $0; }

## HANDLE CODING
## make sure that control sequences are always at the front
/ *{{{/ { sub(/ */, "") }
/ *}}}/ { sub(/ */, "") }

!coding && /^{{{/ {

  if (quoting) {
    print "\\end{quotation}";
    print ""
    quoting = 0;
  }

  print "\\begin{verbatim}";
  coding = 1;
  next;
}

coding && /^}}}/ {
  sub(/}}}.*$/, "", origLine);
  if (origLine) {
    print "{\\small " origLine
    print "}"
  }
  print "\\end{verbatim}";
  print ""
  coding = 0;
  next;
}

coding {
  sub(/^/, "{\\small ");
  print;
  print "}";
  next;
}

## NO CODING BELOW THIS POINT
coding { print "###code???####"; next; }

## pragmas do not count
/^\#labels/ {
  next;
}

## make sure that control sequences are always at the front
/^ *=/ { sub(/^ *=/, "=") }

## check itemness
{ inItem = 0 }
/^   *\*/ {
  inItem = 1
  ## print " ########## ITEM"
}

itemizing && !inItem {
  print "\\end{itemize}";
  print "";
  itemizing = 0;
}

## quoting stops when item appears
## or not indented any more
quoting && (inItem || !/^ /) {
  print "\\end{quotation}";
  print ""
  quoting = 0;
}

## empty lines do not count
/^ *$/ {
  next;
}

/`/ {
  line = $0;
  #sub(/`/, "\\texttt{", line);
  #sub(/`/, "}", line);
  $0 = gensub(/`([^`]*)`/,"\\\\texttt{\\1}","g",line)
  #$0 = line
  ## repeat!
}

/.#/ {
  gsub(/#/, "\\#");
}
/\[/ {
  gsub(/\[/, "\\mbox{$[$}");
}
/\]/ {
  gsub(/\]/, "\\mbox{$]$}");
}

/^==/ {
  sub(/^==/, "\\subsection*{");
  sub(/==/, "}");
  print;
  next;
}

/^=/ {
  sub(/^=/, "\\section*{");
  sub(/=/, "}");
  print;
  print "";
  print "\\label{f0}";
  print "";
  next;
}

quoting && /^ / {
  sub(/^ /, "");
  print;
  next;
}

!itemizing && inItem {
  print "\\begin{itemize}";
  itemizing = 1;
}

itemizing && inItem {
  sub(/^   *\*/, "\\item");
  print;
  print "";
  next;
}

/^ / {
  print ""
  print "\\begin{quotation}";
  quoting = 1;
  sub(/^ /, "");
  print;
  next;
}

{ print }

BEGIN {
  print "%TCIDATA{Version=5.00.0.2606}";
  print "%TCIDATA{LaTeXparent=0,0,classes.tex}";
  print "                      ";
  print "";
  print "%%%%% BEGINNING OF DOCUMENT BODY %%%%%";
  print "% html: Beginning of file: `clean.html'";
  print "% DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"";
  print "%  This is a (PRE) block.  Make sure it's left aligned or your toc title will be off. ";
  print "";
  quoting = 0;
  itemizing = 0;
  coding = 0;
}

END {
  print "% html: End of file: `clean.html'";
  print "";
  print "%%%%% END OF DOCUMENT BODY %%%%%";
  print "% In the future, we might want to put some additional data here, such";
  print "% as when the documentation was converted from wiki to TeX.";
  print "%";
}

{ origLine = $0; }

## HANDLE CODING
## make sure that control sequences are always at the front
/ *{{{/ { sub(/ */, "") }
/ *}}}/ { sub(/ */, "") }

!coding && /^{{{/ {

  if (quoting) {
    print "\\end{quotation}";
    print ""
    quoting = 0;
  }

  print "\\begin{verbatim}";
  coding = 1;
  next;
}

coding && /^}}}/ {
  sub(/}}}.*$/, "", origLine);
  if (origLine) {
    print "{\\small " origLine
    print "}"
  }
  print "\\end{verbatim}";
  print ""
  coding = 0;
  next;
}

coding {
  sub(/^/, "{\\small ");
  print;
  print "}";
  next;
}

## NO CODING BELOW THIS POINT
coding { print "###code???####"; next; }

## pragmas do not count
/^\#labels/ {
  next;
}

## make sure that control sequences are always at the front
/^ *=/ { sub(/^ *=/, "=") }

## check itemness
{ inItem = 0 }
/^   *\*/ {
  inItem = 1
  ## print " ########## ITEM"
}

itemizing && !inItem {
  print "\\end{itemize}";
  print "";
  itemizing = 0;
}

## quoting stops when item appears
## or not indented any more
quoting && (inItem || !/^ /) {
  print "\\end{quotation}";
  print ""
  quoting = 0;
}

## empty lines do not count
/^ *$/ {
  next;
}

/`/ {
  line = $0;
  sub(/`/, "\\texttt{", line);
  sub(/`/, "}", line);
  $0 = line
  ## repeat!
}

/.#/ {
  gsub(/#/, "\\#");
}
/\[/ {
  gsub(/\[/, "\\mbox{$[$}");
}
/\]/ {
  gsub(/\]/, "\\mbox{$]$}");
}

/^==/ {
  sub(/^==/, "\\subsection*{");
  sub(/==/, "}");
  print;
  next;
}

/^=/ {
  sub(/^=/, "\\section*{");
  sub(/=/, "}");
  print;
  print "";
  print "\\label{f0}";
  print "";
  next;
}

quoting && /^ / {
  sub(/^ /, "");
  print;
  next;
}

!itemizing && inItem {
  print "\\begin{itemize}";
  itemizing = 1;
}

itemizing && inItem {
  sub(/^   *\*/, "\\item");
  print;
  print "";
  next;
}

/^ / {
  print ""
  print "\\begin{quotation}";
  quoting = 1;
  sub(/^ /, "");
  print;
  next;
}

{ print }


Fix license.

Fix license.

/*************************************************************************
 * Copyright (c) 2011 AT&T Intellectual Property
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors: See CVS logs. Details at http://www.graphviz.org/
 *************************************************************************/

# remove leading underscore, then translate leading dot to underscore
function canon(s) {
	gsub(/\./,"_",s);
	gsub(/^_*/,"",s);
	gsub(/_*$/,"",s);
	return s;
}

BEGIN			{
	maxhue = .65
	minsaturation = .2;
	brightness = 1.0;
	printf ("digraph prof {\n");
	printf ("	node [style=filled];\n");
	}

$1 ~ /\[/ && $0 !~ "as a"		{
					if (NF == 6) {
						tail = $5
						source = 1
					}
					else {
						tail = $6
						source = 0
					}
					mytotal = $4

					while (getline) {
						if (NF == 0) {
#							if (source == 1) printf("\t%s;\n",canon(tail));
							break
						}
						if ( $2 ~ /\./ ) {
							ntc = $3;
							gsub("/.*","",ntc);
							if (mytotal < 0.0000000001)
								color = 0.0
							else
								color = $2/(mytotal + .01);
							color = color/ntc;

							tailc = canon(tail);
							headc = canon($4);
							hue = maxhue * (1.0 - color);
							saturation = minsaturation
								+ (1.0 - minsaturation) * color;
							brightness = .7 + .3*color;
							if ((tailc != "") && (headc != "")) {
								printf ("\t%s -> %s [color=\"%.3f %.3f %.3f\"];\n",
								# tailc, headc, hue, saturation, brightness);
								tailc, headc, hue, brightness, brightness);
								Degree[tailc]++; Degree[headc]++;
							}
							else { 		# Recursive call
								printf ("\t%s -> %s;\n ", canon(tail), canon($2))
							}
						}
				}
		}

$2 == "cumulative" {
	while ($1 != "time") getline;
	getline;
	scale = $1;
	brightness = 1.0;
	while (NF > 0) {
		if ($1 == "") exit;
		if ($(NF - 2) == "<cycle") func_name = $(NF - 3);
		else func_name = $(NF - 1);
		func_name = canon(func_name);
		if (Degree[func_name] > 0) {
			hue = maxhue * (1.0 - $1/scale);
			saturation = minsaturation + ((1.0 - minsaturation) * $1/scale);
			printf("%s [color=\"%.3f %.3f %.3f\"];\n",func_name,
				hue,saturation,brightness);
		}
		getline;
	}
	exit;
}

END				{ print "}" }

# Copyright (c) AT&T Corp. 1994, 1995.
# This code is licensed by AT&T Corp.  For the
# terms and conditions of the license, see
# http://www.research.att.com/orgs/ssr/book/reuse

# remove leading underscore, then translate leading dot to underscore
function canon(s) {
	gsub(/\./,"_",s);
	gsub(/^_*/,"",s);
	gsub(/_*$/,"",s);
	return s;
}

BEGIN			{
	maxhue = .65
	minsaturation = .2;
	brightness = 1.0;
	printf ("digraph prof {\n");
	printf ("	node [style=filled];\n");
	}

$1 ~ /\[/ && $0 !~ "as a"		{
					if (NF == 6) {
						tail = $5
						source = 1
					}
					else {
						tail = $6
						source = 0
					}
					mytotal = $4

					while (getline) {
						if (NF == 0) {
#							if (source == 1) printf("\t%s;\n",canon(tail));
							break
						}
						if ( $2 ~ /\./ ) {
							ntc = $3;
							gsub("/.*","",ntc);
							if (mytotal < 0.0000000001)
								color = 0.0
							else
								color = $2/(mytotal + .01);
							color = color/ntc;

							tailc = canon(tail);
							headc = canon($4);
							hue = maxhue * (1.0 - color);
							saturation = minsaturation
								+ (1.0 - minsaturation) * color;
							brightness = .7 + .3*color;
							if ((tailc != "") && (headc != "")) {
								printf ("\t%s -> %s [color=\"%.3f %.3f %.3f\"];\n",
								# tailc, headc, hue, saturation, brightness);
								tailc, headc, hue, brightness, brightness);
								Degree[tailc]++; Degree[headc]++;
							}
							else { 		# Recursive call
								printf ("\t%s -> %s;\n ", canon(tail), canon($2))
							}
						}
				}
		}

$2 == "cumulative" {
	while ($1 != "time") getline;
	getline;
	scale = $1;
	brightness = 1.0;
	while (NF > 0) {
		if ($1 == "") exit;
		if ($(NF - 2) == "<cycle") func_name = $(NF - 3);
		else func_name = $(NF - 1);
		func_name = canon(func_name);
		if (Degree[func_name] > 0) {
			hue = maxhue * (1.0 - $1/scale);
			saturation = minsaturation + ((1.0 - minsaturation) * $1/scale);
			printf("%s [color=\"%.3f %.3f %.3f\"];\n",func_name,
				hue,saturation,brightness);
		}
		getline;
	}
	exit;
}

END				{ print "}" }


updates to awk script

updates to awk script

#!\ /bin/gawk
# This is to fix the fact that without modifing Factom, the Genesis block is already
# complete before we can print.  So it already has the reward added.  But to make our
# code work, we need to remove the reward.

/200000000000 300000000000/ {
    $5 = 200000000000
}

{ cnt++ }

/^Transaction/ { transCnt++ }
/^Block/ { blockCnt++ }
/^Randomize/ { seed=$4 }

/.*address already in use.*/ {
    noprint = 1
    print "Cannot run the control panel.  Please stop any running factomd client and run again"
    exit(1)
}

/^out / {
    addr[$3]+=$4
    if (addr[$3] != $5) {
        err[$3] = cnt " output " $3 " has a balance of " addr[$3] " but Factom thinks " $5
    }else{
        delete err[$3]
        goodOutput++
    }
}

/^in / {
    addr[$3]-=$4
    if (addr[$3] != $5) {
        err[$3] = cnt " input " $3 " has a balance of " addr[$3] " but Factom thinks " $5
    }else{
        delete err[$3]
        goodInput++
    }
}

/^ec / {
    ecaddr[$3]+=$4
    if (ecaddr[$3] != $5) {
        err[$3] = cnt " ecoutput " $3 " has a balance of " ecaddr[$3] " but Factom thinks " $5
    }else{
        delete err[$3]
        goodECOutput++
    }
}

END {
    if (!noprint) {
        print "Seed = " seed
        print "Processed " transCnt " Transactions and " blockCnt " blocks"
        print "Found " length(err) " Errors"
        print "Found " goodInput " Good Inputs"
        print "Found " goodOutput " Good Outputs"
        print "Found " goodECOutput " Good ECOutputs"

        if (length(err) > 0) {
            for (x in err) {
                print err[x]
            }
        }
    }
}


Little change to process.awk

Little change to process.awk

Signed-off-by: Lu Yong <3812e7ccba28440a3ea55b89d289fd7e0e30069d@cn.ibm.com>

#! /usr/bin/awk -f
#
#    It takes 2894 usec for Allocate and prepare buffers
#    It takes 790 usec for Open the card
#    It takes 14 usec for Attach action
#    It takes 1 usec for SNAP prepare job_t structure
#    It takes 4 usec for Use MMIO to transfer the parameters
#    It takes 1 usec for Use MMIO to kick off "Action Start"
#    It takes 349 usec for Use MMIO to poll "Action Stop" bit
#====================  All job finished ==================
#    It takes 3 usec for Detach action
#    It takes 92 usec for Close the card
#    It takes 18 usec for Free all buffers

# $0: the whole current line
# $1: the first segement
# FS: separate character

BEGIN {
  FS=" "
}
{
  if ($0 ~ "snap_mm_test")	{iter++}

  if ($0 ~ "Allocate")		{usec_table[1][iter]=$3}
  if ($0 ~ "Open the card")	{usec_table[2][iter]=$3}
  if ($0 ~ "Attach")		{usec_table[3][iter]=$3}
  if ($0 ~ "prepare")		{usec_table[4][iter]=$3}
  if ($0 ~ "parameters")	{usec_table[5][iter]=$3}
  if ($0 ~ "Action Start")	{usec_table[6][iter]=$3}
  if ($0 ~ "Action Stop")	{usec_table[7][iter]=$3}
  if ($0 ~ "Detach")		{usec_table[8][iter]=$3}
  if ($0 ~ "Close")		{usec_table[9][iter]=$3}
  if ($0 ~ "Free")		{usec_table[10][iter]=$3}
}
END {
  i=1
  t=1
  error=0

  printf "---------------------- Process %s logs ----------------------------\n", iter
  printf "                       (time in usec)\n", iter
  while (t <= 10) {
	i=1
	min = usec_table[t][1];
	max = usec_table[t][1];
	sum = 0;
	while (i <= iter) {
		if (usec_table[t][i] < min) {min=usec_table[t][i]}
		if (usec_table[t][i] > max) {max=usec_table[t][i]}
		sum = sum + usec_table[t][i]
		i++
	}
	average = sum/iter

	if (t == 1) {printf "%20s", "Allocate"}
	if (t == 2) {printf "%20s", "Open the card"}
	if (t == 3) {printf "%20s", "Attach"}
	if (t == 4) {printf "%20s", "SNAP prepare"}
	if (t == 5) {printf "%20s", "MMIO parameters"}
	if (t == 6) {printf "%20s", "MMIO Action Start"}
	if (t == 7) {printf "%20s", "*** MMIO Action Stop"}
	if (t == 8) {printf "%20s", "Detach"}
	if (t == 9) {printf "%20s", "Close"}
	if (t == 10) {printf "%20s", "Free"}
	printf " average: %-8s (min: %-8s max: %-8s)\n", average, min, max
	t++
  }

}

#! /usr/bin/awk -f
#
#    It takes 2894 usec for Allocate and prepare buffers
#    It takes 790 usec for Open the card
#    It takes 14 usec for Attach action
#    It takes 1 usec for SNAP prepare job_t structure
#    It takes 4 usec for Use MMIO to transfer the parameters
#    It takes 1 usec for Use MMIO to kick off "Action Start"
#    It takes 349 usec for Use MMIO to poll "Action Stop" bit
#====================  All job finished ==================
#    It takes 3 usec for Detach action
#    It takes 92 usec for Close the card
#    It takes 18 usec for Free all buffers

# $0: the whole current line
# $1: the first segement
# FS: separate character

BEGIN {
  FS=" "
}
{
  if ($0 ~ "snap_mm_test")	{iter++}

  if ($0 ~ "Allocate")		{usec_table[1][iter]=$3}
  if ($0 ~ "Open the card")	{usec_table[2][iter]=$3}
  if ($0 ~ "Attach")		{usec_table[3][iter]=$3}
  if ($0 ~ "prepare")		{usec_table[4][iter]=$3}
  if ($0 ~ "parameters")	{usec_table[5][iter]=$3}
  if ($0 ~ "Action Start")	{usec_table[6][iter]=$3}
  if ($0 ~ "Action Stop")	{usec_table[7][iter]=$3}
  if ($0 ~ "Detach")		{usec_table[8][iter]=$3}
  if ($0 ~ "Close")		{usec_table[9][iter]=$3}
  if ($0 ~ "Free")		{usec_table[10][iter]=$3}
}
END {
  i=1
  t=1
  error=0

  printf "---------------------- Process %s logs -------------------------------\n", iter
  while (t <= 10) {
	i=1
	min = usec_table[t][1];
	max = usec_table[t][1];
	sum = 0;
	while (i <= iter) {
		if (usec_table[t][i] < min) {min=usec_table[t][i]}
		if (usec_table[t][i] > max) {max=usec_table[t][i]}
		sum = sum + usec_table[t][i]
		i++
	}
	average = sum/iter

	if (t == 1) {printf "%20s", "Allocate"}
	if (t == 2) {printf "%20s", "Open the card"}
	if (t == 3) {printf "%20s", "Attach"}
	if (t == 4) {printf "%20s", "SNAP prepare"}
	if (t == 5) {printf "%20s", "MMIO parameters"}
	if (t == 6) {printf "%20s", "MMIO Action Start"}
	if (t == 7) {printf "%20s", "*** MMIO Action Stop"}
	if (t == 8) {printf "%20s", "Detach"}
	if (t == 9) {printf "%20s", "Close"}
	if (t == 10) {printf "%20s", "Free"}
	printf " average: %-8s (min: %-8s max: %-8s)\n", average, min, max
	t++
  }

}


SCCS mkdoc.awk 10/06/22 08:50:28: Replaced script headers for Open Source release

#-------------------------------------------------------------------------------
# Phorward Software Development Kit
# Copyright (C) 2009, 2010 by Phorward Software Technologies, Jan Max Meyer
# http://www.phorward-software.com ++ contact<at>phorward<dash>software<dot>com
# All rights reserved. See $PHOME/LICENSE for more information.
#
# Script:		mkdoc.awk
# Author:		Jan Max Meyer
# Usage:		Template-based documentation generator
#-------------------------------------------------------------------------------

BEGIN	{
			SUBSEP = "#"

			print_sections = 1
			full_index = 0
			tpldir = getenv( "PHOME" ) "/tpl"
			manual = ""

			t = 1

			# Begin of program
			for( i = 1; i < ARGC; i++ )
			{
				if( ARGV[i] == "-o" )
					output = ARGV[++i]
				else if( ARGV[i] == "-i" )
					full_index = 1
				else if( ARGV[i] == "-ns" )
					print_sections = 0
				else if( ARGV[i] == "-tpl" && i+1 < ARGC )
					tpldir = ARGV[++i]
				else if( ARGV[i] == "-m" && i+1 < ARGC )
					manual = readfile( ARGV[++i] )
				else if( ARGV[i] == "-c" && i+1 < ARGC )
					allcopyright = ARGV[++i]
				else if( ARGV[i] == "-t" && i+1 < ARGC )
					alltitle = ARGV[++i]
				else if( ARGV[i] == "-u" && i+1 < ARGC )
					allusage = ARGV[++i]
				else
					files[ j++ ] = ARGV[i]
			}

			if( substr( tpldir, length( tpldir ) - 1, 1 ) != "/" )
				tpldir = tpldir "/"

			# Read all templates
			tpl[ "DOCUMENT" ] = readfile( tpldir "document.tpl" )
			tpl[ "MODULE" ] = readfile( tpldir "module.tpl" )
			tpl[ "FUNCTION" ] = readfile( tpldir "function.tpl" )
			tpl[ "parameters" ] = readfile( tpldir "parameter.tpl" )
			tpl[ "returns" ] = readfile( tpldir "returns.tpl" )
			tpl[ "refentry" ] = readfile( tpldir "refentry.tpl" )

			document = tpl[ "DOCUMENT" ]

			# Process the files
			for( i in files )
			{
				process( files[i] )

				if( print_sections )
				{
					#Output the header and index of contents
					gsub( "!!" "index", print_idx(), head_text )
					gsub( "!!" "contents", print_ref(), head_text )

					#all_text = all_text head_text
					#all_text = all_text print_ref()

					all_text = all_text head_text

					head_text = ""
				}

				if( full_index )
				{
					for( s in functions )
						all_functions[ s ] = functions[ s ]
				}

				for( s in functions )
					delete functions[s]
			}

			if( full_index )
			{
				for( s in all_functions )
					functions[ s ] = all_functions[ s ]

				if( !print_sections )
					all_text = print_ref()

				allindex = print_idx()
			}

			if( document != "" )
			{
				gsub( "!!" "title", alltitle, document )
				gsub( "!!" "usage", allusage, document )
				gsub( "!!" "copyright", allcopyright, document )
				gsub( "!!" "manual", manual, document )
				gsub( "!!" "index", allindex, document )
				gsub( "!!" "reference", all_text, document )
			}
			else
				document = all_text

			#Finally print all!
			print document
		}

function process( file )
{
	section = ""
	current_item = ""
	old_item = ""
	nodoc = 0
	line = 1

	while( getline line <file > 0 )
	{
		#this could be windoof files
		gsub( "\r", "", line )
		line = trim( line )

		section_line++

		if( line == "" )
		{
			if( current_item == "usage" || current_item == "copyright" )
				items[ current_item ] = items[ current_item ] "\n"

			continue
		}

		#Look for section?
		if( section == "NODOC" && index( line, "COD_ON" ) >= 1 )
			section = "";
		else if( section == "" )
		{
			if( index( line, "/* -MODULE" ) == 1 )
				section = "MODULE"
			else if( index( line, "/* -FUNCTION" ) == 1 )
				section = "FUNCTION"
			else if( index( line, "NO_DOC" ) >= 1 )
				section = "NODOC";

			current_item = ""
			section_line = 0
		}
		else if( section != "NODOC" )
		{
			#End of a section
			if( index( line, "--------------------------------------------" \
					"--------------------------------- */" ) == 1 \
				|| index( line, "~~~ CHANGES & NOTES ~~~" ) > 0 )
			{
				# create a function parameter synopsis!
				if( section == "FUNCTION" )
				{
					#Generate RETURNTYPE and RETURNDESC
					if( items[ "returns" ] != "" )
					{
						if( index( items[ "returns" ], " " ) )
						{
							datatype = substr( items[ "returns" ], 1, \
										index( items[ "returns" ], " " ) - 1 )
							desc = 	substr( items[ "returns" ], \
										index( items[ "returns" ], " " ) + 1 )
						}
						else
						{
							datatype = items[ "returns" ]
							desc = "Nothing"
						}

						items[ "returntype" ] = datatype

						if( desc == "" )
							desc = "Nothing."

						items[ "returndesc" ] = desc
					}

					#Generate function-name
					gsub( /\(.*\)$/, "", items[ "function" ] )

					#GENERATE SYNOPSIS
					items[ "synopsis" ] = items[ "function" ]
					#gsub( /\(.*\)$/, "", items[ "synopsis" ] )
					items[ "synopsis" ] = items[ "synopsis" ] "( "

					first = 1
					for( item in items )
					{
						if( match( item, "parameters" SUBSEP "[0-9]+" ) )
						{
							datatype = substr( items[ item ], 1, \
								index( items[ item ], " " ) - 1 )
							desc = substr( items[ item ], \
										index( items[ item ], " " ) + 1 )

							if( datatype != "..." )
							{
								param = substr( desc, 1, index( desc, " " ) - 1 )
								desc = 	substr( desc, index( desc, " " ) + 1 )
							}
							else
								param = ""

							if( !first )
								items[ "synopsis" ] = items[ "synopsis" ] ", "
							first = 0

							items[ "synopsis" ] = items[ "synopsis" ] datatype
							if( param != "" )
								items[ "synopsis" ] = items[ "synopsis" ] \
									" " param
						}
					}

					if( first )
						items[ "synopsis" ] = items[ "synopsis" ] "void )"
					else
						items[ "synopsis" ] = items[ "synopsis" ] " )"

					items[ "synopsis" ] = items[ "returntype" ] " " \
											items[ "synopsis" ]

					#Put code in array first, for sorting
					funcname =  items[ "function" ]
					#gsub( /_|\(|\)/, "", funcname )

					# GENERATE OUTPUT...
					#write index of contents as a quick reference
					if( !index( items[ "usage" ], "." ) )
						items[ "usage" ] = trim( items[ "usage" ] ) "."

					content[ funcname ] = substr( items[ "usage" ], \
											1, index( items[ "usage" ], "." ) )

					#Write output into functions array
					functions[ funcname ] = fill_tex_tpl( section, items )
				}
				else if( head_text == "" )
					head_text = fill_tex_tpl( section, items )

				#for( item in items )
					#print item " => \"" items[ item ] "\""

				for( item in items )
					delete items[ item ]

				section = ""
				current_item = ""
				continue
			}
			else if( index( line, "File:" ) == 1 )
				current_item = "file"
			else if( index( line, "Author:" ) == 1 )
				current_item = "author"
			else if( index( line, "Usage:" ) == 1 )
				current_item = "usage"
			else if( index( line, "Function:" ) == 1 )
				current_item = "function"
			else if( index( line, "Parameters:" ) == 1 )
			{
				akt_parm = 1
				current_item = "parameters" SUBSEP akt_parm
			}
			else if( index( line, "Returns:" ) == 1 )
			{
				current_item = "returns"
			}
			else if( section == "MODULE" && section_line == 1 )
				current_item = "title"
			else if( section == "MODULE" && section_line == 2 )
				current_item = "copyright"

			#Read in the item information
			if( current_item != "" )
			{
				if( current_item != old_item && items[ current_item ] == "" )
				{
					if( current_item != "title" \
						&& current_item != "copyright" )
					{
						line = trim( substr( line, index( line, ":" ) + 1 ) )
					}

					gsub( /[ \t]+/, " ", line )

					items[ current_item ] = line
				}
				else
				{
					if( match( current_item, "parameters" SUBSEP "[0-9]+" ) )
					{
						if( match( line, \
							"[A-Za-z0-9_*]+[ \t][ \t]+[A-Za-z0-9_*()]+[ \t]+|\\.\\.\\.[ \t]+" ) )
						{
							akt_parm++
							current_item = "parameters" SUBSEP akt_parm

							gsub( /[ \t]+/, " ", line )
							items[ current_item ] = line
						}
					}

					if( old_item == current_item )
					{
						gsub( /[ \t]+/, " ", line )

						if( ( section == "MODULE" && current_item == "usage" ) \
							|| current_item == "copyright" )
							delim = "\n"
						else
							delim = " "

						items[ current_item ] = items[ current_item ] delim line
					}
				}
			}

			old_item = current_item
		}

		line++;
	}

	close( getline line <file > 0 )
}

function fill_tex_tpl( section, items )
{
	final = tpl[ section ]
	parameters = ""

	for( item in items )
	{
		#print "!!" item " " items[ item ]

		#for LaTeX
		#gsub( "_", "\\_", items[ item ] )

		if( match( item, "parameters" SUBSEP "[0-9]+" ) )
		{
			datatype = substr( items[ item ], 1, \
						index( items[ item ], " " ) - 1 )
			desc = 	substr( items[ item ], index( items[ item ], " " ) + 1 )

			if( datatype != "..." )
			{
				param = substr( desc, 1, index( desc, " " ) - 1 )
				desc = 	substr( desc, index( desc, " " ) + 1 )
			}
			else
				param = ""

			subfinal = tpl[ "parameters" ]
			gsub( "!!" "datatype", datatype, subfinal )
			gsub( "!!" "param", param, subfinal )
			gsub( "!!" "desc", desc, subfinal )

			parameters = parameters subfinal
		}
		else if( item == "returns" )
		{
			subfinal = tpl[ "returns" ]
			gsub( "!!" "datatype", items[ "returntype" ], subfinal )
			gsub( "!!" "desc", items[ "returndesc" ], subfinal )

			gsub( "!!" item, subfinal, final )
		}
		else
		{
			gsub( "!!" item, items[ item ], final )
		}
	}

	if( section == "FUNCTION" )
		gsub( "!!parameters", parameters, final )

	return final
}

function print_idx()
{
	t = 1
	for( s in functions )
		sort[t++] = s

	n = asort( sort )

	refindex = ""
	for( s = 1; s <= n; s++ )
	{
		refentry = tpl[ "refentry" ]

		gsub( "!!" "function", sort[s], refentry )
		gsub( "!!" "usage", content[ sort[s] ], refentry )
		refindex = refindex refentry
	}

	for( s in sort )
		delete sort[s]

	return refindex
}

function print_ref()
{
	t = 1
	for( s in functions )
		sort[t++] = s

	n = asort( sort )

	text = ""
	for( s = 1; s <= n; s++ )
		text = text functions[sort[s]]

	for( s in sort )
		delete sort[s]

	return text
}

#--- Utility functions go here ---

function ltrim( s )
{
	sub( /^[ \t\n]+/, "", s )
	return s
}

function rtrim( s )
{
	sub( /[ \t\n]+$/, "", s )
	return s
}

function trim( s )
{
	return rtrim( ltrim( s ) )
}

function readfile( s )
{
	f = ""

	while( ( getline l < s ) > 0 )
		f = f l "\n"

	close( s )
	return f
}

function getenv( s )
{
	"echo $" s | getline v
	return v
}

#-------------------------------------------------------------------------------
# Phorward SDK - mkdoc :: Documentation Generator
# Copyright (C) 2009, 2010 by Phorward Software Technologies, Jan Max Meyer
# http://www.phorward-software.com ++ mail<<AT>>phorward-software<<DOT>>com
#
# Script:		mkdoc.awk
# Author:		Jan Max Meyer
#-------------------------------------------------------------------------------

BEGIN	{
			SUBSEP = "#"

			print_sections = 1
			full_index = 0
			tpldir = getenv( "PHOME" ) "/tpl"
			manual = ""

			t = 1

			# Begin of program
			for( i = 1; i < ARGC; i++ )
			{
				if( ARGV[i] == "-o" )
					output = ARGV[++i]
				else if( ARGV[i] == "-i" )
					full_index = 1
				else if( ARGV[i] == "-ns" )
					print_sections = 0
				else if( ARGV[i] == "-tpl" && i+1 < ARGC )
					tpldir = ARGV[++i]
				else if( ARGV[i] == "-m" && i+1 < ARGC )
					manual = readfile( ARGV[++i] )
				else if( ARGV[i] == "-c" && i+1 < ARGC )
					allcopyright = ARGV[++i]
				else if( ARGV[i] == "-t" && i+1 < ARGC )
					alltitle = ARGV[++i]
				else if( ARGV[i] == "-u" && i+1 < ARGC )
					allusage = ARGV[++i]
				else
					files[ j++ ] = ARGV[i]
			}

			if( substr( tpldir, length( tpldir ) - 1, 1 ) != "/" )
				tpldir = tpldir "/"

			# Read all templates
			tpl[ "DOCUMENT" ] = readfile( tpldir "document.tpl" )
			tpl[ "MODULE" ] = readfile( tpldir "module.tpl" )
			tpl[ "FUNCTION" ] = readfile( tpldir "function.tpl" )
			tpl[ "parameters" ] = readfile( tpldir "parameter.tpl" )
			tpl[ "returns" ] = readfile( tpldir "returns.tpl" )
			tpl[ "refentry" ] = readfile( tpldir "refentry.tpl" )

			document = tpl[ "DOCUMENT" ]

			# Process the files
			for( i in files )
			{
				process( files[i] )

				if( print_sections )
				{
					#Output the header and index of contents
					gsub( "!!" "index", print_idx(), head_text )
					gsub( "!!" "contents", print_ref(), head_text )

					#all_text = all_text head_text
					#all_text = all_text print_ref()

					all_text = all_text head_text

					head_text = ""
				}

				if( full_index )
				{
					for( s in functions )
						all_functions[ s ] = functions[ s ]
				}

				for( s in functions )
					delete functions[s]
			}

			if( full_index )
			{
				for( s in all_functions )
					functions[ s ] = all_functions[ s ]

				if( !print_sections )
					all_text = print_ref()

				allindex = print_idx()
			}

			if( document != "" )
			{
				gsub( "!!" "title", alltitle, document )
				gsub( "!!" "usage", allusage, document )
				gsub( "!!" "copyright", allcopyright, document )
				gsub( "!!" "manual", manual, document )
				gsub( "!!" "index", allindex, document )
				gsub( "!!" "reference", all_text, document )
			}
			else
				document = all_text

			#Finally print all!
			print document
		}

function process( file )
{
	section = ""
	current_item = ""
	old_item = ""
	nodoc = 0
	line = 1

	while( getline line <file > 0 )
	{
		#this could be windoof files
		gsub( "\r", "", line )
		line = trim( line )

		section_line++

		if( line == "" )
		{
			if( current_item == "usage" || current_item == "copyright" )
				items[ current_item ] = items[ current_item ] "\n"

			continue
		}

		#Look for section?
		if( section == "NODOC" && index( line, "COD_ON" ) >= 1 )
			section = "";
		else if( section == "" )
		{
			if( index( line, "/* -MODULE" ) == 1 )
				section = "MODULE"
			else if( index( line, "/* -FUNCTION" ) == 1 )
				section = "FUNCTION"
			else if( index( line, "NO_DOC" ) >= 1 )
				section = "NODOC";

			current_item = ""
			section_line = 0
		}
		else if( section != "NODOC" )
		{
			#End of a section
			if( index( line, "--------------------------------------------" \
					"--------------------------------- */" ) == 1 \
				|| index( line, "~~~ CHANGES & NOTES ~~~" ) > 0 )
			{
				# create a function parameter synopsis!
				if( section == "FUNCTION" )
				{
					#Generate RETURNTYPE and RETURNDESC
					if( items[ "returns" ] != "" )
					{
						if( index( items[ "returns" ], " " ) )
						{
							datatype = substr( items[ "returns" ], 1, \
										index( items[ "returns" ], " " ) - 1 )
							desc = 	substr( items[ "returns" ], \
										index( items[ "returns" ], " " ) + 1 )
						}
						else
						{
							datatype = items[ "returns" ]
							desc = "Nothing"
						}

						items[ "returntype" ] = datatype

						if( desc == "" )
							desc = "Nothing."

						items[ "returndesc" ] = desc
					}

					#Generate function-name
					gsub( /\(.*\)$/, "", items[ "function" ] )

					#GENERATE SYNOPSIS
					items[ "synopsis" ] = items[ "function" ]
					#gsub( /\(.*\)$/, "", items[ "synopsis" ] )
					items[ "synopsis" ] = items[ "synopsis" ] "( "

					first = 1
					for( item in items )
					{
						if( match( item, "parameters" SUBSEP "[0-9]+" ) )
						{
							datatype = substr( items[ item ], 1, \
								index( items[ item ], " " ) - 1 )
							desc = substr( items[ item ], \
										index( items[ item ], " " ) + 1 )

							if( datatype != "..." )
							{
								param = substr( desc, 1, index( desc, " " ) - 1 )
								desc = 	substr( desc, index( desc, " " ) + 1 )
							}
							else
								param = ""

							if( !first )
								items[ "synopsis" ] = items[ "synopsis" ] ", "
							first = 0

							items[ "synopsis" ] = items[ "synopsis" ] datatype
							if( param != "" )
								items[ "synopsis" ] = items[ "synopsis" ] \
									" " param
						}
					}

					if( first )
						items[ "synopsis" ] = items[ "synopsis" ] "void )"
					else
						items[ "synopsis" ] = items[ "synopsis" ] " )"

					items[ "synopsis" ] = items[ "returntype" ] " " \
											items[ "synopsis" ]

					#Put code in array first, for sorting
					funcname =  items[ "function" ]
					#gsub( /_|\(|\)/, "", funcname )

					# GENERATE OUTPUT...
					#write index of contents as a quick reference
					if( !index( items[ "usage" ], "." ) )
						items[ "usage" ] = trim( items[ "usage" ] ) "."

					content[ funcname ] = substr( items[ "usage" ], \
											1, index( items[ "usage" ], "." ) )

					#Write output into functions array
					functions[ funcname ] = fill_tex_tpl( section, items )
				}
				else if( head_text == "" )
					head_text = fill_tex_tpl( section, items )

				#for( item in items )
					#print item " => \"" items[ item ] "\""

				for( item in items )
					delete items[ item ]

				section = ""
				current_item = ""
				continue
			}
			else if( index( line, "File:" ) == 1 )
				current_item = "file"
			else if( index( line, "Author:" ) == 1 )
				current_item = "author"
			else if( index( line, "Usage:" ) == 1 )
				current_item = "usage"
			else if( index( line, "Function:" ) == 1 )
				current_item = "function"
			else if( index( line, "Parameters:" ) == 1 )
			{
				akt_parm = 1
				current_item = "parameters" SUBSEP akt_parm
			}
			else if( index( line, "Returns:" ) == 1 )
			{
				current_item = "returns"
			}
			else if( section == "MODULE" && section_line == 1 )
				current_item = "title"
			else if( section == "MODULE" && section_line == 2 )
				current_item = "copyright"

			#Read in the item information
			if( current_item != "" )
			{
				if( current_item != old_item && items[ current_item ] == "" )
				{
					if( current_item != "title" \
						&& current_item != "copyright" )
					{
						line = trim( substr( line, index( line, ":" ) + 1 ) )
					}

					gsub( /[ \t]+/, " ", line )

					items[ current_item ] = line
				}
				else
				{
					if( match( current_item, "parameters" SUBSEP "[0-9]+" ) )
					{
						if( match( line, \
							"[A-Za-z0-9_*]+[ \t][ \t]+[A-Za-z0-9_*()]+[ \t]+|\\.\\.\\.[ \t]+" ) )
						{
							akt_parm++
							current_item = "parameters" SUBSEP akt_parm

							gsub( /[ \t]+/, " ", line )
							items[ current_item ] = line
						}
					}

					if( old_item == current_item )
					{
						gsub( /[ \t]+/, " ", line )

						if( ( section == "MODULE" && current_item == "usage" ) \
							|| current_item == "copyright" )
							delim = "\n"
						else
							delim = " "

						items[ current_item ] = items[ current_item ] delim line
					}
				}
			}

			old_item = current_item
		}

		line++;
	}

	close( getline line <file > 0 )
}

function fill_tex_tpl( section, items )
{
	final = tpl[ section ]
	parameters = ""

	for( item in items )
	{
		#print "!!" item " " items[ item ]

		#for LaTeX
		#gsub( "_", "\\_", items[ item ] )

		if( match( item, "parameters" SUBSEP "[0-9]+" ) )
		{
			datatype = substr( items[ item ], 1, \
						index( items[ item ], " " ) - 1 )
			desc = 	substr( items[ item ], index( items[ item ], " " ) + 1 )

			if( datatype != "..." )
			{
				param = substr( desc, 1, index( desc, " " ) - 1 )
				desc = 	substr( desc, index( desc, " " ) + 1 )
			}
			else
				param = ""

			subfinal = tpl[ "parameters" ]
			gsub( "!!" "datatype", datatype, subfinal )
			gsub( "!!" "param", param, subfinal )
			gsub( "!!" "desc", desc, subfinal )

			parameters = parameters subfinal
		}
		else if( item == "returns" )
		{
			subfinal = tpl[ "returns" ]
			gsub( "!!" "datatype", items[ "returntype" ], subfinal )
			gsub( "!!" "desc", items[ "returndesc" ], subfinal )

			gsub( "!!" item, subfinal, final )
		}
		else
		{
			gsub( "!!" item, items[ item ], final )
		}
	}

	if( section == "FUNCTION" )
		gsub( "!!parameters", parameters, final )

	return final
}

function print_idx()
{
	t = 1
	for( s in functions )
		sort[t++] = s

	n = asort( sort )

	refindex = ""
	for( s = 1; s <= n; s++ )
	{
		refentry = tpl[ "refentry" ]

		gsub( "!!" "function", sort[s], refentry )
		gsub( "!!" "usage", content[ sort[s] ], refentry )
		refindex = refindex refentry
	}

	for( s in sort )
		delete sort[s]

	return refindex
}

function print_ref()
{
	t = 1
	for( s in functions )
		sort[t++] = s

	n = asort( sort )

	text = ""
	for( s = 1; s <= n; s++ )
		text = text functions[sort[s]]

	for( s in sort )
		delete sort[s]

	return text
}

#--- Utility functions go here ---

function ltrim( s )
{
	sub( /^[ \t\n]+/, "", s )
	return s
}

function rtrim( s )
{
	sub( /[ \t\n]+$/, "", s )
	return s
}

function trim( s )
{
	return rtrim( ltrim( s ) )
}

function readfile( s )
{
	f = ""

	while( ( getline l < s ) > 0 )
		f = f l "\n"

	close( s )
	return f
}

function getenv( s )
{
	"echo $" s | getline v
	return v
}


addr_utils.awk		-- new func AU_match() that handles street ranges

function AU_parse(rply, do_subs, addr, result, towns, st_types, st_quals, dirs,   nf, ary, i, f_st, nf2, ary2, i1, name, street, quals, town, k) {

	result["status"] = "B"
	result["emsg"  ] = ""
	result["name"  ] = ""
	result["street"] = ""
	result["quals" ] = ""
	result["town"  ] = ""
	result["state" ] = ""

	nf = split(addr, ary, ",")
	for(i = 1; i <= nf; i++){
		sub(/^ */, "", ary[i])
		sub(/ *$/, "", ary[i])
	}

	if(rply){
		# TODO: generalize to all states?
		if(ary[nf] != "United States of America"){
			result["emsg"] = "not.usa"
			return 1
		}else
			nf--
		if(ary[nf] == "CA")
			nf--
		else if(ary[nf] ~ /CA [0-9]{5}$/)
			nf--
		else if(ary[nf] ~ /CA [0-9]{5}-[0-9]{4}$/)
			nf--
		else if(ary[nf] == "California")
			nf--
		else{
			result["emsg"] = "not.CA"
			return 1
		}
	}

	# minimal address has 2 fields: street, town
	if(nf < 2){
		result["emsg"] = "short.addr"
		return 1
	}

	# check that we have a known town
	if(!(ary[nf] in towns)){
		result["emsg"] = "bad.town"
		return 1
	}
	town = do_subs ? towns[ary[nf]] : ary[nf]

	# find the street.
	# street is 1) 1st elt of ary[1:nf-1] that begins w/number & ends w/st_type or 2) last elt of ary[1:nf-1] that begins w/number
	f_st = 0
	for(i = 1; i < nf; i++){
		if(ary[i] ~ /^[1-9]/){
			nf2 = split(ary[i], ary2, /  */)
			if(ary2[nf2] in st_types){
				f_st = i
				break
			}
			f_st = i
		}
	}
	if(f_st == 0){
		result["emsg"] = "no.street"
		return 1
	}

	# The name is what precedes f_st.  if f_st == 1, set name to "Residence"
	if(f_st == 1)
		name = "Residence"
	else{
		name = ary[1]
		for(i = 2; i < f_st; i++)
			name = name ", " ary[i]
	}

	# Street qualifiers are those fields, if any, that follow f_st and precede nf
	if(nf - f_st > 1){
		quals = ary[f_st+1]
		for(i = f_st+2; i < nf; i++)
			quals = quals ", " ary[i]
	}else
		quals = ""

	# clean up street
	nf2 = split(ary[f_st], ary2, /  */)
	if(nf2 < 2){
		result["emsg"] = "short.street"
		return 1
	}

	# Street Hacks: Probably should just compare ignore case but not ready for that step yet
	# change el Camino X to El Camino X
	# change el Monte X  to El Monte X
	if(do_subs){
		if(nf2 > 3){
			if(ary2[nf2-2] == "el" && ary2[nf2-1] == "Camino")
				ary2[nf2-2] = "El"
			else if(ary2[nf2-2] == "el" && ary2[nf2-1] == "Monte")
				ary2[nf2-2] =  "El"
		}
	}

	# Street should be num [ dir ] str [ st ]
	street = ary2[1]
	if(do_subs){
		# This is amusing.  What is South Court? Is is S. Court or South Ct.  No idea so, leave such streets in long form
		if(nf2 == 3 && (ary2[2] in dirs) && (ary2[3] in st_types)){
			for(i = 2; i <= nf2; i++)
				street = street " " ary2[i]
		}else{
			if(ary2[2] in dirs){
				street = street " " dirs[ary2[2]]
				i1 = 3
			}else
				i1 = 2
			for(i = i1; i < nf2; i++){
				street = street " " ary2[i]
			}
			street = street ((ary2[nf2] in st_types) ? (" " st_types[ary2[nf2]]) : (" " ary2[nf2]))
		}
	}else{
		for(i = 2; i <= nf2; i++)
			street = street " " ary2[i]
	}

	result["status"] = "G"
	result["name"  ] = name
	result["street"] = street
	result["quals" ] = quals
	result["town"  ] = town
	result["state" ] = "CA"

	return 0
}
function AU_get_addr_data(addr_info, key, data,   k, keys, nk, i) {
	n_data = 0
	for(k in addr_info){
		nk = split(k, keys, SUBSEP)
		if(keys[1] == key){
			data[keys[2]] = addr_info[k]
			n_data++
		}
	}
	return n_data
}
function AU_match(cand, ref,   is_mat, i, n_cand_st, cand_st, cand_odd, n_ref_st, ref_st, ref_odd) {

	is_mat = 0

	# deal with street ranges
	n_cand_st = split(cand["street"], cand_st, /-/)
	n_ref_st = split(ref["street"], ref_st, /-/)

	# US rule: require all numbers to be either odd or even
	cand_odd = cand_st[1] % 2
	for(i = 2; i <= n_cand_st; i++){
		if(cand_st[i] % 2 != cand_odd){
			printf("ERROR: odd/even candidate address range: %s\n", cand["street"]) > "/dev/stderr"
			return 0
		}
	}
	n_ref_st = split(ref["street"], ref_st, /-/)
	ref_odd = ref_st[1] % 2
	for(i = 2; i <= n_ref_st; i++){
		if(ref_st[i] % 2 != ref_odd){
			printf("ERROR: odd/even reference address range: %s\n", ref["street"]) > "/dev/stderr"
			return 0
		}
	}
	if(cand_odd != ref_off)
		return 0

	# odd/even good, check the actual numbers/ranges
	if(n_cand_st == 1){
		if(n_ref_st == 1){
			printf("DEBUG: AU_match: case 1: c.N v r.N: %d vs %d\n", cand_st[1], ref_st[1]) > "/dev/stderr"
			is_mat = cand_st[1] == ref_st[1]
		}else{
			printf("DEBUG: AU_match: case 2: c.N v r.R: %d vs %d-%d\n", cand_st[1], ref_st[1], ref_st[2]) > "/dev/stderr"
			is_mat = cand_st[1] >= ref_st[1] && cand_st[1] <= ref_st[2];
		}
	}else if(n_ref_st == 1){
		printf("DEBUG: AU_match: case 3: c.R v r.N: %d-%d vs %d\n", cand_st[1], cand_st[2], ref_st[1]) > "/dev/stderr"
		is_mat = ref_st[1] >= cand_st[1] && ref_st[1] <= cand_st[2]
	}else{
		printf("DEBUG: AU_match: case 4: c.R v r.R: %d-%d vs %d-%d\n", cand_st[1], cand_st[2], ref_st[1], ref_st[2]) > "/dev/stderr"
		is_mat = !(cand_st[2] < ref_st[1] || cand_st[1] > ref_st[2])
	}

	# streets are good, deal with towns
	if(is_mat){
		is_mat = cand["town"] == ref["town"]
	}

	return is_mat
}

function AU_parse(rply, addr, result, towns, st_types, st_quals, dirs,   nf, ary, i, f_st, nf2, ary2, i1, name, street, quals, town, k) {

	result["status"] = "B"
	result["emsg"  ] = ""
	result["name"  ] = ""
	result["street"] = ""
	result["quals" ] = ""
	result["town"  ] = ""
	result["state" ] = ""

	nf = split(addr, ary, ",")
	for(i = 1; i <= nf; i++){
		sub(/^ */, "", ary[i])
		sub(/ *$/, "", ary[i])
	}

	if(rply){
		if(ary[nf] != "United States of America"){
			result["emsg"] = "not.usa"
			return 1
		}else
			nf--
		if(ary[nf] == "CA")
			nf--
		else if(ary[nf] ~ /CA [0-9]{5}$/)
			nf--
		else if(ary[nf] ~ /CA [0-9]{5}-[0-9]{4}$/)
			nf--
		else if(ary[nf] == "California")
			nf--
		else{
			result["emsg"] = "not.CA"
			return 1
		}
	}

	# minimal address has 2 fields: street, town
	if(nf < 2){
		result["emsg"] = "short.addr"
		return 1
	}

	# check that we have a known town
	if(!(ary[nf] in towns)){
		result["emsg"] = "bad.town"
		return 1
	}
	town = towns[ary[nf]]

	# find the street.
	# street is 1) 1st elt of ary[1:nf-1] that begins w/number & ends w/st_type or 2) last elt of ary[1:nf-1] that begins w/number
	f_st = 0
	for(i = 1; i < nf; i++){
		if(ary[i] ~ /^[1-9]/){
			nf2 = split(ary[i], ary2, /  */)
			if(ary2[nf2] in st_types){
				f_st = i
				break
			}
			f_st = i
		}
	}
	if(f_st == 0){
		result["emsg"] = "no.street"
		return 1
	}

	# The name is what precedes f_st.  if f_st == 1, set name to "Residence"
	if(f_st == 1)
		name = "Residence"
	else{
		name = ary[1]
		for(i = 2; i < f_st; i++)
			name = name ", " ary[i]
	}

	# Street qualifiers are those fields, if any, that follow f_st and precede nf
	if(nf - f_st > 1){
		quals = ary[f_st+1]
		for(i = f_st+2; i < nf; i++)
			quals = quals ", " ary[i]
	}else
		quals = ""

	# clean up street
	nf2 = split(ary[f_st], ary2, /  */)
	if(nf2 < 2){
		result["emsg"] = "short.street"
		return 1
	}

	# Street Hacks: Probably should just compare ignore case but not ready for that step yet
	# change el Camino X to El Camino X
	# change el Monte X  to El Monte X
	if(nf2 > 3){
		if(ary2[nf2-2] == "el" && ary2[nf2-1] == "Camino")
			ary2[nf2-2] = "El"
		else if(ary2[nf2-2] == "el" && ary2[nf2-1] == "Monte")
			ary2[nf2-2] =  "El"
	}

	# Street should be num [ dir ] str [ st ]
	street = ary2[1]

	# This is amusing.  What is South Court? Is is S. Court or South Ct.  No idea so, leave such streets in long form
	if(nf2 == 3 && (ary2[2] in dirs) && (ary2[3] in st_types)){
		for(i = 2; i <= nf2; i++)
			street = street " " ary2[i]
	}else{
		if(ary2[2] in dirs){
			street = street " " dirs[ary2[2]]
			i1 = 3
		}else
			i1 = 2
		for(i = i1; i < nf2; i++){
			street = street " " ary2[i]
		}
		street = street ((ary2[nf2] in st_types) ? (" " st_types[ary2[nf2]]) : (" " ary2[nf2]))
	}

	result["status"] = "G"
	result["name"  ] = name
	result["street"] = street
	result["quals" ] = quals
	result["town"  ] = town
	result["state" ] = "CA"

	return 0
}
function AU_get_addr_data(addr_info, key, data,   k, keys, nk, i) {
	n_data = 0
	for(k in addr_info){
		nk = split(k, keys, SUBSEP)
		if(keys[1] == key){
			data[keys[2]] = addr_info[k]
			n_data++
		}
	}
	return n_data
}


TestCommons: add explode()

TestCommons: add explode()

BEGIN {
    START_TEST("Commons.awk")

    # Arrays
    T("anything()", 3)
    {
        delete something
        assertFalse(anything(nothing))
        something[0] = 0; assertFalse(anything(something)) # edge case
        something[0] = 1; assertTrue(anything(something))
    }
    T("exists()", 4)
    {
        delete something
        assertFalse(exists(something))
        something[0] = 0; assertFalse(exists(something)) # edge case
        something[0] = 1; assertTrue(exists(something))
        assertTrue(exists(something[0]))
    }
    T("belongsTo()", 3)
    {
        delete array; array[0] = "foo"; array[1] = "bar"
        assertTrue(belongsTo("foo", array))
        assertTrue(belongsTo("bar", array))
        assertFalse(belongsTo("world", array))
    }
    T("identical()", 4)
    {
        delete x; x[0] = 42
        delete y; y[0][0] = 42
        assertFalse(identical(x, y))
        assertTrue(identical(x, y[0]))
        assertFalse(identical(x[0], y[0]))
        assertTrue(identical(x[0], y[0][0]))
    }
    T("append()", 1)
    {
        delete array; array[0] = "foo"
        delete expected; expected[0] = "foo"; expected[1] = "bar"
        append(array, "bar")
        assertTrue(identical(array, expected))
    }

    # Strings
    T("startsWithAny()", 3)
    {
        delete substrings; substrings[0] = "A"; substrings[1] = "a"
        assertTrue(startsWithAny("absolute", substrings))
        assertTrue(startsWithAny("ABSOLUTE", substrings))
        assertFalse(startsWithAny("ZOO", substrings))
    }
    T("matchesAny()", 4)
    {
        delete patterns; patterns[0] = "[[:space:]]"; patterns[1] = "[0Oo]"
        assertTrue(matchesAny("  ", patterns))
        assertTrue(matchesAny("obsolete", patterns))
        assertTrue(matchesAny("0.0", patterns))
        assertFalse(matchesAny("1.0", patterns))
    }
    T("replicate()", 4)
    {
        assertEqual(replicate("", 0), "")
        assertEqual(replicate("", 2), "")
        assertEqual(replicate("foo bar", 1), "foo bar")
        assertEqual(replicate("foo bar", 3), "foo barfoo barfoo bar")
    }
    T("join()", 4)
    {
        assertEqual(join("", "-"), "")
        delete array; array[0]
        assertEqual(join(array, "-"), "")
        delete array; array[0] = "foo"; array[1] = "bar"
        assertEqual(join(array, " "), "foo bar")
        assertEqual(join(array, ","), "foo,bar")
    }
    T("explode()", 1)
    {
        delete array
        delete expected; expected[1] = "f"; expected[2] = "o"; expected[3] = "o"
        explode("foo", array)
        assertTrue(identical(array, expected))
    }
    T("escapeChar()", 8)
    {
        assertEqual(escapeChar("b"), "\b")
        assertEqual(escapeChar("f"), "\f")
        assertEqual(escapeChar("n"), "\n")
        assertEqual(escapeChar("r"), "\r")
        assertEqual(escapeChar("t"), "\t")
        assertEqual(escapeChar("v"), "\v")
        assertEqual(escapeChar("_"), "_")
        assertNotEqual(escapeChar("_"), "\\_")
    }
    T("literal()", 4)
    {
        assertEqual(literal(""), "")
        assertEqual(literal("foo"), "foo")
        assertEqual(literal("\"foo\""), "foo")
        assertEqual(literal("\"\\\"foo\\\"\""), "\"foo\"")
    }
    T("escape()", 4)
    {
        assertEqual(escape(""), "")
        assertEqual(escape("foo"), "foo")
        assertEqual(escape("\""), "\\\\\"")
        assertEqual(escape("\"foo\""), "\\\\\"foo\\\\\"")
    }
    T("parameterize()", 10)
    {
        assertEqual(parameterize(""), "''")
        assertEqual(parameterize("foo"), "'foo'")
        assertEqual(parameterize("foo bar"), "'foo bar'")
        assertEqual(parameterize("foo 'bar"), "'foo '\\''bar'")
        assertEqual(parameterize("foo \"bar\""), "'foo \"bar\"'")
        assertEqual(parameterize("", "\""), "\"\"")
        assertEqual(parameterize("foo", "\""), "\"foo\"")
        assertEqual(parameterize("foo bar", "\""), "\"foo bar\"")
        assertEqual(parameterize("foo 'bar", "\""), "\"foo 'bar\"")
        assertEqual(parameterize("foo \"bar\"", "\""), "\"foo \\\\\"bar\\\\\"\"")
    }
    T("squeeze()", 4)
    {
        assertEqual(squeeze(""), "")
        assertEqual(squeeze(" "), "")
        assertEqual(squeeze("	"), "")
        assertEqual(squeeze("  foo = bar #comments"), "foo = bar")
    }
    T("yn()", 12)
    {
        assertFalse(yn(0))
        assertFalse(yn("0"))
        assertFalse(yn("false"))
        assertFalse(yn("no"))
        assertFalse(yn("off"))
        assertFalse(yn("OFF"))
        assertTrue(yn(1))
        assertTrue(yn("1"))
        assertTrue(yn("true"))
        assertTrue(yn("yes"))
        assertTrue(yn("on"))
        assertTrue(yn("ON"))
    }

    # URLs
    T("quote()", 4)
    {
        assertEqual(quote(""), "")
        assertEqual(quote("foo"), "foo")
        assertEqual(quote("foo bar"), "foo%20bar")
        assertEqual(quote("\"hello, world!\""), "%22hello%2C%20world%21%22")
    }

    # System
    T("fileExists()", 4)
    {
        assertFalse(fileExists("README"))
        assertFalse(fileExists("README .md"))
        assertTrue(fileExists("README.md"))
        assertFalse(fileExists("."))
    }
    T("dirExists()", 4)
    {
        assertFalse(dirExists("README"))
        assertFalse(dirExists("README.md"))
        assertTrue(dirExists("."))
        assertFalse(dirExists(" ."))
    }
    T("detectProgram()", 2)
    {
        assertTrue(detectProgram("ls"))
        assertTrue(detectProgram("gawk", "--version"))
    }

    END_TEST()
}

BEGIN {
    START_TEST("Commons.awk")

    # Arrays
    T("anything()", 3)
    {
        delete something
        assertFalse(anything(nothing))
        something[0] = 0; assertFalse(anything(something)) # edge case
        something[0] = 1; assertTrue(anything(something))
    }
    T("exists()", 4)
    {
        delete something
        assertFalse(exists(something))
        something[0] = 0; assertFalse(exists(something)) # edge case
        something[0] = 1; assertTrue(exists(something))
        assertTrue(exists(something[0]))
    }
    T("belongsTo()", 3)
    {
        delete array; array[0] = "foo"; array[1] = "bar"
        assertTrue(belongsTo("foo", array))
        assertTrue(belongsTo("bar", array))
        assertFalse(belongsTo("world", array))
    }
    T("identical()", 4)
    {
        delete x; x[0] = 42
        delete y; y[0][0] = 42
        assertFalse(identical(x, y))
        assertTrue(identical(x, y[0]))
        assertFalse(identical(x[0], y[0]))
        assertTrue(identical(x[0], y[0][0]))
    }
    T("append()", 1)
    {
        delete array; array[0] = "foo"
        delete expected; expected[0] = "foo"; expected[1] = "bar"
        append(array, "bar")
        assertTrue(identical(array, expected))
    }

    # Strings
    T("startsWithAny()", 3)
    {
        delete substrings; substrings[0] = "A"; substrings[1] = "a"
        assertTrue(startsWithAny("absolute", substrings))
        assertTrue(startsWithAny("ABSOLUTE", substrings))
        assertFalse(startsWithAny("ZOO", substrings))
    }
    T("matchesAny()", 4)
    {
        delete patterns; patterns[0] = "[[:space:]]"; patterns[1] = "[0Oo]"
        assertTrue(matchesAny("  ", patterns))
        assertTrue(matchesAny("obsolete", patterns))
        assertTrue(matchesAny("0.0", patterns))
        assertFalse(matchesAny("1.0", patterns))
    }
    T("replicate()", 4)
    {
        assertEqual(replicate("", 0), "")
        assertEqual(replicate("", 2), "")
        assertEqual(replicate("foo bar", 1), "foo bar")
        assertEqual(replicate("foo bar", 3), "foo barfoo barfoo bar")
    }
    T("join()", 4)
    {
        assertEqual(join("", "-"), "")
        delete array; array[0]
        assertEqual(join(array, "-"), "")
        delete array; array[0] = "foo"; array[1] = "bar"
        assertEqual(join(array, " "), "foo bar")
        assertEqual(join(array, ","), "foo,bar")
    }
    T("escapeChar()", 8)
    {
        assertEqual(escapeChar("b"), "\b")
        assertEqual(escapeChar("f"), "\f")
        assertEqual(escapeChar("n"), "\n")
        assertEqual(escapeChar("r"), "\r")
        assertEqual(escapeChar("t"), "\t")
        assertEqual(escapeChar("v"), "\v")
        assertEqual(escapeChar("_"), "_")
        assertNotEqual(escapeChar("_"), "\\_")
    }
    T("literal()", 4)
    {
        assertEqual(literal(""), "")
        assertEqual(literal("foo"), "foo")
        assertEqual(literal("\"foo\""), "foo")
        assertEqual(literal("\"\\\"foo\\\"\""), "\"foo\"")
    }
    T("escape()", 4)
    {
        assertEqual(escape(""), "")
        assertEqual(escape("foo"), "foo")
        assertEqual(escape("\""), "\\\\\"")
        assertEqual(escape("\"foo\""), "\\\\\"foo\\\\\"")
    }
    T("parameterize()", 10)
    {
        assertEqual(parameterize(""), "''")
        assertEqual(parameterize("foo"), "'foo'")
        assertEqual(parameterize("foo bar"), "'foo bar'")
        assertEqual(parameterize("foo 'bar"), "'foo '\\''bar'")
        assertEqual(parameterize("foo \"bar\""), "'foo \"bar\"'")
        assertEqual(parameterize("", "\""), "\"\"")
        assertEqual(parameterize("foo", "\""), "\"foo\"")
        assertEqual(parameterize("foo bar", "\""), "\"foo bar\"")
        assertEqual(parameterize("foo 'bar", "\""), "\"foo 'bar\"")
        assertEqual(parameterize("foo \"bar\"", "\""), "\"foo \\\\\"bar\\\\\"\"")
    }
    T("squeeze()", 4)
    {
        assertEqual(squeeze(""), "")
        assertEqual(squeeze(" "), "")
        assertEqual(squeeze("	"), "")
        assertEqual(squeeze("  foo = bar #comments"), "foo = bar")
    }
    T("yn()", 12)
    {
        assertFalse(yn(0))
        assertFalse(yn("0"))
        assertFalse(yn("false"))
        assertFalse(yn("no"))
        assertFalse(yn("off"))
        assertFalse(yn("OFF"))
        assertTrue(yn(1))
        assertTrue(yn("1"))
        assertTrue(yn("true"))
        assertTrue(yn("yes"))
        assertTrue(yn("on"))
        assertTrue(yn("ON"))
    }

    # URLs
    T("quote()", 4)
    {
        assertEqual(quote(""), "")
        assertEqual(quote("foo"), "foo")
        assertEqual(quote("foo bar"), "foo%20bar")
        assertEqual(quote("\"hello, world!\""), "%22hello%2C%20world%21%22")
    }

    # System
    T("fileExists()", 4)
    {
        assertFalse(fileExists("README"))
        assertFalse(fileExists("README .md"))
        assertTrue(fileExists("README.md"))
        assertFalse(fileExists("."))
    }
    T("dirExists()", 4)
    {
        assertFalse(dirExists("README"))
        assertFalse(dirExists("README.md"))
        assertTrue(dirExists("."))
        assertFalse(dirExists(" ."))
    }
    T("detectProgram()", 2)
    {
        assertTrue(detectProgram("ls"))
        assertTrue(detectProgram("gawk", "--version"))
    }

    END_TEST()
}


tag_dll.awk: tag exports work with Open C, too

darcs-hash:20070628122057-1b897-b8c8d7e541c699c1bb48f84b6e137bbae6fc7416.gz

#! /usr/bin/env awk
#
# Create reference tags and WIN32 initialization functions for tags.
#
# --------------------------------------------------------------------
#
# This file is part of the Sofia-SIP package
#
# Copyright (C) 2005 Nokia Corporation.
#
# Contact: Pekka Pessi <pekka.pessi@nokia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1 of
# the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
# 02110-1301 USA
#
# --------------------------------------------------------------------
#
# Author: Pekka Pessi <Pekka.Pessi@nokia.com>
#
# Created: Tue Feb 26 14:11:25 2002 ppessi
#

BEGIN {
  DEFS = 0;
  HEADER = 1;
}

HEADER {
  HEADER = 0;

  if (OUTNAME != "") {
    fn = OUTNAME;
  } else {
    fn = FILENAME;
  }

  if (REF == "") {
    REF = fn;
    sub(/[.]c(at)?/, "_ref.c", REF);
  }

  if (NODLL) DLL = "/dev/null";

  if (DLL == "") {
    DLL = fn;
    sub(/[.]c(at)?/, "_dll.c", DLL);
  }

  base = fn;
  sub(/.*\//, "", base);

  printf("#ifdef _WIN32\n"\
	 "/*\n" \
	 " * PLEASE NOTE: \n" \
	 " * \n" \
	 " * This file is automatically generated by tag_dll.awk.\n"\
	 " * It contains magic required by Win32 DLLs to initialize\n"\
	 " * tag_typedef_t variables.\n"\
	 " * \n"\
	 " * Do not, repeat, do not edit this file. Edit '%s' instead.\n"\
	 " * \n"\
	 " */\n\n"\
	 "#define EXPORT __declspec(dllexport)\n\n", base) > DLL;

  if (DLLREF) {
    print \
      "#include \"config.h\"\n\n" \
      "#include <sofia-sip/su_tag_class.h>\n\n" > DLL;
  }

  printf("/*\n" \
	 " * PLEASE NOTE: \n" \
	 " * \n" \
	 " * This file is automatically generated by tag_dll.awk.\n"\
	 " * It contains tag_typedef_t for tag references.\n"\
	 " * \n"\
	 " * Do not, repeat, do not edit this file. Edit '%s' instead.\n"\
	 " * \n"\
	 " */\n\n"\
	 "#include \"config.h\"\n\n"\
	 "#include <sofia-sip/su_tag_class.h>\n"\
	 "\n"\
	 "#if defined _WIN32 || defined HAVE_OPEN_C\n"\
	 "#define EXPORT __declspec(dllexport)\n"\
	 "#else\n"\
	 "#define EXPORT\n"\
	 "#endif\n\n",
	 base) > REF;

  if (LIST) {
    print "#include <stddef.h>" >REF;
  }
  print "" > REF;
}

/^#define TAG_NAMESPACE/ {
  print "#undef TAG_NAMESPACE" > REF;
  print $0 > REF;
  print "" > REF;
  print "#undef TAG_NAMESPACE" > DLL;
  print $0 > DLL;
  print "" > DLL;
}

/SU_HAVE_EXPERIMENTAL/ {
  print $0 > REF;
  print $0 > DLL;
}

!DEFS && /^tag_typedef_t/ { DEFS = 1; }

DEFS && /tag_typedef_t/ {
  tag = $2;
  typedefs[tag] = $0;

  if ($0 !~ /NSTAG_TYPEDEF/) {
    print "extern tag_typedef_t "tag";" > REF;
    print "EXPORT tag_typedef_t "tag"_ref;" > DLL;
    print "EXPORT tag_typedef_t "tag"_ref = \n  REFTAG_TYPEDEF("tag");" > REF;
  }

  gsub(/ = .*$/, ";");
}

!DLLREF { print $0 > DLL; }

END {
  if (LIST) {
    print "\nEXPORT tag_type_t " LIST "[] =\n{" > REF;
    print "\nEXPORT tag_type_t " LIST "[] =\n{" > DLL;
    for (tag in typedefs) {
      if (typedefs[tag] !~ /NSTAG_TYPEDEF/) {
        print "  " tag "," > REF;
        print "  " tag "," > DLL;
      }
    }
    print "  NULL\n};" > REF;
    print "  NULL\n};" > DLL;
  }

  printf("\n" \
	 "#include <windows.h>\n"\
	 "\n"\
	 "BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fwdReason, LPVOID fImpLoad)\n"\
	 "{\n") > DLL;

   for (tag in typedefs) {
     def = typedefs[tag];
     if (!DLLREF) {
       sub(/^tag_typedef_t /, "  tag_typedef_t _", def);
       print def > DLL;
     }
     else {
       print "  extern tag_typedef_t "tag";" > DLL;
     }
     print "  tag_typedef_t _"tag"_ref =\n    REFTAG_TYPEDEF("tag");" > DLL;
   }

   print "" > DLL;

   for (tag in typedefs) {
     if (!DLLREF) {
       print "  *(struct tag_type_s *)" tag " = *_" tag ";" > DLL;
     }
     print "  *(struct tag_type_s *)" tag "_ref = *_" tag "_ref;" > DLL;
   }

   print "\n  return TRUE;" > DLL;
   print "}" > DLL;
   print "\n#endif" > DLL;
}

#! /usr/bin/env awk
#
# Create reference tags and WIN32 initialization functions for tags.
#
# --------------------------------------------------------------------
#
# This file is part of the Sofia-SIP package
#
# Copyright (C) 2005 Nokia Corporation.
#
# Contact: Pekka Pessi <pekka.pessi@nokia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1 of
# the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
# 02110-1301 USA
#
# --------------------------------------------------------------------
#
# Author: Pekka Pessi <Pekka.Pessi@nokia.com>
#
# Created: Tue Feb 26 14:11:25 2002 ppessi
#

BEGIN {
  DEFS = 0;
  HEADER = 1;
}

HEADER {
  HEADER = 0;

  if (OUTNAME != "") {
    fn = OUTNAME;
  } else {
    fn = FILENAME;
  }

  if (REF == "") {
    REF = fn;
    sub(/[.]c(at)?/, "_ref.c", REF);
  }

  if (NODLL) DLL = "/dev/null";

  if (DLL == "") {
    DLL = fn;
    sub(/[.]c(at)?/, "_dll.c", DLL);
  }

  base = fn;
  sub(/.*\//, "", base);

  printf("#ifdef _WIN32\n"\
	 "/*\n" \
	 " * PLEASE NOTE: \n" \
	 " * \n" \
	 " * This file is automatically generated by tag_dll.awk.\n"\
	 " * It contains magic required by Win32 DLLs to initialize\n"\
	 " * tag_typedef_t variables.\n"\
	 " * \n"\
	 " * Do not, repeat, do not edit this file. Edit '%s' instead.\n"\
	 " * \n"\
	 " */\n\n"\
	 "#define EXPORT __declspec(dllexport)\n\n", base) > DLL;

  if (DLLREF) {
    print \
      "#include \"config.h\"\n\n" \
      "#include <sofia-sip/su_tag_class.h>\n\n" > DLL;
  }

  printf("/*\n" \
	 " * PLEASE NOTE: \n" \
	 " * \n" \
	 " * This file is automatically generated by tag_dll.awk.\n"\
	 " * It contains tag_typedef_t for tag references.\n"\
	 " * \n"\
	 " * Do not, repeat, do not edit this file. Edit '%s' instead.\n"\
	 " * \n"\
	 " */\n\n"\
	 "#include \"config.h\"\n\n"\
	 "#include <sofia-sip/su_tag_class.h>\n"\
	 "\n"\
	 "#ifdef _WIN32\n"\
	 "#define EXPORT __declspec(dllexport)\n"\
	 "#else\n"\
	 "#define EXPORT\n"\
	 "#endif\n\n",
	 base) > REF;

  if (LIST) {
    print "#include <stddef.h>" >REF;
  }
  print "" > REF;
}

/^#define TAG_NAMESPACE/ {
  print "#undef TAG_NAMESPACE" > REF;
  print $0 > REF;
  print "" > REF;
  print "#undef TAG_NAMESPACE" > DLL;
  print $0 > DLL;
  print "" > DLL;
}

/SU_HAVE_EXPERIMENTAL/ {
  print $0 > REF;
  print $0 > DLL;
}

!DEFS && /^tag_typedef_t/ { DEFS = 1; }

DEFS && /tag_typedef_t/ {
  tag = $2;
  typedefs[tag] = $0;

  if ($0 !~ /NSTAG_TYPEDEF/) {
    print "extern tag_typedef_t "tag";" > REF;
    print "EXPORT tag_typedef_t "tag"_ref;" > DLL;
    print "EXPORT tag_typedef_t "tag"_ref = \n  REFTAG_TYPEDEF("tag");" > REF;
  }

  gsub(/ = .*$/, ";");
}

!DLLREF { print $0 > DLL; }

END {
  if (LIST) {
    print "\nEXPORT tag_type_t " LIST "[] =\n{" > REF;
    print "\nEXPORT tag_type_t " LIST "[] =\n{" > DLL;
    for (tag in typedefs) {
      if (typedefs[tag] !~ /NSTAG_TYPEDEF/) {
        print "  " tag "," > REF;
        print "  " tag "," > DLL;
      }
    }
    print "  NULL\n};" > REF;
    print "  NULL\n};" > DLL;
  }

  printf("\n" \
	 "#include <windows.h>\n"\
	 "\n"\
	 "BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fwdReason, LPVOID fImpLoad)\n"\
	 "{\n") > DLL;

   for (tag in typedefs) {
     def = typedefs[tag];
     if (!DLLREF) {
       sub(/^tag_typedef_t /, "  tag_typedef_t _", def);
       print def > DLL;
     }
     else {
       print "  extern tag_typedef_t "tag";" > DLL;
     }
     print "  tag_typedef_t _"tag"_ref =\n    REFTAG_TYPEDEF("tag");" > DLL;
   }

   print "" > DLL;

   for (tag in typedefs) {
     if (!DLLREF) {
       print "  *(struct tag_type_s *)" tag " = *_" tag ";" > DLL;
     }
     print "  *(struct tag_type_s *)" tag "_ref = *_" tag "_ref;" > DLL;
   }

   print "\n  return TRUE;" > DLL;
   print "}" > DLL;
   print "\n#endif" > DLL;
}


Special case.

Special case.

function printUrlQueryString(queryString, j)
{
    split(queryString, pairs, "&", seps)
    len = length(pairs)
    for (j = 1; j < len; ++j) {
        split(pairs[j], keyValue, "=", seps2)
        k = keyValue[1]
        v = keyValue[2]
        printf "\t\t\"%s\": \"%s\",\n", k, urlDecode(urlDecode(v))
    }
}

function base64Decode(thing)
{
  "echo "$thing" | base64 --decode" | getline x
  return x
}

function urlDecode(url, i) {
    for (i = 0x20; i < 0x40; ++i) {
        repl = sprintf("%c", i);
        if ((repl == "&") || (repl == "\\")) {
            repl = "\\" repl;
        }
        url = gensub(sprintf("%%%02X", i), repl, "g", url);
        url = gensub(sprintf("%%%02x", i), repl, "g", url);
    }
    return url;
}

BEGIN {
    logLineSeen = 0
    print "{";
}

END {
    print "}"
}

/^#Version: / {
    printf "\t\"version\": \"%s\",\n", substr($0, length("#Version: ") + 1)
}

/^#Fields: / {
    rest = substr($0, length("#Fields: ") + 1)
    split(rest, fields, " ")
    numFields = length(fields)
}

/^#/ {
    # ignore anything else starting with "#"
}

! /^#/ {
    if (logLineSeen == 1) {
        printf ",\n"
    }
    split($0, fieldsThisLine, " ")
    urlIndex = 0
    if (NF == numFields) {
        print "\t{"
        for(i = 1; i < NF; i++)
        {
            fieldName = fields[i]
            if (fieldName == "cs-uri-query") {
                urlIndex = i;
                continue;
            }
            fieldValue = fieldsThisLine[i]
            maybeDecoded = (fieldName == "cs-uri-query" ? urlDecode(fieldValue) : fieldValue)
            printf "\t\t\"%s\": \"%s\"", fieldName, fieldValue
            if (i + 1 < NF && urlIndex > 0) {
                printf ","
            }
            printf "\n"
        }
        if (urlIndex > 0) {
            printf "\t\t\"cs-url-query\": {\n"
            printUrlQueryString(fieldsThisLine[urlIndex])
            printf "}\n"
        }
        printf "\t}"
    }
    logLineSeen = 1;
}

function printUrlQueryString(queryString, j)
{
    split(queryString, pairs, "&", seps)
    len = length(pairs)
    for (j = 1; j < len; ++j) {
        split(pairs[j], keyValue, "=", seps2)
        k = keyValue[1]
        v = keyValue[2]
        printf "\t\t\"%s\": \"%s\",\n", k, urlDecode(urlDecode(v))
    }
}

function base64Decode(thing)
{
  "echo "$thing" | base64 --decode" | getline x
  return x
}

function urlDecode(url, i) {
    for (i = 0x20; i < 0x40; ++i) {
        repl = sprintf("%c", i);
        if ((repl == "&") || (repl == "\\")) {
            repl = "\\" repl;
        }
        url = gensub(sprintf("%%%02X", i), repl, "g", url);
        url = gensub(sprintf("%%%02x", i), repl, "g", url);
    }
    return url;
}

BEGIN {
    logLineSeen = 0
    print "{";
}

END {
    print "}"
}

/^#Version: / {
    printf "\t\"version\": \"%s\",\n", substr($0, length("#Version: ") + 1)
}

/^#Fields: / {
    rest = substr($0, length("#Fields: ") + 1)
    split(rest, fields, " ")
    numFields = length(fields)
}

/^#/ {
    # ignore anything else starting with "#"
}

! /^#/ {
    if (logLineSeen == 1) {
        printf ",\n"
    }
    split($0, fieldsThisLine, " ")
    if (NF == numFields) {
        print "\t{"
        for(i = 1; i < NF; i++)
        {
            printf "\t\t\"%s\": \"%s\"", fields[i], fieldsThisLine[i]
            if (i + 1 < NF) {
                printf ","
            }
            printf "\n"
        }
        printf "\t}"
    }
    logLineSeen = 1;
}


Fixed a test script.

Fixed a test script.

{
  if (testdir == "")
    testdir = $0;
  else if (last_cr == "")
    last_cr = $0;
  else
  {
    if (inner_line)
      printf "\n";
    else
      inner_line = 1;

    gsub("\\${testdir}",testdir);
    printf $0;
  }
}
END {
  if (last_cr != "no")
    printf "\n";
}

{
  if (testdir == "")
    testdir = $0;
  else if (last_cr == "")
    last_cr = $0;
  else
  {
    if (inner_line)
      printf "\n";
    else
      inner_line = 1;

    gsub("\$\{testdir\}",testdir);
    printf $0;
  }
}
END {
  if (last_cr != "no")
    printf "\n";
}


tools: update log-analyze to search for GWRequestHandler

Signed-off-by: Jan Viktorin <3eab6ac54c005c4408f608556a6663b7f2abede7@fit.vutbr.cz>

#! /usr/bin/gawk -f

function log_trim_left(line)
{
	sub(/^.+\[(Trace|Debug|Information|Notice|Warning|Error|Critical|Fatal)\] /, "", line)
	return line
}

function log_trim_right(line)
{
	sub(/\([^)]*:[0-9]*\)$/, "", line)
	return line
}

function log_extract_pid_tid(line)
{
	sub(/:\{/, "-", line)
	sub(/,.+$/, "", line)

	return line
}

function extract_message(line)
{
	line = log_trim_left(line)
	return log_trim_right(line)
}

function time2ms(time, part, result)
{
	split(time, part, "[:.]")

	result = part[1] * 60 * 60 * 1000
	result += part[2] * 60 * 1000
	result += part[3] * 1000
	result += part[4]

	return result
}

function time_stat(start, end)
{
	if (start == 0)
		return "??:??:??.???.." end " ?ms"

	return start ".." end " " (time2ms(end) - time2ms(start)) "ms"
}

function report_counts(title, counts)
{
	if (counts[0] > 0) {
		print title ":", counts[0]

		for (key in counts) {
			if (key == 0)
				continue

			print "*", key ":", counts[key]
		}
	}
}

function clear_array(array, first_zero) {
	for (key in array)
		delete array[key]

	if (first_zero)
		array[0] = 0
}

function count_array(array, key, count) {
	count = 0
	for (key in array)
		count += 1

	return count
}

function report_server_stats()
{
	report_counts("Criticals", criticals)
	report_counts("Fatals", fatals)

	report_counts("XML-UI clients", xmlui_clients)

	if (xmlui_requests[0] > 0) {
		print "Requests"

		for (key in xmlui_requests) {
			if (key == 0)
				continue

			split(key, multikey, SUBSEP)
			if (multikey[2] != "ip")
				continue

			ip = xmlui_requests[multikey[1], "ip"]
			start = xmlui_requests[multikey[1], "start"]
			end = xmlui_requests[multikey[1], "end"]
			info = xmlui_requests[multikey[1], "info"]

			print "*", ip ":", time_stat(start, end), info
		}
	}

	report_counts("REST-UI clients", restui_clients)

	if (count_array(restui_requests_total) > 0) {
		print "REST-UI Requests"
		for (key in restui_requests_total) {
			print "*", key ":", restui_requests_total[key]
		}
	}

	if (count_array(restui_requests_uri) > 0) {
		print "REST-UI URIs"
		for (key in restui_requests_uri) {
			print "*", key ":", restui_requests_uri[key]
		}
	}

	report_counts("GWS clients", gws_clients)

	if (durations[0] > 0) {
		print "Durations"

		for (key in durations) {
			if (key == 0)
				continue

			split(key, multikey, SUBSEP)
			if (multikey[2] != "duration")
				continue

			clazz = durations[multikey[1], "class"]
			duration = durations[multikey[1], "duration"]
			time = durations[multikey[1], "time"]
			date = durations[multikey[1], "date"]
			thread = durations[multikey[1], "thread"]

			if (date != 0)
				datetime = date " " time
			else
				datetime = time

			duration_per_clazz[clazz] += duration
			count_per_clazz[clazz] += 1

			if (min_per_clazz[clazz] == 0 || duration < min_per_clazz[clazz]) {
				min_per_clazz[clazz] = duration
				min_per_clazz[clazz, "info"] = thread " " datetime
			}

			if (max_per_clazz[clazz] == 0 || duration > max_per_clazz[clazz]) {
				max_per_clazz[clazz] = duration
				max_per_clazz[clazz, "info"] = thread " " datetime
			}
		}

		for (clazz in count_per_clazz) {
			_avg = duration_per_clazz[clazz] / count_per_clazz[clazz]
			_avg = "avg " _avg "us"
			_min = "min " min_per_clazz[clazz] "us"
			_min_info = "(" min_per_clazz[clazz, "info"] ")"
			_max = "max " max_per_clazz[clazz] "us"
			_max_info = "(" max_per_clazz[clazz, "info"] ")"
			_total = "total " count_per_clazz[clazz]

			print "* " clazz ": " _avg " " _total
			print "  " _min " " _min_info
			print "  " _max " " _max_info

			delete count_per_clazz[clazz]
			delete duration_per_clazz[clazz]
			delete min_per_clazz[clazz]
			delete min_per_clazz[clazz, "info"]
			delete max_per_clazz[clazz]
			delete max_per_clazz[clazz, "info"]
		}
	}

	if (transactions[0] > 0) {
		print "Transactions"

		sort = "sort -b -n -k4,4 -r"

		for (key in transactions) {
			if (key == 0)
				continue

			split(key, multikey, SUBSEP)
			if (multikey[2] != "id")
				continue

			id = transactions[multikey[1], "id"]
			type = transactions[multikey[1], "type"]
			start = transactions[multikey[1], "start"]
			end = transactions[multikey[1], "end"]

			print "*", id ":", time_stat(start, end), type | sort
		}

		close(sort)
	}
}

function clear_stats()
{
	clear_array(fatals, 1)
	clear_array(criticals, 1)
	clear_array(xmlui_clients, 1)
	clear_array(xmlui_requests, 1)
	clear_array(restui_clients, 1)
	clear_array(restui_requests_total, 0)
	clear_array(restui_requests_uri, 0)
	clear_array(gws_clients, 1)
	clear_array(transactions, 1)
	clear_array(thread_pools, 1)
	clear_array(durations, 1)

	types_count = 0
	enums_count = 0
	devs_count = 0
	queries_count = 0
	instances_count = 0
	rest_mappings = 0
}

BEGIN {
	clear_stats()
	HAS_DATE = 0
}

{
	if (NR % 13759 == 0) {
		printf "\r%u", NR > "/dev/stderr"
	}
}

/ (19[0-9]{2}|20[0-9]{2})-[012]?[0-9]-[0-3][0-9] / {
	HAS_DATE = 1
}

!/ (19[0-9]{2}|20[0-9]{2})-[012]?[0-9]-[0-3][0-9] / {
	HAS_DATE = 0
}

/PermitAuthProvider/ && /SOME AUTHS/ {
	print
}

/PermitAuthProvider/ {
	next
}

/\[Fatal\]/ {
	_line = $0
	_msg = extract_message($0)

	if (fatals[_msg] == 0)
		fatals[0] += 1

	fatals[_msg] += 1
	if (fatals[_msg] == 1)
		print line
}

/\[Critical\]/ {
	_line = $0
	_msg = extract_message($0)

	if (criticals[_msg] == 0)
		criticals[0] += 1

	criticals[_msg] += 1
	if (criticals[_msg] == 1)
		print line
}

/^Application / && / version / {
	report_server_stats()
	clear_stats()

	if (HAS_DATE)
		_time = $2 " " $3
	else
		_time = $2

	print "# Server run report " _time
	print extract_message($0)
}
/^Application / && / Poco library / {
	print extract_message($0)
}

/IcuLocaleManager/ && /using ICU / {
	print extract_message($0)
}

/IcuLocaleManager/ && /resource dir: / {
	print extract_message($0)
}

/IcuLocaleManager/ && /detected locale: / {
	$0 = log_trim_left($0)
	print "locale: ", $3
}

/PocoDaoManager/ && /initialize database/ {
	print extract_message($0)
}

/PocoDaoManager/ && /Session / && /transact/ {
	print extract_message($0)
}

/PocoSqitchInitializer/ && /unsafe deploy / {
	print extract_message($0)
}

/PocoSimpleDBInitializer/ && /executed script / {
	print extract_message($0)
}

/TypeInfoProvider/ && /register type / {
	types_count += 1
}

/SubtypeInfoProvider/ && /register enum / {
	enums_count += 1
}

/DevicesSAXHandler/ && /Warning/ {
	print extract_message($0)
}

/DeviceInfoProvider/ && /register device / {
	devs_count += 1
}

/ loading query / {
	queries_count += 1
}

/DependencyInjector/ && /successfully created / {
	instances_count += 1
}

/RestRouter/ && /mapping / {
	rest_mappings += 1
}

/^Application/ && /starting runner/ {
	print extract_message($0)
}

/ creating thread pool / {
	$0 = log_trim_left($0)
	_curr = thread_pools[0] + 1
	thread_pools[0] = _curr
	thread_pools[_curr, "min"] += $5
	thread_pools[_curr, "max"] += $7
}

/LoopRunner/ && / started / && / loops / {
	print "Value types:   ", types_count
	print "Enum subtypes: ", enums_count
	print "Device types:  ", devs_count
	print "DB queries:    ", queries_count
	print "DI instances:  ", instances_count
	print "REST mappings: ", rest_mappings

	$0 = log_trim_left($0)
	print "Loops:         ", $2

	threads_min = 0
	threads_max = 0

	for (i = 0; i < thread_pools[0]; ++i) {
		threads_min += thread_pools[i + 1, "min"]
		threads_max += thread_pools[i + 1, "max"]
	}

	print "Thread pools:  ", thread_pools[0]
	print "Threads:       ", threads_min ".." threads_max
}

/XmlRequestHandlerFactory/ && / accepting client / {
	_id = log_extract_pid_tid($(3 + HAS_DATE))
	$0 = log_trim_left($0)

	split($3, _addr, ":")
	_ip = _addr[1]

	if (xmlui_clients[_ip] == 0)
		xmlui_clients[0] += 1

	xmlui_clients[_ip] += 1
	xmlui_requests_tmp[_id, "ip"] = _ip
}

/XmlRequestHandlerFactory/ && /document: <request/ {
	_id = log_extract_pid_tid($(3 + HAS_DATE))
	xmlui_requests_tmp[_id, "start"] = $(2 + HAS_DATE)

	_msg = extract_message($0)

	if (match(_msg, / ns="[^"]+" /) > 0) {
		_ns = substr(_msg, RSTART, RLENGTH)
		sub(/^.*="/, "", _ns)
		sub(/".*$/, "", _ns)
	}
	if (match(_msg, / type="[^"]+" /) > 0) {
		_type = substr(_msg, RSTART, RLENGTH)
		sub(/^.*="/, "", _type)
		sub(/".*$/, "", _type)
	}

	xmlui_requests_tmp[_id, "info"] = _ns ":" _type
}

/XmlRequestHandler/ && /<response/ {
	_id = log_extract_pid_tid($(3 + HAS_DATE))
	_curr = xmlui_requests[0] + 1
	xmlui_requests[0] = _curr

	xmlui_requests[_curr, "ip"] = xmlui_requests_tmp[_id, "ip"]
	delete xmlui_requests_tmp[_id, "ip"]
	xmlui_requests[_curr, "start"] = xmlui_requests_tmp[_id, "start"]
	delete xmlui_requests_tmp[_id, "start"]
	xmlui_requests[_curr, "info"] = xmlui_requests_tmp[_id, "info"]
	delete xmlui_requests_tmp[_id, "info"]

	xmlui_requests[_curr, "end"] = $(2 + HAS_DATE)
}

/PocoRestRequestFactory/ && /handling request/ {
	$0 = extract_message($0)
	restui_requests_total[$3] += 1
	restui_requests_uri[$4] += 1

	split($6, _addr, ":")
	_ip = _addr[1]

	if (restui_clients[_ip] == 0)
		restui_clients[0] += 1

	restui_clients[_ip] += 1
}

/GWRequestHandler/ && /connection from / {
	$0 = log_trim_left($0)

	split($3, _addr, ":")
	_ip = _addr[1]

	if (gws_clients[_ip] == 0)
		gws_clients[0] += 1

	gws_clients[_ip] += 1
}

/ transaction / && / started / {
	_time = $(2 + HAS_DATE)
	_thread = log_extract_pid_tid($(3 + HAS_DATE))
	$0 = extract_message($0)
	_id = $2 "-" _thread
	transactions_tmp[_id, "start"] = _time
}

/ transaction / && (/ commit / || / rollback /) {
	_time = $(2 + HAS_DATE)
	_thread = log_extract_pid_tid($(3 + HAS_DATE))
	$0 = extract_message($0)
	_id = $2 "-" _thread
	_type = $3

	_curr = transactions[0] + 1
	transactions[0] = _curr

	transactions[_curr, "start"] = transactions_tmp[_id, "start"]
	delete transactions_tmp[_id, "start"]

	transactions[_curr, "end"] = _time
	transactions[_curr, "type"] = _type
	transactions[_curr, "id"] = _id
}

/ duration: / {
	_clazz = $1
	_thread = log_extract_pid_tid($(3 + HAS_DATE))

	if ($5 == "duration:")
		_duration = $6
	else if ($6 == "duration:")
		_duration = $7

	sub(/us$/, "", _duration)

	_curr = durations[0] + 1
	durations[0] = _curr

	durations[_curr, "class"] = _clazz
	durations[_curr, "thread"] = _thread

	if (HAS_DATE) {
		durations[_curr, "date"] = $2
		durations[_curr, "time"] = $3
	}
	else {
		durations[_curr, "time"] = $2
	}

	durations[_curr, "duration"] = int(_duration)
}

END {
	report_server_stats()
	clear_stats()
}

#! /usr/bin/gawk -f

function log_trim_left(line)
{
	sub(/^.+\[(Trace|Debug|Information|Notice|Warning|Error|Critical|Fatal)\] /, "", line)
	return line
}

function log_trim_right(line)
{
	sub(/\([^)]*:[0-9]*\)$/, "", line)
	return line
}

function log_extract_pid_tid(line)
{
	sub(/:\{/, "-", line)
	sub(/,.+$/, "", line)

	return line
}

function extract_message(line)
{
	line = log_trim_left(line)
	return log_trim_right(line)
}

function time2ms(time, part, result)
{
	split(time, part, "[:.]")

	result = part[1] * 60 * 60 * 1000
	result += part[2] * 60 * 1000
	result += part[3] * 1000
	result += part[4]

	return result
}

function time_stat(start, end)
{
	if (start == 0)
		return "??:??:??.???.." end " ?ms"

	return start ".." end " " (time2ms(end) - time2ms(start)) "ms"
}

function report_counts(title, counts)
{
	if (counts[0] > 0) {
		print title ":", counts[0]

		for (key in counts) {
			if (key == 0)
				continue

			print "*", key ":", counts[key]
		}
	}
}

function clear_array(array, first_zero) {
	for (key in array)
		delete array[key]

	if (first_zero)
		array[0] = 0
}

function count_array(array, key, count) {
	count = 0
	for (key in array)
		count += 1

	return count
}

function report_server_stats()
{
	report_counts("Criticals", criticals)
	report_counts("Fatals", fatals)

	report_counts("XML-UI clients", xmlui_clients)

	if (xmlui_requests[0] > 0) {
		print "Requests"

		for (key in xmlui_requests) {
			if (key == 0)
				continue

			split(key, multikey, SUBSEP)
			if (multikey[2] != "ip")
				continue

			ip = xmlui_requests[multikey[1], "ip"]
			start = xmlui_requests[multikey[1], "start"]
			end = xmlui_requests[multikey[1], "end"]
			info = xmlui_requests[multikey[1], "info"]

			print "*", ip ":", time_stat(start, end), info
		}
	}

	report_counts("REST-UI clients", restui_clients)

	if (count_array(restui_requests_total) > 0) {
		print "REST-UI Requests"
		for (key in restui_requests_total) {
			print "*", key ":", restui_requests_total[key]
		}
	}

	if (count_array(restui_requests_uri) > 0) {
		print "REST-UI URIs"
		for (key in restui_requests_uri) {
			print "*", key ":", restui_requests_uri[key]
		}
	}

	report_counts("GWS clients", gws_clients)

	if (durations[0] > 0) {
		print "Durations"

		for (key in durations) {
			if (key == 0)
				continue

			split(key, multikey, SUBSEP)
			if (multikey[2] != "duration")
				continue

			clazz = durations[multikey[1], "class"]
			duration = durations[multikey[1], "duration"]
			time = durations[multikey[1], "time"]
			date = durations[multikey[1], "date"]
			thread = durations[multikey[1], "thread"]

			if (date != 0)
				datetime = date " " time
			else
				datetime = time

			duration_per_clazz[clazz] += duration
			count_per_clazz[clazz] += 1

			if (min_per_clazz[clazz] == 0 || duration < min_per_clazz[clazz]) {
				min_per_clazz[clazz] = duration
				min_per_clazz[clazz, "info"] = thread " " datetime
			}

			if (max_per_clazz[clazz] == 0 || duration > max_per_clazz[clazz]) {
				max_per_clazz[clazz] = duration
				max_per_clazz[clazz, "info"] = thread " " datetime
			}
		}

		for (clazz in count_per_clazz) {
			_avg = duration_per_clazz[clazz] / count_per_clazz[clazz]
			_avg = "avg " _avg "us"
			_min = "min " min_per_clazz[clazz] "us"
			_min_info = "(" min_per_clazz[clazz, "info"] ")"
			_max = "max " max_per_clazz[clazz] "us"
			_max_info = "(" max_per_clazz[clazz, "info"] ")"
			_total = "total " count_per_clazz[clazz]

			print "* " clazz ": " _avg " " _total
			print "  " _min " " _min_info
			print "  " _max " " _max_info

			delete count_per_clazz[clazz]
			delete duration_per_clazz[clazz]
			delete min_per_clazz[clazz]
			delete min_per_clazz[clazz, "info"]
			delete max_per_clazz[clazz]
			delete max_per_clazz[clazz, "info"]
		}
	}

	if (transactions[0] > 0) {
		print "Transactions"

		sort = "sort -b -n -k4,4 -r"

		for (key in transactions) {
			if (key == 0)
				continue

			split(key, multikey, SUBSEP)
			if (multikey[2] != "id")
				continue

			id = transactions[multikey[1], "id"]
			type = transactions[multikey[1], "type"]
			start = transactions[multikey[1], "start"]
			end = transactions[multikey[1], "end"]

			print "*", id ":", time_stat(start, end), type | sort
		}

		close(sort)
	}
}

function clear_stats()
{
	clear_array(fatals, 1)
	clear_array(criticals, 1)
	clear_array(xmlui_clients, 1)
	clear_array(xmlui_requests, 1)
	clear_array(restui_clients, 1)
	clear_array(restui_requests_total, 0)
	clear_array(restui_requests_uri, 0)
	clear_array(gws_clients, 1)
	clear_array(transactions, 1)
	clear_array(thread_pools, 1)
	clear_array(durations, 1)

	types_count = 0
	enums_count = 0
	devs_count = 0
	queries_count = 0
	instances_count = 0
	rest_mappings = 0
}

BEGIN {
	clear_stats()
	HAS_DATE = 0
}

{
	if (NR % 13759 == 0) {
		printf "\r%u", NR > "/dev/stderr"
	}
}

/ (19[0-9]{2}|20[0-9]{2})-[012]?[0-9]-[0-3][0-9] / {
	HAS_DATE = 1
}

!/ (19[0-9]{2}|20[0-9]{2})-[012]?[0-9]-[0-3][0-9] / {
	HAS_DATE = 0
}

/PermitAuthProvider/ && /SOME AUTHS/ {
	print
}

/PermitAuthProvider/ {
	next
}

/\[Fatal\]/ {
	_line = $0
	_msg = extract_message($0)

	if (fatals[_msg] == 0)
		fatals[0] += 1

	fatals[_msg] += 1
	if (fatals[_msg] == 1)
		print line
}

/\[Critical\]/ {
	_line = $0
	_msg = extract_message($0)

	if (criticals[_msg] == 0)
		criticals[0] += 1

	criticals[_msg] += 1
	if (criticals[_msg] == 1)
		print line
}

/^Application / && / version / {
	report_server_stats()
	clear_stats()

	if (HAS_DATE)
		_time = $2 " " $3
	else
		_time = $2

	print "# Server run report " _time
	print extract_message($0)
}
/^Application / && / Poco library / {
	print extract_message($0)
}

/IcuLocaleManager/ && /using ICU / {
	print extract_message($0)
}

/IcuLocaleManager/ && /resource dir: / {
	print extract_message($0)
}

/IcuLocaleManager/ && /detected locale: / {
	$0 = log_trim_left($0)
	print "locale: ", $3
}

/PocoDaoManager/ && /initialize database/ {
	print extract_message($0)
}

/PocoDaoManager/ && /Session / && /transact/ {
	print extract_message($0)
}

/PocoSqitchInitializer/ && /unsafe deploy / {
	print extract_message($0)
}

/PocoSimpleDBInitializer/ && /executed script / {
	print extract_message($0)
}

/TypeInfoProvider/ && /register type / {
	types_count += 1
}

/SubtypeInfoProvider/ && /register enum / {
	enums_count += 1
}

/DevicesSAXHandler/ && /Warning/ {
	print extract_message($0)
}

/DeviceInfoProvider/ && /register device / {
	devs_count += 1
}

/ loading query / {
	queries_count += 1
}

/DependencyInjector/ && /successfully created / {
	instances_count += 1
}

/RestRouter/ && /mapping / {
	rest_mappings += 1
}

/^Application/ && /starting runner/ {
	print extract_message($0)
}

/ creating thread pool / {
	$0 = log_trim_left($0)
	_curr = thread_pools[0] + 1
	thread_pools[0] = _curr
	thread_pools[_curr, "min"] += $5
	thread_pools[_curr, "max"] += $7
}

/LoopRunner/ && / started / && / loops / {
	print "Value types:   ", types_count
	print "Enum subtypes: ", enums_count
	print "Device types:  ", devs_count
	print "DB queries:    ", queries_count
	print "DI instances:  ", instances_count
	print "REST mappings: ", rest_mappings

	$0 = log_trim_left($0)
	print "Loops:         ", $2

	threads_min = 0
	threads_max = 0

	for (i = 0; i < thread_pools[0]; ++i) {
		threads_min += thread_pools[i + 1, "min"]
		threads_max += thread_pools[i + 1, "max"]
	}

	print "Thread pools:  ", thread_pools[0]
	print "Threads:       ", threads_min ".." threads_max
}

/XmlRequestHandlerFactory/ && / accepting client / {
	_id = log_extract_pid_tid($(3 + HAS_DATE))
	$0 = log_trim_left($0)

	split($3, _addr, ":")
	_ip = _addr[1]

	if (xmlui_clients[_ip] == 0)
		xmlui_clients[0] += 1

	xmlui_clients[_ip] += 1
	xmlui_requests_tmp[_id, "ip"] = _ip
}

/XmlRequestHandlerFactory/ && /document: <request/ {
	_id = log_extract_pid_tid($(3 + HAS_DATE))
	xmlui_requests_tmp[_id, "start"] = $(2 + HAS_DATE)

	_msg = extract_message($0)

	if (match(_msg, / ns="[^"]+" /) > 0) {
		_ns = substr(_msg, RSTART, RLENGTH)
		sub(/^.*="/, "", _ns)
		sub(/".*$/, "", _ns)
	}
	if (match(_msg, / type="[^"]+" /) > 0) {
		_type = substr(_msg, RSTART, RLENGTH)
		sub(/^.*="/, "", _type)
		sub(/".*$/, "", _type)
	}

	xmlui_requests_tmp[_id, "info"] = _ns ":" _type
}

/XmlRequestHandler/ && /<response/ {
	_id = log_extract_pid_tid($(3 + HAS_DATE))
	_curr = xmlui_requests[0] + 1
	xmlui_requests[0] = _curr

	xmlui_requests[_curr, "ip"] = xmlui_requests_tmp[_id, "ip"]
	delete xmlui_requests_tmp[_id, "ip"]
	xmlui_requests[_curr, "start"] = xmlui_requests_tmp[_id, "start"]
	delete xmlui_requests_tmp[_id, "start"]
	xmlui_requests[_curr, "info"] = xmlui_requests_tmp[_id, "info"]
	delete xmlui_requests_tmp[_id, "info"]

	xmlui_requests[_curr, "end"] = $(2 + HAS_DATE)
}

/PocoRestRequestFactory/ && /handling request/ {
	$0 = extract_message($0)
	restui_requests_total[$3] += 1
	restui_requests_uri[$4] += 1

	split($6, _addr, ":")
	_ip = _addr[1]

	if (restui_clients[_ip] == 0)
		restui_clients[0] += 1

	restui_clients[_ip] += 1
}

/WebSocketRequestHandler/ && /connection from / {
	$0 = log_trim_left($0)

	split($3, _addr, ":")
	_ip = _addr[1]

	if (gws_clients[_ip] == 0)
		gws_clients[0] += 1

	gws_clients[_ip] += 1
}

/ transaction / && / started / {
	_time = $(2 + HAS_DATE)
	_thread = log_extract_pid_tid($(3 + HAS_DATE))
	$0 = extract_message($0)
	_id = $2 "-" _thread
	transactions_tmp[_id, "start"] = _time
}

/ transaction / && (/ commit / || / rollback /) {
	_time = $(2 + HAS_DATE)
	_thread = log_extract_pid_tid($(3 + HAS_DATE))
	$0 = extract_message($0)
	_id = $2 "-" _thread
	_type = $3

	_curr = transactions[0] + 1
	transactions[0] = _curr

	transactions[_curr, "start"] = transactions_tmp[_id, "start"]
	delete transactions_tmp[_id, "start"]

	transactions[_curr, "end"] = _time
	transactions[_curr, "type"] = _type
	transactions[_curr, "id"] = _id
}

/ duration: / {
	_clazz = $1
	_thread = log_extract_pid_tid($(3 + HAS_DATE))

	if ($5 == "duration:")
		_duration = $6
	else if ($6 == "duration:")
		_duration = $7

	sub(/us$/, "", _duration)

	_curr = durations[0] + 1
	durations[0] = _curr

	durations[_curr, "class"] = _clazz
	durations[_curr, "thread"] = _thread

	if (HAS_DATE) {
		durations[_curr, "date"] = $2
		durations[_curr, "time"] = $3
	}
	else {
		durations[_curr, "time"] = $2
	}

	durations[_curr, "duration"] = int(_duration)
}

END {
	report_server_stats()
	clear_stats()
}