Fix NULL string implementation

Fix NULL string implementation

PG COPY uses \N as the default NULL placeholder.
http://www.postgresql.org/docs/9.3/static/sql-copy.html

#!/usr/bin/awk -F, -f

BEGIN {
    FS=","
    print "#!/usr/bin/awk -F, -f" RS

    print "# generated by: awk-csvalid https://github.com/tesera/awk-csvalid" RS
    print "# awk-csvalid csv toolset generator: https://github.com/tesera/awk-csvalid"
    print "# usage:"
    print "#    validate:     awk -f action=validate validator.awk > violations.txt"
    print "#    create table: awk -v action=table -f validator.awk | psql afgo_dev"
    print "#    sanitize csv: awk -v action=sanitize -f validator.awk > sanitized.csv"
    print "#    insert sql:   awk -v action=insert -f validator.awk | psql afgo_dev" RS
    print "# awk is a simple unix text file parser: http://www.gnu.org/software/gawk/manual/gawk.html"
    print "# awk primer:"
    print "#    NR = number/index current record"
    print "#    RS = record seperator new line i.e. \\n"
    print "#    FS = field seperator i.e. ,"
    print "#    /pattern/ { expression } = if pattern is truthy run expression" RS

    print "BEGIN {"
    print "    FS=\",\"; OFS=\",\"; err_count=0; cats[\"na\"]=0;"
    print "    if(!action) action = \"validate\""
    print "    summary_header=\"file_name,field_name,rule,message,violation_count\""
    print "    CSVFILENAME = CSVFILENAME ? CSVFILENAME : FILENAME"
    print "}" RS

    print "# builtin helper functions"
    print "function eql(x,y) {v=1; for (i in x) v=(v&&x[i]==y[i]); return v;}"
    print "function are_headers_valid(valid_headers) { v=0; split($0, h, \",\"); split(valid_headers, vh, \"|\"); return eql(h, vh); }"
    print "function is_unique(i, val) { if (vals[i,val]) { return 0; } else { vals[i,val] = 1; return 1; } }"
    print "function is_numeric(x){ return(x==x+0) }"
    print "function print_cats(categories) { for (category in categories) { if (categories[category]) print \"      \" category \": \" categories[category]; } }"
    print "function log_err(cat) { cats[cat]++; err_count++; }"
    print RS
    print "#get rid of the evil windows cr"
    print "{ sub(\"\\r$\", \"\") }"
    print RS
}

# BUILD OPTIONS MAP
$1 == "option" {
    options[$2]=$3;
}

# RENDER PFKEY CHECK
$1 == "file" {
    file_options[$2]=$3;
    if ($2 == "pkey") {
        pkeys=$3
        gsub(/\|/, " \"-\" ", pkeys)
        pkeycheck="action ~ /validate/ && NR > 1 { pkey=" pkeys "; if(keys[pkey]) { if (dupkeys[pkey]) dupkeys[pkey]++; else dupkeys[pkey] = 1 } else { keys[pkey] = NR } }"
    }
}

# RENDER HEADERS CHECK
$1 == "headers" && $2 == "names" {
    split($3, headers, "|")
    print "# make header index/map"
    print "NR > 1 {"
    for (i in headers) {
        header_name = headers[i]
        header_index[header_name] = i
        print "    " headers[i]  "=$" i
    }

    print "}" RS
    print "# awk rules based on user csv ruleset"
    print "NR == 1 && action == \"validate\" { headers=\"" $3 "\"; if (!are_headers_valid(headers)) { gsub(/\\|/, FS, headers); print RS \"INVALID HEADERS IN \" CSVFILENAME RS \"WAS: \" RS $0 RS \"EXPECTED:\" RS headers RS; exit 0; } }"
    print "NR == 1 && action == \"validate:summary\" { headers=\"" $3 "\"; if (!are_headers_valid(headers)) { violations[CSVFILENAME FS \"headers\" FS  \"names\" FS \"csv headers are invalid\" FS \"error\"]=1; exit 0; } }"
    print pkeycheck
}

# RENDER VALUE CHECKS
$1 == "field" {
    # SET DEFAULTS IF NOT PASSED IN VIA RULES
    defaults["mode"]    = "text"
    defaults["summary"] = "true"
    defaults["dcat"]    = "warning"
    for (option in defaults) {
        if (!options[option]) options[option] = defaults[option]
    }

    # PARSE RULE FIELDS
    rule_type     = $2
    split($3,params," ")
    field         = params[1]
    cat           = params[3]
    field_index   = header_index[field]
    test          = ""
    msg           = ""

    # BUILD VIOLATION TEST
    if (rule_type == "type") {
        type = params[2]

        if (type ~ /^integer|number$/) {
            cat       = "error"
            test      = field " && !is_numeric(" field ")"
            msg       = "Field " field " in \" CSVFILENAME \" line \" NR \" should be a numeric but was \" " field " \" "
        }

    } else if (rule_type == "required") {
        req = params[2]

        if (req == "true") {
            test        = field " == \"\""
            mini_msg    = "value is required but was empty"
            msg         = "Field " field " in \" CSVFILENAME \" line \" NR \" is required"
        } else if (req != "false") {
            test        = req " && " field " == \"\""
            mini_msg    = "value is required if " req
            msg         = "Field " field " in \" CSVFILENAME \" line \" NR \" is required if " req
        }

    } else if (rule_type == "unique") {
        cat         = "error"
        test        = "!is_unique(" field ")"
        mini_msg    = "value should be unique but had duplicates"
        msg         = "Field " field " in \" CSVFILENAME \" line \" NR \" is a duplicate and should be unique"

    } else if (rule_type ~ /^minimum|maximum$/) {
        comparator  = $2 == "maximum" ? ">" : "<"
        limit       = params[2]
        term        = $2 == "maximum" ? "less" : "greater"

        cat         = "error"
        test        = field " != \"\" && " field " " comparator " " limit
        mini_msg    = "value should be " term " than: " limit
        msg         = field " in \" CSVFILENAME \" line \" NR \" should be " term " than " limit " and was \" " field " \" "

    } else if (rule_type == "pattern") {
        pattern     = params[2]

        cat         = "error"
        test        = field " != \"\" && " field " !~ " pattern
        mini_msg    = "value should match: " pattern
        msg         = field " in \" CSVFILENAME \" line \" NR \" should match the following pattern " pattern " and was \" " field " \" "

    } else if (rule_type ~ /^minLength|maxLength$/) {
        comparator  = $2 == "maxLength" ? ">" : "<"
        limit       = params[2]
        term        = $2 == "maxLength" ? "less" : "greater"

        cat         = "error"
        test        = field " != \"\" && length(" field ") " comparator " " limit
        mini_msg    = "max length is: " limit
        msg         = field " length in \" CSVFILENAME \" line \" NR \" should be " term " than " limit " and was \" length(" field ") \" "
    }

    # RENDER VIOLATION HANDLER
    if (test) {
        if (!cat) cat = options["dcat"]
        handler_prefix = "{ log_err(\"" cat "\"); "

        if (options["mode"] == "text") {
            err_handler = handler_prefix "print \"" msg "\" RS $0 RS; }"

        } else if (options["mode"] == "wrap") {
            acc = "$" field_index
            err_handler = handler_prefix "FS=\",\";" acc "=\">>\" " acc " \"<<\"; print $0 }"

        } else if (options["mode"] == "append") {
            err_handler = handler_prefix "print $0 FS \"" msg "\" }"
        }

        if (rule_type != "none") {
            print "action == \"validate\" && NR > 1 && " test " " err_handler " "
            summary_handler = "key=CSVFILENAME FS \"" field "\" FS  \"" rule_type "\" FS \"" mini_msg "\" FS \"" cat "\"; if(!violations[key]) { violations[key]=0; } violations[key]++;"
            print "action == \"validate:summary\" && NR > 1 && " test " { " summary_handler " } "
        }
    }
}

# BUILD MAP OF SQL TYPES FOR SQL CREATE AND COPY
$1 == "field" {
    sql_types_map["string"] = "text"
    sql_types_map["integer"] = "integer"
    sql_types_map["number"] = "numeric"

    rule_type = $2 ? sql_types_map[$2] : sql_types_map["string"]

    split($3, params, " ")
    field = params[1]

    sql_types[field] = rule_type
}

END {
    # SANITIZE EMPTY VALUES TO PASSED IN DEFAULTS OR NULL
    print RS "# sanitize rules"
    print "action ~ /^(sanitize|insert)$/ && NR > 1 {"
    for (field in sql_types) {
        sql_type    = sql_types[field]
        field_index = header_index[field]
        dval        = sql_type == "numeric" ? options["dvalnum"] : options["dvalstr"]
        dval        = dval ? dval : "\\\\N"

        print "    if (" field " == \"\") $" field_index " = \"" dval "\""
    }
    print "}"

    # SQL COPY
    print RS "# action handlers"
    print "action == \"insert\" && NR == 1 {"
    print "    print \"COPY " file_options["table"] " (\" addfields FS \"source_row_index\" FS $0 \") FROM stdin;\""
    print "}"

    print "action == \"insert\" && NR > 1 {"
    print "    gsub(\",\", \"\\t\");"
    print "    print addvals \"\\t\" NR \"\\t\" $0;"
    print "}"

    # SQL CREATE TABLE
    print "action == \"table\" && NR == 1 {"
    fields=""
    for (i = 1; i <= length(headers); i++) {
        field     = headers[i]
        sql_type  = sql_types[field]
        fields    = fields field " " sql_type
        if (i < length(headers)) fields = fields ","
    }

    if(file_options["pkey"]) {
        cols    = file_options["pkey"]
        gsub(/\|/, ",", cols)
        fields  = fields ", CONSTRAINT " file_options["table"] "_pkey PRIMARY KEY (" cols ") "
    }
    if(file_options["fkey"]) {
        split(file_options["fkey"], ref, " ")
        ftable  = ref[1]
        fcols   = ref[2]
        gsub(/\|/, ",", fcols)
        fields  = fields ", CONSTRAINT " file_options["table"] "_" ftable "_fkey FOREIGN KEY (" fcols ") REFERENCES " ftable " (" fcols ") MATCH FULL "
        fields  = fields "ON UPDATE CASCADE ON DELETE NO ACTION"
    }
    print "     print \"CREATE TABLE IF NOT EXISTS " file_options["table"] " (" fields ");\""
    print "}"

    print "action == \"sanitize\" { print }"

    # END
    print "# la fin"
    print "END {"
    print "    if (action == \"validate:summary\" && length(dupkeys) > 0) for (dup in dupkeys) { violation=CSVFILENAME FS \"" file_options["pkey"] "\" FS  \"duplicate\" FS dup \" violates pkey\" FS \"error\"; violations[violation] = dupkeys[dup]}"
    print "    if (action == \"validate:summary\") { if (length(violations) > 0) for (violation in violations) { print violation FS violations[violation]; } }"
    #print "    if (action == \"insert\") print \"\\\\.\""
    print "    if (action == \"validate\" && options[\"summary\"] == \"true\") { print RS \"violation summary: \" RS \"   counts:   \" RS \"      total: \" err_count; print_cats(cats); }"
    print "}"
}

