Translate: improve getResponse() and print detailed debug message (for issue #64)

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

# 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() {
    HttpProtocol = "http://"
    HttpHost = "translate.google.com"
    HttpPort = 80
    if (Option["proxy"]) {
        match(Option["proxy"], /^(http:\/*)?([^\/]*):([^\/:]*)/, HttpProxySpec)
        HttpService = "/inet/tcp/0/" HttpProxySpec[2] "/" HttpProxySpec[3]
        HttpPathPrefix = HttpProtocol HttpHost
    } else {
        HttpService = "/inet/tcp/0/" HttpHost "/" HttpPort
        HttpPathPrefix = ""
    }
}

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

# 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 request and get response from Google Translate.
function getResponse(text, sl, tl, hl,    content, header, url) {
    url = HttpPathPrefix "/translate_a/single?client=t"                 \
        "&ie=UTF-8&oe=UTF-8"                                            \
        "&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&dt=t&dt=at"  \
        "&q=" preprocess(text) "&sl=" sl "&tl=" tl "&hl=" 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"

    print header |& HttpService
    while ((HttpService |& getline) > 0) {
        if (length($0) > 1) content = $0
        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 Google Text-to-Speech engine.
function play(text, tl,    url) {
    url = HttpProtocol HttpHost "/translate_tts?ie=UTF-8"       \
        "&tl=" tl "&q=" preprocess(text)

    # 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,
                        ####
                        r,
                        content, tokens, ast,
                        _sl, _tl, _hl, il, ils, isPhonetic,
                        article, example, explanation, ref, word,
                        translation, translations, phonetics,
                        wordClasses, words, segments, altTranslations,
                        original, oPhonetics, oWordClasses, oWords,
                        oRefs, oSynonymClasses, oSynonyms,
                        oExamples, oSeeAlso,
                        wShowOriginal, wShowOriginalPhonetics,
                        wShowTranslation, wShowTranslationPhonetics,
                        wShowPromptMessage, wShowLanguages,
                        wShowOriginalDictionary, wShowDictionary,
                        wShowAlternatives,
                        hasWordClasses, hasAltTranslations,
                        i, j, k, group, temp, saveSortedIn) {
    isPhonetic = match(tl, /^@/)
    tl = substr(tl, 1 + isPhonetic)

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

    # Convert codes or aliases to standard codes used by Google Translate
    # If the code or alias cannot be found, use as it is
    _sl = getCode(sl); if (!_sl) _sl = sl
    _tl = getCode(tl); if (!_tl) _tl = tl
    _hl = getCode(hl); if (!_hl) _hl = hl
    content = getResponse(text, _sl, _tl, _hl)
    tokenize(tokens, content)
    parseJsonArray(ast, tokens)

    l(content, "content", 1, 1)
    l(tokens, "tokens", 1, 0, 1)
    l(ast, "ast")
    if (!isarray(ast) || !anything(ast)) {
        e("[ERROR] Oops! Something went wrong and I can't translate it for you :(")
        ExitCode = 1
        return
    }

    saveSortedIn = PROCINFO["sorted_in"]
    PROCINFO["sorted_in"] = "@ind_num_asc"
    for (i in ast) {
        if (i ~ "^0" SUBSEP "0" SUBSEP "[[:digit:]]+" SUBSEP "0$")
            append(translations, postprocess(literal(ast[i])))
        if (i ~ "^0" SUBSEP "0" SUBSEP "[[:digit:]]+" SUBSEP "1$")
            append(original, literal(ast[i]))
        if (i ~ "^0" SUBSEP "0" SUBSEP "[[:digit:]]+" SUBSEP "2$")
            append(phonetics, literal(ast[i]))
        if (i ~ "^0" SUBSEP "0" SUBSEP "[[:digit:]]+" SUBSEP "3$")
            append(oPhonetics, literal(ast[i]))

        # 1 - word classes and explanations
        if (match(i, "^0" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group))
            wordClasses[group[1]] = literal(ast[i])
        if (match(i, "^0" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "2" SUBSEP "([[:digit:]]+)" SUBSEP "([[:digit:]]+)$", group))
            words[group[1]][group[2]][group[3]] = literal(ast[i])
        if (match(i, "^0" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "2" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)$", group))
            words[group[1]][group[2]]["1"][group[3]] = literal(ast[i])

        # 5 - alternative translations
        if (match(i, "^0" SUBSEP "5" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group)) {
            segments[group[1]] = literal(ast[i])
            altTranslations[group[1]][0] = ""
        }
        if (match(i, "^0" SUBSEP "5" SUBSEP "([[:digit:]]+)" SUBSEP "2" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group))
            altTranslations[group[1]][group[2]] = postprocess(literal(ast[i]))

        # 8 - identified source languages
        if (i ~ "^0" SUBSEP "8" SUBSEP "0" SUBSEP "[[:digit:]]+$" ||
            i ~ "^0" SUBSEP "2$")
            append(ils, literal(ast[i]))

        # 11 - (original) word classes and synonyms
        if (match(i, "^0" SUBSEP "11" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group))
            oSynonymClasses[group[1]] = literal(ast[i])
        if (match(i, "^0" SUBSEP "11" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "1$", group))
            if (ast[i]) {
                oRefs[literal(ast[i])][1] = group[1]
                oRefs[literal(ast[i])][2] = group[2]
            }
        if (match(i, "^0" SUBSEP "11" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "0" SUBSEP "([[:digit:]]+)$", group))
            oSynonyms[group[1]][group[2]][group[3]] = literal(ast[i])

        # 12 - (original) word classes and explanations
        if (match(i, "^0" SUBSEP "12" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group))
            oWordClasses[group[1]] = literal(ast[i])
        if (match(i, "^0" SUBSEP "12" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group))
            oWords[group[1]][group[2]][0] = literal(ast[i])
        if (match(i, "^0" SUBSEP "12" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "1$", group))
            oWords[group[1]][group[2]][1] = literal(ast[i])
        if (match(i, "^0" SUBSEP "12" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "2$", group))
            oWords[group[1]][group[2]][2] = postprocess(literal(ast[i]))

        # 13 - (original) examples
        if (match(i, "^0" SUBSEP "13" SUBSEP "0" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group))
            oExamples[group[1]] = postprocess(literal(ast[i]))

        # 14 - (original) see also
        if (match(i, "^0" SUBSEP "14" SUBSEP "0" SUBSEP "([[:digit:]]+)$", group))
            oSeeAlso[group[1]] = literal(ast[i])
    }
    PROCINFO["sorted_in"] = saveSortedIn

    translation = join(translations)

    il = !anything(ils) || belongsTo(sl, ils) ? sl : ils[0]

    # Generate output
    if (!isVerbose) {
        # Brief mode

        r = isPhonetic && anything(phonetics) ?
            prettify("brief-translation-phonetics", join(phonetics)) :
            prettify("brief-translation", s(translation, tl))

        if (toSpeech) {
            returnPlaylist[0]["text"] = translation
            returnPlaylist[0]["tl"] = tl
        }

    } else {
        # Verbose mode

        wShowOriginal = Option["show-original"]
        wShowOriginalPhonetics = Option["show-original-phonetics"]
        wShowTranslation = Option["show-translation"]
        wShowTranslationPhonetics = Option["show-translation-phonetics"]
        wShowPromptMessage = Option["show-prompt-message"]
        wShowLanguages = Option["show-languages"]
        wShowOriginalDictionary = Option["show-original-dictionary"]
        wShowDictionary = Option["show-dictionary"]
        wShowAlternatives = Option["show-alternatives"]

        if (!anything(oPhonetics)) wShowOriginalPhonetics = 0
        if (!anything(phonetics)) wShowTranslationPhonetics = 0
        if (getCode(il) == getCode(tl) &&                               \
            (isarray(oWordClasses) || isarray(oSynonymClasses) ||       \
             isarray(oExamples) || isarray(oSeeAlso))) {
            wShowOriginalDictionary = 1
            wShowTranslation = 0
        }
        hasWordClasses = exists(wordClasses)
        hasAltTranslations = exists(altTranslations[0])
        if (!hasWordClasses && !hasAltTranslations)
            wShowPromptMessage = wShowLanguages = 0
        if (!hasWordClasses) wShowDictionary = 0
        if (hasWordClasses || !hasAltTranslations) wShowAlternatives = 0

        if (wShowOriginal) {
            # Display: original text & phonetics
            if (r) r = r RS RS
            r = r m("-- display original text & phonetics")
            r = r prettify("original", s(join(original), il))
            if (wShowOriginalPhonetics)
                r = r RS prettify("original-phonetics", showPhonetics(join(oPhonetics), il))
        }

        if (wShowTranslation) {
            # Display: major translation & phonetics
            if (r) r = r RS RS
            r = r m("-- display major translation & phonetics")
            r = r prettify("translation", s(translation, tl))
            if (wShowTranslationPhonetics)
                r = r RS prettify("translation-phonetics", showPhonetics(join(phonetics), tl))
        }

        if (wShowPromptMessage || wShowLanguages)
            if (r) r = r RS
        if (wShowPromptMessage) {
            if (hasWordClasses) {
                # Display: prompt message (Definitions of ...)
                if (r) r = r RS
                r = r m("-- display prompt message (Definitions of ...)")
                if (isRTL(hl)) # home language is R-to-L
                    r = r prettify("prompt-message", s(showDefinitionsOf(hl, join(original))))
                else # home language is L-to-R
                    r = r prettify("prompt-message", showDefinitionsOf(hl, prettify("prompt-message-original", show(join(original), il))))
            } else if (hasAltTranslations) {
                # Display: prompt message (Translations of ...)
                if (r) r = r RS
                r = r m("-- display prompt message (Translations of ...)")
                if (isRTL(hl)) # home language is R-to-L
                    r = r prettify("prompt-message", s(showTranslationsOf(hl, join(original))))
                else # home language is L-to-R
                    r = r prettify("prompt-message", showTranslationsOf(hl, prettify("prompt-message-original", show(join(original), il))))
            }
        }
        if (wShowLanguages) {
            # Display: source language -> target language
            if (r) r = r RS
            r = r m("-- display source language -> target language")
            temp = Option["fmt-languages"]
            if (!temp) temp = "[ %s -> %t ]"
            split(temp, group, /(%s|%S|%t|%T)/)
            r = r prettify("languages", group[1])
            if (temp ~ /%s/)
                r = r prettify("languages-sl", getDisplay(il))
            if (temp ~ /%S/)
                r = r prettify("languages-sl", getName(il))
            r = r prettify("languages", group[2])
            if (temp ~ /%t/)
                r = r prettify("languages-tl", getDisplay(tl))
            if (temp ~ /%T/)
                r = r prettify("languages-tl", getName(tl))
            r = r prettify("languages", group[3])
        }

        if (wShowOriginalDictionary) {
            # Display: original dictionary
            if (exists(oWordClasses)) {
                # Detailed explanations
                if (r) r = r RS
                r = r m("-- display original dictionary (detailed explanations)")
                for (i = 0; i < length(oWordClasses); i++) {
                    r = (i > 0 ? r RS : r) RS prettify("original-dictionary-detailed-word-class", s(oWordClasses[i], hl))
                    for (j = 0; j < length(oWords[i]); j++) {
                        explanation = oWords[i][j][0]
                        ref = oWords[i][j][1]
                        example = oWords[i][j][2]

                        r = (j > 0 ? r RS : r) RS prettify("original-dictionary-detailed-explanation", ins(1, explanation, il))
                        if (example)
                            r = r RS prettify("original-dictionary-detailed-example", ins(2, "- \"" example "\"", il))
                        if (ref && isarray(oRefs[ref])) {
                            temp = prettify("original-dictionary-detailed-synonyms", ins(1, show(showSynonyms(hl), hl) ": "))
                            temp = temp prettify("original-dictionary-detailed-synonyms-item", show(oSynonyms[oRefs[ref][1]][oRefs[ref][2]][0], il))
                            for (k = 1; k < length(oSynonyms[oRefs[ref][1]][oRefs[ref][2]]); k++)
                                temp = temp prettify("original-dictionary-detailed-synonyms", ", ") \
                                    prettify("original-dictionary-detailed-synonyms-item", show(oSynonyms[oRefs[ref][1]][oRefs[ref][2]][k], il))
                            r = r RS temp
                        }
                    }
                }
            }
            if (exists(oSynonymClasses)) {
                # Synonyms
                r = r RS RS
                r = r m("-- display original dictionary (synonyms)")
                r = r prettify("original-dictionary-synonyms", s(showSynonyms(hl), hl))
                for (i = 0; i < length(oSynonymClasses); i++) {
                    r = (i > 0 ? r RS : r) RS prettify("original-dictionary-synonyms-word-class", ins(1, oSynonymClasses[i], hl))
                    for (j = 0; j < length(oSynonyms[i]); j++) {
                        temp = prettify("original-dictionary-synonyms-synonyms", ins(2, "- "))
                        temp = temp prettify("original-dictionary-synonyms-synonyms-item", show(oSynonyms[i][j][0], il))
                        for (k = 1; k < length(oSynonyms[i][j]); k++)
                            temp = temp prettify("original-dictionary-synonyms-synonyms", ", ") \
                                prettify("original-dictionary-synonyms-synonyms-item", show(oSynonyms[i][j][k], il))
                        r = r RS temp
                    }
                }
            }
            if (exists(oExamples)) {
                # Examples
                r = r RS RS
                r = r m("-- display original dictionary (examples)")
                r = r prettify("original-dictionary-examples", s(showExamples(hl), hl))
                for (i = 0; i < length(oExamples); i++) {
                    example = oExamples[i]

                    temp = prettify("original-dictionary-examples-example", ins(1, "- "))
                    split(example, group, /(\u003cb\u003e|\u003c\/b\u003e)/)
                    if (isRTL(il)) # target language is R-to-L
                        temp = temp show(group[1] group[2] group[3], il)
                    else # target language is L-to-R
                        temp = temp prettify("original-dictionary-examples-example", group[1]) \
                            prettify("original-dictionary-examples-original", group[2]) \
                            prettify("original-dictionary-examples-example", group[3])
                    r = (i > 0 ? r RS : r) RS temp
                }
            }
            if (exists(oSeeAlso)) {
                # See also
                r = r RS RS
                r = r m("-- display original dictionary (see also)")
                r = r prettify("original-dictionary-see-also", s(showSeeAlso(hl), hl))
                temp = ins(1, prettify("original-dictionary-see-also-phrases-item", show(oSeeAlso[0], il)))
                for (k = 1; k < length(oSeeAlso); k++)
                    temp = temp prettify("original-dictionary-see-also-phrases", ", ") \
                        prettify("original-dictionary-see-also-phrases-item", show(oSeeAlso[k], il))
                r = r RS temp
            }
        }

        if (wShowDictionary) {
            # Display: dictionary entries
            if (r) r = r RS
            r = r m("-- display dictionary entries")
            for (i = 0; i < length(wordClasses); i++) {
                r = (i > 0 ? r RS : r) RS prettify("dictionary-word-class", s(wordClasses[i], hl))
                for (j = 0; j < length(words[i]); j++) {
                    word = words[i][j][0]
                    article = words[i][j][4]
                    if (isRTL(il))
                        explanation = join(words[i][j][1], ", ")
                    else {
                        explanation = prettify("dictionary-explanation-item", words[i][j][1][0])
                        for (k = 1; k < length(words[i][j][1]); k++)
                            explanation = explanation prettify("dictionary-explanation", ", ") \
                                prettify("dictionary-explanation-item", words[i][j][1][k])
                    }

                    r = r RS prettify("dictionary-word", ins(1, (article ? "(" article ") " : "") word, tl))
                    if (isRTL(il))
                        r = r RS prettify("dictionary-explanation-item", ins(2, explanation, il))
                    else
                        r = r RS ins(2, explanation)
                }
            }
        }

        if (wShowAlternatives) {
            # Display: alternative translations
            if (r) r = r RS RS
            r = r m("-- display alternative translations")
            for (i = 0; i < length(altTranslations); i++) {
                r = (i > 0 ? r RS : r) prettify("alternatives-original", show(segments[i], il))
                if (isRTL(tl)) {
                    temp = join(altTranslations[i], ", ")
                    r = r RS prettify("alternatives-translations-item", ins(1, temp, tl))
                } else {
                    temp = prettify("alternatives-translations-item", altTranslations[i][0])
                    for (j = 1; j < length(altTranslations[i]); j++)
                        temp = temp prettify("alternatives-translations", ", ") \
                            prettify("alternatives-translations-item", altTranslations[i][j])
                    r = r RS ins(1, temp)
                }
            }
        }

        if (toSpeech) {
            if (index(showTranslationsOf(hl, "%s"), "%s") > 2) {
                returnPlaylist[0]["text"] = showTranslationsOf(hl)
                returnPlaylist[0]["tl"] = hl
                returnPlaylist[1]["text"] = join(original)
                returnPlaylist[1]["tl"] = il
            } else {
                returnPlaylist[0]["text"] = join(original)
                returnPlaylist[0]["tl"] = il
                returnPlaylist[1]["text"] = showTranslationsOf(hl)
                returnPlaylist[1]["tl"] = hl
            }
            returnPlaylist[2]["text"] = translation
            returnPlaylist[2]["tl"] = tl
        }
    }

    return r
}

# 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("https://translate.google.com/translate?" \
                                              "hl=" hl "&sl=" sl "&tl=" tl "&u=" uri) "&")
}

# Translate the source text (into all target languages).
function translate(text, inline,
                   ####
                   i, j, playlist, 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))

            if (Option["play"])
                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
        }
    }
    PROCINFO["sorted_in"] = saveSortedIn
}

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

    i = 0
    while (getline line < Option["input"]) {
        # 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)
    }
}

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

# 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() {
    HttpProtocol = "http://"
    HttpHost = "translate.google.com"
    HttpPort = 80
    if (Option["proxy"]) {
        match(Option["proxy"], /^(http:\/*)?([^\/]*):([^\/:]*)/, HttpProxySpec)
        HttpService = "/inet/tcp/0/" HttpProxySpec[2] "/" HttpProxySpec[3]
        HttpPathPrefix = HttpProtocol HttpHost
    } else {
        HttpService = "/inet/tcp/0/" HttpHost "/" HttpPort
        HttpPathPrefix = ""
    }
}

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

# 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 request and get response from Google Translate.
function getResponse(text, sl, tl, hl,    content, header, url) {
    url = HttpPathPrefix "/translate_a/single?client=t"                 \
        "&ie=UTF-8&oe=UTF-8"                                            \
        "&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&dt=t&dt=at"  \
        "&q=" preprocess(text) "&sl=" sl "&tl=" tl "&hl=" 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"

    print header |& HttpService
    while ((HttpService |& getline) > 0)
        content = $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 Google Text-to-Speech engine.
function play(text, tl,    url) {
    url = HttpProtocol HttpHost "/translate_tts?ie=UTF-8"       \
        "&tl=" tl "&q=" preprocess(text)

    # 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,
                        ####
                        r,
                        content, tokens, ast,
                        _sl, _tl, _hl, il, ils, isPhonetic,
                        article, example, explanation, ref, word,
                        translation, translations, phonetics,
                        wordClasses, words, segments, altTranslations,
                        original, oPhonetics, oWordClasses, oWords,
                        oRefs, oSynonymClasses, oSynonyms,
                        oExamples, oSeeAlso,
                        wShowOriginal, wShowOriginalPhonetics,
                        wShowTranslation, wShowTranslationPhonetics,
                        wShowPromptMessage, wShowLanguages,
                        wShowOriginalDictionary, wShowDictionary,
                        wShowAlternatives,
                        hasWordClasses, hasAltTranslations,
                        i, j, k, group, temp, saveSortedIn) {
    isPhonetic = match(tl, /^@/)
    tl = substr(tl, 1 + isPhonetic)

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

    # Convert codes or aliases to standard codes used by Google Translate
    # If the code or alias cannot be found, use as it is
    _sl = getCode(sl); if (!_sl) _sl = sl
    _tl = getCode(tl); if (!_tl) _tl = tl
    _hl = getCode(hl); if (!_hl) _hl = hl
    content = getResponse(text, _sl, _tl, _hl)
    tokenize(tokens, content)
    parseJsonArray(ast, tokens)

    l(content, "content", 1, 1)
    l(tokens, "tokens", 1, 0, 1)
    l(ast, "ast")
    if (!isarray(ast) || !anything(ast)) {
        e("[ERROR] Oops! Something went wrong and I can't translate it for you :(")
        ExitCode = 1
        return
    }

    saveSortedIn = PROCINFO["sorted_in"]
    PROCINFO["sorted_in"] = "@ind_num_asc"
    for (i in ast) {
        if (i ~ "^0" SUBSEP "0" SUBSEP "[[:digit:]]+" SUBSEP "0$")
            append(translations, postprocess(literal(ast[i])))
        if (i ~ "^0" SUBSEP "0" SUBSEP "[[:digit:]]+" SUBSEP "1$")
            append(original, literal(ast[i]))
        if (i ~ "^0" SUBSEP "0" SUBSEP "[[:digit:]]+" SUBSEP "2$")
            append(phonetics, literal(ast[i]))
        if (i ~ "^0" SUBSEP "0" SUBSEP "[[:digit:]]+" SUBSEP "3$")
            append(oPhonetics, literal(ast[i]))

        # 1 - word classes and explanations
        if (match(i, "^0" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group))
            wordClasses[group[1]] = literal(ast[i])
        if (match(i, "^0" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "2" SUBSEP "([[:digit:]]+)" SUBSEP "([[:digit:]]+)$", group))
            words[group[1]][group[2]][group[3]] = literal(ast[i])
        if (match(i, "^0" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "2" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)$", group))
            words[group[1]][group[2]]["1"][group[3]] = literal(ast[i])

        # 5 - alternative translations
        if (match(i, "^0" SUBSEP "5" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group)) {
            segments[group[1]] = literal(ast[i])
            altTranslations[group[1]][0] = ""
        }
        if (match(i, "^0" SUBSEP "5" SUBSEP "([[:digit:]]+)" SUBSEP "2" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group))
            altTranslations[group[1]][group[2]] = postprocess(literal(ast[i]))

        # 8 - identified source languages
        if (i ~ "^0" SUBSEP "8" SUBSEP "0" SUBSEP "[[:digit:]]+$" ||
            i ~ "^0" SUBSEP "2$")
            append(ils, literal(ast[i]))

        # 11 - (original) word classes and synonyms
        if (match(i, "^0" SUBSEP "11" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group))
            oSynonymClasses[group[1]] = literal(ast[i])
        if (match(i, "^0" SUBSEP "11" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "1$", group))
            if (ast[i]) {
                oRefs[literal(ast[i])][1] = group[1]
                oRefs[literal(ast[i])][2] = group[2]
            }
        if (match(i, "^0" SUBSEP "11" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "0" SUBSEP "([[:digit:]]+)$", group))
            oSynonyms[group[1]][group[2]][group[3]] = literal(ast[i])

        # 12 - (original) word classes and explanations
        if (match(i, "^0" SUBSEP "12" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group))
            oWordClasses[group[1]] = literal(ast[i])
        if (match(i, "^0" SUBSEP "12" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group))
            oWords[group[1]][group[2]][0] = literal(ast[i])
        if (match(i, "^0" SUBSEP "12" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "1$", group))
            oWords[group[1]][group[2]][1] = literal(ast[i])
        if (match(i, "^0" SUBSEP "12" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "2$", group))
            oWords[group[1]][group[2]][2] = postprocess(literal(ast[i]))

        # 13 - (original) examples
        if (match(i, "^0" SUBSEP "13" SUBSEP "0" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group))
            oExamples[group[1]] = postprocess(literal(ast[i]))

        # 14 - (original) see also
        if (match(i, "^0" SUBSEP "14" SUBSEP "0" SUBSEP "([[:digit:]]+)$", group))
            oSeeAlso[group[1]] = literal(ast[i])
    }
    PROCINFO["sorted_in"] = saveSortedIn

    translation = join(translations)

    il = !anything(ils) || belongsTo(sl, ils) ? sl : ils[0]

    # Generate output
    if (!isVerbose) {
        # Brief mode

        r = isPhonetic && anything(phonetics) ?
            prettify("brief-translation-phonetics", join(phonetics)) :
            prettify("brief-translation", s(translation, tl))

        if (toSpeech) {
            returnPlaylist[0]["text"] = translation
            returnPlaylist[0]["tl"] = tl
        }

    } else {
        # Verbose mode

        wShowOriginal = Option["show-original"]
        wShowOriginalPhonetics = Option["show-original-phonetics"]
        wShowTranslation = Option["show-translation"]
        wShowTranslationPhonetics = Option["show-translation-phonetics"]
        wShowPromptMessage = Option["show-prompt-message"]
        wShowLanguages = Option["show-languages"]
        wShowOriginalDictionary = Option["show-original-dictionary"]
        wShowDictionary = Option["show-dictionary"]
        wShowAlternatives = Option["show-alternatives"]

        if (!anything(oPhonetics)) wShowOriginalPhonetics = 0
        if (!anything(phonetics)) wShowTranslationPhonetics = 0
        if (getCode(il) == getCode(tl) &&                               \
            (isarray(oWordClasses) || isarray(oSynonymClasses) ||       \
             isarray(oExamples) || isarray(oSeeAlso))) {
            wShowOriginalDictionary = 1
            wShowTranslation = 0
        }
        hasWordClasses = exists(wordClasses)
        hasAltTranslations = exists(altTranslations[0])
        if (!hasWordClasses && !hasAltTranslations)
            wShowPromptMessage = wShowLanguages = 0
        if (!hasWordClasses) wShowDictionary = 0
        if (hasWordClasses || !hasAltTranslations) wShowAlternatives = 0

        if (wShowOriginal) {
            # Display: original text & phonetics
            if (r) r = r RS RS
            r = r m("-- display original text & phonetics")
            r = r prettify("original", s(join(original), il))
            if (wShowOriginalPhonetics)
                r = r RS prettify("original-phonetics", showPhonetics(join(oPhonetics), il))
        }

        if (wShowTranslation) {
            # Display: major translation & phonetics
            if (r) r = r RS RS
            r = r m("-- display major translation & phonetics")
            r = r prettify("translation", s(translation, tl))
            if (wShowTranslationPhonetics)
                r = r RS prettify("translation-phonetics", showPhonetics(join(phonetics), tl))
        }

        if (wShowPromptMessage || wShowLanguages)
            if (r) r = r RS
        if (wShowPromptMessage) {
            if (hasWordClasses) {
                # Display: prompt message (Definitions of ...)
                if (r) r = r RS
                r = r m("-- display prompt message (Definitions of ...)")
                if (isRTL(hl)) # home language is R-to-L
                    r = r prettify("prompt-message", s(showDefinitionsOf(hl, join(original))))
                else # home language is L-to-R
                    r = r prettify("prompt-message", showDefinitionsOf(hl, prettify("prompt-message-original", show(join(original), il))))
            } else if (hasAltTranslations) {
                # Display: prompt message (Translations of ...)
                if (r) r = r RS
                r = r m("-- display prompt message (Translations of ...)")
                if (isRTL(hl)) # home language is R-to-L
                    r = r prettify("prompt-message", s(showTranslationsOf(hl, join(original))))
                else # home language is L-to-R
                    r = r prettify("prompt-message", showTranslationsOf(hl, prettify("prompt-message-original", show(join(original), il))))
            }
        }
        if (wShowLanguages) {
            # Display: source language -> target language
            if (r) r = r RS
            r = r m("-- display source language -> target language")
            temp = Option["fmt-languages"]
            if (!temp) temp = "[ %s -> %t ]"
            split(temp, group, /(%s|%S|%t|%T)/)
            r = r prettify("languages", group[1])
            if (temp ~ /%s/)
                r = r prettify("languages-sl", getDisplay(il))
            if (temp ~ /%S/)
                r = r prettify("languages-sl", getName(il))
            r = r prettify("languages", group[2])
            if (temp ~ /%t/)
                r = r prettify("languages-tl", getDisplay(tl))
            if (temp ~ /%T/)
                r = r prettify("languages-tl", getName(tl))
            r = r prettify("languages", group[3])
        }

        if (wShowOriginalDictionary) {
            # Display: original dictionary
            if (exists(oWordClasses)) {
                # Detailed explanations
                if (r) r = r RS
                r = r m("-- display original dictionary (detailed explanations)")
                for (i = 0; i < length(oWordClasses); i++) {
                    r = (i > 0 ? r RS : r) RS prettify("original-dictionary-detailed-word-class", s(oWordClasses[i], hl))
                    for (j = 0; j < length(oWords[i]); j++) {
                        explanation = oWords[i][j][0]
                        ref = oWords[i][j][1]
                        example = oWords[i][j][2]

                        r = (j > 0 ? r RS : r) RS prettify("original-dictionary-detailed-explanation", ins(1, explanation, il))
                        if (example)
                            r = r RS prettify("original-dictionary-detailed-example", ins(2, "- \"" example "\"", il))
                        if (ref && isarray(oRefs[ref])) {
                            temp = prettify("original-dictionary-detailed-synonyms", ins(1, show(showSynonyms(hl), hl) ": "))
                            temp = temp prettify("original-dictionary-detailed-synonyms-item", show(oSynonyms[oRefs[ref][1]][oRefs[ref][2]][0], il))
                            for (k = 1; k < length(oSynonyms[oRefs[ref][1]][oRefs[ref][2]]); k++)
                                temp = temp prettify("original-dictionary-detailed-synonyms", ", ") \
                                    prettify("original-dictionary-detailed-synonyms-item", show(oSynonyms[oRefs[ref][1]][oRefs[ref][2]][k], il))
                            r = r RS temp
                        }
                    }
                }
            }
            if (exists(oSynonymClasses)) {
                # Synonyms
                r = r RS RS
                r = r m("-- display original dictionary (synonyms)")
                r = r prettify("original-dictionary-synonyms", s(showSynonyms(hl), hl))
                for (i = 0; i < length(oSynonymClasses); i++) {
                    r = (i > 0 ? r RS : r) RS prettify("original-dictionary-synonyms-word-class", ins(1, oSynonymClasses[i], hl))
                    for (j = 0; j < length(oSynonyms[i]); j++) {
                        temp = prettify("original-dictionary-synonyms-synonyms", ins(2, "- "))
                        temp = temp prettify("original-dictionary-synonyms-synonyms-item", show(oSynonyms[i][j][0], il))
                        for (k = 1; k < length(oSynonyms[i][j]); k++)
                            temp = temp prettify("original-dictionary-synonyms-synonyms", ", ") \
                                prettify("original-dictionary-synonyms-synonyms-item", show(oSynonyms[i][j][k], il))
                        r = r RS temp
                    }
                }
            }
            if (exists(oExamples)) {
                # Examples
                r = r RS RS
                r = r m("-- display original dictionary (examples)")
                r = r prettify("original-dictionary-examples", s(showExamples(hl), hl))
                for (i = 0; i < length(oExamples); i++) {
                    example = oExamples[i]

                    temp = prettify("original-dictionary-examples-example", ins(1, "- "))
                    split(example, group, /(\u003cb\u003e|\u003c\/b\u003e)/)
                    if (isRTL(il)) # target language is R-to-L
                        temp = temp show(group[1] group[2] group[3], il)
                    else # target language is L-to-R
                        temp = temp prettify("original-dictionary-examples-example", group[1]) \
                            prettify("original-dictionary-examples-original", group[2]) \
                            prettify("original-dictionary-examples-example", group[3])
                    r = (i > 0 ? r RS : r) RS temp
                }
            }
            if (exists(oSeeAlso)) {
                # See also
                r = r RS RS
                r = r m("-- display original dictionary (see also)")
                r = r prettify("original-dictionary-see-also", s(showSeeAlso(hl), hl))
                temp = ins(1, prettify("original-dictionary-see-also-phrases-item", show(oSeeAlso[0], il)))
                for (k = 1; k < length(oSeeAlso); k++)
                    temp = temp prettify("original-dictionary-see-also-phrases", ", ") \
                        prettify("original-dictionary-see-also-phrases-item", show(oSeeAlso[k], il))
                r = r RS temp
            }
        }

        if (wShowDictionary) {
            # Display: dictionary entries
            if (r) r = r RS
            r = r m("-- display dictionary entries")
            for (i = 0; i < length(wordClasses); i++) {
                r = (i > 0 ? r RS : r) RS prettify("dictionary-word-class", s(wordClasses[i], hl))
                for (j = 0; j < length(words[i]); j++) {
                    word = words[i][j][0]
                    article = words[i][j][4]
                    if (isRTL(il))
                        explanation = join(words[i][j][1], ", ")
                    else {
                        explanation = prettify("dictionary-explanation-item", words[i][j][1][0])
                        for (k = 1; k < length(words[i][j][1]); k++)
                            explanation = explanation prettify("dictionary-explanation", ", ") \
                                prettify("dictionary-explanation-item", words[i][j][1][k])
                    }

                    r = r RS prettify("dictionary-word", ins(1, (article ? "(" article ") " : "") word, tl))
                    if (isRTL(il))
                        r = r RS prettify("dictionary-explanation-item", ins(2, explanation, il))
                    else
                        r = r RS ins(2, explanation)
                }
            }
        }

        if (wShowAlternatives) {
            # Display: alternative translations
            if (r) r = r RS RS
            r = r m("-- display alternative translations")
            for (i = 0; i < length(altTranslations); i++) {
                r = (i > 0 ? r RS : r) prettify("alternatives-original", show(segments[i], il))
                if (isRTL(tl)) {
                    temp = join(altTranslations[i], ", ")
                    r = r RS prettify("alternatives-translations-item", ins(1, temp, tl))
                } else {
                    temp = prettify("alternatives-translations-item", altTranslations[i][0])
                    for (j = 1; j < length(altTranslations[i]); j++)
                        temp = temp prettify("alternatives-translations", ", ") \
                            prettify("alternatives-translations-item", altTranslations[i][j])
                    r = r RS ins(1, temp)
                }
            }
        }

        if (toSpeech) {
            if (index(showTranslationsOf(hl, "%s"), "%s") > 2) {
                returnPlaylist[0]["text"] = showTranslationsOf(hl)
                returnPlaylist[0]["tl"] = hl
                returnPlaylist[1]["text"] = join(original)
                returnPlaylist[1]["tl"] = il
            } else {
                returnPlaylist[0]["text"] = join(original)
                returnPlaylist[0]["tl"] = il
                returnPlaylist[1]["text"] = showTranslationsOf(hl)
                returnPlaylist[1]["tl"] = hl
            }
            returnPlaylist[2]["text"] = translation
            returnPlaylist[2]["tl"] = tl
        }
    }

    return r
}

# 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("https://translate.google.com/translate?" \
                                              "hl=" hl "&sl=" sl "&tl=" tl "&u=" uri) "&")
}

# Translate the source text (into all target languages).
function translate(text, inline,
                   ####
                   i, j, playlist, 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))

            if (Option["play"])
                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
        }
    }
    PROCINFO["sorted_in"] = saveSortedIn
}

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

    i = 0
    while (getline line < Option["input"]) {
        # 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)
    }
}


Generate `include_directories` for `CMakeLists.txt` etc.

	modified:   code/awk/generate_source_list_from_make_output.awk

#!/usr/bin/awk -f
BEGIN{
    # This program generates source list to be used in `CMakeLists.txt`
    # or elsewhere using `foo.txt` produced by `make >foo.txt 2>&1` as input.

    array_index = 0;
    split("", dir_array);
    current_dir = "";
    new_filename = "";
    current_filename = "";
    entering_directory_regex = "Entering directory '";
    cpp_regex = "\\<[a-zA-Z0-9_-]+\\.cpp\\>";
    c_regex = "\\<[a-zA-Z0-9_-]+\\.c\\>";
} # BEGIN

function get_filename()
{
    # OK, there's a `.cpp` file on this line.
    cpp_filename = substr($0, RSTART, RLENGTH);
    new_filename = current_dir "/" cpp_filename;

    if (new_filename != current_filename)
    {
        current_filename = new_filename;

        # find out if there's a matching source file.
        if (system("test -f " current_filename) == 0)
        {
            # print the filename.
            print current_filename;
        }

        # find out if there's a matching `*.h` header file too.
        header_filename = gensub(/(^.*\.)c(|pp)$/, "\\1h", "g", current_filename);

        if (system("test -f " header_filename) == 0)
        {
            # header exists, so print its filename too!
            print header_filename;
        }
    }
}
{
    entering_directory_match_index = match($0, entering_directory_regex);
    cpp_match_index = match($0, cpp_regex);
    c_match_index = match($0, c_regex);

    if (entering_directory_match_index > 0)
    {
        # OK, we are entering a new directory on this line.
        junk_part_length = entering_directory_match_index + length(entering_directory_regex);

        # read current dir.
        current_dir = substr($0, junk_part_length, length($0) - junk_part_length);

        # store current dir into array.
        dir_array[array_index++] = current_dir "/";
    }
    else if (cpp_match_index > 0)
    {
        match($0, cpp_regex); # update `RSTART` & `RLENGTH`.
        get_filename();
    }
    else if (c_match_index > 0)
    {
        match($0, c_regex); # update `RSTART` & `RLENGTH`.
        get_filename();
    }
}
END{
    n_dirs = array_index;
    asort(dir_array);

    print "";
    print "include_directories(";

    for (i = 0; i < n_dirs; i++)
    {
        print dir_array[i];
    }
} # END

#!/usr/bin/awk -f
BEGIN{
    # This program generates source list to be used in `CMakeLists.txt`
    # or elsewhere using `foo.txt` produced by `make >foo.txt 2>&1` as input.

    current_dir = "";
    new_filename = "";
    current_filename = "";
    entering_directory_regex = "Entering directory '";
    cpp_regex = "\\<[a-zA-Z0-9_-]+\\.cpp\\>";
    c_regex = "\\<[a-zA-Z0-9_-]+\\.c\\>";
} # BEGIN

function get_filename()
{
    # OK, there's a `.cpp` file on this line.
    cpp_filename = substr($0, RSTART, RLENGTH);
    new_filename = current_dir "/" cpp_filename;

    if (new_filename != current_filename)
    {
        current_filename = new_filename;

        # find out if there's a matching source file.
        if (system("test -f " current_filename) == 0)
        {
            # print the filename.
            print current_filename;
        }

        # find out if there's a matching `*.h` header file too.
        header_filename = gensub(/(^.*\.)c(|pp)$/, "\\1h", "g", current_filename);

        if (system("test -f " header_filename) == 0)
        {
            # header exists, so print its filename too!
            print header_filename;
        }
    }
}
{
    entering_directory_match_index = match($0, entering_directory_regex);
    cpp_match_index = match($0, cpp_regex);
    c_match_index = match($0, c_regex);

    if (entering_directory_match_index > 0)
    {
        # OK, we are entering a new directory on this line.
        junk_part_length = entering_directory_match_index + length(entering_directory_regex);

        # read current dir.
        current_dir = substr($0, junk_part_length, length($0) - junk_part_length);
    }
    else if (cpp_match_index > 0)
    {
        match($0, cpp_regex); # update `RSTART` & `RLENGTH`.
        get_filename();
    }
    else if (c_match_index > 0)
    {
        match($0, c_regex); # update `RSTART` & `RLENGTH`.
        get_filename();
    }
}


Added exactCells parameter to the soloBasicCellFilter.awk script tp specify exact number of cells. Parameters can now be specified as variables with awk -v ...

# usage awk -v exactCells=... -v maxCells=... -v maxPercentile=... -v maxMinRatio=... -f soloBasicCellFilter.awk
BEGIN {
    # default values - if variables were not defined by the user
    if (exactCells==0)
        exactCells=0;
    if (maxCells==0)
        maxCells=3000;
    if (maxPercentile==0)
        maxPercentile=0.99;
    if (maxMinRatio==0)
        maxMinRatio=10;

    print "Parameters: " "exactCells=" exactCells, "maxCells=" maxCells, "maxPercentile=", maxPercentile, "maxMinRatio=", maxMinRatio;

    nHeaderLines=3;
    fOutCB=ARGV[1] ".filtered";
    fOutMat=ARGV[2] ".filtered";
}

{
    if (ARGIND==1) {# read barcodes
        CB[NR]=$1;
    } else if (FNR<=nHeaderLines) {
         A[FNR]=$0;
         if (FNR==nHeaderLines) {
             nGenes=$1;
         };
    } else {
         cellG[FNR]=$1;
         cellI[FNR]=$2;
         cellN[FNR]=$3;
         cellTot[$2]+=$3;
         nLines=FNR;
    };
}
END {
    asort(cellTot,cellTotSort);
    nMax=cellTotSort[-int((1-maxPercentile)*maxCells)+length(cellTot)];
    nMin=nMax/maxMinRatio;

    if (exactCells>0)
        nMin=length(cellTot)<exactCells ? cellTotSort[1] : cellTotSort[length(cellTot)-exactCells];

    nCell=0;
    for (ii=1; ii<=length(CB); ii++) {
        if (cellTot[ii]>=nMin) {
            print CB[ii] > fOutCB;
            nCell++;
            cellInew[ii]=nCell;
            print nCell,cellTot[ii] > fOutCB ".counts";
        };
    };

    if (exactCells==0) {
        print "maxUMIperCell=" cellTotSort[length(cellTotSort)]+0,"Robust maxUMIperCel=" nMax,"minUMIperCell=" nMin, "Filtered N cells=", nCell;
    } else {
        print "total N cells=" length(cellTot), "exactCells=" exactCells, "minUMIperCell=" nMin;
    };

    nMat=0;
    for (ii=nHeaderLines+1; ii<=nLines; ii++) {
        if (cellTot[cellI[ii]]>=nMin) {
            nMat++;
        };
    };

    for (ii=1;ii<nHeaderLines;ii++) {
        print A[ii] > fOutMat;
    };

    print nGenes,nCell+0,nMat+0 > fOutMat;

    for (ii=nHeaderLines+1; ii<=nLines; ii++) {
        if (cellTot[cellI[ii]]>=nMin) {
            print cellG[ii],cellInew[cellI[ii]],cellN[ii] > fOutMat;
        };
    };

};

BEGIN {
    maxCells=3000;
    maxPercentile=0.99;
    maxMinRatio=10;
    nHeaderLines=3;
    fOutCB=ARGV[1] ".filtered";
    fOutMat=ARGV[2] ".filtered";
}

{
    if (ARGIND==1) {# read barcodes
        CB[NR]=$1;
    } else if (FNR<=nHeaderLines) {
         A[FNR]=$0;
         if (FNR==nHeaderLines) {
             nGenes=$1;
         };
    } else {
         cellG[FNR]=$1;
         cellI[FNR]=$2;
         cellN[FNR]=$3;
         cellTot[$2]+=$3;
         nLines=FNR;
    };
}
END {
    asort(cellTot,cellTotSort);
    nMax=cellTotSort[-int((1-maxPercentile)*maxCells)+length(cellTot)];
    nMin=nMax/maxMinRatio;

    nCell=0;
    for (ii=1; ii<=length(CB); ii++) {
        if (cellTot[ii]>=nMin) {
            print CB[ii] > fOutCB;
            nCell++;
            cellInew[ii]=nCell;
            print nCell,cellTot[ii] > fOutCB ".counts";
        };
    };

    print cellTotSort[length(cellTotSort)]+0,nMax,nMin,nCell;

    nMat=0;
    for (ii=nHeaderLines+1; ii<=nLines; ii++) {
        if (cellTot[cellI[ii]]>=nMin) {
            nMat++;
        };
    };

    for (ii=1;ii<nHeaderLines;ii++) {
        print A[ii] > fOutMat;
    };

    print nGenes,nCell+0,nMat+0 > fOutMat;

    for (ii=nHeaderLines+1; ii<=nLines; ii++) {
        if (cellTot[cellI[ii]]>=nMin) {
            print cellG[ii],cellInew[cellI[ii]],cellN[ii] > fOutMat;
        };
    };

};


AWK converstion script for CMIP5-table to Bash case list

#! /bin/bash -

#########################
#
# Name: rewrite-to-bash-case.bash
#
# Purpose: Rewrite CMIP5-tables to bash case selection
#
# Usage: ./rewrite-to-bash-case.bash
#
# Revision history: 2016-02-25  --  Script created, Martin Evaldsson, Rossby Centre
#
# Contact persons:  martin.evaldsson@smhi.se
#
########################
function ceil(x){return x%1 ? int(x)+1 : x}
function floor(x){return x%1 ? int(x) : x}

function repeat( str, n,    rep, i )
{
    for( ; i<n; i++ )
        rep = rep str
    return rep
}

function pre_ws( str, maxws )
{
    prews=floor(maxws/2)
    str_ws = repeat(" ", prews, i, ii)
    return str_ws
}

function post_ws( str, maxws )
{
    postws=no_ws - length(str)-3
    str_ws = repeat(" ", postws, i, ii)
    return str_ws
}

BEGIN {
  newvar="none"
  no_ws=20
  ws=repeat(" ", no_ws, rep, i)
  print "case ${var_in[$var]} in"
}

{
    #print newvar"  "$1",  --   "$0
    if ($1 == "variable_entry:")
    {
        if (newvar == "true")
            printf "%s", ";;\n\n"
        prews=pre_ws($2, length(ws))
        postws=post_ws($2, length(ws))
        printf "%s", "  "$2")"postws
        newvar="true"
    }
    if ($1 == "standard_name:" && newvar=="true")
    {
        printf "%s", "standard_name='"$2"'"
    }

    attrs[0] = "long_name:"
    attrs[1] = "units:"
    attrs[2] = "cell_methods:"

    for (attr in attrs)
    {
        if ($1 == attrs[attr] && newvar=="true")
        {
            output=substr($0,index($2,$9))
            sub(":[[:space:]]*", "='", output)
            printf "%s", "\n"ws""output"'"
        }
    }
}

END {
  print "\nesac"
}

#! /bin/bash -

#########################
#
# Name: rewrite-to-bash-case.bash
#
# Purpose: Rewrite CMIP5-tables to bash case selection
#
# Usage: ./rewrite-to-bash-case.bash
#
# Revision history: 2016-02-25  --  Script created, Martin Evaldsson, Rossby Centre
#
# Contact persons:  martin.evaldsson@smhi.se
#
########################
function ceil(x){return x%1 ? int(x)+1 : x}
function floor(x){return x%1 ? int(x) : x}

function repeat( str, n,    rep, i )
{
    for( ; i<n; i++ )
        rep = rep str
    return rep
}

function pre_ws( str, maxws )
{
    prews=floor(maxws/2)
    str_ws = repeat(" ", prews, i, ii)
    return str_ws
}

function post_ws( str, maxws )
{
    postws=no_ws - length(str)-3
    str_ws = repeat(" ", postws, i, ii)
    return str_ws
}

BEGIN {
  newvar="none"
  no_ws=20
  ws=repeat(" ", no_ws, rep, i)
  print "case"
}

{
    #print newvar"  "$1",  --   "$0
    if ($1 == "variable_entry:")
    {
        if (newvar == "true")
            printf "%s", ";;\n\n"
        prews=pre_ws($2, length(ws))
        postws=post_ws($2, length(ws))
        printf "%s", "  "$2")"postws
        newvar="true"
    }
    if ($1 == "standard_name:" && newvar=="true")
    {
        printf "%s", "standard_name='"$2"'"
    }

    attrs[0] = "long_name:"
    attrs[1] = "units:"
    attrs[2] = "cell_methods:"

    for (attr in attrs)
    {
        if ($1 == attrs[attr] && newvar=="true")
        {
            output=substr($0,index($2,$9))
            sub(":[[:space:]]*", "='", output)
            printf "%s", "\n"ws""output"'"
        }
    }
}

END {
  print "\nesac"
}


dtx_check.gawk: add check for repetitive SID FIRST

Change-Id: Id7acdfae7880fb488b65435ab8d33f2874499967

#!/usr/bin/gawk -f

# Expected input format: FN TYPE

BEGIN {
	DELTA = 0
	ERR = 0
	FORCE = 0
	FN = 0
	SILENCE = 0
	TYPE = ""
	CHK = ""
	U_MAX = 8 * 20 + 120 / 26
	U_MIN = 8 * 20 - 120 / 26
	F_MAX = 3 * 20 + 120 / 26
	F_MIN = 3 * 20 - 120 / 26
}

{
	if (NR > 2) { # we have data from previous record to compare to
		DELTA = ($1 - FN) * 120 / 26
		CHK = "OK"
		if ("FACCH" == $2 && "ONSET" == TYPE) { # ONSET due to FACCH is NOT a talkspurt
			SILENCE = 1
		}
		if (("UPDATE" == TYPE || "FIRST" == TYPE) && ("FACCH" == $2 || "SPEECH" == $2)) { # check for missing ONSET:
				CHK = "FAIL: missing ONSET (" $2 ") after " TYPE "."
				ERR++
		}
		if ("SID_P1" == $2) {
			CHK = "FAIL: regular AMR payload with FT SID and STI=0 (should be either pyaload Update or STI=1)."
			ERR++
		}
		if ("FORCED_FIRST" == $2 || "FORCED_NODATA" == $2 || "FORCED_F_P2" == $2 || "FORCED_F_INH" == $2 || "FORCED_U_INH" == $2) {
			CHK = "FAIL: event " $2 " inserted by DSP."
			FORCE++
			ERR++
		}
		if ("FIRST_P2" != $2 && "FIRST_P1" == TYPE) {
			CHK = "FAIL: " TYPE " followed by " $2 " instead of P2."
			ERR++
		}
		if ("FIRST" == $2 && "FIRST" == TYPE) {
			CHK = "FAIL: multiple SID FIRST in a row."
			ERR++
		}
		if ("OK" == CHK && "ONSET" != $2) { # check inter-SID distances:
			if ("UPDATE" == TYPE) {
				if (DELTA > U_MAX) {
					CHK = "FAIL: delta (" $1 - FN "fn) from previous SID UPDATE (@" FN ") too big " DELTA "ms > " U_MAX "ms."
					ERR++
				}
				if ("UPDATE" == $2 && DELTA < U_MIN) {
					CHK = "FAIL: delta (" $1 - FN "fn) from previous SID UPDATE (@" FN ") too small " DELTA "ms < " U_MIN "ms."
					ERR++
				}
			}
			if ("FIRST" == TYPE) {
				if (DELTA > F_MAX) {
					CHK = "FAIL: delta (" $1 - FN "fn) from previous SID FIRST (@" FN ") too big " DELTA "ms > " F_MAX "ms."
					ERR++
				}
				if ("UPDATE" == $2 && DELTA < F_MIN) {
					CHK = "FAIL: delta (" $1 - FN "fn) from previous SID UPDATE (@" FN ") too small " DELTA "ms < " F_MIN "ms."
					ERR++
				}
			}
		}
		if ("FACCH" == TYPE && "FIRST" != $2 && "FACCH" != $2 && 1 == SILENCE) { # check FACCH handling
			CHK = "FAIL: incorrect silence resume with " $2 " after FACCH."
			ERR++
		}
	}
	if ("SPEECH" == $2 || "ONSET" == $2) { # talkspurt
		SILENCE = 0
	}
	if ("UPDATE" == $2 || "FIRST" == $2) { # silence
		SILENCE = 1
	}
	print $1, $2, CHK
	if ($2 != "EMPTY") { # skip over EMPTY records
		TYPE = $2
		FN = $1
	}
}

END {
	print "Check completed: found " ERR " errors (" FORCE " events inserted by DSP) in " NR " records."
}

#!/usr/bin/gawk -f

# Expected input format: FN TYPE

BEGIN {
	DELTA = 0
	ERR = 0
	FORCE = 0
	FN = 0
	SILENCE = 0
	TYPE = ""
	CHK = ""
	U_MAX = 8 * 20 + 120 / 26
	U_MIN = 8 * 20 - 120 / 26
	F_MAX = 3 * 20 + 120 / 26
	F_MIN = 3 * 20 - 120 / 26
}

{
	if (NR > 2) { # we have data from previous record to compare to
		DELTA = ($1 - FN) * 120 / 26
		CHK = "OK"
		if ("FACCH" == $2 && "ONSET" == TYPE) { # ONSET due to FACCH is NOT a talkspurt
			SILENCE = 1
		}
		if (("UPDATE" == TYPE || "FIRST" == TYPE) && ("FACCH" == $2 || "SPEECH" == $2)) { # check for missing ONSET:
				CHK = "FAIL: missing ONSET (" $2 ") after " TYPE "."
				ERR++
		}
		if ("SID_P1" == $2) {
			CHK = "FAIL: regular AMR payload with FT SID and STI=0 (should be either pyaload Update or STI=1)."
			ERR++
		}
		if ("FORCED_FIRST" == $2 || "FORCED_NODATA" == $2 || "FORCED_F_P2" == $2 || "FORCED_F_INH" == $2 || "FORCED_U_INH" == $2) {
			CHK = "FAIL: event " $2 " inserted by DSP."
			FORCE++
			ERR++
		}
		if ("FIRST_P2" != $2 && "FIRST_P1" == TYPE) {
			CHK = "FAIL: " TYPE " followed by " $2 " instead of P2."
			ERR++
		}
		if ("OK" == CHK && "ONSET" != $2) { # check inter-SID distances:
			if ("UPDATE" == TYPE) {
				if (DELTA > U_MAX) {
					CHK = "FAIL: delta (" $1 - FN "fn) from previous SID UPDATE (@" FN ") too big " DELTA "ms > " U_MAX "ms."
					ERR++
				}
				if ("UPDATE" == $2 && DELTA < U_MIN) {
					CHK = "FAIL: delta (" $1 - FN "fn) from previous SID UPDATE (@" FN ") too small " DELTA "ms < " U_MIN "ms."
					ERR++
				}
			}
			if ("FIRST" == TYPE) {
				if (DELTA > F_MAX) {
					CHK = "FAIL: delta (" $1 - FN "fn) from previous SID FIRST (@" FN ") too big " DELTA "ms > " F_MAX "ms."
					ERR++
				}
				if ("UPDATE" == $2 && DELTA < F_MIN) {
					CHK = "FAIL: delta (" $1 - FN "fn) from previous SID UPDATE (@" FN ") too small " DELTA "ms < " F_MIN "ms."
					ERR++
				}
			}
		}
		if ("FACCH" == TYPE && "FIRST" != $2 && "FACCH" != $2 && 1 == SILENCE) { # check FACCH handling
			CHK = "FAIL: incorrect silence resume with " $2 " after FACCH."
			ERR++
		}
	}
	if ("SPEECH" == $2 || "ONSET" == $2) { # talkspurt
		SILENCE = 0
	}
	if ("UPDATE" == $2 || "FIRST" == $2) { # silence
		SILENCE = 1
	}
	print $1, $2, CHK
	if ($2 != "EMPTY") { # skip over EMPTY records
		TYPE = $2
		FN = $1
	}
}

END {
	print "Check completed: found " ERR " errors (" FORCE " events inserted by DSP) in " NR " records."
}


Make the media and session attribute marker, a little bit more specific

git-svn-id: 392d0e5ce652024192c571322d40dcc23d6d83b0@128 8a65f31f-a56f-4bd6-98e8-2755f64920ec

BEGIN{
  first = 1;
}

/^Frame [0-9]+: (.*)/ {
  # New frame, closing
 and html> from previous frame before compute new filename
  if (first == 0) {
    printf "
\n" >filename
    printf " \n" >filename
    printf "\n" >filename
    printf "\n" >filename
  } else {
    first = 0
  }

  nbr = $2
  sub (":", "", nbr)
  filename = destDir "/frames/Frame" nbr ".html"
  printf "\n" >filename
  printf " \n" >filename
  printf "  <link rel=\"stylesheet\" type=\"text/css\" href=\"callflow.css\" />\n" >filename
  # The style tag is actually not desired, but it is needed as otherwise the
  # SIP messages are displayed incorrectly in dynamic presentation mode.
  # The java script used in the dynamic presentation mode does not read the stylesheet.
  printf "  \n" >filename
  printf "  <title>callflow - frame %s</title>\n", nbr >filename
  printf " \n" >filename
  printf " \n" >filename
  printf "
\n" >filename
  print $0 >filename
  # Print time
  getline
  gsub("^ *", "")
  print $0 >filename;
  discard = 1;
}
/^Internet Protocol,/ {
  print $0 >filename;
  discard = 1;
}
/^Internet Protocol$/ {
  discard = 0;
}
/^User Datagram Protocol,/ {
  print $0 >filename;
  discard = 1;
}
/^User Datagram Protocol$/ {
  discard = 0;
}
/^Transmission Control Protocol,/ {
  print $0 >filename;
  discard = 1;
}
/^Transmission Control Protocol$/ {
  discard = 0;
}
/^Internet Control Message Protocol/ {
  discard = 0;
}
/^Session Initiation Protocol/ {
  discard = 0;
}
/^Diameter Protocol/ {
  discard = 0;
}
/^WebSocket Protocol/ {
  discard = 0;
}
/^Hypertext Transfer Protocol/ {
  discard = 0;
}
/^Call Specification Language/ {
  discard = 0;
}
{
  if (discard==0)
    {

      gsub("&","\\&");
      gsub(">","\\>");
      gsub("<","\\<");

      MARK = "no"
      if ($0 ~ "Session Initiation Protocol" ) MARK = "sip"
      if ($1 ~ "Accept:" ) MARK = "sip"
      if ($1 ~ "Allow:" ) MARK = "sip"
      if ($1 ~ "Allow-Events:" ) MARK = "sip"
      if ($0 ~ "Call-ID:" ) MARK = "sip"
      if ($1 ~ "Charge:" ) MARK = "sip"
      if ($1 ~ "Contact:" ) MARK = "sip"
      if ($1 ~ "Content-Disposition:" ) MARK = "sip"
      if ($1 ~ "Content-Length:" ) MARK = "sip"
      if ($1 ~ "Content-Type:" ) MARK = "sip"
      if ($1 ~ "CSeq:" ) MARK = "sip"
      if ($1 ~ "Date:" ) MARK = "sip"
      if ($1 ~ "Expires:" ) MARK = "sip"
      if ($1 ~ "From:" ) MARK = "sip"
      if ($1 ~ "From:" ) MARK = "sip"
      if ($1 ~ "P-[A-Z][a-z].*:" ) MARK = "sip"
      if ($1 ~ "Privacy:" ) MARK = "sip"
      if ($1 ~ "RAck:" ) MARK = "sip"
      if ($1 ~ "Remote-Party-ID:" ) MARK = "sip"
      if ($1 ~ "Require:" ) MARK = "sip"
      if ($1 ~ "Request-Line:" ) MARK = "sip"
      if ($1 ~ "Route:" ) MARK = "sip"
      if ($1 ~ "RSeq:" ) MARK = "sip"
      if ($1 ~ "Server:" ) MARK = "sip"
      if ($1 ~ "Supported:" ) MARK = "sip"
      if ($1 ~ "Timestamp:" ) MARK = "sip"
      if ($1 ~ "To:" ) MARK = "sip"
      if ($1 ~ "User-Agent:" ) MARK = "sip"
      if ($1 ~ "Via:" ) MARK = "sip"

      if ($0 ~ "Session Description Protocol") MARK = "media"
      if ($0 ~ "Connection Information") MARK = "media"
      # Use ... here.  Actually "(a)" should be used, but awk does not like the
      # parenthesis (), workaround it with the dots.
      if ($0 ~ "Media Attribute .a.:") MARK = "media"
      if ($0 ~ "Media Description, name and address") MARK = "media"
      if ($0 ~ "Owner/Creator, Session Id") MARK = "media"
      if ($0 ~ "Session Attribute .a.:") MARK = "media"
      if ($0 ~ "Session Description Protocol Version") MARK = "media"
      if ($0 ~ "Session Name") MARK = "media"
      if ($0 ~ "Time Description, active time") MARK = "media"

      if (MARK == "sip") {
        printf "
%s
\n", $0 > filename;

      } else if (MARK == "media") {
        printf "
%s
\n", $0 > filename;

      } else {
        print $0 > filename;
      }
    }
}
END {
  printf "
\n" >filename
  printf " \n" >filename
  printf "\n" >filename
  printf "\n" >filename
}

BEGIN{
  first = 1;
}

/^Frame [0-9]+: (.*)/ {
  # New frame, closing
 and html> from previous frame before compute new filename
  if (first == 0) {
    printf "
\n" >filename
    printf " \n" >filename
    printf "\n" >filename
    printf "\n" >filename
  } else {
    first = 0
  }

  nbr = $2
  sub (":", "", nbr)
  filename = destDir "/frames/Frame" nbr ".html"
  printf "\n" >filename
  printf " \n" >filename
  printf "  <link rel=\"stylesheet\" type=\"text/css\" href=\"callflow.css\" />\n" >filename
  # The style tag is actually not desired, but it is needed as otherwise the
  # SIP messages are displayed incorrectly in dynamic presentation mode.
  # The java script used in the dynamic presentation mode does not read the stylesheet.
  printf "  \n" >filename
  printf "  <title>callflow - frame %s</title>\n", nbr >filename
  printf " \n" >filename
  printf " \n" >filename
  printf "
\n" >filename
  print $0 >filename
  # Print time
  getline
  gsub("^ *", "")
  print $0 >filename;
  discard = 1;
}
/^Internet Protocol,/ {
  print $0 >filename;
  discard = 1;
}
/^Internet Protocol$/ {
  discard = 0;
}
/^User Datagram Protocol,/ {
  print $0 >filename;
  discard = 1;
}
/^User Datagram Protocol$/ {
  discard = 0;
}
/^Transmission Control Protocol,/ {
  print $0 >filename;
  discard = 1;
}
/^Transmission Control Protocol$/ {
  discard = 0;
}
/^Internet Control Message Protocol/ {
  discard = 0;
}
/^Session Initiation Protocol/ {
  discard = 0;
}
/^Diameter Protocol/ {
  discard = 0;
}
/^WebSocket Protocol/ {
  discard = 0;
}
/^Hypertext Transfer Protocol/ {
  discard = 0;
}
/^Call Specification Language/ {
  discard = 0;
}
{
  if (discard==0)
    {

      gsub("&","\\&");
      gsub(">","\\>");
      gsub("<","\\<");

      MARK = "no"
      if ($0 ~ "Session Initiation Protocol" ) MARK = "sip"
      if ($1 ~ "Accept:" ) MARK = "sip"
      if ($1 ~ "Allow:" ) MARK = "sip"
      if ($1 ~ "Allow-Events:" ) MARK = "sip"
      if ($0 ~ "Call-ID:" ) MARK = "sip"
      if ($1 ~ "Charge:" ) MARK = "sip"
      if ($1 ~ "Contact:" ) MARK = "sip"
      if ($1 ~ "Content-Disposition:" ) MARK = "sip"
      if ($1 ~ "Content-Length:" ) MARK = "sip"
      if ($1 ~ "Content-Type:" ) MARK = "sip"
      if ($1 ~ "CSeq:" ) MARK = "sip"
      if ($1 ~ "Date:" ) MARK = "sip"
      if ($1 ~ "Expires:" ) MARK = "sip"
      if ($1 ~ "From:" ) MARK = "sip"
      if ($1 ~ "From:" ) MARK = "sip"
      if ($1 ~ "P-[A-Z][a-z].*:" ) MARK = "sip"
      if ($1 ~ "Privacy:" ) MARK = "sip"
      if ($1 ~ "RAck:" ) MARK = "sip"
      if ($1 ~ "Remote-Party-ID:" ) MARK = "sip"
      if ($1 ~ "Require:" ) MARK = "sip"
      if ($1 ~ "Request-Line:" ) MARK = "sip"
      if ($1 ~ "Route:" ) MARK = "sip"
      if ($1 ~ "RSeq:" ) MARK = "sip"
      if ($1 ~ "Server:" ) MARK = "sip"
      if ($1 ~ "Supported:" ) MARK = "sip"
      if ($1 ~ "Timestamp:" ) MARK = "sip"
      if ($1 ~ "To:" ) MARK = "sip"
      if ($1 ~ "User-Agent:" ) MARK = "sip"
      if ($1 ~ "Via:" ) MARK = "sip"

      if ($0 ~ "Session Description Protocol") MARK = "media"
      if ($0 ~ "Connection Information") MARK = "media"
      # Use ... here.  Actually "(a)" should be used, but awk does not like the
      # parenthesis (), workaround it with the dots.
      if ($0 ~ "Media Attribute ...:") MARK = "media"
      if ($0 ~ "Media Description, name and address") MARK = "media"
      if ($0 ~ "Owner/Creator, Session Id") MARK = "media"
      if ($0 ~ "Session Attribute ...:") MARK = "media"
      if ($0 ~ "Session Description Protocol Version") MARK = "media"
      if ($0 ~ "Session Name") MARK = "media"
      if ($0 ~ "Time Description, active time") MARK = "media"

      if (MARK == "sip") {
        printf "
%s
\n", $0 > filename;

      } else if (MARK == "media") {
        printf "
%s
\n", $0 > filename;

      } else {
        print $0 > filename;
      }
    }
}
END {
  printf "
\n" >filename
  printf " \n" >filename
  printf "\n" >filename
  printf "\n" >filename
}


Changed some formatting of for and if, then, else statements

git-svn-id: 392d0e5ce652024192c571322d40dcc23d6d83b0@79 8a65f31f-a56f-4bd6-98e8-2755f64920ec

print "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>";
  print "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">";

  for(i=0;i<numHosts;i++) {
    lookup[hosts[i]] = i;
    printf "\n", hosts[i], i;
  }

  w = (numHosts-1) * xHostSpace + leftMargin + rightMargin;
  h = numLines * yLineSpace + topMargin + bottomMargin;

  printf "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"%d\" height=\"%d\" viewBox=\"0 0 %d %d\">\n",w,h,w,h;

  insertStyleDefs();

  ystart = 60;
  yend = h;

  printf "<text x=\"%d\" y=\"%d\" class=\"label title-text\">%s</text>\n",
    (w/2),
    ystart-35,
    title;

  for(i=0;i<numTraces;i++) {

    if (label[i] == "")
      label[i] = hosts[i];

    printf "<text x=\"%d\" y=\"%d\" class=\"label host-text\">%s</text>\n",
      leftMargin+(i*xHostSpace),
      ystart-(15*(i%2)+2),
      label[i];

    printf "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" class=\"element-line\" />\n", leftMargin+(i*xHostSpace), ystart, leftMargin+(i*xHostSpace), yend;
  }

  printf "   <map name=\"callflowmap\" id=\"callflowmap\">\n" > "imagemap";
}

func insertStyleDefs () {
  printf "<defs>\n\n</defs>\n";
}

func arrow(x,y,d,c) {

  printf "<polygon points=\"%d,%d %d,%d %d,%d %d,%d\" class=\"arrowhead\" style=\"fill: %s; stroke: %s;\"/>\n",
  x,y,
  x+5*d, y-3,
  x+3*d, y,
  x+5*d, y+3,
  color[c], color[c];
}

func line(x1,x2,y,output, c) {

  if (x1 == x2) {
    printf "<polyline points=\"%d,%d %d,%d %d,%d %d,%d %d,%d %d,%d %d,%d %d,%d\" fill=\"none\" class=\"traceline\"/>\n",
      x1,y-18+7,
      x1+5,y-18+7,
      x1+12,y-15+7,
      x1+15,y-10+7,
      x1+15,y-5+7,
      x1+12,y-2+7,
      x1+5,y+7,
      x1,y+7;
    arrow(x1,y+7,1);

    xtext = x1 + 18;

    printf "    <area href=\"frames/Frame%d.html\" coords=\"%d,%d,%d,%d\" alt=\"frame %d\"  onmouseover=\"return getFrame('frames/Frame%d.html');\" onmouseout=\"return nd();\"/>\n", $3, x1, y-yLineSpace+2, x1+15, y+7+1, $3, $3 >> "imagemap"

  } else if (x1<x2) {
    printf "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" class=\"traceline\" style=\"stroke: %s;\"/>\n", x1, y, x2, y, color[c];
    arrow(x2,y,-1,c);

    xtext = x1 + 10;

    printf "    <area href=\"frames/Frame%d.html\" coords=\"%d,%d,%d,%d\" alt=\"frame %d\"  onmouseover=\"return getFrame('frames/Frame%d.html');\" onmouseout=\"return nd();\"/>\n", $3, x1, y-yLineSpace+2, x2, y+1, $3, $3 >> "imagemap"

  } else {
    printf "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" class=\"traceline\" style=\"stroke: %s;\"/>\n", x1, y, x2, y, color[c];
    arrow(x2,y,1,c);

    xtext = x2 + 10;

    printf "    <area href=\"frames/Frame%d.html\" coords=\"%d,%d,%d,%d\" alt=\"frame %d\"  onmouseover=\"return getFrame('frames/Frame%d.html');\" onmouseout=\"return nd();\"/>\n", $3, x2, y-yLineSpace+2, x1, y+1, $3, $3 >> "imagemap"
  }

  printf "\n", $3;
  printf "<text x=\"%d\" y=\"%d\" class=\"pkt-text\">%s</text>\n", xtext, y-4, output;
  printf "\n";
}

{
  y = NR;
  y = y * yLineSpace + ystart;

  if($0 ~ "^#") {
    output = "";
    split($0, A, " ")

    for (i=2; i <= length(A); i++) {

      if (A[i] == "!")
        break;
      output = output " " A[i];
    }

    link = "";
    if (A[i] == "!") {

      i++;
      for(;i <= length(A); i++) {
        link = link " " A[i];
      }
    }

    if (link != "") {

      printf("<text x=\"%d\" y=\"%d\" class=\"link\">%s</text>\n", 50, y, output);
      printf("    <area href=\"%s\" coords=\"%d,%d,%d,%d\"/>\n", link, 50, y-yLineSpace+2, w, y+1) >> "imagemap";

    } else {

      printf("<text x=\"%d\" y=\"%d\" class=\"comment-text\">%s</text>\n", 50, y, output);
    }

  } else {

    if ($6 ~ "{([0-9]+)}") {
      str = $6
      gsub("{", "", str)
      gsub("}", "", str)

      colorId = strtonum(str)
      if ( colorId <= colors ) {
        c = colorId
      } else {

        # There are more sessions than configured colors.
        # choose the first color as default...
        c = 0
      }

    } else { c = 0 }

    l1 = sprintf("%s:%s", $4,$5);
    l2 = sprintf("%s:%s", $7,$8);
    for (i=0; i<numHosts; i++) {

      if(l1 ~ hosts[i]) {
        x1 = strtonum(lookup[hosts[i]]);
      }

      if(l2 ~ hosts[i]) {
        x2 = strtonum(lookup[hosts[i]]);
      }
    }

    x1 = x1 * xHostSpace + leftMargin;
    x2 = x2 * xHostSpace + leftMargin;
    if ((x1==x2) && (noLocalLoop==1)) {
      # Do nothing
    } else {
      # Print the line
      ORS = "";
      printf "<text x=\"%d\" y=\"%d\" class=\"pkt-text\">%d</text>\n", leftMargin/2, y, $3;

      output = $9" "$10;

      gsub("SIP(/SDP|/XML|) *(Status|Request): *","", output);
      gsub(", with session description *$"," w/SDP",output);

      line(x1,x2,y,output,c);
    }
  }
}

END {
  printf "   </map>\n" >> "imagemap";
  printf "</svg>\n";
}

print "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>";
  print "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">";

  for(i=0;i<numHosts;i++) {
    lookup[hosts[i]] = i;
    printf "\n", hosts[i], i;
  }

  w = (numHosts-1) * xHostSpace + leftMargin + rightMargin;
  h = numLines * yLineSpace + topMargin + bottomMargin;

  printf "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"%d\" height=\"%d\" viewBox=\"0 0 %d %d\">\n",w,h,w,h;

  insertStyleDefs();

  ystart = 60;
  yend = h;

  printf "<text x=\"%d\" y=\"%d\" class=\"label title-text\">%s</text>\n",
    (w/2),
    ystart-35,
    title;

  for(i=0;i<numTraces;i++) {

    if (label[i] == "")
      label[i] = hosts[i];

    printf "<text x=\"%d\" y=\"%d\" class=\"label host-text\">%s</text>\n",
      leftMargin+(i*xHostSpace),
      ystart-(15*(i%2)+2),
      label[i];

    printf "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" class=\"element-line\" />\n", leftMargin+(i*xHostSpace), ystart, leftMargin+(i*xHostSpace), yend;
  }

  printf "   <map name=\"callflowmap\" id=\"callflowmap\">\n" > "imagemap";
}

func insertStyleDefs () {
  printf "<defs>\n\n</defs>\n";
}

func arrow(x,y,d,c) {

  printf "<polygon points=\"%d,%d %d,%d %d,%d %d,%d\" class=\"arrowhead\" style=\"fill: %s; stroke: %s;\"/>\n",
  x,y,
  x+5*d, y-3,
  x+3*d, y,
  x+5*d, y+3,
  color[c], color[c];
}

func line(x1,x2,y,output, c) {

  if (x1 == x2) {
    printf "<polyline points=\"%d,%d %d,%d %d,%d %d,%d %d,%d %d,%d %d,%d %d,%d\" fill=\"none\" class=\"traceline\"/>\n",
      x1,y-18+7,
      x1+5,y-18+7,
      x1+12,y-15+7,
      x1+15,y-10+7,
      x1+15,y-5+7,
      x1+12,y-2+7,
      x1+5,y+7,
      x1,y+7;
    arrow(x1,y+7,1);

    xtext = x1 + 18;

    printf "    <area href=\"frames/Frame%d.html\" coords=\"%d,%d,%d,%d\" alt=\"frame %d\"  onmouseover=\"return getFrame('frames/Frame%d.html');\" onmouseout=\"return nd();\"/>\n", $3, x1, y-yLineSpace+2, x1+15, y+7+1, $3, $3 >> "imagemap"

  } else if (x1<x2) {
    printf "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" class=\"traceline\" style=\"stroke: %s;\"/>\n", x1, y, x2, y, color[c];
    arrow(x2,y,-1,c);

    xtext = x1 + 10;

    printf "    <area href=\"frames/Frame%d.html\" coords=\"%d,%d,%d,%d\" alt=\"frame %d\"  onmouseover=\"return getFrame('frames/Frame%d.html');\" onmouseout=\"return nd();\"/>\n", $3, x1, y-yLineSpace+2, x2, y+1, $3, $3 >> "imagemap"

  } else {
    printf "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" class=\"traceline\" style=\"stroke: %s;\"/>\n", x1, y, x2, y, color[c];
    arrow(x2,y,1,c);

    xtext = x2 + 10;

    printf "    <area href=\"frames/Frame%d.html\" coords=\"%d,%d,%d,%d\" alt=\"frame %d\"  onmouseover=\"return getFrame('frames/Frame%d.html');\" onmouseout=\"return nd();\"/>\n", $3, x2, y-yLineSpace+2, x1, y+1, $3, $3 >> "imagemap"
  }

  printf "\n", $3;
  printf "<text x=\"%d\" y=\"%d\" class=\"pkt-text\">%s</text>\n", xtext, y-4, output;
  printf "\n";
}

{
  y = NR;
  y = y * yLineSpace + ystart;

  if($0 ~ "^#")
  {
    output = "";
    split($0, A, " ")

    for (i=2; i <= length(A); i++)
    {
      if (A[i] == "!")
        break;
      output = output " " A[i];
    }

    link = "";
    if (A[i] == "!")
    {
      i++;
      for(;i <= length(A); i++)
      {
        link = link " " A[i];
      }
    }

    if (link != "")
    {
      printf("<text x=\"%d\" y=\"%d\" class=\"link\">%s</text>\n", 50, y, output);
      printf("    <area href=\"%s\" coords=\"%d,%d,%d,%d\"/>\n", link, 50, y-yLineSpace+2, w, y+1) >> "imagemap";
    }
    else
    {
      printf("<text x=\"%d\" y=\"%d\" class=\"comment-text\">%s</text>\n", 50, y, output);
    }
  }
  else
  {
    if ($6 ~ "{([0-9]+)}") {
      str = $6
      gsub("{", "", str)
      gsub("}", "", str)

      colorId = strtonum(str)
      if ( colorId <= colors ) {
        c = colorId
      } else {

        # There are more sessions than configured colors.
        # choose the first color as default...
        c = 0
      }

    } else { c = 0 }

    l1 = sprintf("%s:%s", $4,$5);
    l2 = sprintf("%s:%s", $7,$8);
    for (i=0; i<numHosts; i++)
    {
      if(l1 ~ hosts[i])
      {
        x1 = strtonum(lookup[hosts[i]]);
      }
      if(l2 ~ hosts[i])
      {
        x2 = strtonum(lookup[hosts[i]]);
      }
    }

    x1 = x1 * xHostSpace + leftMargin;
    x2 = x2 * xHostSpace + leftMargin;
    if ((x1==x2) && (noLocalLoop==1)){
      # Do nothing
      }
    else{
      # Print the line
      ORS = "";
      printf "<text x=\"%d\" y=\"%d\" class=\"pkt-text\">%d</text>\n", leftMargin/2, y, $3;

      output = $9" "$10;

      gsub("SIP(/SDP|/XML|) *(Status|Request): *","", output);
      gsub(", with session description *$"," w/SDP",output);

      line(x1,x2,y,output,c);
    }
  }
}

END {
  printf "   </map>\n" >> "imagemap";
  printf "</svg>\n";
}