Adding license to template.awk

Adding license to template.awk

#! /usr/bin/env awk -f
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Copyright © 2011, Damian Carrillo
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
# * Neither the name of the <organization> nor the names of its contributors
#   may be used to endorse or promote products derived from this software
#   without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# template.awk
#
# 2011-10-08
#
#
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

BEGIN {
    split(ARGV[1], components, "\/")

    scriptName = components[length(components)]

    if (!index(scriptName, ".template")) {
        exitWithErrorMessage("Invalid template file - expected filename with .template suffix")
    }

    # strip .template suffix
    sub(/\.template/, "", scriptName)

    tempScriptPath = "/tmp/" scriptName
    scriptPath = "./" scriptName

    includeDir = ""

    for (i = 1; i < length(components); i++) {
        includeDir = includeDir components[i] "/"
    }
}
{
    if ($0 ~ /\#include-fragment/) {
        if (NF != 3) {
            errorMessage = "Unable to parse @include statement on line " \
                NR ".  Expected \"#include-fragment file fragment\", got \"" $0 "\"."

            exitWithErrorMessage(errorMessage)
        }

        line = $0

        location = index(line, "#")
        whitespace = ""

        for (i = 0; i < location - 1; i++) {
            whitespace = whitespace " "
        }

        fragmentFile = includeDir $2
        fragmentLabel = $3

        includeFragment(whitespace, fragmentFile, fragmentLabel)
    }
    else {
        print > tempScriptPath
    }
}
END {
    while ((getline line < tempScriptPath) > 0) {
        print line > scriptPath
    }
}

function includeFragment(linePrefix, fragmentFile, fragmentLabel) {
    while ((getline line < fragmentFile) > 0) {
        if (line ~ "#start-fragment " fragmentLabel) {
            while ((getline line < fragmentFile) > 0) {
                if (line ~ "#end-fragment " fragmentLabel) {
                    return
                }
                print line > tempScriptPath
            }
        }
    }

    exitWithErrorMessage("Unable to find fragment with label \"" fragmentLabel "\" in " fragmentFile)
}

function exitWithErrorMessage(errorMessage) {
    system("echo 'Error: " errorMessage "' 1>&2")
    exit 1
}

#! /usr/bin/env awk -f

BEGIN {
    split(ARGV[1], components, "\/")

    scriptName = components[length(components)]

    if (!index(scriptName, ".template")) {
        exitWithErrorMessage("Invalid template file - expected filename with .template suffix")
    }

    # strip .template suffix
    sub(/\.template/, "", scriptName)

    tempScriptPath = "/tmp/" scriptName
    scriptPath = "./" scriptName

    includeDir = ""

    for (i = 1; i < length(components); i++) {
        includeDir = includeDir components[i] "/"
    }
}
{
    if ($0 ~ /\#include-fragment/) {
        if (NF != 3) {
            errorMessage = "Unable to parse @include statement on line " \
                NR ".  Expected \"#include-fragment file fragment\", got \"" $0 "\"."

            exitWithErrorMessage(errorMessage)
        }

        line = $0

        location = index(line, "#")
        whitespace = ""

        for (i = 0; i < location - 1; i++) {
            whitespace = whitespace " "
        }

        fragmentFile = includeDir $2
        fragmentLabel = $3

        includeFragment(whitespace, fragmentFile, fragmentLabel)
    }
    else {
        print > tempScriptPath
    }
}
END {
    while ((getline line < tempScriptPath) > 0) {
        print line > scriptPath
    }
}

function includeFragment(linePrefix, fragmentFile, fragmentLabel) {
    while ((getline line < fragmentFile) > 0) {
        if (line ~ "#start-fragment " fragmentLabel) {
            while ((getline line < fragmentFile) > 0) {
                if (line ~ "#end-fragment " fragmentLabel) {
                    return
                }
                print line > tempScriptPath
            }
        }
    }

    exitWithErrorMessage("Unable to find fragment with label \"" fragmentLabel "\" in " fragmentFile)
}

function exitWithErrorMessage(errorMessage) {
    system("echo 'Error: " errorMessage "' 1>&2")
    exit 1
}


0 -> empty

0 -> empty

#!/usr/bin/env awk -f

BEGIN {
    fixedwidthsplit_test();
}

##
## fixedwidthsplit(w, a [,t])
##
## Splits the target string t into the array a on the width list string w, and
## returns the number of fields. If t is not supplied, $0 is used instead. The
## string w is splited on non-numeric characters. If the width of the last field
## is empty, the rest of t is used.
##
function fixedwidthsplit(w, a, t,     _wa, _wn, _i, _k) {
    _wn = split(w, _wa, /[^[:digit:]]+/);
    if (_wn == 0) {
        return 0;
    }
    if (t == "") {
        t = $0;
    }
    if (_wa[_wn] == "") {
        _wa[_wn] = length(t);
    }
    _i = 1;
    for (_k = 1; _k <= _wn; ++_k) {
        a[_k] = substr(t, _i, _wa[_k]);
        _i += _wa[_k];
    }
    return _wn;
}

function fixedwidthsplit_test(       _wn, _a, _k) {
#-$
#rw-r--r--$
#  1$
# xxxxxxx$
# tctnb$
#   1054524$
#$
# Mar$
# 30$
# 15:26$
# zyja.txt$
    _wn = fixedwidthsplit( \
            "1,9,3;8,6,,10,0,4:3,6,", \
            _a, \
            "-rw-r--r--  1 xxxxxxx tctnb   1054524 Mar 30 15:26 zyja.txt" \
    );
    for (_k = 1; _k <= _wn; ++_k) {
        printf("#%s$\n", _a[_k]);
    }
}

#!/usr/bin/env awk -f

BEGIN {
    fixedwidthsplit_test();
}

##
## fixedwidthsplit(w, a [,t])
##
## Splits the target string t into the array a on the width list string w, and
## returns the number of fields. If t is not supplied, $0 is used instead. The
## string w is splited on non-numeric characters. If the width of the last field
## is 0, the rest of t is used.
##
function fixedwidthsplit(w, a, t,     _wa, _wn, _i, _k) {
    _wn = split(w, _wa, /[^[:digit:]]+/);
    if (_wn == 0) {
        return 0;
    }
    if (t == "") {
        t = $0;
    }
    if (_wa[_wn] == "") {
        _wa[_wn] = length(t);
    }
    _i = 1;
    for (_k = 1; _k <= _wn; ++_k) {
        a[_k] = substr(t, _i, _wa[_k]);
        _i += _wa[_k];
    }
    return _wn;
}

function fixedwidthsplit_test(       _wn, _a, _k) {
#-$
#rw-r--r--$
#  1$
# xxxxxxx$
# tctnb$
#   1054524$
#$
# Mar$
# 30$
# 15:26$
# zyja.txt$
    _wn = fixedwidthsplit( \
            "1,9,3;8,6,,10,0,4:3,6,", \
            _a, \
            "-rw-r--r--  1 xxxxxxx tctnb   1054524 Mar 30 15:26 zyja.txt" \
    );
    for (_k = 1; _k <= _wn; ++_k) {
        printf("#%s$\n", _a[_k]);
    }
}


Closing files that are read from

Closing files that are read from

#! /usr/bin/env awk -f
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Copyright © 2011, Damian Carrillo
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
# * Neither the name of the <organization> nor the names of its contributors
#   may be used to endorse or promote products derived from this software
#   without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# template.awk
#
# 2011-10-08
#
#
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

BEGIN {
    split(ARGV[1], components, "\/")

    scriptName = components[length(components)]

    if (!index(scriptName, ".template")) {
        exitWithErrorMessage("Invalid template file - expected filename with .template suffix")
    }

    # strip .template suffix
    sub(/\.template/, "", scriptName)

    tempScriptPath = "/tmp/" scriptName
    scriptPath = "./" scriptName

    includeDir = ""

    for (i = 1; i < length(components); i++) {
        includeDir = includeDir components[i] "/"
    }
}
{
    if ($0 ~ /\@include-fragment/) {
        line = $0

        sub(/.*@include-fragment/, "", line)
        split(line, arguments)

        if (length(arguments) < 2) {
            errorMessage = "Unable to parse @include statement on line " \
                NR ".  Expected \"#include-fragment file fragment\", got \"" line "\"."

            exitWithErrorMessage(errorMessage)
        }

        location = index(line, "#")
        whitespace = ""

        for (i = 0; i < location - 1; i++) {
            whitespace = whitespace " "
        }

        fragmentFile = includeDir arguments[1]
        fragmentLabel = arguments[2]

        includeFragment(whitespace, fragmentFile, fragmentLabel)
    }
    else {
        print > tempScriptPath
    }
}
END {
    while ((getline line < tempScriptPath) > 0) {
        print line > scriptPath
    }
    close(tempScriptPath)
}

function includeFragment(linePrefix, fragmentFile, fragmentLabel) {
    while ((getline line < fragmentFile) > 0) {
        if (line ~ "@start-fragment " fragmentLabel) {
            while ((getline line < fragmentFile) > 0) {
                if (line ~ "@end-fragment " fragmentLabel) {
                    close(fragmentFile)
                    return
                }
                print line > tempScriptPath
            }
        }
    }

    exitWithErrorMessage("Unable to find fragment with label \"" fragmentLabel "\" in " fragmentFile)
}

function exitWithErrorMessage(errorMessage) {
    system("echo 'Error: " errorMessage "' 1>&2")
    exit 1
}

#! /usr/bin/env awk -f
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Copyright © 2011, Damian Carrillo
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
# * Neither the name of the <organization> nor the names of its contributors
#   may be used to endorse or promote products derived from this software
#   without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# template.awk
#
# 2011-10-08
#
#
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

BEGIN {
    split(ARGV[1], components, "\/")

    scriptName = components[length(components)]

    if (!index(scriptName, ".template")) {
        exitWithErrorMessage("Invalid template file - expected filename with .template suffix")
    }

    # strip .template suffix
    sub(/\.template/, "", scriptName)

    tempScriptPath = "/tmp/" scriptName
    scriptPath = "./" scriptName

    includeDir = ""

    for (i = 1; i < length(components); i++) {
        includeDir = includeDir components[i] "/"
    }
}
{
    if ($0 ~ /\@include-fragment/) {
        line = $0

        sub(/.*@include-fragment/, "", line)
        split(line, arguments)

        if (length(arguments) < 2) {
            errorMessage = "Unable to parse @include statement on line " \
                NR ".  Expected \"#include-fragment file fragment\", got \"" line "\"."

            exitWithErrorMessage(errorMessage)
        }

        location = index(line, "#")
        whitespace = ""

        for (i = 0; i < location - 1; i++) {
            whitespace = whitespace " "
        }

        fragmentFile = includeDir arguments[1]
        fragmentLabel = arguments[2]

        includeFragment(whitespace, fragmentFile, fragmentLabel)
    }
    else {
        print > tempScriptPath
    }
}
END {
    while ((getline line < tempScriptPath) > 0) {
        print line > scriptPath
    }
}

function includeFragment(linePrefix, fragmentFile, fragmentLabel) {
    while ((getline line < fragmentFile) > 0) {
        if (line ~ "@start-fragment " fragmentLabel) {
            while ((getline line < fragmentFile) > 0) {
                if (line ~ "@end-fragment " fragmentLabel) {
                    return
                }
                print line > tempScriptPath
            }
        }
    }

    exitWithErrorMessage("Unable to find fragment with label \"" fragmentLabel "\" in " fragmentFile)
}

function exitWithErrorMessage(errorMessage) {
    system("echo 'Error: " errorMessage "' 1>&2")
    exit 1
}


Fix for right inclusion on last read pair


checkonly: wallet unlocking is not required anymore.

#!/bin/awk -f

# Copyright (c) 2014 yurm@bitcointalk.org
# Distributed under the MIT/X11 software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.

# Error/warning functions

# Generate error
# @ec: error code
# @str: error message
function generror(ec, str)
{
	print "Error: " str > "/dev/stderr"
	errcode = ec
	exit errcode
}

# Generate warning
# @str: warning message
function genwarning(str)
{
	print "Warning: " str > "/dev/stderr"
}

BEGIN {
	echo_exec = "/bin/echo"
}

# Search executables functions

# Search executable @exec in @path
# Returns: full pathname
function searchin(path, exec,    res)
{
	"[ -f '" path exec "' ] && [ -x '" path exec "' ] && " echo_exec " '" path exec "'" | getline res
	return res
}