#!/usr/bin/awk -F, -f

BEGIN {
    FS=","
    print "#!/usr/bin/awk -F, -f" RS

    print "# generated by: awk-csvalid https://github.com/tesera/awk-csvalid" RS
    print "# awk-csvalid csv toolset generator: https://github.com/tesera/awk-csvalid"
    print "# usage:"
    print "#    validate:     awk -f action=validate validator.awk > violations.txt"
    print "#    create table: awk -v action=table -f validator.awk | psql afgo_dev"
    print "#    sanitize csv: awk -v action=sanitize -f validator.awk > sanitized.csv"
    print "#    insert sql:   awk -v action=insert -f validator.awk | psql afgo_dev" RS
    print "# awk is a simple unix text file parser: http://www.gnu.org/software/gawk/manual/gawk.html"
    print "# awk primer:"
    print "#    NR = number/index current record"
    print "#    RS = record seperator new line i.e. \\n"
    print "#    FS = field seperator i.e. ,"
    print "#    /pattern/ { expression } = if pattern is truthy run expression" RS

    print "BEGIN {"
    print "    FS=\",\"; OFS=\",\"; err_count=0; cats[\"na\"]=0;"
    print "    if(!action) action = \"validate\""
    print "    summary_header=\"file_name,field_name,rule,message,violation_count\""
    print "    CSVFILENAME = CSVFILENAME ? CSVFILENAME : FILENAME"
    print "}" RS

    print "# builtin helper functions"
    print "function eql(x,y) {v=1; for (i in x) v=(v&&x[i]==y[i]); return v;}"
    print "function are_headers_valid(valid_headers) { v=0; split($0, h, \",\"); split(valid_headers, vh, \"|\"); return eql(h, vh); }"
    print "function is_unique(i, val) { if (vals[i,val]) { return 0; } else { vals[i,val] = 1; return 1; } }"
    print "function is_numeric(x){ return(x==x+0) }"
    print "function print_cats(categories) { for (category in categories) { if (categories[category]) print \"      \" category \": \" categories[category]; } }"
    print "function log_err(cat) { cats[cat]++; err_count++; }"
    print RS
    print "#get rid of the evil windows cr"
    print "{ sub(\"\\r$\", \"\") }"
    print RS
}

# BUILD OPTIONS MAP
$1 == "option" {
    options[$2]=$3;
}

# RENDER PFKEY CHECK
$1 == "file" {
    file_options[$2]=$3;
    if ($2 == "pkey") {
        pkeys=$3
        gsub(/\|/, " \"-\" ", pkeys)
        pkeycheck="action ~ /validate/ && NR > 1 { pkey=" pkeys "; if(keys[pkey]) { if (dupkeys[pkey]) dupkeys[pkey]++; else dupkeys[pkey] = 1 } else { keys[pkey] = NR } }"
    }
}

# RENDER HEADERS CHECK
$1 == "headers" && $2 == "names" {
    split($3, headers, "|")
    print "# make header index/map"
    print "NR > 1 {"
    for (i in headers) {
        header_name = headers[i]
        header_index[header_name] = i
        print "    " headers[i]  "=$" i
    }

    print "}" RS
    print "# awk rules based on user csv ruleset"
    print "NR == 1 && action == \"validate\" { headers=\"" $3 "\"; if (!are_headers_valid(headers)) { gsub(/\\|/, FS, headers); print RS \"INVALID HEADERS IN \" CSVFILENAME RS \"WAS: \" RS $0 RS \"EXPECTED:\" RS headers RS; exit 0; } }"
    print "NR == 1 && action == \"validate:summary\" { headers=\"" $3 "\"; if (!are_headers_valid(headers)) { violations[CSVFILENAME FS \"headers\" FS  \"names\" FS \"csv headers are invalid\" FS \"error\"]=1; exit 0; } }"
    print pkeycheck
}

# RENDER VALUE CHECKS
$1 == "field" {
    # SET DEFAULTS IF NOT PASSED IN VIA RULES
    defaults["mode"]    = "text"
    defaults["summary"] = "true"
    defaults["dcat"]    = "warning"
    defaults["dvalnum"] = "NULL"
    defaults["dvalstr"] = "NULL"
    for (option in defaults) {
        if (!options[option]) options[option] = defaults[option]
    }

    # PARSE RULE FIELDS
    rule_type     = $2
    split($3,params," ")
    field         = params[1]
    cat           = params[3]
    field_index   = header_index[field]
    test          = ""
    msg           = ""

    # BUILD VIOLATION TEST
    if (rule_type == "type") {
        type = params[2]

        if (type ~ /^integer|number$/) {
            cat       = "error"
            test      = field " && !is_numeric(" field ")"
            msg       = "Field " field " in \" CSVFILENAME \" line \" NR \" should be a numeric but was \" " field " \" "
        }

    } else if (rule_type == "required") {
        req = params[2]

        if (req == "true") {
            test        = field " == \"\""
            mini_msg    = "value is required but was empty"
            msg         = "Field " field " in \" CSVFILENAME \" line \" NR \" is required"
        } else if (req != "false") {
            test        = req " && " field " == \"\""
            mini_msg    = "value is required if " req
            msg         = "Field " field " in \" CSVFILENAME \" line \" NR \" is required if " req
        }

    } else if (rule_type == "unique") {
        cat         = "error"
        test        = "!is_unique(" field ")"
        mini_msg    = "value should be unique but had duplicates"
        msg         = "Field " field " in \" CSVFILENAME \" line \" NR \" is a duplicate and should be unique"

    } else if (rule_type ~ /^minimum|maximum$/) {
        comparator  = $2 == "maximum" ? ">" : "<"
        limit       = params[2]
        term        = $2 == "maximum" ? "less" : "greater"

        cat         = "error"
        test        = field " != \"\" && " field " " comparator " " limit
        mini_msg    = "value should be " term " than: " limit
        msg         = field " in \" CSVFILENAME \" line \" NR \" should be " term " than " limit " and was \" " field " \" "

    } else if (rule_type == "pattern") {
        pattern     = params[2]

        cat         = "error"
        test        = field " != \"\" && " field " !~ " pattern
        mini_msg    = "value should match: " pattern
        msg         = field " in \" CSVFILENAME \" line \" NR \" should match the following pattern " pattern " and was \" " field " \" "

    } else if (rule_type ~ /^minLength|maxLength$/) {
        comparator  = $2 == "maxLength" ? ">" : "<"
        limit       = params[2]
        term        = $2 == "maxLength" ? "less" : "greater"

        cat         = "error"
        test        = field " != \"\" && length(" field ") " comparator " " limit
        mini_msg    = "max length is: " limit
        msg         = field " length in \" CSVFILENAME \" line \" NR \" should be " term " than " limit " and was \" length(" field ") \" "
    }

    # RENDER VIOLATION HANDLER
    if (test) {
        if (!cat) cat = options["dcat"]
        handler_prefix = "{ log_err(\"" cat "\"); "

        if (options["mode"] == "text") {
            err_handler = handler_prefix "print \"" msg "\" RS $0 RS; }"

        } else if (options["mode"] == "wrap") {
            acc = "$" field_index
            err_handler = handler_prefix "FS=\",\";" acc "=\">>\" " acc " \"<<\"; print $0 }"

        } else if (options["mode"] == "append") {
            err_handler = handler_prefix "print $0 FS \"" msg "\" }"
        }

        if (rule_type != "none") {
            print "action == \"validate\" && NR > 1 && " test " " err_handler " "
            summary_handler = "key=CSVFILENAME FS \"" field "\" FS  \"" rule_type "\" FS \"" mini_msg "\" FS \"" cat "\"; if(!violations[key]) { violations[key]=0; } violations[key]++;"
            print "action == \"validate:summary\" && NR > 1 && " test " { " summary_handler " } "
        }
    }
}

# BUILD MAP OF SQL TYPES FOR SQL CREATE AND COPY
$1 == "field" {
    sql_types_map["string"] = "text"
    sql_types_map["integer"] = "integer"
    sql_types_map["number"] = "numeric"

    rule_type = $2 ? sql_types_map[$2] : sql_types_map["string"]

    split($3, params, " ")
    field = params[1]

    sql_types[field] = rule_type
}

END {
    # SANITIZE EMPTY VALUES TO PASSED IN DEFAULTS OR NULL
    print RS "# sanitize rules"
    print "action ~ /^(sanitize|insert)$/ && NR > 1 {"
    for (field in sql_types) {
        sql_type    = sql_types[field]
        field_index = header_index[field]
        dval        = sql_type == "numeric" ? options["dvalnum"] : options["dvalstr"]

        print "    if (" field " == \"\") $" field_index " = \"" dval "\""
    }
    print "}"

    # SQL COPY
    print RS "# action handlers"
    print "action == \"insert\" && NR == 1 {"
    print "    print \"COPY " file_options["table"] " (\" addfields FS \"source_row_index\" FS $0 \") FROM stdin;\""
    print "}"

    print "action == \"insert\" && NR > 1 {"
    print "    gsub(\",\", \"\\t\");"
    print "    print addvals \"\\t\" NR \"\\t\" $0;"
    print "}"

    # SQL CREATE TABLE
    print "action == \"table\" && NR == 1 {"
    fields=""
    for (i = 1; i <= length(headers); i++) {
        field     = headers[i]
        sql_type  = sql_types[field]
        fields    = fields field " " sql_type
        if (i < length(headers)) fields = fields ","
    }

    if(file_options["pkey"]) {
        cols    = file_options["pkey"]
        gsub(/\|/, ",", cols)
        fields  = fields ", CONSTRAINT " file_options["table"] "_pkey PRIMARY KEY (" cols ") "
    }
    if(file_options["fkey"]) {
        split(file_options["fkey"], ref, " ")
        ftable  = ref[1]
        fcols   = ref[2]
        gsub(/\|/, ",", fcols)
        fields  = fields ", CONSTRAINT " file_options["table"] "_" ftable "_fkey FOREIGN KEY (" fcols ") REFERENCES " ftable " (" fcols ") MATCH FULL "
        fields  = fields "ON UPDATE CASCADE ON DELETE NO ACTION"
    }
    print "     print \"CREATE TABLE IF NOT EXISTS " file_options["table"] " (" fields ");\""
    print "}"

    print "action == \"sanitize\" { print }"

    # END
    print "# la fin"
    print "END {"
    print "    if (action == \"validate:summary\" && length(dupkeys) > 0) for (dup in dupkeys) { violation=CSVFILENAME FS \"" file_options["pkey"] "\" FS  \"duplicate\" FS dup \" violates pkey\" FS \"error\"; violations[violation] = dupkeys[dup]}"
    print "    if (action == \"validate:summary\") { if (length(violations) > 0) for (violation in violations) { print violation FS violations[violation]; } }"
    #print "    if (action == \"insert\") print \"\\\\.\""
    print "    if (action == \"validate\" && options[\"summary\"] == \"true\") { print RS \"violation summary: \" RS \"   counts:   \" RS \"      total: \" err_count; print_cats(cats); }"
    print "}"
}


