Remove string from \/(in regex, / in plain text) from regex lines.

#!/usr/bin/awk -f

# Add domain name to global variable domains.
function add(domain) {
	domain_count = domain_count + 1
	domains[domain_count] = domain
}

# Extract domain name from non-regex line
function extract(line, original_line) {
	sub(/.*:\/\//, "", line) # remove everything till ://, such as http:// https://
	sub(/\/.*/, "", line)    # remove everything from /
	sub(/:.*/, "", line)     # remove everything from :
	sub(/\*/, "", line)      # remove *
	sub(/^\./, "", line)     # remove leading dot

	if (match(line, /([a-zA-Z0-9\-]+\.)+[a-zA-Z0-9\-]+/)) {
		add(line)
	} else {
		print "[WARN] <NR>" NR "</NR><original>" original_line "</original><extracted>" line "</extracted>" | "cat >&2"
	}
}

# Extract domain name from regex line
function extract_regex(line, original_line) {

	# Expand the lines like (aa|bb|cc) into multiple records
	pos = match(line, /\([a-zA-Z0-9\.\|]+\)/)
	if (pos != 0) {
		bracket = substr(line, RSTART + 1, RLENGTH - 2)
		n = split(bracket, a, "|")
		for (i = 1; i <= n; i++) {
			expanded_line = line
			sub(/\([a-zA-Z0-9\.\|]+\)/, a[i], expanded_line)
			extract_regex(expanded_line, original_line)
		}
	} else {
		sub(/.*:\\\/\\\//, "", line)    # remove everything till :\/\/ in regex, :// in plain text, such as http:// https://
		gsub(/\([^\)]+\)\*?/, "", line) # remove (...)*? such as ([^\/]+\.)
		gsub(/\[.*\]\+/, "", line)      # remove [...]
		sub(/\\\/.*/, "", line)         # remove everything from \/ in regex, / in plaint text
		gsub(/\\\./, ".", line)         # replace \. to .
		extract(line, original_line)
	}
}

BEGIN {
	"date \"+%Y-%m-%d %H:%M:%S\"" | getline now
	print "# gfw list ipset rules for dnsmasq"
	print "# updated on " now
	print "#"

	if (host == "") {
		host = "127.0.0.1"
	}
	if (port == "") {
		port = 5353
	}

	domain_count = 0
}
{
	original_line = $0

	if (/^#/) {
		# #
		# comments in user_rule.txt
	} else if (/^\!/) {
		# !
		# https://adblockplus.org/en/filters#comments
		# comments
	} else if (/^\[/) {
		# [
		# comments
	} else if (/^@@/) {
		# @@
		# https://adblockplus.org/en/filters#whitelist
	} else if (/^$/) {
		# Empty line
	} else if (/^\|\|/) {
		# ||
		# https://adblockplus.org/en/filters#anchors
		sub(/^\|\|/, "") # remove leading ||
		extract($0, original_line)
	} else if (/^\|/) {
		# |
		# https://adblockplus.org/en/filters#anchors
		sub(/^\|/, "") # removing leading |
		extract($0, original_line)
	} else if (/^\/.*\/$/) {
		# /.../
		# https://adblockplus.org/en/filters#regexps
		sub(/^\//, "") # remove leading /
		sub(/\/$/, "") # remove tailing /
		extract_regex($0, original_line)
	} else if (/^\./) {
		# start with dot
		extract($0, original_line)
	} else {
		extract($0, original_line)
	}
}
END {
	for (i = 1; i <= domain_count; i++) {
		printf "server=/.%s/%s#%d\n", domains[i], host, port | "sort | uniq"
	}
}

#!/usr/bin/awk -f

# Add domain name to global variable domains.
function add(domain) {
	domain_count = domain_count + 1
	domains[domain_count] = domain
}

# Extract domain name from non-regex line
function extract(line, original_line) {
	sub(/.*:\/\//, "", line) # remove everything till ://, such as http:// https://
	sub(/\/.*/, "", line)    # remove everything from /
	sub(/:.*/, "", line)     # remove everything from :
	sub(/\*/, "", line)      # remove *
	sub(/^\./, "", line)     # remove leading dot

	if (match(line, /([a-zA-Z0-9\-]+\.)+[a-zA-Z0-9\-]+/)) {
		add(line)
	} else {
		print "[WARN] <NR>" NR "</NR><original>" original_line "</original><extracted>" line "</extracted>" | "cat >&2"
	}
}

# Extract domain name from regex line
function extract_regex(line, original_line) {

	# Expand the lines like (aa|bb|cc) into multiple records
	pos = match(line, /\([a-zA-Z0-9\.\|]+\)/)
	if (pos != 0) {
		bracket = substr(line, RSTART + 1, RLENGTH - 2)
		n = split(bracket, a, "|")
		for (i = 1; i <= n; i++) {
			expanded_line = line
			sub(/\([a-zA-Z0-9\.\|]+\)/, a[i], expanded_line)
			extract_regex(expanded_line, original_line)
		}
	} else {
		sub(/.*:\\\/\\\//, "", line)    # remove everything till :\/\/
		gsub(/\([^\)]+\)\*?/, "", line) # remove (...)*? such as ([^\/]+\.)
		gsub(/\[.*\]\+/, "", line)      # remove [...]
		gsub(/\\\./, ".", line)         # replace \. to .
		extract(line, original_line)
	}
}

BEGIN {
	"date \"+%Y-%m-%d %H:%M:%S\"" | getline now
	print "# gfw list ipset rules for dnsmasq"
	print "# updated on " now
	print "#"

	if (host == "") {
		host = "127.0.0.1"
	}
	if (port == "") {
		port = 5353
	}

	domain_count = 0
}
{
	original_line = $0

	if (/^#/) {
		# #
		# comments in user_rule.txt
	} else if (/^\!/) {
		# !
		# https://adblockplus.org/en/filters#comments
		# comments
	} else if (/^\[/) {
		# [
		# comments
	} else if (/^@@/) {
		# @@
		# https://adblockplus.org/en/filters#whitelist
	} else if (/^$/) {
		# Empty line
	} else if (/^\|\|/) {
		# ||
		# https://adblockplus.org/en/filters#anchors
		sub(/^\|\|/, "") # remove leading ||
		extract($0, original_line)
	} else if (/^\|/) {
		# |
		# https://adblockplus.org/en/filters#anchors
		sub(/^\|/, "") # removing leading |
		extract($0, original_line)
	} else if (/^\/.*\/$/) {
		# /.../
		# https://adblockplus.org/en/filters#regexps
		sub(/^\//, "") # remove leading /
		sub(/\/$/, "") # remove tailing /
		extract_regex($0, original_line)
	} else if (/^\./) {
		# start with dot
		extract($0, original_line)
	} else {
		extract($0, original_line)
	}
}
END {
	for (i = 1; i <= domain_count; i++) {
		printf "server=/.%s/%s#%d\n", domains[i], host, port | "sort | uniq"
	}
}


Translate: unquote user-name for basic authentication (per #193)

####################################################################
# Translate.awk                                                    #
####################################################################

function provides(engineName) {
    Translator[tolower(engineName)] = TRUE
}

function engineMethod(methodName,    engine, translator) {
    if (!Translator[Option["engine"]]) {
        # case-insensitive match engine name
        engine = tolower(Option["engine"])
        if (!Translator[engine]) # fuzzy match engine name
            for (translator in Translator)
                if (Translator[translator] && # there IS such a translator
                    translator ~ "^"engine) {
                    engine = translator
                    break
                }
        if (!Translator[engine]) {
            e("[ERROR] Translator not found: " Option["engine"] "\n"    \
              "        Run '-list-engines / -S' to see a list of available engines.")
            exit 1
        }
        Option["engine"] = engine
    }
    return Option["engine"] methodName
}

# Detect external audio player (mplayer, mpv, mpg123).
function initAudioPlayer() {
    AudioPlayer = !system("mplayer" SUPOUT SUPERR) ?
        "mplayer" :
        (!system("mpv" SUPOUT SUPERR) ?
         "mpv" :
         (!system("mpg123 --version" SUPOUT SUPERR) ?
          "mpg123" :
          ""))
}

# Detect external speech synthesizer (say, espeak).
function initSpeechSynthesizer() {
    SpeechSynthesizer = !system("say ''" SUPOUT SUPERR) ?
        "say" :
        (!system("espeak ''" SUPOUT SUPERR) ?
         "espeak" :
         "")
}

# Detect external terminal pager (less, more, most).
function initPager() {
    Pager = !system("less -V" SUPOUT SUPERR) ?
        "less" :
        (!system("more -V" SUPOUT SUPERR) ?
         "more" :
         (!system("most" SUPOUT SUPERR) ?
          "most" :
          ""))
}

# Initialize `HttpService`.
function initHttpService() {
    _Init()

    if (Option["proxy"]) {
        match(Option["proxy"], /^(http:\/*)?(([^:]+):([^@]+)@)?([^\/]*):([^\/:]*)/, HttpProxySpec)
        HttpAuthUser = HttpProxySpec[3]
        HttpAuthPass = HttpProxySpec[4]
        HttpService = "/inet/tcp/0/" HttpProxySpec[5] "/" HttpProxySpec[6]
        HttpPathPrefix = HttpProtocol HttpHost
    } else {
        HttpService = "/inet/tcp/0/" HttpHost "/" HttpPort
        HttpPathPrefix = ""
    }
}

# Pre-process string (URL-encode before send).
function preprocess(text) {
    return quote(text)
}

# [OBSOLETE] Is this function still relevant?
# Post-process string (remove any redundant whitespace).
function postprocess(text) {
    text = gensub(/ ([.,;:?!"])/, "\\1", "g", text)
    text = gensub(/(["]) /, "\\1", "g", text)
    return text
}

# Send an HTTP GET request and get response from an online translator.
function getResponse(text, sl, tl, hl,    content, header, isBody, url) {
    url = _RequestUrl(text, sl, tl, hl)

    header = "GET " url " HTTP/1.1\n"           \
        "Host: " HttpHost "\n"                  \
        "Connection: close\n"
    if (Option["user-agent"])
        header = header "User-Agent: " Option["user-agent"] "\n"
    if (Cookie)
        header = header "Cookie: " Cookie "\n"
    if (HttpAuthUser && HttpAuthPass)
        # TODO: digest auth
        header = header "Proxy-Authorization: Basic "   \
            base64(unquote(HttpAuthUser) ":" HttpAuthPass) "\n"

    content = NULLSTR; isBody = 0
    print header |& HttpService
    while ((HttpService |& getline) > 0) {
        if (isBody)
            content = content ? content "\n" $0 : $0
        else if (length($0) <= 1)
            isBody = 1
        l(sprintf("%4s bytes > %s", length($0), $0))
    }
    close(HttpService)

    return assert(content, "[ERROR] Null response.")
}

# Print a string (to output file or terminal pager).
function p(string) {
    if (Option["view"])
        print string | Option["pager"]
    else
        print string > Option["output"]
}

# Play using a Text-to-Speech engine.
function play(text, tl,    url) {
    url = _TTSUrl(text, tl)

    # Don't use getline from pipe here - the same pipe will be run only once for each AWK script!
    system(Option["player"] " " parameterize(url) SUPOUT SUPERR)
}

# Get the translation of a string.
function getTranslation(text, sl, tl, hl,
                        isVerbose, toSpeech, returnPlaylist, returnIl) {
    return _Translate(text, sl, tl, hl,
                      isVerbose, toSpeech, returnPlaylist, returnIl)
}

# Translate a file.
function fileTranslation(uri,    group, temp1, temp2) {
    temp1 = Option["input"]
    temp2 = Option["verbose"]

    match(uri, /^file:\/\/(.*)/, group)
    Option["input"] = group[1]
    Option["verbose"] = 0

    translateMain()

    Option["input"] = temp1
    Option["verbose"] = temp2
}

# Start a browser session and translate a web page.
function webTranslation(uri, sl, tl, hl) {
    system(Option["browser"] " "                                \
           parameterize(_WebTranslateUrl(uri, sl, tl, hl)) "&")
}

# Translate the source text (into all target languages).
function translate(text, inline,
                   ####
                   i, j, playlist, il, saveSortedIn) {

    if (!getCode(Option["hl"])) {
        # Check if home language is supported
        w("[WARNING] Unknown language code: " Option["hl"] ", fallback to English: en")
        Option["hl"] = "en" # fallback to English
    } else if (isRTL(Option["hl"])) {
        # Check if home language is R-to-L
        if (!FriBidi)
            w("[WARNING] " getName(Option["hl"]) " is a right-to-left language, but FriBidi is not found.")
    }

    if (!getCode(Option["sl"])) {
        # Check if source language is supported
        w("[WARNING] Unknown source language code: " Option["sl"])
    } else if (isRTL(Option["sl"])) {
        # Check if source language is R-to-L
        if (!FriBidi)
            w("[WARNING] " getName(Option["sl"]) " is a right-to-left language, but FriBidi is not found.")
    }

    saveSortedIn = PROCINFO["sorted_in"]
    PROCINFO["sorted_in"] = "@ind_num_asc"
    for (i in Option["tl"]) {
        # Non-interactive verbose mode: separator between targets
        if (!Option["interactive"])
            if (Option["verbose"] && i > 1)
                p(prettify("target-seperator", replicate(Option["chr-target-seperator"], Option["width"])))

        if (inline &&
            startsWithAny(text, UriSchemes) == "file://") {
            # translate URL only from command-line parameters (inline)
            fileTranslation(text)
        } else if (inline &&
                   startsWithAny(text, UriSchemes) == "http://" ||
                   startsWithAny(text, UriSchemes) == "https://") {
            # translate URL only from command-line parameters (inline)
            webTranslation(text, Option["sl"], Option["tl"][i], Option["hl"])
        } else {
            p(getTranslation(text, Option["sl"], Option["tl"][i], Option["hl"], Option["verbose"], Option["play"], playlist, il))

            if (Option["play"] == 1) {
                if (Option["player"])
                    for (j in playlist)
                        play(playlist[j]["text"], playlist[j]["tl"])
                else if (SpeechSynthesizer)
                    for (j in playlist)
                        print playlist[j]["text"] | SpeechSynthesizer
            } else if (Option["play"] == 2) {
                if (Option["player"])
                    play(text, il[0])
                else if (SpeechSynthesizer)
                    print text | SpeechSynthesizer
            }
        }
    }
    PROCINFO["sorted_in"] = saveSortedIn
}

# Read from input and translate each line.
function translateMain(    i, line) {
    if (Option["interactive"])
        prompt()

    if (Option["input"] == STDIN || fileExists(Option["input"])) {
        i = 0
        while (getline line < Option["input"])
            if (line) {
                # Non-interactive verbose mode: separator between sources
                if (!Option["interactive"])
                    if (Option["verbose"] && i++ > 0)
                        p(prettify("source-seperator",
                                   replicate(Option["chr-source-seperator"],
                                             Option["width"])))

                if (Option["interactive"])
                    repl(line)
                else
                    translate(line)
            } else {
                # Non-interactive brief mode: preserve line breaks
                if (!Option["interactive"])
                    if (!Option["verbose"])
                        p(line)
            }
    } else
        e("[ERROR] File not found: " Option["input"])
}

####################################################################
# Translate.awk                                                    #
####################################################################

function provides(engineName) {
    Translator[tolower(engineName)] = TRUE
}

function engineMethod(methodName,    engine, translator) {
    if (!Translator[Option["engine"]]) {
        # case-insensitive match engine name
        engine = tolower(Option["engine"])
        if (!Translator[engine]) # fuzzy match engine name
            for (translator in Translator)
                if (Translator[translator] && # there IS such a translator
                    translator ~ "^"engine) {
                    engine = translator
                    break
                }
        if (!Translator[engine]) {
            e("[ERROR] Translator not found: " Option["engine"] "\n"    \
              "        Run '-list-engines / -S' to see a list of available engines.")
            exit 1
        }
        Option["engine"] = engine
    }
    return Option["engine"] methodName
}

# Detect external audio player (mplayer, mpv, mpg123).
function initAudioPlayer() {
    AudioPlayer = !system("mplayer" SUPOUT SUPERR) ?
        "mplayer" :
        (!system("mpv" SUPOUT SUPERR) ?
         "mpv" :
         (!system("mpg123 --version" SUPOUT SUPERR) ?
          "mpg123" :
          ""))
}

# Detect external speech synthesizer (say, espeak).
function initSpeechSynthesizer() {
    SpeechSynthesizer = !system("say ''" SUPOUT SUPERR) ?
        "say" :
        (!system("espeak ''" SUPOUT SUPERR) ?
         "espeak" :
         "")
}

# Detect external terminal pager (less, more, most).
function initPager() {
    Pager = !system("less -V" SUPOUT SUPERR) ?
        "less" :
        (!system("more -V" SUPOUT SUPERR) ?
         "more" :
         (!system("most" SUPOUT SUPERR) ?
          "most" :
          ""))
}

# Initialize `HttpService`.
function initHttpService() {
    _Init()

    if (Option["proxy"]) {
        match(Option["proxy"], /^(http:\/*)?(([^:]+):([^@]+)@)?([^\/]*):([^\/:]*)/, HttpProxySpec)
        HttpAuthUser = HttpProxySpec[3]
        HttpAuthPass = HttpProxySpec[4]
        HttpService = "/inet/tcp/0/" HttpProxySpec[5] "/" HttpProxySpec[6]
        HttpPathPrefix = HttpProtocol HttpHost
    } else {
        HttpService = "/inet/tcp/0/" HttpHost "/" HttpPort
        HttpPathPrefix = ""
    }
}

# Pre-process string (URL-encode before send).
function preprocess(text) {
    return quote(text)
}

# [OBSOLETE] Is this function still relevant?
# Post-process string (remove any redundant whitespace).
function postprocess(text) {
    text = gensub(/ ([.,;:?!"])/, "\\1", "g", text)
    text = gensub(/(["]) /, "\\1", "g", text)
    return text
}

# Send an HTTP GET request and get response from an online translator.
function getResponse(text, sl, tl, hl,    content, header, isBody, url) {
    url = _RequestUrl(text, sl, tl, hl)

    header = "GET " url " HTTP/1.1\n"           \
        "Host: " HttpHost "\n"                  \
        "Connection: close\n"
    if (Option["user-agent"])
        header = header "User-Agent: " Option["user-agent"] "\n"
    if (Cookie)
        header = header "Cookie: " Cookie "\n"
    if (HttpAuthUser && HttpAuthPass)
        # TODO: digest auth
        header = header "Proxy-Authorization: Basic "   \
            base64(HttpAuthUser ":" HttpAuthPass) "\n"

    content = NULLSTR; isBody = 0
    print header |& HttpService
    while ((HttpService |& getline) > 0) {
        if (isBody)
            content = content ? content "\n" $0 : $0
        else if (length($0) <= 1)
            isBody = 1
        l(sprintf("%4s bytes > %s", length($0), $0))
    }
    close(HttpService)

    return assert(content, "[ERROR] Null response.")
}

# Print a string (to output file or terminal pager).
function p(string) {
    if (Option["view"])
        print string | Option["pager"]
    else
        print string > Option["output"]
}

# Play using a Text-to-Speech engine.
function play(text, tl,    url) {
    url = _TTSUrl(text, tl)

    # Don't use getline from pipe here - the same pipe will be run only once for each AWK script!
    system(Option["player"] " " parameterize(url) SUPOUT SUPERR)
}

# Get the translation of a string.
function getTranslation(text, sl, tl, hl,
                        isVerbose, toSpeech, returnPlaylist, returnIl) {
    return _Translate(text, sl, tl, hl,
                      isVerbose, toSpeech, returnPlaylist, returnIl)
}

# Translate a file.
function fileTranslation(uri,    group, temp1, temp2) {
    temp1 = Option["input"]
    temp2 = Option["verbose"]

    match(uri, /^file:\/\/(.*)/, group)
    Option["input"] = group[1]
    Option["verbose"] = 0

    translateMain()

    Option["input"] = temp1
    Option["verbose"] = temp2
}

# Start a browser session and translate a web page.
function webTranslation(uri, sl, tl, hl) {
    system(Option["browser"] " "                                \
           parameterize(_WebTranslateUrl(uri, sl, tl, hl)) "&")
}

# Translate the source text (into all target languages).
function translate(text, inline,
                   ####
                   i, j, playlist, il, saveSortedIn) {

    if (!getCode(Option["hl"])) {
        # Check if home language is supported
        w("[WARNING] Unknown language code: " Option["hl"] ", fallback to English: en")
        Option["hl"] = "en" # fallback to English
    } else if (isRTL(Option["hl"])) {
        # Check if home language is R-to-L
        if (!FriBidi)
            w("[WARNING] " getName(Option["hl"]) " is a right-to-left language, but FriBidi is not found.")
    }

    if (!getCode(Option["sl"])) {
        # Check if source language is supported
        w("[WARNING] Unknown source language code: " Option["sl"])
    } else if (isRTL(Option["sl"])) {
        # Check if source language is R-to-L
        if (!FriBidi)
            w("[WARNING] " getName(Option["sl"]) " is a right-to-left language, but FriBidi is not found.")
    }

    saveSortedIn = PROCINFO["sorted_in"]
    PROCINFO["sorted_in"] = "@ind_num_asc"
    for (i in Option["tl"]) {
        # Non-interactive verbose mode: separator between targets
        if (!Option["interactive"])
            if (Option["verbose"] && i > 1)
                p(prettify("target-seperator", replicate(Option["chr-target-seperator"], Option["width"])))

        if (inline &&
            startsWithAny(text, UriSchemes) == "file://") {
            # translate URL only from command-line parameters (inline)
            fileTranslation(text)
        } else if (inline &&
                   startsWithAny(text, UriSchemes) == "http://" ||
                   startsWithAny(text, UriSchemes) == "https://") {
            # translate URL only from command-line parameters (inline)
            webTranslation(text, Option["sl"], Option["tl"][i], Option["hl"])
        } else {
            p(getTranslation(text, Option["sl"], Option["tl"][i], Option["hl"], Option["verbose"], Option["play"], playlist, il))

            if (Option["play"] == 1) {
                if (Option["player"])
                    for (j in playlist)
                        play(playlist[j]["text"], playlist[j]["tl"])
                else if (SpeechSynthesizer)
                    for (j in playlist)
                        print playlist[j]["text"] | SpeechSynthesizer
            } else if (Option["play"] == 2) {
                if (Option["player"])
                    play(text, il[0])
                else if (SpeechSynthesizer)
                    print text | SpeechSynthesizer
            }
        }
    }
    PROCINFO["sorted_in"] = saveSortedIn
}

# Read from input and translate each line.
function translateMain(    i, line) {
    if (Option["interactive"])
        prompt()

    if (Option["input"] == STDIN || fileExists(Option["input"])) {
        i = 0
        while (getline line < Option["input"])
            if (line) {
                # Non-interactive verbose mode: separator between sources
                if (!Option["interactive"])
                    if (Option["verbose"] && i++ > 0)
                        p(prettify("source-seperator",
                                   replicate(Option["chr-source-seperator"],
                                             Option["width"])))

                if (Option["interactive"])
                    repl(line)
                else
                    translate(line)
            } else {
                # Non-interactive brief mode: preserve line breaks
                if (!Option["interactive"])
                    if (!Option["verbose"])
                        p(line)
            }
    } else
        e("[ERROR] File not found: " Option["input"])
}


update

update

###########################################################
## generate proto payload struct
##
@include "client/header.awk"
BEGIN { RS = "==="; FS ="\n"
print ""
}
{

	pack_code = ""
	unpack_code = ""
	for (i=1;i<=NF;i++)
	{
		if ($i ~ /[A-Za-z_]+=/) {
			name = substr($i,1, match($i,/=/)-1)
			print "\npublic class " name " {"
			typeok = "true"
		} else {
			if ($i!="" && typeok) {
				print(_field($i))
				pack_code = pack_code "\t\t" _writer($i)
				unpack_code = unpack_code  _reader($i)
			}
		}

	}

	if (typeok) {
		#writer
		print "\n\tpublic void Pack(ByteArray w) {"
		print pack_code
		print "\t}"
		#reader
		print "\tpublic  static "name" UnPack(ByteArray reader){"
		print "\t\t"name " tbl = new " name "();"
		print unpack_code
		print "\t\treturn tbl;\n\t}"

		print "}"
	}

	typeok=false
}
END { }

function _field(line) {
	split(line, a, " ")

	if (a[2] in TYPES) {
		return "\tpublic " TYPES[a[2]] " " a[1] ";"
	} else if (a[2] == "array") {
		if (a[3] in TYPES) {
			return "\tpublic " TYPES[a[3]] "[] " a[1] ";"
		} else {
			return "\tpublic " a[3] "[] " a[1] ";"
		}
	} else {
		return "\tpublic " a[2]" " a[1] ";"
	}
}

function _writer(line) {
	split(line, a, " ")

	if (a[2] in WRITERS) {
		return "w." WRITERS[a[2]] "(this." a[1] ");\n"
	} else if (a[2] == "array") {
		ret = "w.WriteU16(uint16(len(this." a[1] ")));\n"
		if (a[3] == "byte") {
			ret = ret "w.WriteRawBytes(this."a[1]");\n"
			return ret
		} else if (a[3] in TYPES) {
			ret = ret "\tforeach (int k in this." a[1] ") {\n"
				ret = ret "\t\tw." WRITERS[a[3]] "(this." a[1] "[k]);\n"
			return ret "\t}\n"
		} else {
			ret = ret "\tforeach (int k in this." a[1] ") {\n"
				ret = ret "\t\tthis."a[1]"[k].Pack(w);\n"
			return ret "\t}\n"
		}
	} else {
		return "this." a[1] ".Pack(w);\n"
	}
}

function _reader(line){
	if (line ~ /^#.*/ || line ~ /^===/) {
		return ""
	}
	ret =  "\t"
	split(line, a, " ")
	if (a[2] ==  "array") {
		if (a[3] == "byte") { 		## bytes
			ret = ret "\ttbl."a[1]" = reader.ReadBytes();"

		} else if (a[3] in READERS) {	## primitives

			ret = ret "\tshort narr = reader.ReadU16();\n"
			ret = ret "\t\tfor (int i = 0; i < narr; i++) {\n"
			ret = ret "\t\t\ttbl."a[1]"[i] =reader."READERS[a[3]]"();\n"
			#ret = ret "\t\t\ttbl."a[1]"[i] =  v;\n"
			ret = ret "\t\t}"

		} else {	## struct

			ret = ret "\tshort narr = reader.ReadU16();\n"
			ret = ret "\t\ttbl."a[1]" = new "a[3]"[narr];\n"
			ret = ret "\t\tfor (int i = 0; i < narr; i++){\n"
			ret = ret "\t\t\ttbl."a[1]"[i] = "a[3]".Unpack(reader);\n"
			ret = ret "\t\t}"

		}
	} else if (!(a[2] in READERS)) {
		ret = ret "\ttbl."a[1]"  = "a[2]".UnPack(reader);"
	} else {
		ret = ret "\ttbl."a[1]"  = reader." READERS[a[2]] "();"
	}
	return ret "\n";
}

###########################################################
## generate proto payload struct
##
@include "client/header.awk"
BEGIN { RS = "==="; FS ="\n"
print ""
}
{

	pack_code = ""
	unpack_code = ""
	for (i=1;i<=NF;i++)
	{
		if ($i ~ /[A-Za-z_]+=/) {
			name = substr($i,1, match($i,/=/)-1)
			print "\npublic class " name " {"
			typeok = "true"
		} else {
			if ($i!="" && typeok) {
				print(_field($i))
				pack_code = pack_code "\t\t" _writer($i)
				unpack_code = unpack_code  _reader($i)
			}
		}

	}

	if (typeok) {
		#writer
		print "\n\tpublic void Pack(ByteArray w) {"
		print pack_code
		print "\t}"
		#reader
		print "\tpublic  static "name" UnPack(ByteArray reader){"
		print "\t\t"name " tbl = new " name "();"
		print unpack_code
		print "\t\treturn tbl;\n\t}"

		print "}"
	}

	typeok=false
}
END { }

function _field(line) {
	split(line, a, " ")

	if (a[2] in TYPES) {
		return "\tpublic " TYPES[a[2]] " " a[1] ";"
	} else if (a[2] == "array") {
		if (a[3] in TYPES) {
			return "\tpublic " TYPES[a[3]] "[] " a[1] ";"
		} else {
			return "\tpublic " a[3] "[] " a[1] ";"
		}
	} else {
		return "\tpublic " a[2]" " a[1] ";"
	}
}

function _writer(line) {
	split(line, a, " ")

	if (a[2] in WRITERS) {
		return "w." WRITERS[a[2]] "(this." a[1] ");\n"
	} else if (a[2] == "array") {
		ret = "w.WriteU16(uint16(len(this." a[1] ")));\n"
		if (a[3] == "byte") {
			ret = ret "w.WriteRawBytes(this."a[1]");\n"
			return ret
		} else if (a[3] in TYPES) {
			ret = ret "\tforeach (int k in this." a[1] ") {\n"
				ret = ret "\t\tw." WRITERS[a[3]] "(this." a[1] "[k]);\n"
			return ret "\t}\n"
		} else {
			ret = ret "\tforeach (int k in this." a[1] ") {\n"
				ret = ret "\t\tthis."a[1]"[k].Pack(w);\n"
			return ret "\t}\n"
		}
	} else {
		return "this." a[1] ".Pack(w);\n"
	}
}

function _reader(line){
	if (line ~ /^#.*/ || line ~ /^===/) {
		return ""
	}
	ret =  "\t"
	split(line, a, " ")
	if (a[2] ==  "array") {
		if (a[3] == "byte") { 		## bytes
			ret = ret "\ttbl."a[1]" = reader.ReadBytes();"

		} else if (a[3] in READERS) {	## primitives

			ret = ret "\tshort narr = reader.ReadU16();"
			ret = ret "\tfor (int i = 0; i < narr; i++) {"
			ret = ret "\t\tv := reader."READERS[a[3]]"();"
			ret = ret "\t\ttbl."a[1]" = append(tbl."a[1]", v);"
			ret = ret "\t}\n"

		} else {	## struct

			ret = ret "\tshort narr = reader.ReadU16();"
			ret = ret "\ttbl."a[1]" = new "a[3]"[narr];"
			ret = ret "\tfor (int i = 0; i < narr; i++){"
			ret = ret "\t\ttbl."a[1]"[i] = PKT_"a[3]"(reader);"
			ret = ret "\t}\n"

		}
	} else if (!(a[2] in READERS)) {
		ret = ret "\t\ttbl."a[1]"  = new "a[2]"(reader);"
	} else {
		ret = ret "\ttbl."a[1]"  = reader." READERS[a[2]] "();"
	}
	return ret "\n";
}