# Search executable @exec in predefined paths
# Returns: full pathname
function searchexec(exec,    res)
{
	gsub(/'/, "", exec)
	gsub(/\s.*$/, "", exec)
	res = searchin("/usr/local/bin/", exec)
	if (res ~ /^\//) return res
	res = searchin("/usr/bin/", exec)
	if (res ~ /^\//) return res
	res = searchin("/bin/", exec)
	if (res ~ /^\//) return res
	res = searchin("/opt/bin/", exec)
	if (res ~ /^\//) return res
	return ""
}

BEGIN {
	# Excluded for security
	# coind_exec = (ENVIRON["COINDAEMON"] != "") ? ENVIRON["COINDAEMON"] : searchexec("bitcoind")

	# Used executables:
	coind_exec = searchexec("bitcoind")
	sed_exec =   searchexec("sed")
	bc_exec =    searchexec("bc")
	date_exec =  searchexec("date")
	if (coind_exec == "") generror(51, "bitcoind executable not found")
	if (sed_exec == "")   generror(52, "sed executable not found")
	if (bc_exec == "")    generror(53, "bc executable not found")
	if (date_exec == "")  generror(54, "date executable not found")

	# Error code
	errcode = 0
	# Address index, input index, output index (grows up), commission "output" index (grows down)
	aix = -1; iix = -1; oix = -1; ocommix = 0
	# Sequence number (for every output) as reverse byte ordered hex,
	# nlocktime as reverse byte ordered hex
	hexlock[0] = "ffffffff" ; hexlock[1] = "00000000"
	# Regular address regexp
	singleaddrheadregexp = "^1"
	# Multisignature address regexp
	multiaddrheadregexp = "^3"
}

# Log print functions

# Print debug message @str
# @sensitive is not empty for sensitive data (private keys)
function printdebug(str, sensitive, level)
{
	return
	if (sensitive != "") return
	print "DEBUG: " str > "/dev/stderr"
}

# Print final debug message @str
# @sensitive is not empty for sensitive data (private keys)
function printfinal(str, sensitive)
{
	return
	if (sensitive != "") return
	print "FINAL: " str > "/dev/stderr"
}

# Print external command to execute (@cmd)
# @category: caller function
function printcmd(category, cmd)
{
	return
	if (category == "signtransaction_nocheck")
		gsub(/'\[\".*\"\]'/, "'[\"<private keys>\"]'", cmd)
	print "CMD: " cmd > "/dev/stderr"
}

# General functions without side effects

# Calculate @expr via external command (bc)
# Used to avoid precision loss
function calculate_nocheck(expr,    cmd, res)
{
	gsub(/'/, "", expr)
	cmd = echo_exec " 'scale=8;" expr "' | " bc_exec
	printcmd("calculate_nocheck", cmd)
	cmd | getline res
	close(cmd)
	return gensub(/^\./, "0.", "", res)
}

# Convert @dec to reverse byte ordered hexadecimal
function hexrevert32_nocheck(dec,    hex)
{
	hex = sprintf("%08x", dec)
	return gensub(/^([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})$/, "\\4\\3\\2\\1", "", hex)
}

# bitcoind getrawtransaction call (without return value check)
# @id: transaction id
# Returns: raw transaction
function getrawtransaction_nocheck(id,    cmd, res)
{
	cmd = coind_exec " getrawtransaction " id
	printcmd("getrawtransaction_nocheck", cmd)
	cmd | getline res
	close(cmd)
	return res
}

# Parse functions

# Parse line @line of bitcoind decoderawtransaction JSON result
# Set appropriate variables:
#     tx_i_id[txid, input_number], tx_i_vout[...] -
#         {id, vout} of previous corresponding transaction output for each input
#     tx_o_val[txid, output_number], tx_o_n[...], tx_o_addr[...], tx_o_script[...] -
#         {value, n, destination address, scriptPubKey} for each output
# @res: res[0] is used to return txid of decoded transaction
# @st: parser state
function parsedecodedtxline(res, st, line)
{
	printdebug(st[0] "\t" st[1] "\t" st[2] "\t" line, "", 4)
	switch (st[0]) {
	case 0:
		if      (line ~ /^ *\"txid\" *: *\"[[:xdigit:]]+\"/)
			res[0] = gensub(/^ *\"txid\" *: *\"([^\"]+)\".*$/, "\\1", "", line)
		else if (line ~ /^ *\"vin\" *: *\[ *$/)  {++st[0]; st[1] = 1; st[2] = -1}
		else if (line ~ /^ *\"vout\" *: *\[ *$/) {++st[0]; st[1] = 2; st[2] = -1}
		return
	case 1:
		switch (st[1]) {
		case 1: case 2:
			if      (line ~ /^ *\{ *$/)       {++st[0]; ++st[2]  }
			else if (line ~ /^ *\] *(, *)?$/) {--st[0]; st[1] = 0}
			return
		default:
			generror(41, "Invalid parser state (" st[0] "," st[1] "," st[2] ")")
		}
		return
	default:
		switch (st[1]) {
		case 1:
			if      (line ~ /^ *\"txid\" *: *\"[[:xdigit:]]+\"/)
				tx_i_id[res[0], st[2]] = gensub(/^ *\"txid\" *: *\"([^\"]*)\".*$/, "\\1", "", line)
			else if (line ~ /^ *\"vout\" *: *[0-9]+ *(,.*)?$/)
				tx_i_vout[res[0], st[2]] = gensub(/^ *\"vout\" *: *([0-9]+) *(,.*)?$/, "\\1", "", line)
			break
		case 2:
			if      (line ~ /^ *\"value\" *: *[0-9]+(\.[0-9]+)? *(,.*)?$/)
				tx_o_val[res[0], st[2]] = gensub(/^ *\"value\" *: *([0-9\.]+) *(,.*)?$/, "\\1", "", line)
			else if (line ~ /^ *\"n\" *: *[0-9]+ *(,.*)?$/)
				tx_o_n[res[0], st[2]] = gensub(/^ *\"n\" *: *([0-9]+) *(,.*)?$/, "\\1", "", line)
			else if (line ~ /^ *\"scriptPubKey\" *: *\{ *$/)
				st[1] = 4
			break
		case 4:
			if      (line ~ /^ *\"hex\" *: *\"[[:xdigit:]]+\"/)
				tx_o_script[res[0], st[2]] = gensub(/^ *\"hex\" *: *\"([^\"]+)\".*$/, "\\1", "", line)
			else if (line ~ /^ *\"addresses\" *: *\[ *$/)
				st[1] = 6
			else if (line ~ /^ *\} *(, *)?$/)
				st[1] = 2
			break
		case 6:
			if      (line ~ /^ *\"[[:alnum:]]+\"/)
				tx_o_addr[res[0], st[2]] = gensub(/^ *\"([[:alnum:]]+)\".*$/, "\\1", "", line)
			else if (line ~ /^ *\] *(, *)?$/)
				st[1] = 4
			break
		default:
			generror(41, "Invalid parser state (" st[0] "," st[1] "," st[2] ")")
		}
		if (line ~ /[{\[] *$/)         {++st[0]; return}
		if (line ~ /^ *[]}] *(, *)?$/) {--st[0]; return}
	}
}

# Parse transaction @tx
# Set appropriate variables (see parsedecodedtxline description)
# @tx: raw transaction
# Returns: @tx id
function parsetransaction(tx,    cmd, line, state, res)
{
	printdebug("parsetransaction")
	res[0] = rawtx_id[tx]
	if (res[0] != "") return res[0]
	cmd = coind_exec " decoderawtransaction " tx
	printcmd("parsetransaction", cmd)
	state[0] = 0
	while ((cmd | getline line) > 0)
		parsedecodedtxline(res, state, line)
	# Don't close cmd - results are buffered
	rawtx_id[tx] = res[0]
	return res[0]
}

# Parse seqnumber/nlocktime string @str
# @hexres: hexres[0] - sequence number as reverse byte ordered hex,
#          hexres[1] - nlocktime as reverse byte ordered hex
function parselock(hexres, str,    ltstr, cmd, declt, lt, nseq, n)
{
	printdebug("parselock")
	ltstr = gensub(/^l\s+[[:digit:]]+\s+/, "", "", str)
	gsub(/'/, "", ltstr)
	cmd = date_exec " --date='" ltstr "' '+%s'"
	printcmd("parselock", cmd)
	cmd | getline declt
	close(cmd)
	if (declt !~ /^[[:digit:]]+$/)
		generror(22, "Unable to convert locktime '" ltstr "'")
	lt = hexrevert32_nocheck(declt)
	if (lt !~ /^[[:xdigit:]]{8}$/)
		generror(22, "Unable to convert locktime '" ltstr "'")
	nseq = gensub(/^l\s+([[:digit:]]+)\s.*$/, "\\1", "", str)
	n = hexrevert32_nocheck(nseq)
	if (n !~ /^[[:xdigit:]]{8}$/)
		generror(23, "Unable to convert sequence number '" nseq "'")
	hexres[0] = n
	hexres[1] = lt
}

# Parse address string
# No complex actions, just concatenation
# Returns: combined address string (components separated by SUBSEP; used by makeaddress)
function parseaddress(num, a1, a2, a3)
{
	printdebug("parseaddress " num " " a1 " " a2 " " a3)
	if (num == "") return num
	if (a1 == "") return num
	num = num SUBSEP a1
	if (a2 == "") return num
	num = num SUBSEP a2
	if (a3 == "") return num
	num = num SUBSEP a3
	return num
}

# Functions that need real address

# Set bidirectional mapping between address @addr and public key @pub
# For multisignature @addr @pub is treated as redeemScript
function setpubkey(addr, pub)
{
	if ((addr == "") || (pub == "")) return
	if (addr ~ multiaddrheadregexp)
		redeem[addr] = pub
	else if (addr ~ singleaddrheadregexp) {
		addr_pub[addr] = pub
		pub_addr[pub] = addr
	}
}

# Validate address @addr (bitcoind validateaddress call)
# Set appropriate variables: addr_isvalid[@addr], addr_ismine[@addr], addr_isscript[@addr]
# Calls setpubkey
function validateaddress(addr,    cmd, line)
{
	printdebug("validateaddress " addr)
	cmd = coind_exec " validateaddress " addr
	printcmd("validateaddress", cmd)
	while ((cmd | getline line) > 0) {
		if      (line ~ /^ *\"isvalid\" *:/)
			addr_isvalid[addr] = (line ~ /^ *\"isvalid\" *: *true/) ? "1" : ""
		else if (line ~ /^ *\"ismine\" *:/)
			addr_ismine[addr] = (line ~ /^ *\"ismine\" *: *true/) ? "1" : ""
		else if (line ~ /^ *\"isscript\" *:/)
			addr_isscript[addr] = (line ~ /^ *\"isscript\" *: *true/) ? "1" : ""
		else if (line ~ /^ *\"pubkey\" *: *\"[[:xdigit:]]+\"/)
			setpubkey(addr, gensub(/^ *\"pubkey\" *: *\"([^\"]*)\".*$/, "\\1", "", line))
	}
	# Don't close cmd - results are buffered
}

# bitcoind dumpprivkey call for address @addr
# Set addr_priv[@addr]
function dumpprivkey(addr,    cmd, line)
{
	printdebug("dumpprivkey " addr)
	if (addr_priv[addr] != "") return
	cmd = coind_exec " dumpprivkey " addr
	printcmd("dumpprivkey", cmd)
	while ((cmd | getline line) > 0)
		if (line ~ /^[[:alnum:]]+$/) {
			addr_priv[addr] = line
			return
		}
	# Don't close cmd - results are buffered
}

# Retrieve private key for address @addr
# Returns: 1 on successful extraction, 0 otherwise (including previous extraction success for same @addr)
function retrieveprivkey(addr)
{
	printdebug("retrieveprivkey " addr)
	if (addr_priv[addr] != "") return 0
	validateaddress(addr)
	if ((addr_ismine[addr] == "") || (addr_isscript[addr] != "")) return 0
	dumpprivkey(addr)
	if (addr_priv[addr] == "")
		generror(3, "Cannot retrieve private key for address " addr " (is wallet locked?)")
	return (addr_priv[addr] != "") ? 1 : 0
}

# Retrieve private keys for multisignature address (@addr) participants
# Returns: number of newly retrieved private keys
function retrievemultisigprivkey(addr,    i, all)
{
	printdebug("retrievemultisigprivkey " addr)
	all = 0
	for (i = 0; multisig[addr, i] != ""; ++i)
		all += retrieveprivkey(multisig[addr, i])
	return all
}

# Retrieve scriptPubKey for address @addr
# Set script[@addr]
function retrieveaddressscript(addr,    cmdcreate, faketx, cmddecode, res)
{
	printdebug("retrieveaddressscript " addr)
	if ((addr == "") || (script[addr] != "")) return
	cmdcreate = coind_exec " createrawtransaction '[{\
		\"txid\":\"0000000000000000000000000000000000000000000000000000000000000000\",\
		\"vout\":0\
	}]' '{\"" addr "\":1.0}'"
	printcmd("retrieveaddressscript", cmdcreate)
	cmdcreate | getline faketx
	# Don't close cmd - results are buffered
	if (faketx !~ /^[[:xdigit:]]+$/)
		generror(7, "Cannot retrieve address script for address '" addr "' (is it valid?)")
	cmddecode = coind_exec " decoderawtransaction " faketx " | " sed_exec " -n '\
	/^ *\"vout\" *: *\\[/,$ {\
		/^ *\"hex\" *:/ {\
			s/^ *\"hex\" *: *\"\\([^\"]*\\)\".*$/\\1/;\
			p\
		}\
	}'"
	printcmd("retrieveaddressscript", cmddecode)
	cmddecode | getline res
	if (res !~ /^[[:xdigit:]]+$/)
		generror(6, "Cannot retrieve address script for address '" addr "'")
	script[addr] = res
	# Don't close cmd - results are buffered
}

# Read previous corresponding transaction for input @ix
# Set appropriate variables:
#     amount[@ix], src[@ix], script[src[@ix]] -
#         amount, source address, scriptPubKey of source address for input @ix
function readtransaction(ix,    rawtx, rix, val, asrc)
{
	printdebug("readtransaction " ix)
	rawtx = getrawtransaction_nocheck(txid[ix])
	if (rawtx !~ /^[[:xdigit:]]+$/)
		generror(5, "Unable to get raw transaction " txid[ix])
	rix = parsetransaction(rawtx)
	if (rix != txid[ix])
		generror(101, "Internal error - transaction id mismatch (" rix " != " txid[ix] ")")
	val = tx_o_val[txid[ix], vout[ix]]
	if ((amount[ix] != "") && (calculate_nocheck(amount[ix] "-" val) != 0))
		generror(11, "txin " ix ": amount '" amount[ix] "' is not valid (retrieved amount is '" val "')")
	amount[ix] = val
	asrc = tx_o_addr[txid[ix], vout[ix]]
	if ((src[ix] != "") && (src[ix] != asrc))
		generror(12, "txin " ix ": source address '" src[ix] "' is not valid (retrieved address is '" asrc "')")
	src[ix] = asrc
	if (script[src[ix]] == "")
		script[src[ix]] = tx_o_script[txid[ix], vout[ix]]
}

# Line pattern actions block

{
	parsed = 0
}

# Address description
/^a\s+(-\s+[[:alnum:]]+(\s+[[:alnum:]]+)?|[123](\s+[[:alnum:]]+){1,3})\s*$/ {
	printdebug("----addr----")
	parsed = 1
	addrtxt[++aix] = parseaddress($2, $3, $4, $5)
}

# Sequence number/nlocktime
/^l\s+[[:digit:]]+\s+/ {
	printdebug("----lock----")
	parsed = 1
	parselock(hexlock, $0)
}

# Input description
/^i\s+[[:xdigit:]]{64}\s+[[:digit:]]+(\s+-(\s+(-|[[:digit:]]+(\.[[:digit:]]{1,8})?)(\s+(-\s+[[:alnum:]]+(\s+[[:alnum:]]+)?|[123](\s+[[:alnum:]]+){1,3}))?)?)?\s*$/ {
	printdebug("----txin----")
	parsed = 1
	++iix

	txid[iix] = $2
	vout[iix] = $3
	# $4 is reserved for SIGHASH
	if (($5 != "-") && ($5 != "")) amount[iix] = $5
	srctxt[iix] = parseaddress($6, $7, $8, $9)
}

# Output description
/^o\s+(-|[[:digit:]]+(\.[[:digit:]]{1,8})?)(\s+(-\s+[[:alnum:]]+(\s+[[:alnum:]]+)?|[123](\s+[[:alnum:]]+){1,3}))?\s*$/ {
	printdebug("----txout----")
	parsed = 1

	dsttxtaddr = parseaddress($3, $4, $5, $6)
	dstvalue = ($2 != "-") ? calculate_nocheck("0+" $2) : $2
	if ($2 != 0) {
		coix = (dsttxtaddr != "") ? ++oix : --ocommix
		outval[coix] = dstvalue
		dsttxt[coix] = dsttxtaddr
	}
	else
		genwarning("Empty outputs are ignored, skipped '" $0 "'")
}

# Transaction (partial signed or full (for transaction check))
/^t\s+[[:xdigit:]]+\s*$/ {
	printdebug("----partial----")
	parsed = 1

	if (partialtx)
		generror(18, "Multiple partial transactions are not acceptable")
	partialtx = $2
}

# Action (no signing, check only)
/^checkonly\s*$/ {
	printdebug("----checkonly----")
	parsed = 1
	checkonly = 1
}

# Commentary
/^\s*([#;-].*)?$/ {
	parsed = 1
}

{
	if (parsed == 0)
		generror(21, "Unrecognized string '" $0 "'")
}

# Final parameters list creating functions

# Form transaction inputs list
# Returns: JSON array of transaction inputs
function formtxinlist(    res, i)
{
	res = "'["
	for (i = 0; txid[i] != ""; ++i) {
		if (res != "'[") res = res ","
		res = res "{\"txid\":\"" txid[i] "\",\"vout\":" vout[i] ",\"scriptPubKey\":\"" script[src[i]] "\""
		if (redeem[src[i]] != "") res = res ",\"redeemScript\":\"" redeem[src[i]] "\""
		res = res "}"
	}
	res = res "]'"
	return res
}

# Form transaction outputs list
# Returns: JSON list of transaction outputs
function formtxoutlist(    res, o)
{
	res = "'{"
	for (o = 0; outval[o] != ""; ++o) {
		if (dst[o] == "") continue
		if (res != "'{") res = res ","
		res = res "\"" dst[o] "\":" outval[o]
	}
	res = res "}'"
	return res
}

# Form private keys list
# Returns: JSON array of private keys
function formprivkeylist(    res, a)
{
	res = "'["
	for (a in addr_priv) {
		if (addr_priv[a] == "") continue
		if (res != "'[") res = res ","
		res = res "\"" addr_priv[a] "\""
	}
	res = res "]'"
	return res
}

# General final functions without side effects

# Modify sequence number and nlocktime for newly created transaction @createdtx
# @createdtx: newly created raw transaction
# @txincount: number of @createdtx inputs
# @hexnseq: sequence number to replace to (as reverse byte ordered hex)
# @hexlocktime: nlocktime to replace to (as reverse byte ordered hex)
# Returns: raw modified transaction
function modifylockfortransaction(createdtx, txincount, hexnseq, hexlocktime,    res, parts, n, i)
{
# Should parse raw transaction here instead of regexp replacing
	n = split(createdtx, parts, "00ffffffff")
	if (n > txincount + 1)
		generror(32, "Sequence number replacing failed (some false positives are possible): " createdtx)
	if (n < txincount + 1)
		generror(33, "Sequence number replacing failed (already replaced?): " createdtx)
	res = parts[1]
	for (i = 2; i <= n; ++i)
		res = res "00" hexnseq parts[i]
	gsub(/00000000$/, hexlocktime, res)
	return res
}

# Final action functions

# Check partial transaction (where id == @id) conformity with accumulated inputs/outputs
# (txid[], vout[], outval[], dst[])
function checkpartialtransaction(id,    ires, i, imax, ores, o, ox, oxmax)
{
	printdebug("checkpartialtransaction")
	ires = 0
	for (i = 0; tx_i_id[id, i] != ""; ++i) {
		if (tx_i_id[id, i] != txid[i]) {
			printdebug("(txid) " i " " tx_i_id[id, i] " != " txid[i], "", 3)
			ires = 1
		}
		if (tx_i_vout[id, i] != vout[i]) {
			printdebug("(vout) " i " " tx_i_vout[id, i] " != " vout[i], "", 3)
			ires = 1
		}
	}
	imax = i - 1

	ores = 0
	oxmax = -1
	for (o = 0; tx_o_n[id, o] != ""; ++o) {
		ox = tx_o_n[id, o]
		if (oxmax < ox) oxmax = ox
		# tx_o_val[id, o] is compared with outval[ox] (there is no mistyping in index)
		if (calculate_nocheck(tx_o_val[id, o] "-" outval[ox]) != 0) {
			printdebug("(value) " ox " " tx_o_val[id, o] " != " outval[ox], "", 3)
			ores = 1
		}
		if (tx_o_addr[id, o] != dst[ox]) {
			printdebug("(dst) " ox " " tx_o_addr[id, o] " != " dst[ox], "", 3)
			ores = 1
		}
	}

	if (ires != 0)
		generror(36, "Partial transaction doesn't match provided inputs")
	if (imax != iix)
		generror(37, "Partial transaction doesn't match provided inputs (additional inputs detected)")
	if (ores != 0)
		generror(38, "Partial transaction doesn't match provided outputs")
	if (oxmax != oix)
		generror(39, "Partial transaction doesn't match provided outputs (additional outputs detected)")
}

# Create multisignature address @num-of-{@a1, @a2, @a3}
# (or @num-of-{@a1, @a2} if @a3 is not set, or @num-of-{@a1} if @a2 and @a3 are not set)
# Attention: address orded is significant (e.g., 2-of-{a, b} != 2-of-{b, a})
# Returns: multisignature address
function createmultisigaddress(num, a1, a2, a3,    addr, aarray, parray, publist, cmd, line, aredeem, i)
{
	printdebug("createmultisigaddress " num " " a1 " " a2 " " a3)
	if ((a1 == "") && (a2 == "") && (a3 == "")) return ""
	i = -1
	if (a1 != "") aarray[++i] = a1
	if (a2 != "") aarray[++i] = a2
	if (a3 != "") aarray[++i] = a3
	for (i = 0; aarray[i] != ""; ++i) {
		parray[i] = (addr_pub[aarray[i]] != "") ? addr_pub[aarray[i]] : aarray[i]
		aarray[i] = (pub_addr[parray[i]] != "") ? pub_addr[parray[i]] : parray[i]
	}
	addr = multi_addr[num, aarray[0], aarray[1], aarray[2]]
	if (addr != "") return addr
	publist = "'["
	for (i = 0; aarray[i] != ""; ++i) {
		if (publist != "'[") publist = publist ","
		publist = publist "\"" parray[i] "\""
	}
	publist = publist "]'"
	cmd = coind_exec " createmultisig " num " " publist
	printcmd("createmultisigaddress", cmd)
	while ((cmd | getline line) > 0) {
		if      (line ~ /^ *\"address\" *: *\"[[:alnum:]]+\"/)
			addr = gensub(/^ *\"address\" *: *\"([^\"]*)\".*$/, "\\1", "", line)
		else if (line ~ /^ *\"redeemScript\" *: *\"[[:xdigit:]]+\"/)
			aredeem = gensub(/^ *\"redeemScript\" *: *\"([^\"]*)\".*$/, "\\1", "", line)
	}
	# Don't close cmd - results are buffered
	if ((addr !~ multiaddrheadregexp) || (aredeem !~ /^[[:xdigit:]]+$/))
		generror(2, "Invalid source multisig address " num "-of-" publist)
	printdebug("= createmultisigaddress " addr " " aredeem)
	multi_addr[num, aarray[0], aarray[1], aarray[2]] = addr
	redeem[addr] = aredeem
	for (i in aarray) multisig[addr, i] = aarray[i]
	return addr
}

# Convert text representation @atxt of address to real address (see parseaddress)
# @acceptmulti: permit createmultisigaddress call (for multisig addresses) if nonzero
# Returns: real address or empty string
function makeaddress(atxt, acceptmulti,    parts, n)
{
	printdebug("makeaddress " atxt)
	if (atxt == "") return ""
	n = split(atxt, parts, SUBSEP)
	if (n < 2)
		generror(1, "Invalid address '" atxt "' (you should use '- <address>' format)")
	if (parts[1] != "-") {
		if ((! acceptmulti) || (acceptmulti == 0)) return ""
		return createmultisigaddress(parts[1], parts[2], parts[3], parts[4])
	}
	validateaddress(parts[2])
	if (addr_isvalid[parts[2]] == "")
		generror(1, "Invalid address '" parts[2] "'")
	if (parts[3] != "") {
		if ((addr_pub[parts[2]] != "") && (addr_pub[parts[2]] != parts[3]))
			generror(4, "Invalid pubkey for address " parts[2])
		setpubkey(parts[2], parts[3])
	}
	return parts[2]
}

# Convert accumulated text representations (addrtxt[], srctxt[], dsttxt[]) of addresses to real addresses
# (see parseaddress)
# Set appropriate variables (src[i], dst[o])
function makeaddresses(    stage, a, i, o)
{
	for (stage = 0; stage <= 1; ++stage) {
		for (a in addrtxt)
			if (addrtxt[a] != "")
				makeaddress(addrtxt[a], stage)
		for (i in srctxt)
			if ((src[i] == "") && (srctxt[i] != ""))
				src[i] = makeaddress(srctxt[i], stage)
		for (o in dsttxt)
			if ((dst[o] == "") && (dsttxt[o] != ""))
				dst[o] = makeaddress(dsttxt[o], stage)
	}
}

# Process delayed actions (convert addresses,
# read previous corresponding transactions for inputs without full information,
# retrieve scriptPubKey of source address for each input,
# retrieve private keys for each input)
function processdelayedactions(    i)
{
	makeaddresses()
	for (i in txid) {
		if ((amount[i] == "") || (src[i] == ""))
			readtransaction(i)
		retrieveaddressscript(src[i])
		if (checkonly) continue
		if (src[i] ~ singleaddrheadregexp)
			retrieveprivkey(src[i])
		else if (src[i] ~ multiaddrheadregexp) {
			if ((multisig[src[i], 0] != "") && (redeem[src[i]] != ""))
				retrievemultisigprivkey(src[i])
			else
				genwarning("Input " i " signing is skipped, you should provide '" src[i] "' participants")
		}
	}
}

# Write change to output(s) where outval[o] == "-"
# Divide change equally if there are several such outputs
# Set outval[o]
function writechange(    volume, i, partvol, ochange, o, ochcount)
{
	volume = "0"
	for (i = 0; amount[i] != ""; ++i)
		volume = calculate_nocheck(volume "+" amount[i])

	ochcount = 0
	for (o = -1; outval[o] != ""; --o);
	for (++o; outval[o] != ""; ++o) {
		if (outval[o] == "-")
			ochange[ochcount++] = o
		else {
			volume = calculate_nocheck(volume "-" outval[o])
			if (volume < 0)
				generror(19, "Volume of outputs exseeds volume of inputs")
				#! May be correct (SIGHASH_ANYONECANPAY)
		}
	}
	if ((ochcount == 0) && (volume > 0))
		generror(20, "Volume of outputs isn't equal to volume of inputs (difference == " volume "). You should explicitly set change address and commission")
	if ((ochcount == 0) || (volume <= 0)) return

	partvol = calculate_nocheck(volume "/" ochcount)
	volume = calculate_nocheck(volume "-((" ochcount "-1)*" partvol ")")
	for (o = 0; ochange[o] != ""; ++o)
		outval[ochange[o]] = partvol
	outval[ochange[o - 1]] = volume
}

# bitcoind signrawtransaction call (without return value check)
# @tx: raw transaction to sign
# @txinlist: JSON array of transaction inputs
# @privkeylist: JSON array of private keys to sign with
# Returns: (fully/partially) signed raw transaction concatenated with result (true/false) via space
#
# Attention: there is a possibility of private key stealing via commandline (e.g., by "ps aux" or such command).
# Workaround: use distinct virtual guest system only for wallet and this script
function signtransaction_nocheck(tx, txinlist, privkeylist,    cmd, res)
{
	cmd = coind_exec " signrawtransaction " tx " " txinlist " " privkeylist " | " sed_exec " -n '\
	/^ *\"hex\" *:/ {\
		s/^ *\"hex\" *: *\"\\([^\"]*\\)\".*$/\\1/;\
		h\
	};\
	/^ *\"complete\" *:/ {\
		s/^ *\"complete\" *: *\\([^ ]*\\).*$/\\1/;\
		H\
	};\
	$ {\
		x;\
		s/\\n/ /g;\
		p\
	}\
	'"
	printcmd("signtransaction_nocheck", cmd)
	cmd | getline res
	close(cmd)
	return res
}

# Create raw transaction and call modifylockfortransaction
# @txinlist: JSON array of transaction inputs
# @txoutlist: JSON list of transaction outputs
# @txincount: number of transaction inputs
# @hexnseq, @hexlocktime: see modifylockfortransaction
# Returns: modified raw transaction
function maketransaction(txinlist, txoutlist, txincount, hexnseq, hexlocktime,    cmd, createdtx, modifiedtx)
{
	cmd = coind_exec " createrawtransaction " txinlist " " txoutlist
	printcmd("maketransaction", cmd)
	cmd | getline createdtx
	close(cmd)
	if (createdtx !~ /^[[:xdigit:]]+$/)
		generror(13, "Unable to create new transaction " txinlist " " txoutlist)
	modifiedtx = modifylockfortransaction(createdtx, txincount, hexnseq, hexlocktime)
	if (modifiedtx !~ /^[[:xdigit:]]+$/)
		generror(14, "Unable to modify new transaction " createdtx)
	return modifiedtx
}

# Main action block

END {
	printdebug("----final----")
	if (errcode != 0) exit errcode
	if (iix < 0)
		generror(16, "Transactions without inputs are unacceptable")

	processdelayedactions()
	writechange()

	if (partialtx != "")
		checkpartialtransaction(parsetransaction(partialtx))

	txinlist = formtxinlist()
	printfinal(txinlist)
	txoutlist = formtxoutlist()
	printfinal(txoutlist)
	privkeylist = checkonly ? "'[]'" : formprivkeylist()
	printfinal(privkeylist, "true")
	printfinal(hexlock[0] ":" hexlock[1])

	if (partialtx == "")
		partialtx = maketransaction(txinlist, txoutlist, iix + 1, hexlock[0], hexlock[1])
	signedtx = signtransaction_nocheck(partialtx, txinlist, privkeylist)
	if (signedtx !~ /^[[:xdigit:]]+ (true|false)$/)
		generror(9, "Unable to sign new transaction " partialtx)
	split(signedtx, signedtxparts)
	print (signedtxparts[2] == "true") ? parsetransaction(signedtxparts[1]) : signedtxparts[2]
	print signedtxparts[1]
}

#!/bin/awk -f

# Copyright (c) 2014 yurm@bitcointalk.org
# Distributed under the MIT/X11 software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.

# Error/warning functions

# Generate error
# @ec: error code
# @str: error message
function generror(ec, str)
{
	print "Error: " str > "/dev/stderr"
	errcode = ec
	exit errcode
}

# Generate warning
# @str: warning message
function genwarning(str)
{
	print "Warning: " str > "/dev/stderr"
}

BEGIN {
	echo_exec = "/bin/echo"
}

# Search executables functions

# Search executable @exec in @path
# Returns: full pathname
function searchin(path, exec,    res)
{
	"[ -f '" path exec "' ] && [ -x '" path exec "' ] && " echo_exec " '" path exec "'" | getline res
	return res
}

# Search executable @exec in predefined paths
# Returns: full pathname
function searchexec(exec,    res)
{
	gsub(/'/, "", exec)
	gsub(/\s.*$/, "", exec)
	res = searchin("/usr/local/bin/", exec)
	if (res ~ /^\//) return res
	res = searchin("/usr/bin/", exec)
	if (res ~ /^\//) return res
	res = searchin("/bin/", exec)
	if (res ~ /^\//) return res
	res = searchin("/opt/bin/", exec)
	if (res ~ /^\//) return res
	return ""
}

BEGIN {
	# Excluded for security
	# coind_exec = (ENVIRON["COINDAEMON"] != "") ? ENVIRON["COINDAEMON"] : searchexec("bitcoind")

	# Used executables:
	coind_exec = searchexec("bitcoind")
	sed_exec =   searchexec("sed")
	bc_exec =    searchexec("bc")
	date_exec =  searchexec("date")
	if (coind_exec == "") generror(51, "bitcoind executable not found")
	if (sed_exec == "")   generror(52, "sed executable not found")
	if (bc_exec == "")    generror(53, "bc executable not found")
	if (date_exec == "")  generror(54, "date executable not found")

	# Error code
	errcode = 0
	# Address index, input index, output index (grows up), commission "output" index (grows down)
	aix = -1; iix = -1; oix = -1; ocommix = 0
	# Sequence number (for every output) as reverse byte ordered hex,
	# nlocktime as reverse byte ordered hex
	hexlock[0] = "ffffffff" ; hexlock[1] = "00000000"
	# Regular address regexp
	singleaddrheadregexp = "^1"
	# Multisignature address regexp
	multiaddrheadregexp = "^3"
}

# Log print functions

# Print debug message @str
# @sensitive is not empty for sensitive data (private keys)
function printdebug(str, sensitive, level)
{
	return
	if (sensitive != "") return
	print "DEBUG: " str > "/dev/stderr"
}

# Print final debug message @str
# @sensitive is not empty for sensitive data (private keys)
function printfinal(str, sensitive)
{
	return
	if (sensitive != "") return
	print "FINAL: " str > "/dev/stderr"
}

# Print external command to execute (@cmd)
# @category: caller function
function printcmd(category, cmd)
{
	return
	if (category == "signtransaction_nocheck")
		gsub(/'\[\".*\"\]'/, "'[\"<private keys>\"]'", cmd)
	print "CMD: " cmd > "/dev/stderr"
}

# General functions without side effects

# Calculate @expr via external command (bc)
# Used to avoid precision loss
function calculate_nocheck(expr,    cmd, res)
{
	gsub(/'/, "", expr)
	cmd = echo_exec " 'scale=8;" expr "' | " bc_exec
	printcmd("calculate_nocheck", cmd)
	cmd | getline res
	close(cmd)
	return gensub(/^\./, "0.", "", res)
}

# Convert @dec to reverse byte ordered hexadecimal
function hexrevert32_nocheck(dec,    hex)
{
	hex = sprintf("%08x", dec)
	return gensub(/^([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})$/, "\\4\\3\\2\\1", "", hex)
}

# bitcoind getrawtransaction call (without return value check)
# @id: transaction id
# Returns: raw transaction
function getrawtransaction_nocheck(id,    cmd, res)
{
	cmd = coind_exec " getrawtransaction " id
	printcmd("getrawtransaction_nocheck", cmd)
	cmd | getline res
	close(cmd)
	return res
}

# Parse functions

# Parse line @line of bitcoind decoderawtransaction JSON result
# Set appropriate variables:
#     tx_i_id[txid, input_number], tx_i_vout[...] -
#         {id, vout} of previous corresponding transaction output for each input
#     tx_o_val[txid, output_number], tx_o_n[...], tx_o_addr[...], tx_o_script[...] -
#         {value, n, destination address, scriptPubKey} for each output
# @res: res[0] is used to return txid of decoded transaction
# @st: parser state
function parsedecodedtxline(res, st, line)
{
	printdebug(st[0] "\t" st[1] "\t" st[2] "\t" line, "", 4)
	switch (st[0]) {
	case 0:
		if      (line ~ /^ *\"txid\" *: *\"[[:xdigit:]]+\"/)
			res[0] = gensub(/^ *\"txid\" *: *\"([^\"]+)\".*$/, "\\1", "", line)
		else if (line ~ /^ *\"vin\" *: *\[ *$/)  {++st[0]; st[1] = 1; st[2] = -1}
		else if (line ~ /^ *\"vout\" *: *\[ *$/) {++st[0]; st[1] = 2; st[2] = -1}
		return
	case 1:
		switch (st[1]) {
		case 1: case 2:
			if      (line ~ /^ *\{ *$/)       {++st[0]; ++st[2]  }
			else if (line ~ /^ *\] *(, *)?$/) {--st[0]; st[1] = 0}
			return
		default:
			generror(41, "Invalid parser state (" st[0] "," st[1] "," st[2] ")")
		}
		return
	default:
		switch (st[1]) {
		case 1:
			if      (line ~ /^ *\"txid\" *: *\"[[:xdigit:]]+\"/)
				tx_i_id[res[0], st[2]] = gensub(/^ *\"txid\" *: *\"([^\"]*)\".*$/, "\\1", "", line)
			else if (line ~ /^ *\"vout\" *: *[0-9]+ *(,.*)?$/)
				tx_i_vout[res[0], st[2]] = gensub(/^ *\"vout\" *: *([0-9]+) *(,.*)?$/, "\\1", "", line)
			break
		case 2:
			if      (line ~ /^ *\"value\" *: *[0-9]+(\.[0-9]+)? *(,.*)?$/)
				tx_o_val[res[0], st[2]] = gensub(/^ *\"value\" *: *([0-9\.]+) *(,.*)?$/, "\\1", "", line)
			else if (line ~ /^ *\"n\" *: *[0-9]+ *(,.*)?$/)
				tx_o_n[res[0], st[2]] = gensub(/^ *\"n\" *: *([0-9]+) *(,.*)?$/, "\\1", "", line)
			else if (line ~ /^ *\"scriptPubKey\" *: *\{ *$/)
				st[1] = 4
			break
		case 4:
			if      (line ~ /^ *\"hex\" *: *\"[[:xdigit:]]+\"/)
				tx_o_script[res[0], st[2]] = gensub(/^ *\"hex\" *: *\"([^\"]+)\".*$/, "\\1", "", line)
			else if (line ~ /^ *\"addresses\" *: *\[ *$/)
				st[1] = 6
			else if (line ~ /^ *\} *(, *)?$/)
				st[1] = 2
			break
		case 6:
			if      (line ~ /^ *\"[[:alnum:]]+\"/)
				tx_o_addr[res[0], st[2]] = gensub(/^ *\"([[:alnum:]]+)\".*$/, "\\1", "", line)
			else if (line ~ /^ *\] *(, *)?$/)
				st[1] = 4
			break
		default:
			generror(41, "Invalid parser state (" st[0] "," st[1] "," st[2] ")")
		}
		if (line ~ /[{\[] *$/)         {++st[0]; return}
		if (line ~ /^ *[]}] *(, *)?$/) {--st[0]; return}
	}
}

# Parse transaction @tx
# Set appropriate variables (see parsedecodedtxline description)
# @tx: raw transaction
# Returns: @tx id
function parsetransaction(tx,    cmd, line, state, res)
{
	printdebug("parsetransaction")
	res[0] = rawtx_id[tx]
	if (res[0] != "") return res[0]
	cmd = coind_exec " decoderawtransaction " tx
	printcmd("parsetransaction", cmd)
	state[0] = 0
	while ((cmd | getline line) > 0)
		parsedecodedtxline(res, state, line)
	# Don't close cmd - results are buffered
	rawtx_id[tx] = res[0]
	return res[0]
}

# Parse seqnumber/nlocktime string @str
# @hexres: hexres[0] - sequence number as reverse byte ordered hex,
#          hexres[1] - nlocktime as reverse byte ordered hex
function parselock(hexres, str,    ltstr, cmd, declt, lt, nseq, n)
{
	printdebug("parselock")
	ltstr = gensub(/^l\s+[[:digit:]]+\s+/, "", "", str)
	gsub(/'/, "", ltstr)
	cmd = date_exec " --date='" ltstr "' '+%s'"
	printcmd("parselock", cmd)
	cmd | getline declt
	close(cmd)
	if (declt !~ /^[[:digit:]]+$/)
		generror(22, "Unable to convert locktime '" ltstr "'")
	lt = hexrevert32_nocheck(declt)
	if (lt !~ /^[[:xdigit:]]{8}$/)
		generror(22, "Unable to convert locktime '" ltstr "'")
	nseq = gensub(/^l\s+([[:digit:]]+)\s.*$/, "\\1", "", str)
	n = hexrevert32_nocheck(nseq)
	if (n !~ /^[[:xdigit:]]{8}$/)
		generror(23, "Unable to convert sequence number '" nseq "'")
	hexres[0] = n
	hexres[1] = lt
}

# Parse address string
# No complex actions, just concatenation
# Returns: combined address string (components separated by SUBSEP; used by makeaddress)
function parseaddress(num, a1, a2, a3)
{
	printdebug("parseaddress " num " " a1 " " a2 " " a3)
	if (num == "") return num
	if (a1 == "") return num
	num = num SUBSEP a1
	if (a2 == "") return num
	num = num SUBSEP a2
	if (a3 == "") return num
	num = num SUBSEP a3
	return num
}

# Functions that need real address

# Set bidirectional mapping between address @addr and public key @pub
# For multisignature @addr @pub is treated as redeemScript
function setpubkey(addr, pub)
{
	if ((addr == "") || (pub == "")) return
	if (addr ~ multiaddrheadregexp)
		redeem[addr] = pub
	else if (addr ~ singleaddrheadregexp) {
		addr_pub[addr] = pub
		pub_addr[pub] = addr
	}
}

# Validate address @addr (bitcoind validateaddress call)
# Set appropriate variables: addr_isvalid[@addr], addr_ismine[@addr], addr_isscript[@addr]
# Calls setpubkey
function validateaddress(addr,    cmd, line)
{
	printdebug("validateaddress " addr)
	cmd = coind_exec " validateaddress " addr
	printcmd("validateaddress", cmd)
	while ((cmd | getline line) > 0) {
		if      (line ~ /^ *\"isvalid\" *:/)
			addr_isvalid[addr] = (line ~ /^ *\"isvalid\" *: *true/) ? "1" : ""
		else if (line ~ /^ *\"ismine\" *:/)
			addr_ismine[addr] = (line ~ /^ *\"ismine\" *: *true/) ? "1" : ""
		else if (line ~ /^ *\"isscript\" *:/)
			addr_isscript[addr] = (line ~ /^ *\"isscript\" *: *true/) ? "1" : ""
		else if (line ~ /^ *\"pubkey\" *: *\"[[:xdigit:]]+\"/)
			setpubkey(addr, gensub(/^ *\"pubkey\" *: *\"([^\"]*)\".*$/, "\\1", "", line))
	}
	# Don't close cmd - results are buffered
}

# bitcoind dumpprivkey call for address @addr
# Set addr_priv[@addr]
function dumpprivkey(addr,    cmd, line)
{
	printdebug("dumpprivkey " addr)
	if (addr_priv[addr] != "") return
	cmd = coind_exec " dumpprivkey " addr
	printcmd("dumpprivkey", cmd)
	while ((cmd | getline line) > 0)
		if (line ~ /^[[:alnum:]]+$/) {
			addr_priv[addr] = line
			return
		}
	# Don't close cmd - results are buffered
}

# Retrieve private key for address @addr
# Returns: 1 on successful extraction, 0 otherwise (including previous extraction success for same @addr)
function retrieveprivkey(addr)
{
	printdebug("retrieveprivkey " addr)
	if (addr_priv[addr] != "") return 0
	validateaddress(addr)
	if ((addr_ismine[addr] == "") || (addr_isscript[addr] != "")) return 0
	dumpprivkey(addr)
	if (addr_priv[addr] == "")
		generror(3, "Cannot retrieve private key for address " addr " (is wallet locked?)")
	return (addr_priv[addr] != "") ? 1 : 0
}

# Retrieve private keys for multisignature address (@addr) participants
# Returns: number of newly retrieved private keys
function retrievemultisigprivkey(addr,    i, all)
{
	printdebug("retrievemultisigprivkey " addr)
	all = 0
	for (i = 0; multisig[addr, i] != ""; ++i)
		all += retrieveprivkey(multisig[addr, i])
	return all
}

# Retrieve scriptPubKey for address @addr
# Set script[@addr]
function retrieveaddressscript(addr,    cmdcreate, faketx, cmddecode, res)
{
	printdebug("retrieveaddressscript " addr)
	if ((addr == "") || (script[addr] != "")) return
	cmdcreate = coind_exec " createrawtransaction '[{\
		\"txid\":\"0000000000000000000000000000000000000000000000000000000000000000\",\
		\"vout\":0\
	}]' '{\"" addr "\":1.0}'"
	printcmd("retrieveaddressscript", cmdcreate)
	cmdcreate | getline faketx
	# Don't close cmd - results are buffered
	if (faketx !~ /^[[:xdigit:]]+$/)
		generror(7, "Cannot retrieve address script for address '" addr "' (is it valid?)")
	cmddecode = coind_exec " decoderawtransaction " faketx " | " sed_exec " -n '\
	/^ *\"vout\" *: *\\[/,$ {\
		/^ *\"hex\" *:/ {\
			s/^ *\"hex\" *: *\"\\([^\"]*\\)\".*$/\\1/;\
			p\
		}\
	}'"
	printcmd("retrieveaddressscript", cmddecode)
	cmddecode | getline res
	if (res !~ /^[[:xdigit:]]+$/)
		generror(6, "Cannot retrieve address script for address '" addr "'")
	script[addr] = res
	# Don't close cmd - results are buffered
}

# Read previous corresponding transaction for input @ix
# Set appropriate variables:
#     amount[@ix], src[@ix], script[src[@ix]] -
#         amount, source address, scriptPubKey of source address for input @ix
function readtransaction(ix,    rawtx, rix, val, asrc)
{
	printdebug("readtransaction " ix)
	rawtx = getrawtransaction_nocheck(txid[ix])
	if (rawtx !~ /^[[:xdigit:]]+$/)
		generror(5, "Unable to get raw transaction " txid[ix])
	rix = parsetransaction(rawtx)
	if (rix != txid[ix])
		generror(101, "Internal error - transaction id mismatch (" rix " != " txid[ix] ")")
	val = tx_o_val[txid[ix], vout[ix]]
	if ((amount[ix] != "") && (calculate_nocheck(amount[ix] "-" val) != 0))
		generror(11, "txin " ix ": amount '" amount[ix] "' is not valid (retrieved amount is '" val "')")
	amount[ix] = val
	asrc = tx_o_addr[txid[ix], vout[ix]]
	if ((src[ix] != "") && (src[ix] != asrc))
		generror(12, "txin " ix ": source address '" src[ix] "' is not valid (retrieved address is '" asrc "')")
	src[ix] = asrc
	if (script[src[ix]] == "")
		script[src[ix]] = tx_o_script[txid[ix], vout[ix]]
}

# Line pattern actions block

{
	parsed = 0
}

# Address description
/^a\s+(-\s+[[:alnum:]]+(\s+[[:alnum:]]+)?|[123](\s+[[:alnum:]]+){1,3})\s*$/ {
	printdebug("----addr----")
	parsed = 1
	addrtxt[++aix] = parseaddress($2, $3, $4, $5)
}

# Sequence number/nlocktime
/^l\s+[[:digit:]]+\s+/ {
	printdebug("----lock----")
	parsed = 1
	parselock(hexlock, $0)
}

# Input description
/^i\s+[[:xdigit:]]{64}\s+[[:digit:]]+(\s+-(\s+(-|[[:digit:]]+(\.[[:digit:]]{1,8})?)(\s+(-\s+[[:alnum:]]+(\s+[[:alnum:]]+)?|[123](\s+[[:alnum:]]+){1,3}))?)?)?\s*$/ {
	printdebug("----txin----")
	parsed = 1
	++iix

	txid[iix] = $2
	vout[iix] = $3
	# $4 is reserved for SIGHASH
	if (($5 != "-") && ($5 != "")) amount[iix] = $5
	srctxt[iix] = parseaddress($6, $7, $8, $9)
}

# Output description
/^o\s+(-|[[:digit:]]+(\.[[:digit:]]{1,8})?)(\s+(-\s+[[:alnum:]]+(\s+[[:alnum:]]+)?|[123](\s+[[:alnum:]]+){1,3}))?\s*$/ {
	printdebug("----txout----")
	parsed = 1

	dsttxtaddr = parseaddress($3, $4, $5, $6)
	dstvalue = ($2 != "-") ? calculate_nocheck("0+" $2) : $2
	if ($2 != 0) {
		coix = (dsttxtaddr != "") ? ++oix : --ocommix
		outval[coix] = dstvalue
		dsttxt[coix] = dsttxtaddr
	}
	else
		genwarning("Empty outputs are ignored, skipped '" $0 "'")
}

# Transaction (partial signed or full (for transaction check))
/^t\s+[[:xdigit:]]+\s*$/ {
	printdebug("----partial----")
	parsed = 1

	if (partialtx)
		generror(18, "Multiple partial transactions are not acceptable")
	partialtx = $2
}

# Action (no signing, check only)
/^checkonly\s*$/ {
	printdebug("----checkonly----")
	parsed = 1
	checkonly = 1
}

# Commentary
/^\s*([#;-].*)?$/ {
	parsed = 1
}

{
	if (parsed == 0)
		generror(21, "Unrecognized string '" $0 "'")
}

# Final parameters list creating functions

# Form transaction inputs list
# Returns: JSON array of transaction inputs
function formtxinlist(    res, i)
{
	res = "'["
	for (i = 0; txid[i] != ""; ++i) {
		if (res != "'[") res = res ","
		res = res "{\"txid\":\"" txid[i] "\",\"vout\":" vout[i] ",\"scriptPubKey\":\"" script[src[i]] "\""
		if (redeem[src[i]] != "") res = res ",\"redeemScript\":\"" redeem[src[i]] "\""
		res = res "}"
	}
	res = res "]'"
	return res
}

# Form transaction outputs list
# Returns: JSON list of transaction outputs
function formtxoutlist(    res, o)
{
	res = "'{"
	for (o = 0; outval[o] != ""; ++o) {
		if (dst[o] == "") continue
		if (res != "'{") res = res ","
		res = res "\"" dst[o] "\":" outval[o]
	}
	res = res "}'"
	return res
}

# Form private keys list
# Returns: JSON array of private keys
function formprivkeylist(    res, a)
{
	res = "'["
	for (a in addr_priv) {
		if (addr_priv[a] == "") continue
		if (res != "'[") res = res ","
		res = res "\"" addr_priv[a] "\""
	}
	res = res "]'"
	return res
}

# General final functions without side effects

# Modify sequence number and nlocktime for newly created transaction @createdtx
# @createdtx: newly created raw transaction
# @txincount: number of @createdtx inputs
# @hexnseq: sequence number to replace to (as reverse byte ordered hex)
# @hexlocktime: nlocktime to replace to (as reverse byte ordered hex)
# Returns: raw modified transaction
function modifylockfortransaction(createdtx, txincount, hexnseq, hexlocktime,    res, parts, n, i)
{
# Should parse raw transaction here instead of regexp replacing
	n = split(createdtx, parts, "00ffffffff")
	if (n > txincount + 1)
		generror(32, "Sequence number replacing failed (some false positives are possible): " createdtx)
	if (n < txincount + 1)
		generror(33, "Sequence number replacing failed (already replaced?): " createdtx)
	res = parts[1]
	for (i = 2; i <= n; ++i)
		res = res "00" hexnseq parts[i]
	gsub(/00000000$/, hexlocktime, res)
	return res
}

# Final action functions

# Check partial transaction (where id == @id) conformity with accumulated inputs/outputs
# (txid[], vout[], outval[], dst[])
function checkpartialtransaction(id,    ires, i, imax, ores, o, ox, oxmax)
{
	printdebug("checkpartialtransaction")
	ires = 0
	for (i = 0; tx_i_id[id, i] != ""; ++i) {
		if (tx_i_id[id, i] != txid[i]) {
			printdebug("(txid) " i " " tx_i_id[id, i] " != " txid[i], "", 3)
			ires = 1
		}
		if (tx_i_vout[id, i] != vout[i]) {
			printdebug("(vout) " i " " tx_i_vout[id, i] " != " vout[i], "", 3)
			ires = 1
		}
	}
	imax = i - 1

	ores = 0
	oxmax = -1
	for (o = 0; tx_o_n[id, o] != ""; ++o) {
		ox = tx_o_n[id, o]
		if (oxmax < ox) oxmax = ox
		# tx_o_val[id, o] is compared with outval[ox] (there is no mistyping in index)
		if (calculate_nocheck(tx_o_val[id, o] "-" outval[ox]) != 0) {
			printdebug("(value) " ox " " tx_o_val[id, o] " != " outval[ox], "", 3)
			ores = 1
		}
		if (tx_o_addr[id, o] != dst[ox]) {
			printdebug("(dst) " ox " " tx_o_addr[id, o] " != " dst[ox], "", 3)
			ores = 1
		}
	}

	if (ires != 0)
		generror(36, "Partial transaction doesn't match provided inputs")
	if (imax != iix)
		generror(37, "Partial transaction doesn't match provided inputs (additional inputs detected)")
	if (ores != 0)
		generror(38, "Partial transaction doesn't match provided outputs")
	if (oxmax != oix)
		generror(39, "Partial transaction doesn't match provided outputs (additional outputs detected)")
}

# Create multisignature address @num-of-{@a1, @a2, @a3}
# (or @num-of-{@a1, @a2} if @a3 is not set, or @num-of-{@a1} if @a2 and @a3 are not set)
# Attention: address orded is significant (e.g., 2-of-{a, b} != 2-of-{b, a})
# Returns: multisignature address
function createmultisigaddress(num, a1, a2, a3,    addr, aarray, parray, publist, cmd, line, aredeem, i)
{
	printdebug("createmultisigaddress " num " " a1 " " a2 " " a3)
	if ((a1 == "") && (a2 == "") && (a3 == "")) return ""
	i = -1
	if (a1 != "") aarray[++i] = a1
	if (a2 != "") aarray[++i] = a2
	if (a3 != "") aarray[++i] = a3
	for (i = 0; aarray[i] != ""; ++i) {
		parray[i] = (addr_pub[aarray[i]] != "") ? addr_pub[aarray[i]] : aarray[i]
		aarray[i] = (pub_addr[parray[i]] != "") ? pub_addr[parray[i]] : parray[i]
	}
	addr = multi_addr[num, aarray[0], aarray[1], aarray[2]]
	if (addr != "") return addr
	publist = "'["
	for (i = 0; aarray[i] != ""; ++i) {
		if (publist != "'[") publist = publist ","
		publist = publist "\"" parray[i] "\""
	}
	publist = publist "]'"
	cmd = coind_exec " createmultisig " num " " publist
	printcmd("createmultisigaddress", cmd)
	while ((cmd | getline line) > 0) {
		if      (line ~ /^ *\"address\" *: *\"[[:alnum:]]+\"/)
			addr = gensub(/^ *\"address\" *: *\"([^\"]*)\".*$/, "\\1", "", line)
		else if (line ~ /^ *\"redeemScript\" *: *\"[[:xdigit:]]+\"/)
			aredeem = gensub(/^ *\"redeemScript\" *: *\"([^\"]*)\".*$/, "\\1", "", line)
	}
	# Don't close cmd - results are buffered
	if ((addr !~ multiaddrheadregexp) || (aredeem !~ /^[[:xdigit:]]+$/))
		generror(2, "Invalid source multisig address " num "-of-" publist)
	printdebug("= createmultisigaddress " addr " " aredeem)
	multi_addr[num, aarray[0], aarray[1], aarray[2]] = addr
	redeem[addr] = aredeem
	for (i in aarray) multisig[addr, i] = aarray[i]
	return addr
}

# Convert text representation @atxt of address to real address (see parseaddress)
# @acceptmulti: permit createmultisigaddress call (for multisig addresses) if nonzero
# Returns: real address or empty string
function makeaddress(atxt, acceptmulti,    parts, n)
{
	printdebug("makeaddress " atxt)
	if (atxt == "") return ""
	n = split(atxt, parts, SUBSEP)
	if (n < 2)
		generror(1, "Invalid address '" atxt "' (you should use '- <address>' format)")
	if (parts[1] != "-") {
		if ((! acceptmulti) || (acceptmulti == 0)) return ""
		return createmultisigaddress(parts[1], parts[2], parts[3], parts[4])
	}
	validateaddress(parts[2])
	if (addr_isvalid[parts[2]] == "")
		generror(1, "Invalid address '" parts[2] "'")
	if (parts[3] != "") {
		if ((addr_pub[parts[2]] != "") && (addr_pub[parts[2]] != parts[3]))
			generror(4, "Invalid pubkey for address " parts[2])
		setpubkey(parts[2], parts[3])
	}
	return parts[2]
}

# Convert accumulated text representations (addrtxt[], srctxt[], dsttxt[]) of addresses to real addresses
# (see parseaddress)
# Set appropriate variables (src[i], dst[o])
function makeaddresses(    stage, a, i, o)
{
	for (stage = 0; stage <= 1; ++stage) {
		for (a in addrtxt)
			if (addrtxt[a] != "")
				makeaddress(addrtxt[a], stage)
		for (i in srctxt)
			if ((src[i] == "") && (srctxt[i] != ""))
				src[i] = makeaddress(srctxt[i], stage)
		for (o in dsttxt)
			if ((dst[o] == "") && (dsttxt[o] != ""))
				dst[o] = makeaddress(dsttxt[o], stage)
	}
}

# Process delayed actions (convert addresses,
# read previous corresponding transactions for inputs without full information,
# retrieve scriptPubKey of source address for each input,
# retrieve private keys for each input)
function processdelayedactions(    i)
{
	makeaddresses()
	for (i in txid) {
		if ((amount[i] == "") || (src[i] == ""))
			readtransaction(i)
		retrieveaddressscript(src[i])
		if (src[i] ~ singleaddrheadregexp)
			retrieveprivkey(src[i])
		else if (src[i] ~ multiaddrheadregexp) {
			if ((multisig[src[i], 0] != "") && (redeem[src[i]] != ""))
				retrievemultisigprivkey(src[i])
			else if (! checkonly)
				genwarning("Input " i " signing is skipped, you should provide '" src[i] "' participants")
		}
	}
}

# Write change to output(s) where outval[o] == "-"
# Divide change equally if there are several such outputs
# Set outval[o]
function writechange(    volume, i, partvol, ochange, o, ochcount)
{
	volume = "0"
	for (i = 0; amount[i] != ""; ++i)
		volume = calculate_nocheck(volume "+" amount[i])

	ochcount = 0
	for (o = -1; outval[o] != ""; --o);
	for (++o; outval[o] != ""; ++o) {
		if (outval[o] == "-")
			ochange[ochcount++] = o
		else {
			volume = calculate_nocheck(volume "-" outval[o])
			if (volume < 0)
				generror(19, "Volume of outputs exseeds volume of inputs")
				#! May be correct (SIGHASH_ANYONECANPAY)
		}
	}
	if ((ochcount == 0) && (volume > 0))
		generror(20, "Volume of outputs isn't equal to volume of inputs (difference == " volume "). You should explicitly set change address and commission")
	if ((ochcount == 0) || (volume <= 0)) return

	partvol = calculate_nocheck(volume "/" ochcount)
	volume = calculate_nocheck(volume "-((" ochcount "-1)*" partvol ")")
	for (o = 0; ochange[o] != ""; ++o)
		outval[ochange[o]] = partvol
	outval[ochange[o - 1]] = volume
}

# bitcoind signrawtransaction call (without return value check)
# @tx: raw transaction to sign
# @txinlist: JSON array of transaction inputs
# @privkeylist: JSON array of private keys to sign with
# Returns: (fully/partially) signed raw transaction concatenated with result (true/false) via space
#
# Attention: there is a possibility of private key stealing via commandline (e.g., by "ps aux" or such command).
# Workaround: use distinct virtual guest system only for wallet and this script
function signtransaction_nocheck(tx, txinlist, privkeylist,    cmd, res)
{
	cmd = coind_exec " signrawtransaction " tx " " txinlist " " privkeylist " | " sed_exec " -n '\
	/^ *\"hex\" *:/ {\
		s/^ *\"hex\" *: *\"\\([^\"]*\\)\".*$/\\1/;\
		h\
	};\
	/^ *\"complete\" *:/ {\
		s/^ *\"complete\" *: *\\([^ ]*\\).*$/\\1/;\
		H\
	};\
	$ {\
		x;\
		s/\\n/ /g;\
		p\
	}\
	'"
	printcmd("signtransaction_nocheck", cmd)
	cmd | getline res
	close(cmd)
	return res
}

# Create raw transaction and call modifylockfortransaction
# @txinlist: JSON array of transaction inputs
# @txoutlist: JSON list of transaction outputs
# @txincount: number of transaction inputs
# @hexnseq, @hexlocktime: see modifylockfortransaction
# Returns: modified raw transaction
function maketransaction(txinlist, txoutlist, txincount, hexnseq, hexlocktime,    cmd, createdtx, modifiedtx)
{
	cmd = coind_exec " createrawtransaction " txinlist " " txoutlist
	printcmd("maketransaction", cmd)
	cmd | getline createdtx
	close(cmd)
	if (createdtx !~ /^[[:xdigit:]]+$/)
		generror(13, "Unable to create new transaction " txinlist " " txoutlist)
	modifiedtx = modifylockfortransaction(createdtx, txincount, hexnseq, hexlocktime)
	if (modifiedtx !~ /^[[:xdigit:]]+$/)
		generror(14, "Unable to modify new transaction " createdtx)
	return modifiedtx
}

# Main action block

END {
	printdebug("----final----")
	if (errcode != 0) exit errcode
	if (iix < 0)
		generror(16, "Transactions without inputs are unacceptable")

	processdelayedactions()
	writechange()

	if (partialtx != "")
		checkpartialtransaction(parsetransaction(partialtx))

	txinlist = formtxinlist()
	printfinal(txinlist)
	txoutlist = formtxoutlist()
	printfinal(txoutlist)
	privkeylist = checkonly ? "'[]'" : formprivkeylist()
	printfinal(privkeylist, "true")
	printfinal(hexlock[0] ":" hexlock[1])

	if (partialtx == "")
		partialtx = maketransaction(txinlist, txoutlist, iix + 1, hexlock[0], hexlock[1])
	signedtx = signtransaction_nocheck(partialtx, txinlist, privkeylist)
	if (signedtx !~ /^[[:xdigit:]]+ (true|false)$/)
		generror(9, "Unable to sign new transaction " partialtx)
	split(signedtx, signedtxparts)
	print (signedtxparts[2] == "true") ? parsetransaction(signedtxparts[1]) : signedtxparts[2]
	print signedtxparts[1]
}


e-params-update.awk handles updating the URI of the versioned dataset

# e-params-increment.awk
# params: eID

{
   # TODO: change the URI of the dataset, too.
   if(/^@prefix : /){
      # change
      # @prefix :           <http://logd.tw.rpi.edu/source/nitrd-gov/dataset/fed_RD_IT/version/2011-Jan-27/conversion/enhancement/1> .
      # to
      # @prefix :           <http://logd.tw.rpi.edu/source/nitrd-gov/dataset/fed_RD_IT/version/2011-Jan-27/conversion/enhancement/2> .
      sub(/enhancement\/[^\/]*$/,"enhancement/"eID"> .");
      print $0
   }else if( $0~"conversion:enhancement_identifier" ) {
      printf("      conversion:enhancement_identifier \"%s\";\n",eID)}
   else {
      print
   }
}

# e-params-increment.awk
# params: eID

{
   # TODO: change the URI of the dataset, too.
   if( $0~"conversion:enhancement_identifier" ) {
      printf("      conversion:enhancement_identifier \"%s\";\n",eID)}
   else {
      print
   }
}


missing newline at the end of usage files

#!/usr/bin/awk -f

BEGIN {
    print("#include <stdio.h>");
    print("#include <libxml/xmlversion.h>");
}

length(command_name) == 0 {
    command_name = FILENAME;
    sub(/\.txt$/, "", command_name);
    sub(/^([^\/]+\/)*/, "", command_name);
    gsub(/-/, "_", command_name);
    printf("static const char %s[] = {\n", command_name);
    progs = 0;
}

/PROG/ {
    for (i = 1; i <= NF; i++) {
        if ($i == "PROG") {
            progs++;
            $i = "%s";
        }
    }
}

/^#/
!/^#/ {
    gsub(/./, "'&',");
    gsub(/'\\'/, "'\\\\'");     # '\' --> '\\'
    gsub(/'''/, "'\\''");       # ''' --> '\''
    print($0 "'\\n',");
}

END {
    print("0 };");
    printf("void fprint_%s(FILE* out, const char* argv0) {\n", command_name);
    printf("  fprintf(out, %s", command_name);
    for (i = 1; i <= progs; i++)
        printf(", argv0");
    print(");\n}");
}

#!/usr/bin/awk -f

BEGIN {
    print("#include <stdio.h>");
    print("#include <libxml/xmlversion.h>");
}

length(command_name) == 0 {
    command_name = FILENAME;
    sub(/\.txt$/, "", command_name);
    sub(/^([^\/]+\/)*/, "", command_name);
    gsub(/-/, "_", command_name);
    printf("static const char %s[] = {\n", command_name);
    progs = 0;
}

/PROG/ {
    for (i = 1; i <= NF; i++) {
        if ($i == "PROG") {
            progs++;
            $i = "%s";
        }
    }
}

/^#/
!/^#/ {
    gsub(/./, "'&',");
    gsub(/'\\'/, "'\\\\'");     # '\' --> '\\'
    gsub(/'''/, "'\\''");       # ''' --> '\''
    print($0 "'\\n',");
}

END {
    print("0 };");
    printf("void fprint_%s(FILE* out, const char* argv0) {\n", command_name);
    printf("  fprintf(out, %s", command_name);
    for (i = 1; i <= progs; i++)
        printf(", argv0");
    printf(");\n}");
}


comment on which file goes first & second

#! /usr/bin/awk -f
#deltadot.awk <encumbant.gv> <candidate.gv>
# Use to illuminate the commonality and differences between dot files created
# by 'ntriples2dot.awk' but might work for other dot files
#
#
# expects two graphviz dot digraph files by convention "old"  then "new"
# for color to mean;  orange lost in "new" and blue gained in "new"
# with simple node names ('C' variable naming conventions)
# edge's unadorned predicate label
# concludes with a space then count in parens
#
# For example:
# NODE1 -> NODE2 [label="curie:term (42)"];
#
# filter for edges in each file associate the count with the edge
# if an edge is common between the files
# then change its "count" to the difference. (second minus first)
# otherwise leave count as is

# associate the count with an edge
function parse(str, arr){
	split(str, a, "(")
	arr[a[1]] = substr(a[2], 1, index(a[2], ")")-1)
}

# strip metadata from filename
function de_path_ext(pth){
	split(pth, a, "/")
	return substr(a[length(a)], 1, index(a[length(a)], ".")-1)
}

# collect the edges
NR==FNR && / -> / {
	label1 = FILENAME;
	parse($0, edge1)
}
NR!=FNR && / -> / {
	label2 = FILENAME;
	parse($0, edge2)
}

END{
	for(e in edge1){
		if(!(e in edge2)) edges[e "(" edge1[e] ")\", color=\"orange\"];"];
		else {
			# TODO flag losses?
			diff =  edge2[e] - edge1[e];
			#if(diff < 0) diff = "<b>" diff "</b>";
			edges[e "(" diff ")\", color=\"black\"];"]
			delete edge2[e]
		}
	}
	for(e in edge2) edges[e "(" edge2[e] ")\", color=\"blue\"];"];

	print "digraph {"
	print "rankdir=LR;"
	print "charset=\"utf-8\";"
	print "LITERAL [shape=record];"
	print "labelloc=\"t\";"
	print "label=\"" de_path_ext(label1) " <=> " de_path_ext(label2) "\""

	n = asorti(edges,output)
	for(i=1;i<=n;i++ ){print output[i]}
	print "\n}\n"
}

#! /usr/bin/awk -f
#deltadot.awk
# Use to illuminate the commonality and differences between dot files created
# by 'ntriples2dot.awk' but might work for other dot files

# expects two graphviz dot digraph files
# with simple node names ('C' variable naming conventions)
# edge's unadorned predicate label
# concludes with a space then count in parens
#
# For example:
# NODE1 -> NODE2 [label="curie:term (42)"];
#
# filter for edges in each file associate the count with the edge
# if an edge is common between the files
# then change its "count" to the difference. (second minus first)
# otherwise leave count as is

# associate the count with an edge
function parse(str, arr){
	split(str, a, "(")
	arr[a[1]] = substr(a[2], 1, index(a[2], ")")-1)
}

# strip metadata from filename
function de_path_ext(pth){
	split(pth, a, "/")
	return substr(a[length(a)], 1, index(a[length(a)], ".")-1)
}

# collect the edges
NR==FNR && / -> / {
	label1 = FILENAME;
	parse($0, edge1)
}
NR!=FNR && / -> / {
	label2 = FILENAME;
	parse($0, edge2)
}

END{
	for(e in edge1){
		if(!(e in edge2)) edges[e "(" edge1[e] ")\", color=\"orange\"];"];
		else {
			# TODO flag losses?
			diff =  edge2[e] - edge1[e];
			#if(diff < 0) diff = "<b>" diff "</b>";
			edges[e "(" diff ")\", color=\"black\"];"]
			delete edge2[e]
		}
	}
	for(e in edge2) edges[e "(" edge2[e] ")\", color=\"blue\"];"];

	print "digraph {"
	print "rankdir=LR;"
	print "charset=\"utf-8\";"
	print "LITERAL [shape=record];"
	print "labelloc=\"t\";"
	print "label=\"" de_path_ext(label1) " <=> " de_path_ext(label2) "\""

	n = asorti(edges,output)
	for(i=1;i<=n;i++ ){print output[i]}
	print "\n}\n"
}


Add statuses.

Add statuses.

git-svn-id: 293778eaa97c8c94097d610b1bd5133a8f478f36@35180 4f837ed2-42f5-46e7-a7a5-fa17313484d4

#!/usr/bin/awk -f
# cat *.tdb | tdbout -t publisher,plugin,publisher:info[contract],year,publisher:info[tester],status,publisher:info[back]

BEGIN {
  FS="\t"
  pn = 0
}

{
  # add a loop to add line only if ending year is gt or eq to contract year
  current_year = 2014
  end_year = 0
  incontract = 0
  test_year = ""
  if ($7 == "") {
    test_year = $3
  } else {
    test_year = $7
  }
  if (length($4) > 3) {
    end_year = substr($4,length($4)-3,4)
  }
  #printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", $1,$2,$3,$4,$7,end_year,test_year
  if (end_year >= test_year) {
    if ((end_year < current_year) || ((end_year >= current_year) && (incl_cur == 1))) {
      incontract = 1
    }
  }

  if (incontract == 1) {
      nn = split($2,na,/\./)
      lp2 = na[nn]
    if (!(($1,lp2,$4) in b)) {
      p[pn] = $1
      n[pn] = lp2
  #    n[pn] = $2
      t[pn] = $3
      k[pn] = $7
      d[pn] = $4
      r[pn] = $5
      pn++
    }
    b[$1,lp2,$4]++
    c[$1,lp2,$4,$6]++
    x[$6]++
    tt++
  }
}

END {
  s[0] = "expected"
  s[1] = "exists"
  s[2] = "manifest"
  s[3] = "wanted"
  s[4] = "testing"
  s[5] = "notReady"
  s[6] = "ready"
  s[7] = "crawling"
  s[8] = "frozen"
  s[9] = "ingNotReady"
  s[10] = "finished"
  s[11] = "down"
  s[12] = "superseded"
  s[13] = "zapped"
  s[14] = "doNotProcess"
  sn = 15

  sc[0] = "expe"
  sc[1] = "exis"
  sc[2] = "mani"
  sc[3] = "want"
  sc[4] = "test"
  sc[5] = "notR"
  sc[6] = "read"
  sc[7] = "craw"
  sc[8] = "froz"
  sc[9] = "ingN"
  sc[10] = "fini"
  sc[11] = "down"
  sc[12] = "supe"
  sc[13] = "zapp"
  sc[14] = "doNP"
  scn = 15

  #print out header
  printf "Publisher\tPlugin\tContr\tBack\tYear\tT\tTotal"
  for (j = 0 ; j < scn ; j++) {
    if (x[s[j]] > 0) {
    printf "\t%s", sc[j]
    }
  }
  printf "\n"

  #print out publisher, plugin, contract year, back, year, tester, total aus
  for (i = 0 ; i < pn ; i++) {
    printf "%s\t%s\t%s\t%s\t%s\t%s\t%d", p[i], n[i], t[i], k[i], d[i], r[i], b[p[i],n[i],d[i]]
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0){
      if (c[p[i],n[i],d[i],s[j]] == 0) {
      printf "\t.."
    } else {
        printf "\t%d", c[p[i],n[i],d[i],s[j]]
      }
    }
    }
    printf "\n"
  }
    #print out bottom line sums
    printf "Publisher\tPlugin\tContr\tBack\tYear\tT\t%d", tt
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0) {
        printf "\t%d", x[s[j]]
      }
    }

  printf "\n"
}

#!/usr/bin/awk -f
# cat *.tdb | tdbout -t publisher,plugin,publisher:info[contract],year,publisher:info[tester],status,publisher:info[back]

BEGIN {
  FS="\t"
  pn = 0
}

{
  # add a loop to add line only if ending year is gt or eq to contract year
  current_year = 2014
  end_year = 0
  incontract = 0
  test_year = ""
  if ($7 == "") {
    test_year = $3
  } else {
    test_year = $7
  }
  if (length($4) > 3) {
    end_year = substr($4,length($4)-3,4)
  }
  #printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", $1,$2,$3,$4,$7,end_year,test_year
  if (end_year >= test_year) {
    if ((end_year < current_year) || ((end_year >= current_year) && (incl_cur == 1))) {
      incontract = 1
    }
  }

  if (incontract == 1) {
      nn = split($2,na,/\./)
      lp2 = na[nn]
    if (!(($1,lp2,$4) in b)) {
      p[pn] = $1
      n[pn] = lp2
  #    n[pn] = $2
      t[pn] = $3
      k[pn] = $7
      d[pn] = $4
      r[pn] = $5
      pn++
    }
    b[$1,lp2,$4]++
    c[$1,lp2,$4,$6]++
    x[$6]++
    tt++
  }
}

END {
  s[0] = "expected"
  s[1] = "exists"
  s[2] = "manifest"
  s[3] = "wanted"
  s[4] = "crawling"
  s[5] = "testing"
  s[6] = "notReady"
  s[7] = "released"
  s[8] = "down"
  s[9] = "superseded"
  s[10] = "zapped"
  s[11] = "doNotProcess"
  sn = 12

  sc[0] = "expe"
  sc[1] = "exis"
  sc[2] = "mani"
  sc[3] = "want"
  sc[4] = "craw"
  sc[5] = "test"
  sc[6] = "notR"
  sc[7] = "rele"
  sc[8] = "down"
  sc[9] = "supe"
  sc[10] = "zapp"
  sc[11] = "doNP"
  scn = 12

  #print out header
  printf "Publisher\tPlugin\tContr\tBack\tYear\tT\tTotal"
  for (j = 0 ; j < scn ; j++) {
    if (x[s[j]] > 0) {
    printf "\t%s", sc[j]
    }
  }
  printf "\n"

  #print out publisher, plugin, contract year, back, year, tester, total aus
  for (i = 0 ; i < pn ; i++) {
    printf "%s\t%s\t%s\t%s\t%s\t%s\t%d", p[i], n[i], t[i], k[i], d[i], r[i], b[p[i],n[i],d[i]]
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0){
      if (c[p[i],n[i],d[i],s[j]] == 0) {
      printf "\t.."
    } else {
        printf "\t%d", c[p[i],n[i],d[i],s[j]]
      }
    }
    }
    printf "\n"
  }
    #print out bottom line sums
    printf "Publisher\tPlugin\tContr\tBack\tYear\tT\t%d", tt
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0) {
        printf "\t%d", x[s[j]]
      }
    }

  printf "\n"
}


Correcting if statement

Correcting if statement

git-svn-id: 293778eaa97c8c94097d610b1bd5133a8f478f36@24004 4f837ed2-42f5-46e7-a7a5-fa17313484d4

#!/usr/bin/awk -f
# cat *.tdb | tdbout -t publisher,plugin,publisher:info[contract],year,publisher:info[tester],status,publisher:info[back]

BEGIN {
  FS="\t"
  pn = 0
}

{
  # add a loop to add line only if ending year is gt or eq to contract year
  end_year = 0
  incontract = 0
  if ($7 == "") {
    test_year = $3
  } else {
    test_year = $7
  }
  if (length($4) > 3) {
    end_year = substr($4,length($4)-3,4)
  }
  if (end_year >= test_year) {
    incontract = 1
  }

  #if ((substr($4,1,4) >= $3) || ((length($4) == 9) && (substr($4,6,4) >= $3))) {
  if (incontract == 1) {
      nn = split($2,na,/\./)
      lp2 = na[nn]
    if (!(($1,lp2,$4) in b)) {
      p[pn] = $1
      n[pn] = lp2
  #    n[pn] = $2
      t[pn] = $3
      k[pn] = $7
      d[pn] = $4
      r[pn] = $5
      pn++
    }
    b[$1,lp2,$4]++
    c[$1,lp2,$4,$6]++
    x[$6]++
    tt++
  }
}

END {
  s[0] = "expected"
  s[1] = "exists"
  s[2] = "manifest"
  s[3] = "wanted"
  s[4] = "crawling"
  s[5] = "testing"
  s[6] = "notReady"
  s[7] = "released"
  s[8] = "down"
  s[9] = "superseded"
  s[10] = "zapped"
  sn = 11

  sc[0] = "expe"
  sc[1] = "exis"
  sc[2] = "mani"
  sc[3] = "want"
  sc[4] = "craw"
  sc[5] = "test"
  sc[6] = "notR"
  sc[7] = "rele"
  sc[8] = "down"
  sc[9] = "supe"
  sc[10] = "zapp"
  scn = 11

  #print out header
  printf "Publisher\tPlugin\tContr\tBack\tYear\tT\tTotal"
  for (j = 0 ; j < scn ; j++) {
    if (x[s[j]] > 0) {
    printf "\t%s", sc[j]
    }
  }
  printf "\n"

  #print out publisher, plugin, contract year, back, year, tester, total aus
  for (i = 0 ; i < pn ; i++) {
    printf "%s\t%s\t%s\t%s\t%s\t%s\t%d", p[i], n[i], t[i], k[i], d[i], r[i], b[p[i],n[i],d[i]]
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0){
      if (c[p[i],n[i],d[i],s[j]] == 0) {
      printf "\t.."
    } else {
        printf "\t%d", c[p[i],n[i],d[i],s[j]]
      }
    }
    }
    printf "\n"
  }
    #print out bottom line sums
    printf "Publisher\tPlugin\tContr\tBack\tYear\tT\t%d", tt
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0) {
        printf "\t%d", x[s[j]]
      }
    }

  printf "\n"
}

#!/usr/bin/awk -f
# cat *.tdb | tdbout -t publisher,plugin,publisher:info[contract],year,publisher:info[tester],status,publisher:info[back]

BEGIN {
  FS="\t"
  pn = 0
}

{
  # add a loop to add line only if ending year is gt or eq to contract year
  end_year = 0
  incontract = 0
  if ($7 != "") {
    test_year = $7
  } else {
    test_year = $3
  }
  if (length($4) > 3) {
    end_year = substr($4,length($4)-3,4)
  }
  if (end_year >= test_year) {
    incontract = 1
  }

  #if ((substr($4,1,4) >= $3) || ((length($4) == 9) && (substr($4,6,4) >= $3))) {
  if (incontract == 1) {
      nn = split($2,na,/\./)
      lp2 = na[nn]
    if (!(($1,lp2,$4) in b)) {
      p[pn] = $1
      n[pn] = lp2
  #    n[pn] = $2
      t[pn] = $3
      k[pn] = $7
      d[pn] = $4
      r[pn] = $5
      pn++
    }
    b[$1,lp2,$4]++
    c[$1,lp2,$4,$6]++
    x[$6]++
    tt++
  }
}

END {
  s[0] = "expected"
  s[1] = "exists"
  s[2] = "manifest"
  s[3] = "wanted"
  s[4] = "crawling"
  s[5] = "testing"
  s[6] = "notReady"
  s[7] = "released"
  s[8] = "down"
  s[9] = "superseded"
  s[10] = "zapped"
  sn = 11

  sc[0] = "expe"
  sc[1] = "exis"
  sc[2] = "mani"
  sc[3] = "want"
  sc[4] = "craw"
  sc[5] = "test"
  sc[6] = "notR"
  sc[7] = "rele"
  sc[8] = "down"
  sc[9] = "supe"
  sc[10] = "zapp"
  scn = 11

  #print out header
  printf "Publisher\tPlugin\tContr\tBack\tYear\tT\tTotal"
  for (j = 0 ; j < scn ; j++) {
    if (x[s[j]] > 0) {
    printf "\t%s", sc[j]
    }
  }
  printf "\n"

  #print out publisher, plugin, contract year, back, year, tester, total aus
  for (i = 0 ; i < pn ; i++) {
    printf "%s\t%s\t%s\t%s\t%s\t%s\t%d", p[i], n[i], t[i], k[i], d[i], r[i], b[p[i],n[i],d[i]]
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0){
      if (c[p[i],n[i],d[i],s[j]] == 0) {
      printf "\t.."
    } else {
        printf "\t%d", c[p[i],n[i],d[i],s[j]]
      }
    }
    }
    printf "\n"
  }
    #print out bottom line sums
    printf "Publisher\tPlugin\tContr\tBack\tYear\tT\t%d", tt
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0) {
        printf "\t%d", x[s[j]]
      }
    }

  printf "\n"
}


change compare and title heads

change compare and title heads

git-svn-id: 293778eaa97c8c94097d610b1bd5133a8f478f36@22152 4f837ed2-42f5-46e7-a7a5-fa17313484d4

#!/usr/bin/awk -f
# input: publisher\tplugin\tcontract_year\tyear\ttester\tstatus

BEGIN {
  FS="\t"
  pn = 0
}

{
  # add a loop to add line only if ending year is gt or eq to contract year
  end_year = 0
  incontract = 0
  if (length($4) > 3) {
    end_year = substr($4,length($4)-3,4)
  }
  if (end_year >= $3) {
    incontract = 1
  }

  #if ((substr($4,1,4) >= $3) || ((length($4) == 9) && (substr($4,6,4) >= $3))) {
  if (incontract == 1) {
      nn = split($2,na,/\./)
      lp2 = na[nn]
    if (!(($1,lp2,$3,$4) in b)) {
      p[pn] = $1
      n[pn] = lp2
  #    n[pn] = $2
      t[pn] = $3
      d[pn] = $4
      r[pn] = $5
      pn++
    }
    b[$1,lp2,$3,$4]++
    c[$1,lp2,$3,$4,$6]++
    x[$6]++
    tt++
  }
}

END {
  s[0] = "expected"
  s[1] = "exists"
  s[2] = "manifest"
  s[3] = "wanted"
  s[4] = "crawling"
  s[5] = "testing"
  s[6] = "notReady"
  s[7] = "released"
  s[8] = "down"
  s[9] = "superseded"
  s[10] = "zapped"
  sn = 11

  sc[0] = "X"
  sc[1] = "E"
  sc[2] = "M"
  sc[3] = "W"
  sc[4] = "C"
  sc[5] = "T"
  sc[6] = "N"
  sc[7] = "R"
  sc[8] = "D"
  sc[9] = "S"
  sc[10] = "Z"
  scn = 11

  #print out header
  printf "Publisher\tPlugin\tContr\tYear\tT\tTotal"
  for (j = 0 ; j < scn ; j++) {
    if (x[s[j]] > 0) {
    printf "\t%s", sc[j]
    }
  }
  printf "\n"

  #print out publisher, plugin, contract year, year, total aus
  for (i = 0 ; i < pn ; i++) {
    printf "%s\t%s\t%s\t%s\t%s\t%d", p[i], n[i], t[i], d[i], r[i], b[p[i],n[i],t[i],d[i]]
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0){
      if (c[p[i],n[i],t[i],d[i],s[j]] == 0) {
      printf "\t.."
    } else {
        printf "\t%d", c[p[i],n[i],t[i],d[i],s[j]]
      }
    }
    }
    printf "\n"
  }
    #print out bottom line sums
    printf "Publisher\tPlugin\tContr\tYear\tT\t%d", tt
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0) {
        printf "\t%d", x[s[j]]
      }
    }

  printf "\n"
}

#!/usr/bin/awk -f
# input: publisher\tplugin\tcontract_year\tyear\ttester\tstatus

BEGIN {
  FS="\t"
  pn = 0
}

{
  # add a loop to add line only if ending year is gt or eq to contract year
  if ((substr($4,1,4) >= $3) || ((length($4) == 9) && (substr($4,6,4) >= $3))) {
      nn = split($2,na,/\./)
      lp2 = na[nn]
    if (!(($1,lp2,$3,$4) in b)) {
      p[pn] = $1
      n[pn] = lp2
  #    n[pn] = $2
      t[pn] = $3
      d[pn] = $4
      r[pn] = $5
      pn++
    }
    b[$1,lp2,$3,$4]++
    c[$1,lp2,$3,$4,$6]++
    x[$6]++
    tt++
  }
}

END {
  s[0] = "expected"
  s[1] = "exists"
  s[2] = "manifest"
  s[3] = "wanted"
  s[4] = "crawling"
  s[5] = "testing"
  s[6] = "notReady"
  s[7] = "released"
  s[8] = "down"
  s[9] = "superseded"
  s[10] = "zapped"
  sn = 11

  sc[0] = "X"
  sc[1] = "E"
  sc[2] = "M"
  sc[3] = "W"
  sc[4] = "C"
  sc[5] = "T"
  sc[6] = "N"
  sc[7] = "R"
  sc[8] = "D"
  sc[9] = "S"
  sc[10] = "Z"
  scn = 11

  #print out header
  printf "Publisher\tPlugin\tContr\tYear\tT\tTotal"
  for (j = 0 ; j < scn ; j++) {
    if (x[s[j]] > 0) {
    printf "\t%s", sc[j]
    }
  }
  printf "\n"

  #print out publisher, plugin, contract year, year, total aus
  for (i = 0 ; i < pn ; i++) {
    printf "%s\t%s\t%s\t%s\t%s\t%d", p[i], n[i], t[i], d[i], r[i], b[p[i],n[i],t[i],d[i]]
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0){
      if (c[p[i],n[i],t[i],d[i],s[j]] == 0) {
      printf "\t.."
    } else {
        printf "\t%d", c[p[i],n[i],t[i],d[i],s[j]]
      }
    }
    }
    printf "\n"
  }
    #print out bottom line sums
    printf "Publisher\tPlugin\tContr\tYear\tT\t%d", tt
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0) {
        printf "\t%d", x[s[j]]
      }
    }

  printf "\n"
}


devtools: fix comment detection in forbidden token check

After a comment section was detected, passing to a new hunk was not seen
as ending the section and all subsequent hunks were ignored.

Fixes: 7413e7f2aeb3 ("devtools: alert on new calls to exit from libs")
Cc: stable@dpdk.org

Reported-by: Thomas Monjalon <5f50a84c1fa3bcff146405017f36aec1a10a9e38@monjalon.net>
Signed-off-by: David Marchand <f28529695cfa9e3e1eb84e7072aa626af4abb18e@redhat.com>

# SPDX-License-Identifier: BSD-3-Clause
# Copyright 2018 Arnon Warshavsky <arnon@qwilt.com>

# This awk script receives a list of expressions to monitor
# and a list of folders to search these expressions in
# - No search is done inside comments
# - Both additions and removals of the expressions are checked
#   A positive balance of additions fails the check

BEGIN {
	split(FOLDERS,deny_folders," ");
	split(EXPRESSIONS,deny_expr," ");
	in_file=0;
	in_comment=0;
	count=0;
	comment_start="/*"
	comment_end="*/"
}
# search for add/remove instances in current file
# state machine assumes the comments structure is enforced by
# checkpatches.pl
(in_file) {
	if ($0 ~ "^@@") {
		in_comment = 0
	}
	# comment start
	if (index($0,comment_start) > 0) {
		in_comment = 1
	}
	# non comment code
	if (in_comment == 0) {
		for (i in deny_expr) {
			forbidden_added = "^\\+.*" deny_expr[i];
			forbidden_removed="^-.*" deny_expr[i];
			current = expressions[deny_expr[i]]
			if ($0 ~ forbidden_added) {
				count = count + 1;
				expressions[deny_expr[i]] = current + 1
			}
			if ($0 ~ forbidden_removed) {
				count = count - 1;
				expressions[deny_expr[i]] = current - 1
			}
		}
	}
	# comment end
	if (index($0,comment_end) > 0) {
		in_comment = 0
	}
}
# switch to next file , check if the balance of add/remove
# of previous filehad new additions
($0 ~ "^\\+\\+\\+ b/") {
	in_file = 0;
	if (count > 0) {
		exit;
	}
	for (i in deny_folders) {
		re = "^\\+\\+\\+ b/" deny_folders[i];
		if ($0 ~ re) {
			in_file = 1
			last_file = $0
		}
	}
}
END {
	if (count > 0) {
		print "Warning in " substr(last_file,7) ":"
		print MESSAGE
		exit RET_ON_FAIL
	}
}

# SPDX-License-Identifier: BSD-3-Clause
# Copyright 2018 Arnon Warshavsky <arnon@qwilt.com>

# This awk script receives a list of expressions to monitor
# and a list of folders to search these expressions in
# - No search is done inside comments
# - Both additions and removals of the expressions are checked
#   A positive balance of additions fails the check

BEGIN {
	split(FOLDERS,deny_folders," ");
	split(EXPRESSIONS,deny_expr," ");
	in_file=0;
	in_comment=0;
	count=0;
	comment_start="/*"
	comment_end="*/"
}
# search for add/remove instances in current file
# state machine assumes the comments structure is enforced by
# checkpatches.pl
(in_file) {
	# comment start
	if (index($0,comment_start) > 0) {
		in_comment = 1
	}
	# non comment code
	if (in_comment == 0) {
		for (i in deny_expr) {
			forbidden_added = "^\\+.*" deny_expr[i];
			forbidden_removed="^-.*" deny_expr[i];
			current = expressions[deny_expr[i]]
			if ($0 ~ forbidden_added) {
				count = count + 1;
				expressions[deny_expr[i]] = current + 1
			}
			if ($0 ~ forbidden_removed) {
				count = count - 1;
				expressions[deny_expr[i]] = current - 1
			}
		}
	}
	# comment end
	if (index($0,comment_end) > 0) {
		in_comment = 0
	}
}
# switch to next file , check if the balance of add/remove
# of previous filehad new additions
($0 ~ "^\\+\\+\\+ b/") {
	in_file = 0;
	if (count > 0) {
		exit;
	}
	for (i in deny_folders) {
		re = "^\\+\\+\\+ b/" deny_folders[i];
		if ($0 ~ re) {
			in_file = 1
			last_file = $0
		}
	}
}
END {
	if (count > 0) {
		print "Warning in " substr(last_file,7) ":"
		print MESSAGE
		exit RET_ON_FAIL
	}
}


Capture all build/test errors and print them at the end

{
    print
    fflush(stdout)
}

/[0-9]+: error:/ {
    errors = errors $0 "\n"
}

/(TEST|BUILD) FAILED/ {
    print "\n", errors
    fflush(stdout)

    # SYS_SOFTWARE
    exit 70
}

{
    print
    fflush(stdout)
}

/(TEST|BUILD) FAILED/ {
    # SYS_SOFTWARE
    exit 70
}


geo_utils.awk		-- renamed h_style, style to h_size, size as they set the marker size not it's style

function GU_pi() {
	return 4*atan2(1, 1)
}
function GU_d2r(d) {
	return (d+0)*GU_pi()/180.0
}
function GU_r2d(r) {
	return (r+0)*180.0/GU_pi()
}
function GU_is_righthanded(count, first, x, y,   area, p, x1, y1, x2, y2) {
	area = 0
	for(p = 0; p < p_count[poly]; p++){
		x1 = x[p_first[poly] + p]
		y1 = y[p_first[poly] + p]
		x2 = x[p_first[poly] + p + 1]
		y2 = y[p_first[poly] + p + 1]
		area += GU_d2r(x2 - x1) * (2.0 + sin(GU_d2r(y1)) + sin(GU_d2r(y2)))
	}
	return area >= 0
}
function GU_pr_header(title, n_points) {

	printf("{\n")
	printf("\"geojson\": {\n")
	printf("\"type\": \"FeatureCollection\",\n")
	printf("\"metadata\": {\n")
	printf("  \"generated\": \"%s\",\n", strftime("%Y%m%dT%H%M%S%Z"))
	printf("  \"title\": \"%s\",\n", title)
	printf("  \"count\": %d\n", n_points)
	printf("},\n")
	printf("\"features\": [\n")
}
function GU_pr_trailer() {
	printf("]\n")
	printf("}\n")
	printf("}\n")
}
function GU_find_pgroups(start, count, longs, lats, pg_starts, pg_counts,   n_pgroups, l_geo, geo, i) {

	n_pgroups = 1
	pg_starts[n_pgroups] = start
	pg_counts[n_pgroups] = 1
	l_geo[1] = longs[start]
	l_geo[2] = lats[start]
	for(i = 1; i < count; i++){
		geo[1] = longs[start + i]
		geo[2] = longs[start + i]
		if(GU_geo_equal(geo, l_geo)){
			pg_counts[n_pgroups]++
		}else{
			n_pgroups++
			pg_starts[n_pgroups] = start + i
			pg_counts[n_pgroups] = 1
		}
		l_geo[1] = geo[1]
		l_geo[2] = geo[2]
	}

	return n_pgroups
}
function GU_geo_equal(g1, g2) {
	return g1[1] == g2[1] && g1[2] == g2[2]
}
function GU_geo_isnull(g) {
	return g[1] == "" || g[2] == ""
}
function GU_geo_adjust(long, lat, n, long_adj, lat_adj,   PI, RAD, i, a) {

	PI = 4.0 * atan2(1, 1)
	RAD = 0.0001

	long_adj[1] = 0
	lat_adj[1] = 0
	if(n > 1){
		a = 2.0*PI/(n-1)
		for(i = 2; i <= n; i++){
			long_adj[i] = RAD * sin(a * (i-2))
			lat_adj[i] = RAD * cos(a * (i-2))
		}
	}
}
function GU_mk_point(file, color, size, long, lat, title, last,   h_color, h_size) {
 	h_color = color != "."
	h_size = size != "."
 	printf("{\n")									> file
 	printf("  \"type\": \"Feature\",\n")						> file
 	printf("  \"geometry\": {")							> file
 	printf("\"type\": \"Point\", ")							> file
 	printf("\"coordinates\": [%.5f, %.5f]", long, lat)				> file
 	printf("},\n")									> file
 	printf("  \"properties\": {\n")							> file
 	printf("    \"title\": \"%s\",\n", GU_str_escape(title))			> file
	if(h_color)
		printf("    \"marker-color\": \"%s\"%s\n", color, h_size ? "," : "")	> file
	if(h_size)
		printf("    %s\n", size)						> file
	printf("  }\n")									> file
	printf("}%s\n", !last ? "," : "")						> file
}
function GU_mk_line(file, lng_1, lat_1, lng_2, lat_2, last) {
	printf("{\n")																> file
	printf("  \"type\": \"Feature\",\n")													> file
	printf("  \"geometry\": {\"type\": \"LineString\", \"coordinates\": [[%.5f, %.5f], [%.5f, %.5f]]}\n", lng_1, lat_1, lng_2, lat_2)	> file
	printf("}%s\n", !last ? "," : "")													> file
}
function GU_gc_dist(lng_1, lat_1, lng_2, lat_2,   R_EARTH, D2R, ph1_1, phi_2, delta_phi, delta_lambda, a, c) {

	R_EARTH = 3.959e3	# mean radius in miles
	D2R = (2*atan2(0, -1))/360.0
	phi_1 = lat_1 * D2R
	phi_2 = lat_2 * D2R
	delta_phi = (lat_2 - lat_1) * D2R
	delta_lambda = (lng_2 - lng_1) * D2R
	a = sin(delta_phi/2.0) * sin(delta_phi/2.0) + cos(phi_1) * cos(phi_2) * sin(delta_lambda/2.0) * sin(delta_lambda/2.0)
	c = 2 * atan2(sqrt(a), sqrt(1.0-a))
	return R_EARTH * c
}
function GU_str_escape(str,   work) {

	work = str
	gsub(/\\/, "\\\\", work)
	gsub(/\"/, "\\\"", work)
	gsub(/\//, "\\/", work)
	return work
}

function GU_pi() {
	return 4*atan2(1, 1)
}
function GU_d2r(d) {
	return (d+0)*GU_pi()/180.0
}
function GU_r2d(r) {
	return (r+0)*180.0/GU_pi()
}
function GU_is_righthanded(count, first, x, y,   area, p, x1, y1, x2, y2) {
	area = 0
	for(p = 0; p < p_count[poly]; p++){
		x1 = x[p_first[poly] + p]
		y1 = y[p_first[poly] + p]
		x2 = x[p_first[poly] + p + 1]
		y2 = y[p_first[poly] + p + 1]
		area += GU_d2r(x2 - x1) * (2.0 + sin(GU_d2r(y1)) + sin(GU_d2r(y2)))
	}
	return area >= 0
}
function GU_pr_header(title, n_points) {

	printf("{\n")
	printf("\"geojson\": {\n")
	printf("\"type\": \"FeatureCollection\",\n")
	printf("\"metadata\": {\n")
	printf("  \"generated\": \"%s\",\n", strftime("%Y%m%dT%H%M%S%Z"))
	printf("  \"title\": \"%s\",\n", title)
	printf("  \"count\": %d\n", n_points)
	printf("},\n")
	printf("\"features\": [\n")
}
function GU_pr_trailer() {
	printf("]\n")
	printf("}\n")
	printf("}\n")
}
function GU_find_pgroups(start, count, longs, lats, pg_starts, pg_counts,   n_pgroups, l_geo, geo, i) {

	n_pgroups = 1
	pg_starts[n_pgroups] = start
	pg_counts[n_pgroups] = 1
	l_geo[1] = longs[start]
	l_geo[2] = lats[start]
	for(i = 1; i < count; i++){
		geo[1] = longs[start + i]
		geo[2] = longs[start + i]
		if(GU_geo_equal(geo, l_geo)){
			pg_counts[n_pgroups]++
		}else{
			n_pgroups++
			pg_starts[n_pgroups] = start + i
			pg_counts[n_pgroups] = 1
		}
		l_geo[1] = geo[1]
		l_geo[2] = geo[2]
	}

	return n_pgroups
}
function GU_geo_equal(g1, g2) {
	return g1[1] == g2[1] && g1[2] == g2[2]
}
function GU_geo_isnull(g) {
	return g[1] == "" || g[2] == ""
}
function GU_geo_adjust(long, lat, n, long_adj, lat_adj,   PI, RAD, i, a) {

	PI = 4.0 * atan2(1, 1)
	RAD = 0.0001

	long_adj[1] = 0
	lat_adj[1] = 0
	if(n > 1){
		a = 2.0*PI/(n-1)
		for(i = 2; i <= n; i++){
			long_adj[i] = RAD * sin(a * (i-2))
			lat_adj[i] = RAD * cos(a * (i-2))
		}
	}
}
function GU_mk_point(file, color, style, long, lat, title, last,   h_color, h_style) {
 	h_color = color != "."
	h_style = style != "."
 	printf("{\n")									> file
 	printf("  \"type\": \"Feature\",\n")						> file
 	printf("  \"geometry\": {")							> file
 	printf("\"type\": \"Point\", ")							> file
 	printf("\"coordinates\": [%.5f, %.5f]", long, lat)				> file
 	printf("},\n")									> file
 	printf("  \"properties\": {\n")							> file
 	printf("    \"title\": \"%s\",\n", GU_str_escape(title))			> file
	if(h_color)
		printf("    \"marker-color\": \"%s\"%s\n", color, h_style ? "," : "")	> file
	if(h_style)
		printf("    %s\n", style)						> file
	printf("  }\n")									> file
	printf("}%s\n", !last ? "," : "")						> file
}
function GU_mk_line(file, lng_1, lat_1, lng_2, lat_2, last) {
	printf("{\n")																> file
	printf("  \"type\": \"Feature\",\n")													> file
	printf("  \"geometry\": {\"type\": \"LineString\", \"coordinates\": [[%.5f, %.5f], [%.5f, %.5f]]}\n", lng_1, lat_1, lng_2, lat_2)	> file
	printf("}%s\n", !last ? "," : "")													> file
}
function GU_gc_dist(lng_1, lat_1, lng_2, lat_2,   R_EARTH, D2R, ph1_1, phi_2, delta_phi, delta_lambda, a, c) {

	R_EARTH = 3.959e3	# mean radius in miles
	D2R = (2*atan2(0, -1))/360.0
	phi_1 = lat_1 * D2R
	phi_2 = lat_2 * D2R
	delta_phi = (lat_2 - lat_1) * D2R
	delta_lambda = (lng_2 - lng_1) * D2R
	a = sin(delta_phi/2.0) * sin(delta_phi/2.0) + cos(phi_1) * cos(phi_2) * sin(delta_lambda/2.0) * sin(delta_lambda/2.0)
	c = 2 * atan2(sqrt(a), sqrt(1.0-a))
	return R_EARTH * c
}
function GU_str_escape(str,   work) {

	work = str
	gsub(/\\/, "\\\\", work)
	gsub(/\"/, "\\\"", work)
	gsub(/\//, "\\/", work)
	return work
}