update copyright

update copyright

#!/usr/bin/awk -f
#
# Copyright (c) 2016 Cyrille Lefevre (Cyrille.Lefevre@laposte.net). All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. 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.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 THE AUTHORS
# OR CONTRIBUTORS 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.

BEGIN {
	split("N S", nsa); split("E W", eoa)
	CONVFMT = "%04.1f"
	fmt = "%.8f"
	epsilon = .00000001
}
function round(l, n) { return +(+(l "e+" n) "e-" n) }
function l2dms(l, t,	n, d, m, s) {
	n = t[1+(l<0)]
	if (l < 0) l = -l
	d = int(l)
	l -= int(l)
	l *= 60
	m = int(l)
	l -= int(l)
	l *= 60
	s = round(l, 2)
	if (s == 60) { m++; s -= 60 }
	return d "°" m "'" s "''" n
}
function dms2l(n, d, m, s,	l, t) {
	return n * (d + (m / 60) + (s / 3600))
}
function l2l(l, a, m, n,	s, o, x, t) {
	s = o = l2dms(l, a)
	x = o ~ m ? n : -n
	gsub(/[^-.0-9]/, " ", o)
	split(o, t)
	return dms2l(x, t[1], t[2], t[3])
}
/./ {
	ns = /N/ ? 1 : -1
	eo = /[OW]/ ? -1 : 1
	gsub(/[^-.0-9]/, " ")
	if (NF != 6) {
		nss = l2dms($1, nsa)
		eos = l2dms($2, eoa)
		nsn = l2l($1, nsa, "N", 1)
		eon = l2l($2, eoa, "[OW]", -1)
		printf fmt " " fmt "\n", nsn, eon
		print nss, eos
	} else {
		nsn = dms2l(ns, $1, $2, $3)
		eon = dms2l(eo, $4, $5, $6)
		nss = l2dms(nsn, nsa)
		eos = l2dms(eon, eoa)
		printf fmt " " fmt "\n", nsn, eon
		print nss, eos
	}
}

# eof

#!/usr/bin/awk -f
#
# Copyright (c) 1995-2016 Cyrille Lefevre (Cyrille.Lefevre@laposte.net). All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. 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.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 THE AUTHORS
# OR CONTRIBUTORS 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.

BEGIN {
	split("N S", nsa); split("E W", eoa)
	CONVFMT = "%04.1f"
	fmt = "%.8f"
	epsilon = .00000001
}
function round(l, n) { return +(+(l "e+" n) "e-" n) }
function l2dms(l, t,	n, d, m, s) {
	n = t[1+(l<0)]
	if (l < 0) l = -l
	d = int(l)
	l -= int(l)
	l *= 60
	m = int(l)
	l -= int(l)
	l *= 60
	s = round(l, 2)
	if (s == 60) { m++; s -= 60 }
	return d "°" m "'" s "''" n
}
function dms2l(n, d, m, s,	l, t) {
	return n * (d + (m / 60) + (s / 3600))
}
function l2l(l, a, m, n,	s, o, x, t) {
	s = o = l2dms(l, a)
	x = o ~ m ? n : -n
	gsub(/[^-.0-9]/, " ", o)
	split(o, t)
	return dms2l(x, t[1], t[2], t[3])
}
/./ {
	ns = /N/ ? 1 : -1
	eo = /[OW]/ ? -1 : 1
	gsub(/[^-.0-9]/, " ")
	if (NF != 6) {
		nss = l2dms($1, nsa)
		eos = l2dms($2, eoa)
		nsn = l2l($1, nsa, "N", 1)
		eon = l2l($2, eoa, "[OW]", -1)
		printf fmt " " fmt "\n", nsn, eon
		print nss, eos
	} else {
		nsn = dms2l(ns, $1, $2, $3)
		eon = dms2l(eo, $4, $5, $6)
		nss = l2dms(nsn, nsa)
		eos = l2dms(eon, eoa)
		printf fmt " " fmt "\n", nsn, eon
		print nss, eos
	}
}

# eof


Debug for source plugins.

Debug for source plugins.

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

BEGIN {
  #After July 1 of this year, start reporting on this year. Before that, ignore this year.
  str = "date +%m-%d-%Y";
  str | getline date;
  str = "date +%Y";
  str | getline current_year;
  #close str;
  str = "date +%W";
  str | getline week;
  #close str;
  if (week<26) {
  	current_year=current_year-1
  }
  #printf "DEBUG: Current-Year:%s Current-Week:%s\n", current_year, week;
  FS="\t"
  pn = 0
}
#$3>date

{
  # add a loop to add line only if either status is (wanted or testing) or ending year is gt or eq to contract year

  #current_year=2018

  #current_year=`date +%Y`
  #week=`date +%W`
  #if [ $week -lt 26 ]
  #  then
  #  ((current_year--))
    #echo current_year $current_year
  #fi
  #echo current_year $current_year

  # end_year is the AU year, or the second half of a range, ie 2014 in 2013-2014
  end_year = 0
  incontract = 0
  # test_year is the contract year, or the back year if there is one.
  test_year = ""
  if ($7 == "") {
    test_year = $3
  } else {
    test_year = $7
  }
  if (length($4) > 3) {
    end_year = substr($4,length($4)-3,4)
  }
  #printf "DEBUG: %s\t%s\t%s\t%s\t%s\t%s\t%s\n", $1,$2,$3,$4,$7,end_year,test_year
  # Is the AU year >= the contract year or the back year. And then decide what to do with the current year.
  if ($6 == "wanted" || $6 == "testing") {
      incontract = 1;
  } else if ((end_year >= test_year) && ((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
      if ($7 == "") {
        k[pn] = ".."
      } else {
        k[pn] = $7
      }
      d[pn] = $4
      r1[pn] = $5
      r2[pn] = $8
      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] = "deepCrawl"
  s[9] = "frozen"
  s[10] = "ingNotReady"
  s[11] = "finished"
  s[12] = "down"
  s[13] = "superseded"
  s[14] = "zapped"
  s[15] = "doNotProcess"
  s[16] = "readySource"
  sn = 17

  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] = "deep"
  sc[9] = "froz"
  sc[10] = "ingN"
  sc[11] = "fini"
  sc[12] = "down"
  sc[13] = "supe"
  sc[14] = "zapp"
  sc[15] = "doNP"
  sc[16] = "rdSc"
  scn = 17

  #print out header
  printf "Publisher\tPlugin\tContr\tBack\tYear\tT\tP\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, poller, total aus
  for (i = 0 ; i < pn ; i++) {
    printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%d", p[i], n[i], t[i], k[i], d[i], r1[i], r2[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\tP\t%d", tt
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0) {
        printf "\t%d", x[s[j]]
      }
    }

  printf "\n"
  printf "%s", date
  printf "\n"

}

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

BEGIN {
  #After July 1 of this year, start reporting on this year. Before that, ignore this year.
  str = "date +%m-%d-%Y";
  str | getline date;
  str = "date +%Y";
  str | getline current_year;
  #close str;
  str = "date +%W";
  str | getline week;
  #close str;
  if (week<26) {
  	current_year=current_year-1
  }
  printf "Current-Year:%s Current-Week:%s", current_year, week;
  FS="\t"
  pn = 0
}
$3>date

{
  # add a loop to add line only if either status is (wanted or testing) or ending year is gt or eq to contract year

  #current_year=2018

  #current_year=`date +%Y`
  #week=`date +%W`
  #if [ $week -lt 26 ]
  #  then
  #  ((current_year--))
    #echo current_year $current_year
  #fi
  #echo current_year $current_year

  # end_year is the AU year, or the second half of a range, ie 2014 in 2013-2014
  end_year = 0
  incontract = 0
  # test_year is the contract year, or the back year if there is one.
  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
  # Is the AU year >= the contract year or the back year. And then decide what to do with the current year.
  if ($6 == "wanted" || $6 == "testing") {
      incontract = 1;
  } else if ((end_year >= test_year) && ((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
      if ($7 == "") {
        k[pn] = ".."
      } else {
        k[pn] = $7
      }
      d[pn] = $4
      r1[pn] = $5
      r2[pn] = $8
      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] = "deepCrawl"
  s[9] = "frozen"
  s[10] = "ingNotReady"
  s[11] = "finished"
  s[12] = "down"
  s[13] = "superseded"
  s[14] = "zapped"
  s[15] = "doNotProcess"
  s[16] = "readySource"
  sn = 17

  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] = "deep"
  sc[9] = "froz"
  sc[10] = "ingN"
  sc[11] = "fini"
  sc[12] = "down"
  sc[13] = "supe"
  sc[14] = "zapp"
  sc[15] = "doNP"
  sc[16] = "rdSc"
  scn = 17

  #print out header
  printf "Publisher\tPlugin\tContr\tBack\tYear\tT\tP\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, poller, total aus
  for (i = 0 ; i < pn ; i++) {
    printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%d", p[i], n[i], t[i], k[i], d[i], r1[i], r2[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\tP\t%d", tt
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0) {
        printf "\t%d", x[s[j]]
      }
    }

  printf "\n"
  printf "%s", date
  printf "\n"

}


BrlAPI: Constant generation needs to parse the BRLAPI_PARAM_TYPE_ definitions. (dm)

###############################################################################
# BRLTTY - A background process providing access to the console screen (when in
#          text mode) for a blind person using a refreshable braille display.
#
# Copyright (C) 1995-2019 by The BRLTTY Developers.
#
# BRLTTY comes with ABSOLUTELY NO WARRANTY.
#
# This is free software, placed under the terms of the
# GNU Lesser General Public License, as published by the Free Software
# Foundation; either version 2.1 of the License, or (at your option) any
# later version. Please see the file LICENSE-LGPL for details.
#
# Web Page: http://brltty.app/
#
# This software is maintained by Dave Mielke <dave@mielke.cc>.
###############################################################################

BEGIN {
  apiRangeTypeCount = 0
  apiParameterTypeCount = 0
}

/#define[ \t]*BRLAPI_(CURSOR|DISPLAY|ERROR|TTY)_/ {
  apiConstant(substr($2, 8), $2, getDefineValue(), "")
  next
}

/#define[ \t]*BRLAPI_KEY_MAX/ {
  apiMask(substr($2, 12), $2, getDefineValue(), "")
  next
}

/#define[ \t]*BRLAPI_KEY_[A-Z_]+_MASK/ {
  apiMask(substr($2, 12), $2, getDefineValue(), "")
  next
}

/#define[ \t]*BRLAPI_KEY_[A-Z_]+_SHIFT/ {
  apiShift(substr($2, 12), $2, getDefineValue(), "")
  next
}

/#define[ \t]*BRLAPI_KEY_TYPE_/ {
  apiType(substr($2, 17), $2, getDefineValue(), "")
  next
}

/#define[ \t]*BRLAPI_KEY_SYM_/ {
  apiKey(substr($2, 16), $2, getDefineValue(), "")
  next
}

/^ *brlapi_rangeType_/ {
  gsub(",", "", $1)
  apiRangeType(substr($1, 18), $1, apiRangeTypeCount++, "")
  next
}

/^ *BRLAPI_PARAM_TYPE_[^ ,]+,?/ {
  gsub(",", "", $1)
  apiConstant(substr($1, 8), $1, apiParameterTypeCount++, getComment($0))
  next
}

/^ *BRLAPI_PARAM_[^ ]+ += +[0-9]+,?/ {
  gsub(",", "", $3)
  apiConstant(substr($1, 8), $1, strtonum($3), getComment($0))
  next
}

###############################################################################
# BRLTTY - A background process providing access to the console screen (when in
#          text mode) for a blind person using a refreshable braille display.
#
# Copyright (C) 1995-2019 by The BRLTTY Developers.
#
# BRLTTY comes with ABSOLUTELY NO WARRANTY.
#
# This is free software, placed under the terms of the
# GNU Lesser General Public License, as published by the Free Software
# Foundation; either version 2.1 of the License, or (at your option) any
# later version. Please see the file LICENSE-LGPL for details.
#
# Web Page: http://brltty.app/
#
# This software is maintained by Dave Mielke <dave@mielke.cc>.
###############################################################################

BEGIN {
  apiRangeTypeCount = 0
  apiParameterNext = 0
}

/#define[ \t]*BRLAPI_(CURSOR|DISPLAY|ERROR|TTY)_/ {
  apiConstant(substr($2, 8), $2, getDefineValue(), "")
  next
}

/#define[ \t]*BRLAPI_KEY_MAX/ {
  apiMask(substr($2, 12), $2, getDefineValue(), "")
  next
}

/#define[ \t]*BRLAPI_KEY_[A-Z_]+_MASK/ {
  apiMask(substr($2, 12), $2, getDefineValue(), "")
  next
}

/#define[ \t]*BRLAPI_KEY_[A-Z_]+_SHIFT/ {
  apiShift(substr($2, 12), $2, getDefineValue(), "")
  next
}

/#define[ \t]*BRLAPI_KEY_TYPE_/ {
  apiType(substr($2, 17), $2, getDefineValue(), "")
  next
}

/#define[ \t]*BRLAPI_KEY_SYM_/ {
  apiKey(substr($2, 16), $2, getDefineValue(), "")
  next
}

/^ *brlapi_rangeType_/ {
  gsub(",", "", $1)
  apiRangeType(substr($1, 18), $1, apiRangeTypeCount++, "")
  next
}

/^ *BRLAPI_PARAM_[^ ]+ += +[0-9]+,?/ {
  gsub(",", "", $3)
  apiParameterNext = strtonum($3)
  apiParameter($0, $1)
  next
}

/^ *BRLAPI_PARAM_[^ ,]+,?/ {
  gsub(",", "", $1)
  apiParameter($0, $1)
  next
}

function apiParameter(line, symbol) {
  apiConstant(substr(symbol, 8), symbol, apiParameterNext++, getComment(line))
}


Help: update getVersion()

Help: update getVersion()

####################################################################
# Help.awk                                                         #
####################################################################

# Return version as a string.
function getVersion(    build, gitHead) {
    if (ENVIRON["TRANS_BUILD"])
        build = "-" ENVIRON["TRANS_BUILD"]
    else {
        gitHead = getGitHead()
        build = gitHead ? "-git:" gitHead : ""
    }

    return sprintf(ansi("bold", "%-22s%s%s") "\n\n", Name, Version, build) \
        sprintf("%-22s%s\n", "gawk (GNU Awk)", PROCINFO["version"])     \
        sprintf("%s\n", FriBidi ? FriBidi : "fribidi (GNU FriBidi) [NOT INSTALLED]") \
        sprintf("%-22s%s\n", "terminal type", ENVIRON["TERM"])          \
        sprintf("%-22s%s (%s)\n", "user locale", UserLocale, getName(UserLang)) \
        sprintf("%-22s%s\n", "home language", Option["hl"])             \
        sprintf("%-22s%s\n", "source language", Option["sl"])           \
        sprintf("%-22s%s\n", "target language", join(Option["tl"], "+")) \
        sprintf("%-22s%s\n", "theme", Option["theme"])                  \
        sprintf("%-22s%s\n", "init file", InitScript ? InitScript : "[NONE]") \
        sprintf("\n%-22s%s", "Report bugs to:", "https://github.com/soimort/translate-shell/issues")
}

# Return help message as a string.
function getHelp() {
    return "Usage:   " ansi("bold", Command " " ansi("negative", "[OPTIONS]") \
                            " " ansi("negative", "[SOURCE]")            \
                            ":" ansi("negative", "[TARGET(S)]")         \
                            " " ansi("negative", "[TEXT]") " ...") RS   \
        "Example: " ansi("underline", "trans -brief la:en 'per aspera ad astra'") RS \
        RS "Information Options:" RS                                    \
        ins(1, ansi("bold", "-V, -version")) RS                         \
        ins(2, "Print version and exit.") RS                            \
        ins(1, ansi("bold", "-H, -help")) RS                            \
        ins(2, "Print help message and exit.") RS                       \
        ins(1, ansi("bold", "-M, -manual")) RS                          \
        ins(2, "Show man page and exit.") RS                            \
        ins(1, ansi("bold", "-T, -reference")) RS                       \
        ins(2, "Print a reference table of languages and exit.") RS     \
        ins(1, ansi("bold", "-R, -reference-english")) RS               \
        ins(2, "Print a reference table of languages (in English names) and exit.") RS \
        RS "Display Options:" RS                                        \
        ins(1, ansi("bold", "-verbose")) RS                             \
        ins(2, "Verbose mode. (default)") RS                            \
        ins(1, ansi("bold", "-b, -brief")) RS                           \
        ins(2, "Brief mode.") RS                                        \
        ins(1, ansi("bold", "-d, -dictionary")) RS                      \
        ins(2, "Dictionary mode.") RS                                   \
        ins(1, ansi("bold", "-show-original " ansi("negative", "Y|n"))) RS \
        ins(2, "Show original text or not.") RS                         \
        ins(1, ansi("bold", "-show-original-phonetics " ansi("negative", "Y|n"))) RS \
        ins(2, "Show phonetic notation of original text or not.") RS    \
        ins(1, ansi("bold", "-show-translation " ansi("negative", "Y|n"))) RS \
        ins(2, "Show translation or not.") RS                           \
        ins(1, ansi("bold", "-show-translation-phonetics " ansi("negative", "Y|n"))) RS \
        ins(2, "Show phonetic notation of translation or not.") RS      \
        ins(1, ansi("bold", "-show-prompt-message " ansi("negative", "Y|n"))) RS \
        ins(2, "Show prompt message or not.") RS                        \
        ins(1, ansi("bold", "-show-languages " ansi("negative", "Y|n"))) RS \
        ins(2, "Show source and target languages or not.") RS           \
        ins(1, ansi("bold", "-show-original-dictionary " ansi("negative", "y|N"))) RS \
        ins(2, "Show dictionary entry of original text or not.") RS     \
        ins(1, ansi("bold", "-show-dictionary " ansi("negative", "Y|n"))) RS \
        ins(2, "Show dictionary entry of translation or not.") RS       \
        ins(1, ansi("bold", "-show-alternatives " ansi("negative", "Y|n"))) RS \
        ins(2, "Show alternative translations or not.") RS              \
        ins(1, ansi("bold", "-w " ansi("negative", "NUM")               \
                    ", -width " ansi("negative", "NUM"))) RS            \
        ins(2, "Specify the screen width for padding.") RS              \
        ins(1, ansi("bold", "-indent " ansi("negative", "NUM"))) RS     \
        ins(2, "Specify the size of indent (number of spaces).") RS     \
        ins(1, ansi("bold", "-no-ansi")) RS                             \
        ins(2, "Do not use ANSI escape codes.") RS                      \
        ins(1, ansi("bold", "-no-theme")) RS                            \
        ins(2, "Do not use any other theme than default.") RS           \
        ins(1, ansi("bold", "-theme " ansi("negative", "FILENAME"))) RS \
        ins(2, "Specify the theme to use.") RS                          \
        RS "Audio Options:" RS                                          \
        ins(1, ansi("bold", "-no-play")) RS                             \
        ins(2, "Do not listen to the translation.") RS                  \
        ins(1, ansi("bold", "-p, -play")) RS                            \
        ins(2, "Listen to the translation.") RS                         \
        ins(1, ansi("bold", "-player " ansi("negative", "PROGRAM"))) RS \
        ins(2, "Specify the audio player to use, and listen to the translation.") RS \
        RS "Terminal Paging and Web Options:" RS                        \
        ins(1, ansi("bold", "-no-view")) RS                             \
        ins(2, "Do not view the translation in a terminal pager.") RS   \
        ins(1, ansi("bold", "-v, -view")) RS                            \
        ins(2, "View the translation in a terminal pager.") RS          \
        ins(1, ansi("bold", "-pager " ansi("negative", "PROGRAM"))) RS  \
        ins(2, "Specify the terminal pager to use, and view the translation.") RS \
        ins(1, ansi("bold", "-browser " ansi("negative", "PROGRAM"))) RS \
        ins(2, "Specify the web browser to use.") RS                    \
        RS "Networking Options:" RS                                     \
        ins(1, ansi("bold", "-x " ansi("negative", "HOST:PORT")         \
                    ", -proxy " ansi("negative", "HOST:PORT"))) RS      \
        ins(2, "Use HTTP proxy on given port.") RS                      \
        ins(1, ansi("bold", "-u " ansi("negative", "STRING")            \
                    ", -user-agent " ansi("negative", "STRING"))) RS    \
        ins(2, "Specify the User-Agent to identify as.") RS             \
        RS "Interactive Shell Options:" RS                              \
        ins(1, ansi("bold", "-no-rlwrap")) RS                           \
        ins(2, "Do not invoke rlwrap when starting an interactive shell.") RS \
        ins(1, ansi("bold", "-I, -interactive, -shell")) RS             \
        ins(2, "Start an interactive shell.") RS                        \
        ins(1, ansi("bold", "-E, -emacs")) RS                           \
        ins(2, "Start the GNU Emacs front-end for an interactive shell.") RS \
        RS "I/O Options:" RS                                            \
        ins(1, ansi("bold", "-i " ansi("negative", "FILENAME")          \
                    ", -input " ansi("negative", "FILENAME"))) RS       \
        ins(2, "Specify the input file.") RS                            \
        ins(1, ansi("bold", "-o " ansi("negative", "FILENAME")          \
                    ", -output " ansi("negative", "FILENAME"))) RS      \
        ins(2, "Specify the output file.") RS                           \
        RS "Language Preference Options:" RS                            \
        ins(1, ansi("bold", "-l " ansi("negative", "CODE")              \
                    ", -lang " ansi("negative", "CODE"))) RS            \
        ins(2, "Specify your home language.") RS                        \
        ins(1, ansi("bold", "-s " ansi("negative", "CODE")              \
                    ", -source " ansi("negative", "CODE"))) RS          \
        ins(2, "Specify the source language.") RS                       \
        ins(1, ansi("bold", "-t " ansi("negative", "CODE(S)")           \
                    ", -target " ansi("negative", "CODE(S)"))) RS       \
        ins(2, "Specify the target language(s), joined by '+'.") RS     \
        RS "Other Options:" RS                                          \
        ins(1, ansi("bold", "-no-init")) RS                             \
        ins(2, "Do not load any initialization script.") RS             \
        RS "See the man page " Command "(1) for more information."
}

# Display man page.
function showMan(    temp) {
    if (ENVIRON["TRANS_MANPAGE"]) {
        initPager()
        temp = "echo -E \"${TRANS_MANPAGE}\""
        temp = temp PIPE                                                \
            "groff -Wall -mtty-char -mandoc -Tutf8 -rLL=${COLUMNS}n -rLT=${COLUMNS}n"
        switch (Pager) {
        case "less":
            temp = temp PIPE Pager " -s -P\"\\ \\Manual page " Command "(1) line %lt (press h for help or q to quit)\""
            break
        case "most":
            temp = temp PIPE Pager " -Cs"
            break
        default: # more
            temp = temp PIPE Pager
        }
        system(temp)
    } else if (system("man " Command SUPERR))
        print getHelp()
}

# Return a list of language codes as a string.
# Parameters:
#     displayName = "endonym" or "name"
function getReference(displayName) {
    if (displayName == "name")
        return "┌─────────────────────────────┬──────────────────────┬─────────────────┐" RS \
            "│ " getName("af") "           - " ansi("bold", "af") "    │ " \
            getName("ha") "          - " ansi("bold", "ha") "  │ "      \
            getName("fa") "    - " ansi("bold", "fa") " │" RS           \
            "│ " getName("sq") "            - " ansi("bold", "sq") "    │ " \
            getName("he") "         - " ansi("bold", "he") "  │ "       \
            getName("pl") "     - " ansi("bold", "pl") " │" RS          \
            "│ " getName("ar") "              - " ansi("bold", "ar") "    │ " \
            getName("hi") "          - " ansi("bold", "hi") "  │ "      \
            getName("pt") " - " ansi("bold", "pt") " │" RS              \
            "│ " getName("hy") "            - " ansi("bold", "hy") "    │ " \
            getName("hmn") "          - " ansi("bold", "hmn") " │ "     \
            getName("pa") "    - " ansi("bold", "pa") " │" RS           \
            "│ " getName("az") "         - " ansi("bold", "az") "    │ " \
            getName("hu") "      - " ansi("bold", "hu") "  │ "          \
            getName("ro") "   - " ansi("bold", "ro") " │" RS            \
            "│ " getName("eu") "              - " ansi("bold", "eu") "    │ " \
            getName("is") "      - " ansi("bold", "is") "  │ "          \
            getName("ru") "    - " ansi("bold", "ru") " │" RS           \
            "│ " getName("be") "          - " ansi("bold", "be") "    │ " \
            getName("ig") "           - " ansi("bold", "ig") "  │ "     \
            getName("sr") "    - " ansi("bold", "sr") " │" RS           \
            "│ " getName("bn") "             - " ansi("bold", "bn") "    │ " \
            getName("id") "     - " ansi("bold", "id") "  │ "           \
            getName("st") "    - " ansi("bold", "st") " │" RS           \
            "│ " getName("bs") "             - " ansi("bold", "bs") "    │ " \
            getName("ga") "          - " ansi("bold", "ga") "  │ "      \
            getName("si") "    - " ansi("bold", "si") " │" RS           \
            "│ " getName("bg") "           - " ansi("bold", "bg") "    │ " \
            getName("it") "        - " ansi("bold", "it") "  │ "        \
            getName("sk") "     - " ansi("bold", "sk") " │" RS          \
            "│ " getName("ca") "             - " ansi("bold", "ca") "    │ " \
            getName("ja") "       - " ansi("bold", "ja") "  │ "         \
            getName("sl") "  - " ansi("bold", "sl") " │" RS             \
            "│ " getName("ceb") "             - " ansi("bold", "ceb") "   │ " \
            getName("jv") "       - " ansi("bold", "jv") "  │ "         \
            getName("so") "     - " ansi("bold", "so") " │" RS          \
            "│ " getName("ny") "            - " ansi("bold", "ny") "    │ " \
            getName("kn") "        - " ansi("bold", "kn") "  │ "        \
            getName("es") "    - " ansi("bold", "es") " │" RS           \
            "│ " getName("zh-CN") "  - " ansi("bold", "zh-CN") " │ "    \
            getName("kk") "         - " ansi("bold", "kk") "  │ "       \
            getName("su") "  - " ansi("bold", "su") " │" RS             \
            "│ " getName("zh-TW") " - " ansi("bold", "zh-TW") " │ "     \
            getName("km") "          - " ansi("bold", "km") "  │ "      \
            getName("sw") "    - " ansi("bold", "sw") " │" RS           \
            "│ " getName("hr") "            - " ansi("bold", "hr") "    │ " \
            getName("ko") "         - " ansi("bold", "ko") "  │ "       \
            getName("sv") "    - " ansi("bold", "sv") " │" RS           \
            "│ " getName("cs") "               - " ansi("bold", "cs") "    │ " \
            getName("lo") "            - " ansi("bold", "lo") "  │ "    \
            getName("tg") "      - " ansi("bold", "tg") " │" RS         \
            "│ " getName("da") "              - " ansi("bold", "da") "    │ " \
            getName("la") "          - " ansi("bold", "la") "  │ "      \
            getName("ta") "      - " ansi("bold", "ta") " │" RS         \
            "│ " getName("nl") "               - " ansi("bold", "nl") "    │ " \
            getName("lv") "        - " ansi("bold", "lv") "  │ "        \
            getName("te") "     - " ansi("bold", "te") " │" RS          \
            "│ " getName("en") "             - " ansi("bold", "en") "    │ " \
            getName("lt") "     - " ansi("bold", "lt") "  │ "           \
            getName("th") "       - " ansi("bold", "th") " │" RS        \
            "│ " getName("eo") "           - " ansi("bold", "eo") "    │ " \
            getName("mk") "     - " ansi("bold", "mk") "  │ "           \
            getName("tr") "    - " ansi("bold", "tr") " │" RS           \
            "│ " getName("et") "            - " ansi("bold", "et") "    │ " \
            getName("mg") "       - " ansi("bold", "mg") "  │ "         \
            getName("uk") "  - " ansi("bold", "uk") " │" RS             \
            "│ " getName("tl") "            - " ansi("bold", "tl") "    │ " \
            getName("ms") "          - " ansi("bold", "ms") "  │ "      \
            getName("ur") "       - " ansi("bold", "ur") " │" RS        \
            "│ " getName("fi") "             - " ansi("bold", "fi") "    │ " \
            getName("ml") "      - " ansi("bold", "ml") "  │ "          \
            getName("uz") "      - " ansi("bold", "uz") " │" RS         \
            "│ " getName("fr") "              - " ansi("bold", "fr") "    │ " \
            getName("mt") "        - " ansi("bold", "mt") "  │ "        \
            getName("vi") " - " ansi("bold", "vi") " │" RS              \
            "│ " getName("gl") "            - " ansi("bold", "gl") "    │ " \
            getName("mi") "          - " ansi("bold", "mi") "  │ "      \
            getName("cy") "      - " ansi("bold", "cy") " │" RS         \
            "│ " getName("ka") "            - " ansi("bold", "ka") "    │ " \
            getName("mr") "        - " ansi("bold", "mr") "  │ "        \
            getName("yi") "    - " ansi("bold", "yi") " │" RS           \
            "│ " getName("de") "              - " ansi("bold", "de") "    │ " \
            getName("mn") "      - " ansi("bold", "mn") "  │ "          \
            getName("yo") "     - " ansi("bold", "yo") " │" RS          \
            "│ " getName("el") "               - " ansi("bold", "el") "    │ " \
            getName("my") "        - " ansi("bold", "my") "  │ "        \
            getName("zu") "       - " ansi("bold", "zu") " │" RS        \
            "│ " getName("gu") "            - " ansi("bold", "gu") "    │ " \
            getName("ne") "         - " ansi("bold", "ne") "  │ "       \
            "                │" RS                                      \
            "│ " getName("ht") "      - " ansi("bold", "ht") "    │ "   \
            getName("no") "      - " ansi("bold", "no") "  │ "          \
            "                │" RS                                      \
            "└─────────────────────────────┴──────────────────────┴─────────────────┘"
    else
        return "┌──────────────────────┬───────────────────────┬─────────────────────┐" RS \
            "│ " getDisplay("af") "      - " ansi("bold", "af") "  │ "  \
            getDisplay("hu") "           - " ansi("bold", "hu") " │ "   \
            getDisplay("pl") "      - " ansi("bold", "pl") "    │" RS   \
            "│ " getDisplay("ar") "        - " ansi("bold", "ar") "  │ " \
            getDisplay("hy") "          - " ansi("bold", "hy") " │ "    \
            getDisplay("pt") "   - " ansi("bold", "pt") "    │" RS      \
            "│ " getDisplay("az") "   - " ansi("bold", "az") "  │ "     \
            getDisplay("id") " - " ansi("bold", "id") " │ "             \
            getDisplay("ro") "      - " ansi("bold", "ro") "    │" RS   \
            "│ " getDisplay("be") "     - " ansi("bold", "be") "  │ "   \
            getDisplay("ig") "             - " ansi("bold", "ig") " │ " \
            getDisplay("ru") "     - " ansi("bold", "ru") "    │" RS    \
            "│ " getDisplay("bg") "      - " ansi("bold", "bg") "  │ "  \
            getDisplay("is") "         - " ansi("bold", "is") " │ "     \
            getDisplay("si") "        - " ansi("bold", "si") "    │" RS \
            "│ " getDisplay("bn") "          - " ansi("bold", "bn") "  │ " \
            getDisplay("it") "         - " ansi("bold", "it") " │ "     \
            getDisplay("sk") "  - " ansi("bold", "sk") "    │" RS       \
            "│ " getDisplay("bs") "       - " ansi("bold", "bs") "  │ " \
            getDisplay("ja") "           - " ansi("bold", "ja") " │ "   \
            getDisplay("sl") " - " ansi("bold", "sl") "    │" RS        \
            "│ " getDisplay("ca") "         - " ansi("bold", "ca") "  │ " \
            getDisplay("jv") "        - " ansi("bold", "jv") " │ "      \
            getDisplay("so") "    - " ansi("bold", "so") "    │" RS     \
            "│ " getDisplay("ceb") "        - " ansi("bold", "ceb") " │ " \
            getDisplay("ka") "          - " ansi("bold", "ka") " │ "    \
            getDisplay("sq") "       - " ansi("bold", "sq") "    │" RS  \
            "│ " getDisplay("cs") "        - " ansi("bold", "cs") "  │ " \
            getDisplay("kk") "       - " ansi("bold", "kk") " │ "       \
            getDisplay("sr") "      - " ansi("bold", "sr") "    │" RS   \
            "│ " getDisplay("cy") "        - " ansi("bold", "cy") "  │ " \
            getDisplay("km") "         - " ansi("bold", "km") " │ "     \
            getDisplay("st") "     - " ansi("bold", "st") "    │" RS    \
            "│ " getDisplay("da") "          - " ansi("bold", "da") "  │ " \
            getDisplay("kn") "             - " ansi("bold", "kn") " │ " \
            getDisplay("su") "  - " ansi("bold", "su") "    │" RS       \
            "│ " getDisplay("de") "        - " ansi("bold", "de") "  │ " \
            getDisplay("ko") "           - " ansi("bold", "ko") " │ "   \
            getDisplay("sv") "     - " ansi("bold", "sv") "    │" RS    \
            "│ " getDisplay("el") "       - " ansi("bold", "el") "  │ " \
            getDisplay("la") "           - " ansi("bold", "la") " │ "   \
            getDisplay("sw") "   - " ansi("bold", "sw") "    │" RS      \
            "│ " getDisplay("en") "        - " ansi("bold", "en") "  │ " \
            getDisplay("lo") "              - " ansi("bold", "lo") " │ " \
            getDisplay("ta") "        - " ansi("bold", "ta") "    │" RS \
            "│ " getDisplay("eo") "      - " ansi("bold", "eo") "  │ "  \
            getDisplay("lt") "         - " ansi("bold", "lt") " │ "     \
            getDisplay("te") "       - " ansi("bold", "te") "    │" RS  \
            "│ " getDisplay("es") "        - " ansi("bold", "es") "  │ " \
            getDisplay("lv") "         - " ansi("bold", "lv") " │ "     \
            getDisplay("tg") "      - " ansi("bold", "tg") "    │" RS   \
            "│ " getDisplay("et") "          - " ansi("bold", "et") "  │ " \
            getDisplay("mg") "         - " ansi("bold", "mg") " │ "     \
            getDisplay("th") "         - " ansi("bold", "th") "    │" RS \
            "│ " getDisplay("eu") "        - " ansi("bold", "eu") "  │ " \
            getDisplay("mi") "            - " ansi("bold", "mi") " │ "  \
            getDisplay("tl") "     - " ansi("bold", "tl") "    │" RS    \
            "│ " getDisplay("fa") "          - " ansi("bold", "fa") "  │ " \
            getDisplay("mk") "       - " ansi("bold", "mk") " │ "       \
            getDisplay("tr") "      - " ansi("bold", "tr") "    │" RS   \
            "│ " getDisplay("fi") "          - " ansi("bold", "fi") "  │ " \
            getDisplay("ml") "           - " ansi("bold", "ml") " │ "   \
            getDisplay("uk") "  - " ansi("bold", "uk") "    │" RS       \
            "│ " getDisplay("fr") "       - " ansi("bold", "fr") "  │ " \
            getDisplay("mn") "           - " ansi("bold", "mn") " │ "   \
            getDisplay("ur") "        - " ansi("bold", "ur") "    │" RS \
            "│ " getDisplay("ga") "        - " ansi("bold", "ga") "  │ " \
            getDisplay("mr") "            - " ansi("bold", "mr") " │ "  \
            getDisplay("uz") " - " ansi("bold", "uz") "    │" RS        \
            "│ " getDisplay("gl") "         - " ansi("bold", "gl") "  │ " \
            getDisplay("ms") "    - " ansi("bold", "ms") " │ "          \
            getDisplay("vi") "  - " ansi("bold", "vi") "    │" RS       \
            "│ " getDisplay("gu") "         - " ansi("bold", "gu") "  │ " \
            getDisplay("mt") "            - " ansi("bold", "mt") " │ "  \
            getDisplay("yi") "       - " ansi("bold", "yi") "    │" RS  \
            "│ " getDisplay("ha") "          - " ansi("bold", "ha") "  │ " \
            getDisplay("my") "          - " ansi("bold", "my") " │ "    \
            getDisplay("yo") "      - " ansi("bold", "yo") "    │" RS   \
            "│ " getDisplay("he") "          - " ansi("bold", "he") "  │ " \
            getDisplay("ne") "            - " ansi("bold", "ne") " │ "  \
            getDisplay("zh-CN") "    - " ansi("bold", "zh-CN") " │" RS  \
            "│ " getDisplay("hi") "          - " ansi("bold", "hi") "  │ " \
            getDisplay("nl") "       - " ansi("bold", "nl") " │ "       \
            getDisplay("zh-TW") "    - " ansi("bold", "zh-TW") " │" RS  \
            "│ " getDisplay("hmn") "          - " ansi("bold", "hmn") " │ " \
            getDisplay("no") "            - " ansi("bold", "no") " │ "  \
            getDisplay("zu") "     - " ansi("bold", "zu") "    │" RS    \
            "│ " getDisplay("hr") "       - " ansi("bold", "hr") "  │ " \
            getDisplay("ny") "           - " ansi("bold", "ny") " │ "   \
            "                    │" RS                                  \
            "│ " getDisplay("ht") " - " ansi("bold", "ht") "  │ "       \
            getDisplay("pa") "            - " ansi("bold", "pa") " │ "  \
            "                    │" RS                                  \
            "└──────────────────────┴───────────────────────┴─────────────────────┘"
}

####################################################################
# Help.awk                                                         #
####################################################################

# Return version as a string.
function getVersion(    build, gitHead) {
    if (ENVIRON["TRANS_BUILD"])
        build = "-" ENVIRON["TRANS_BUILD"]
    else {
        gitHead = getGitHead()
        build = gitHead ? "-git:" gitHead : ""
    }

    return sprintf(ansi("bold", "%-22s%s%s") "\n\n", Name, Version, build) \
        sprintf("%-22s%s\n", "gawk (GNU Awk)", PROCINFO["version"])     \
        sprintf("%s\n", FriBidi ? FriBidi : "fribidi (GNU FriBidi) [NOT INSTALLED]") \
        sprintf("%-22s%s\n", "terminal type", ENVIRON["TERM"])          \
        sprintf("%-22s%s (%s)\n", "user locale", UserLocale, getName(UserLang)) \
        sprintf("%-22s%s\n", "home language", Option["hl"])             \
        sprintf("%-22s%s\n", "source language", Option["sl"])           \
        sprintf("%-22s%s\n", "target language", join(Option["tl"], "+")) \
        sprintf("%-22s%s\n", "theme", Option["theme"])                  \
        sprintf("%-22s%s\n", "init file", InitScript)                   \
        sprintf("\n%-22s%s", "Report bugs to:", "https://github.com/soimort/translate-shell/issues")
}

# Return help message as a string.
function getHelp() {
    return "Usage:   " ansi("bold", Command " " ansi("negative", "[OPTIONS]") \
                            " " ansi("negative", "[SOURCE]")            \
                            ":" ansi("negative", "[TARGET(S)]")         \
                            " " ansi("negative", "[TEXT]") " ...") RS   \
        "Example: " ansi("underline", "trans -brief la:en 'per aspera ad astra'") RS \
        RS "Information Options:" RS                                    \
        ins(1, ansi("bold", "-V, -version")) RS                         \
        ins(2, "Print version and exit.") RS                            \
        ins(1, ansi("bold", "-H, -help")) RS                            \
        ins(2, "Print help message and exit.") RS                       \
        ins(1, ansi("bold", "-M, -manual")) RS                          \
        ins(2, "Show man page and exit.") RS                            \
        ins(1, ansi("bold", "-T, -reference")) RS                       \
        ins(2, "Print a reference table of languages and exit.") RS     \
        ins(1, ansi("bold", "-R, -reference-english")) RS               \
        ins(2, "Print a reference table of languages (in English names) and exit.") RS \
        RS "Display Options:" RS                                        \
        ins(1, ansi("bold", "-verbose")) RS                             \
        ins(2, "Verbose mode. (default)") RS                            \
        ins(1, ansi("bold", "-b, -brief")) RS                           \
        ins(2, "Brief mode.") RS                                        \
        ins(1, ansi("bold", "-d, -dictionary")) RS                      \
        ins(2, "Dictionary mode.") RS                                   \
        ins(1, ansi("bold", "-show-original " ansi("negative", "Y|n"))) RS \
        ins(2, "Show original text or not.") RS                         \
        ins(1, ansi("bold", "-show-original-phonetics " ansi("negative", "Y|n"))) RS \
        ins(2, "Show phonetic notation of original text or not.") RS    \
        ins(1, ansi("bold", "-show-translation " ansi("negative", "Y|n"))) RS \
        ins(2, "Show translation or not.") RS                           \
        ins(1, ansi("bold", "-show-translation-phonetics " ansi("negative", "Y|n"))) RS \
        ins(2, "Show phonetic notation of translation or not.") RS      \
        ins(1, ansi("bold", "-show-prompt-message " ansi("negative", "Y|n"))) RS \
        ins(2, "Show prompt message or not.") RS                        \
        ins(1, ansi("bold", "-show-languages " ansi("negative", "Y|n"))) RS \
        ins(2, "Show source and target languages or not.") RS           \
        ins(1, ansi("bold", "-show-original-dictionary " ansi("negative", "y|N"))) RS \
        ins(2, "Show dictionary entry of original text or not.") RS     \
        ins(1, ansi("bold", "-show-dictionary " ansi("negative", "Y|n"))) RS \
        ins(2, "Show dictionary entry of translation or not.") RS       \
        ins(1, ansi("bold", "-show-alternatives " ansi("negative", "Y|n"))) RS \
        ins(2, "Show alternative translations or not.") RS              \
        ins(1, ansi("bold", "-w " ansi("negative", "NUM")               \
                    ", -width " ansi("negative", "NUM"))) RS            \
        ins(2, "Specify the screen width for padding.") RS              \
        ins(1, ansi("bold", "-indent " ansi("negative", "NUM"))) RS     \
        ins(2, "Specify the size of indent (number of spaces).") RS     \
        ins(1, ansi("bold", "-no-ansi")) RS                             \
        ins(2, "Do not use ANSI escape codes.") RS                      \
        ins(1, ansi("bold", "-no-theme")) RS                            \
        ins(2, "Do not use any other theme than default.") RS           \
        ins(1, ansi("bold", "-theme " ansi("negative", "FILENAME"))) RS \
        ins(2, "Specify the theme to use.") RS                          \
        RS "Audio Options:" RS                                          \
        ins(1, ansi("bold", "-no-play")) RS                             \
        ins(2, "Do not listen to the translation.") RS                  \
        ins(1, ansi("bold", "-p, -play")) RS                            \
        ins(2, "Listen to the translation.") RS                         \
        ins(1, ansi("bold", "-player " ansi("negative", "PROGRAM"))) RS \
        ins(2, "Specify the audio player to use, and listen to the translation.") RS \
        RS "Terminal Paging and Web Options:" RS                        \
        ins(1, ansi("bold", "-no-view")) RS                             \
        ins(2, "Do not view the translation in a terminal pager.") RS   \
        ins(1, ansi("bold", "-v, -view")) RS                            \
        ins(2, "View the translation in a terminal pager.") RS          \
        ins(1, ansi("bold", "-pager " ansi("negative", "PROGRAM"))) RS  \
        ins(2, "Specify the terminal pager to use, and view the translation.") RS \
        ins(1, ansi("bold", "-browser " ansi("negative", "PROGRAM"))) RS \
        ins(2, "Specify the web browser to use.") RS                    \
        RS "Networking Options:" RS                                     \
        ins(1, ansi("bold", "-x " ansi("negative", "HOST:PORT")         \
                    ", -proxy " ansi("negative", "HOST:PORT"))) RS      \
        ins(2, "Use HTTP proxy on given port.") RS                      \
        ins(1, ansi("bold", "-u " ansi("negative", "STRING")            \
                    ", -user-agent " ansi("negative", "STRING"))) RS    \
        ins(2, "Specify the User-Agent to identify as.") RS             \
        RS "Interactive Shell Options:" RS                              \
        ins(1, ansi("bold", "-no-rlwrap")) RS                           \
        ins(2, "Do not invoke rlwrap when starting an interactive shell.") RS \
        ins(1, ansi("bold", "-I, -interactive, -shell")) RS             \
        ins(2, "Start an interactive shell.") RS                        \
        ins(1, ansi("bold", "-E, -emacs")) RS                           \
        ins(2, "Start the GNU Emacs front-end for an interactive shell.") RS \
        RS "I/O Options:" RS                                            \
        ins(1, ansi("bold", "-i " ansi("negative", "FILENAME")          \
                    ", -input " ansi("negative", "FILENAME"))) RS       \
        ins(2, "Specify the input file.") RS                            \
        ins(1, ansi("bold", "-o " ansi("negative", "FILENAME")          \
                    ", -output " ansi("negative", "FILENAME"))) RS      \
        ins(2, "Specify the output file.") RS                           \
        RS "Language Preference Options:" RS                            \
        ins(1, ansi("bold", "-l " ansi("negative", "CODE")              \
                    ", -lang " ansi("negative", "CODE"))) RS            \
        ins(2, "Specify your home language.") RS                        \
        ins(1, ansi("bold", "-s " ansi("negative", "CODE")              \
                    ", -source " ansi("negative", "CODE"))) RS          \
        ins(2, "Specify the source language.") RS                       \
        ins(1, ansi("bold", "-t " ansi("negative", "CODE(S)")           \
                    ", -target " ansi("negative", "CODE(S)"))) RS       \
        ins(2, "Specify the target language(s), joined by '+'.") RS     \
        RS "Other Options:" RS                                          \
        ins(1, ansi("bold", "-no-init")) RS                             \
        ins(2, "Do not load any initialization script.") RS             \
        RS "See the man page " Command "(1) for more information."
}

# Display man page.
function showMan(    temp) {
    if (ENVIRON["TRANS_MANPAGE"]) {
        initPager()
        temp = "echo -E \"${TRANS_MANPAGE}\""
        temp = temp PIPE                                                \
            "groff -Wall -mtty-char -mandoc -Tutf8 -rLL=${COLUMNS}n -rLT=${COLUMNS}n"
        switch (Pager) {
        case "less":
            temp = temp PIPE Pager " -s -P\"\\ \\Manual page " Command "(1) line %lt (press h for help or q to quit)\""
            break
        case "most":
            temp = temp PIPE Pager " -Cs"
            break
        default: # more
            temp = temp PIPE Pager
        }
        system(temp)
    } else if (system("man " Command SUPERR))
        print getHelp()
}

# Return a list of language codes as a string.
# Parameters:
#     displayName = "endonym" or "name"
function getReference(displayName) {
    if (displayName == "name")
        return "┌─────────────────────────────┬──────────────────────┬─────────────────┐" RS \
            "│ " getName("af") "           - " ansi("bold", "af") "    │ " \
            getName("ha") "          - " ansi("bold", "ha") "  │ "      \
            getName("fa") "    - " ansi("bold", "fa") " │" RS           \
            "│ " getName("sq") "            - " ansi("bold", "sq") "    │ " \
            getName("he") "         - " ansi("bold", "he") "  │ "       \
            getName("pl") "     - " ansi("bold", "pl") " │" RS          \
            "│ " getName("ar") "              - " ansi("bold", "ar") "    │ " \
            getName("hi") "          - " ansi("bold", "hi") "  │ "      \
            getName("pt") " - " ansi("bold", "pt") " │" RS              \
            "│ " getName("hy") "            - " ansi("bold", "hy") "    │ " \
            getName("hmn") "          - " ansi("bold", "hmn") " │ "     \
            getName("pa") "    - " ansi("bold", "pa") " │" RS           \
            "│ " getName("az") "         - " ansi("bold", "az") "    │ " \
            getName("hu") "      - " ansi("bold", "hu") "  │ "          \
            getName("ro") "   - " ansi("bold", "ro") " │" RS            \
            "│ " getName("eu") "              - " ansi("bold", "eu") "    │ " \
            getName("is") "      - " ansi("bold", "is") "  │ "          \
            getName("ru") "    - " ansi("bold", "ru") " │" RS           \
            "│ " getName("be") "          - " ansi("bold", "be") "    │ " \
            getName("ig") "           - " ansi("bold", "ig") "  │ "     \
            getName("sr") "    - " ansi("bold", "sr") " │" RS           \
            "│ " getName("bn") "             - " ansi("bold", "bn") "    │ " \
            getName("id") "     - " ansi("bold", "id") "  │ "           \
            getName("st") "    - " ansi("bold", "st") " │" RS           \
            "│ " getName("bs") "             - " ansi("bold", "bs") "    │ " \
            getName("ga") "          - " ansi("bold", "ga") "  │ "      \
            getName("si") "    - " ansi("bold", "si") " │" RS           \
            "│ " getName("bg") "           - " ansi("bold", "bg") "    │ " \
            getName("it") "        - " ansi("bold", "it") "  │ "        \
            getName("sk") "     - " ansi("bold", "sk") " │" RS          \
            "│ " getName("ca") "             - " ansi("bold", "ca") "    │ " \
            getName("ja") "       - " ansi("bold", "ja") "  │ "         \
            getName("sl") "  - " ansi("bold", "sl") " │" RS             \
            "│ " getName("ceb") "             - " ansi("bold", "ceb") "   │ " \
            getName("jv") "       - " ansi("bold", "jv") "  │ "         \
            getName("so") "     - " ansi("bold", "so") " │" RS          \
            "│ " getName("ny") "            - " ansi("bold", "ny") "    │ " \
            getName("kn") "        - " ansi("bold", "kn") "  │ "        \
            getName("es") "    - " ansi("bold", "es") " │" RS           \
            "│ " getName("zh-CN") "  - " ansi("bold", "zh-CN") " │ "    \
            getName("kk") "         - " ansi("bold", "kk") "  │ "       \
            getName("su") "  - " ansi("bold", "su") " │" RS             \
            "│ " getName("zh-TW") " - " ansi("bold", "zh-TW") " │ "     \
            getName("km") "          - " ansi("bold", "km") "  │ "      \
            getName("sw") "    - " ansi("bold", "sw") " │" RS           \
            "│ " getName("hr") "            - " ansi("bold", "hr") "    │ " \
            getName("ko") "         - " ansi("bold", "ko") "  │ "       \
            getName("sv") "    - " ansi("bold", "sv") " │" RS           \
            "│ " getName("cs") "               - " ansi("bold", "cs") "    │ " \
            getName("lo") "            - " ansi("bold", "lo") "  │ "    \
            getName("tg") "      - " ansi("bold", "tg") " │" RS         \
            "│ " getName("da") "              - " ansi("bold", "da") "    │ " \
            getName("la") "          - " ansi("bold", "la") "  │ "      \
            getName("ta") "      - " ansi("bold", "ta") " │" RS         \
            "│ " getName("nl") "               - " ansi("bold", "nl") "    │ " \
            getName("lv") "        - " ansi("bold", "lv") "  │ "        \
            getName("te") "     - " ansi("bold", "te") " │" RS          \
            "│ " getName("en") "             - " ansi("bold", "en") "    │ " \
            getName("lt") "     - " ansi("bold", "lt") "  │ "           \
            getName("th") "       - " ansi("bold", "th") " │" RS        \
            "│ " getName("eo") "           - " ansi("bold", "eo") "    │ " \
            getName("mk") "     - " ansi("bold", "mk") "  │ "           \
            getName("tr") "    - " ansi("bold", "tr") " │" RS           \
            "│ " getName("et") "            - " ansi("bold", "et") "    │ " \
            getName("mg") "       - " ansi("bold", "mg") "  │ "         \
            getName("uk") "  - " ansi("bold", "uk") " │" RS             \
            "│ " getName("tl") "            - " ansi("bold", "tl") "    │ " \
            getName("ms") "          - " ansi("bold", "ms") "  │ "      \
            getName("ur") "       - " ansi("bold", "ur") " │" RS        \
            "│ " getName("fi") "             - " ansi("bold", "fi") "    │ " \
            getName("ml") "      - " ansi("bold", "ml") "  │ "          \
            getName("uz") "      - " ansi("bold", "uz") " │" RS         \
            "│ " getName("fr") "              - " ansi("bold", "fr") "    │ " \
            getName("mt") "        - " ansi("bold", "mt") "  │ "        \
            getName("vi") " - " ansi("bold", "vi") " │" RS              \
            "│ " getName("gl") "            - " ansi("bold", "gl") "    │ " \
            getName("mi") "          - " ansi("bold", "mi") "  │ "      \
            getName("cy") "      - " ansi("bold", "cy") " │" RS         \
            "│ " getName("ka") "            - " ansi("bold", "ka") "    │ " \
            getName("mr") "        - " ansi("bold", "mr") "  │ "        \
            getName("yi") "    - " ansi("bold", "yi") " │" RS           \
            "│ " getName("de") "              - " ansi("bold", "de") "    │ " \
            getName("mn") "      - " ansi("bold", "mn") "  │ "          \
            getName("yo") "     - " ansi("bold", "yo") " │" RS          \
            "│ " getName("el") "               - " ansi("bold", "el") "    │ " \
            getName("my") "        - " ansi("bold", "my") "  │ "        \
            getName("zu") "       - " ansi("bold", "zu") " │" RS        \
            "│ " getName("gu") "            - " ansi("bold", "gu") "    │ " \
            getName("ne") "         - " ansi("bold", "ne") "  │ "       \
            "                │" RS                                      \
            "│ " getName("ht") "      - " ansi("bold", "ht") "    │ "   \
            getName("no") "      - " ansi("bold", "no") "  │ "          \
            "                │" RS                                      \
            "└─────────────────────────────┴──────────────────────┴─────────────────┘"
    else
        return "┌──────────────────────┬───────────────────────┬─────────────────────┐" RS \
            "│ " getDisplay("af") "      - " ansi("bold", "af") "  │ "  \
            getDisplay("hu") "           - " ansi("bold", "hu") " │ "   \
            getDisplay("pl") "      - " ansi("bold", "pl") "    │" RS   \
            "│ " getDisplay("ar") "        - " ansi("bold", "ar") "  │ " \
            getDisplay("hy") "          - " ansi("bold", "hy") " │ "    \
            getDisplay("pt") "   - " ansi("bold", "pt") "    │" RS      \
            "│ " getDisplay("az") "   - " ansi("bold", "az") "  │ "     \
            getDisplay("id") " - " ansi("bold", "id") " │ "             \
            getDisplay("ro") "      - " ansi("bold", "ro") "    │" RS   \
            "│ " getDisplay("be") "     - " ansi("bold", "be") "  │ "   \
            getDisplay("ig") "             - " ansi("bold", "ig") " │ " \
            getDisplay("ru") "     - " ansi("bold", "ru") "    │" RS    \
            "│ " getDisplay("bg") "      - " ansi("bold", "bg") "  │ "  \
            getDisplay("is") "         - " ansi("bold", "is") " │ "     \
            getDisplay("si") "        - " ansi("bold", "si") "    │" RS \
            "│ " getDisplay("bn") "          - " ansi("bold", "bn") "  │ " \
            getDisplay("it") "         - " ansi("bold", "it") " │ "     \
            getDisplay("sk") "  - " ansi("bold", "sk") "    │" RS       \
            "│ " getDisplay("bs") "       - " ansi("bold", "bs") "  │ " \
            getDisplay("ja") "           - " ansi("bold", "ja") " │ "   \
            getDisplay("sl") " - " ansi("bold", "sl") "    │" RS        \
            "│ " getDisplay("ca") "         - " ansi("bold", "ca") "  │ " \
            getDisplay("jv") "        - " ansi("bold", "jv") " │ "      \
            getDisplay("so") "    - " ansi("bold", "so") "    │" RS     \
            "│ " getDisplay("ceb") "        - " ansi("bold", "ceb") " │ " \
            getDisplay("ka") "          - " ansi("bold", "ka") " │ "    \
            getDisplay("sq") "       - " ansi("bold", "sq") "    │" RS  \
            "│ " getDisplay("cs") "        - " ansi("bold", "cs") "  │ " \
            getDisplay("kk") "       - " ansi("bold", "kk") " │ "       \
            getDisplay("sr") "      - " ansi("bold", "sr") "    │" RS   \
            "│ " getDisplay("cy") "        - " ansi("bold", "cy") "  │ " \
            getDisplay("km") "         - " ansi("bold", "km") " │ "     \
            getDisplay("st") "     - " ansi("bold", "st") "    │" RS    \
            "│ " getDisplay("da") "          - " ansi("bold", "da") "  │ " \
            getDisplay("kn") "             - " ansi("bold", "kn") " │ " \
            getDisplay("su") "  - " ansi("bold", "su") "    │" RS       \
            "│ " getDisplay("de") "        - " ansi("bold", "de") "  │ " \
            getDisplay("ko") "           - " ansi("bold", "ko") " │ "   \
            getDisplay("sv") "     - " ansi("bold", "sv") "    │" RS    \
            "│ " getDisplay("el") "       - " ansi("bold", "el") "  │ " \
            getDisplay("la") "           - " ansi("bold", "la") " │ "   \
            getDisplay("sw") "   - " ansi("bold", "sw") "    │" RS      \
            "│ " getDisplay("en") "        - " ansi("bold", "en") "  │ " \
            getDisplay("lo") "              - " ansi("bold", "lo") " │ " \
            getDisplay("ta") "        - " ansi("bold", "ta") "    │" RS \
            "│ " getDisplay("eo") "      - " ansi("bold", "eo") "  │ "  \
            getDisplay("lt") "         - " ansi("bold", "lt") " │ "     \
            getDisplay("te") "       - " ansi("bold", "te") "    │" RS  \
            "│ " getDisplay("es") "        - " ansi("bold", "es") "  │ " \
            getDisplay("lv") "         - " ansi("bold", "lv") " │ "     \
            getDisplay("tg") "      - " ansi("bold", "tg") "    │" RS   \
            "│ " getDisplay("et") "          - " ansi("bold", "et") "  │ " \
            getDisplay("mg") "         - " ansi("bold", "mg") " │ "     \
            getDisplay("th") "         - " ansi("bold", "th") "    │" RS \
            "│ " getDisplay("eu") "        - " ansi("bold", "eu") "  │ " \
            getDisplay("mi") "            - " ansi("bold", "mi") " │ "  \
            getDisplay("tl") "     - " ansi("bold", "tl") "    │" RS    \
            "│ " getDisplay("fa") "          - " ansi("bold", "fa") "  │ " \
            getDisplay("mk") "       - " ansi("bold", "mk") " │ "       \
            getDisplay("tr") "      - " ansi("bold", "tr") "    │" RS   \
            "│ " getDisplay("fi") "          - " ansi("bold", "fi") "  │ " \
            getDisplay("ml") "           - " ansi("bold", "ml") " │ "   \
            getDisplay("uk") "  - " ansi("bold", "uk") "    │" RS       \
            "│ " getDisplay("fr") "       - " ansi("bold", "fr") "  │ " \
            getDisplay("mn") "           - " ansi("bold", "mn") " │ "   \
            getDisplay("ur") "        - " ansi("bold", "ur") "    │" RS \
            "│ " getDisplay("ga") "        - " ansi("bold", "ga") "  │ " \
            getDisplay("mr") "            - " ansi("bold", "mr") " │ "  \
            getDisplay("uz") " - " ansi("bold", "uz") "    │" RS        \
            "│ " getDisplay("gl") "         - " ansi("bold", "gl") "  │ " \
            getDisplay("ms") "    - " ansi("bold", "ms") " │ "          \
            getDisplay("vi") "  - " ansi("bold", "vi") "    │" RS       \
            "│ " getDisplay("gu") "         - " ansi("bold", "gu") "  │ " \
            getDisplay("mt") "            - " ansi("bold", "mt") " │ "  \
            getDisplay("yi") "       - " ansi("bold", "yi") "    │" RS  \
            "│ " getDisplay("ha") "          - " ansi("bold", "ha") "  │ " \
            getDisplay("my") "          - " ansi("bold", "my") " │ "    \
            getDisplay("yo") "      - " ansi("bold", "yo") "    │" RS   \
            "│ " getDisplay("he") "          - " ansi("bold", "he") "  │ " \
            getDisplay("ne") "            - " ansi("bold", "ne") " │ "  \
            getDisplay("zh-CN") "    - " ansi("bold", "zh-CN") " │" RS  \
            "│ " getDisplay("hi") "          - " ansi("bold", "hi") "  │ " \
            getDisplay("nl") "       - " ansi("bold", "nl") " │ "       \
            getDisplay("zh-TW") "    - " ansi("bold", "zh-TW") " │" RS  \
            "│ " getDisplay("hmn") "          - " ansi("bold", "hmn") " │ " \
            getDisplay("no") "            - " ansi("bold", "no") " │ "  \
            getDisplay("zu") "     - " ansi("bold", "zu") "    │" RS    \
            "│ " getDisplay("hr") "       - " ansi("bold", "hr") "  │ " \
            getDisplay("ny") "           - " ansi("bold", "ny") " │ "   \
            "                    │" RS                                  \
            "│ " getDisplay("ht") " - " ansi("bold", "ht") "  │ "       \
            getDisplay("pa") "            - " ansi("bold", "pa") " │ "  \
            "                    │" RS                                  \
            "└──────────────────────┴───────────────────────┴─────────────────────┘"
}