Translate: update

Translate: update

####################################################################
# 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" :
         "")
}

# 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, 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

    print "GET " url " HTTP/1.1\n"             \
          "Host: " HttpHost "\n"               \
          "Connection: close\n" |& HttpService
    while ((HttpService |& getline) > 0)
        content = $0
    close(HttpService)

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

# 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,
                        il, ils, isPhonetic,
                        article, example, explanation, ref, word,
                        translation, translations, phonetics,
                        wordClasses, words, segments, altTranslations,
                        original, oPhonetics, oWordClasses, oWords,
                        oRefs, 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 cannot be found.")
    }

    content = getResponse(text, sl, tl, hl)
    plTokenize(tokens, content)
    plParse(ast, tokens)

    # Debug mode
    if (Option["debug"]) {
        d(content)
        da(tokens, "tokens[%s]='%s'")
        da(ast, "ast[%s]='%s'")
    }

    if (!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))
            oWordClasses[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) ?
            join(phonetics) :  # phonetic transcription
            s(translation, tl) # target language

        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 (il == tl && isarray(oWordClasses)) {
            wShowOriginalDictionary = 1
            wShowTranslation = 0
        }
        hasWordClasses = isarray(wordClasses) && anything(wordClasses)
        hasAltTranslations = isarray(altTranslations[0]) && anything(altTranslations[0])
        if (!hasWordClasses) wShowDictionary = 0
        if (hasWordClasses || !hasAltTranslations) wShowAlternatives = 0

        if (wShowOriginal) {
            # Display: original text
            if (r) r = r RS RS
            r = r ansi("negative", ansi("bold", s(join(original), il)))
            if (wShowOriginalPhonetics)
                r = r RS showPhonetics(join(oPhonetics), il)
        }

        if (wShowTranslation) {
            # Display: major translation & phonetics
            if (r) r = r RS RS
            r = r ansi("bold", s(translation, tl))
            if (wShowTranslationPhonetics)
                r = r RS showPhonetics(join(phonetics), tl)
        }

        if (wShowPromptMessage || wShowLanguages)
            if (r) r = r RS
        if (wShowPromptMessage) {
            # Display: prompt message
            if (hasWordClasses) {
                # Definitions of
                if (r) r = r RS
                if (isRTL(hl)) # home language is R-to-L
                    r = r s(showDefinitionsOf(hl, join(original)))
                else # home language is L-to-R
                    r = r showDefinitionsOf(hl, ansi("underline", show(join(original), il)))
            } else if (hasAltTranslations) {
                # Translations of
                if (r) r = r RS
                if (isRTL(hl)) # home language is R-to-L
                    r = r s(showTranslationsOf(hl, join(original)))
                else # home language is L-to-R
                    r = r showTranslationsOf(hl, ansi("underline", show(join(original), il)))
            }
        }
        if (wShowLanguages) {
            # Display: source language -> target language
            if (hasWordClasses || hasAltTranslations) {
                if (r) r = r RS
                r = r s(sprintf("[ %s -> %s ]", getEndonym(il), getEndonym(tl)))
            }
        }

        if (wShowOriginalDictionary) {
            # Display: original dictionary
            if (r) r = r RS
            if (isarray(oWordClasses) && anything(oWordClasses)) {
                for (i = 0; i < length(oWordClasses); i++) {
                    r = (i > 0 ? r RS : r) RS s(oWordClasses[i], hl)
                    if (isarray(oWords[i])) {
                        # Detailed explanation
                        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 ansi("bold", ins(1, explanation, il))
                            if (example)
                                r = r RS ins(2, "- \"" example "\"", il)
                            if (ref && isarray(oRefs[ref])) {
                                temp = showSynonyms(hl) ": " oSynonyms[oRefs[ref][1]][oRefs[ref][2]][0]
                                for (k = 1; k < length(oSynonyms[oRefs[ref][1]][oRefs[ref][2]]); k++)
                                    temp = temp ", " oSynonyms[oRefs[ref][1]][oRefs[ref][2]][k]
                                r = r RS ins(1, temp)
                            }
                        }
                    } else {
                        # Synonyms only
                        for (j = 0; j < length(oSynonyms[i]); j++) {
                            temp = "* " oSynonyms[i][j][0]
                            for (k = 1; k < length(oSynonyms[i][j]); k++)
                                temp = temp ", " oSynonyms[i][j][k]
                            r = r RS ins(1, temp)
                        }
                    }
                }
            }
            # Examples
            if (isarray(oExamples) && anything(oExamples)) {
                r = r RS RS s(showExamples(hl), hl)
                for (i = 0; i < length(oExamples); i++) {
                    example = oExamples[i]

                    if (isRTL(il)) { # target language is R-to-L
                        sub(/\u003cb\u003e/, "", example)
                        sub(/\u003c\/b\u003e/, "", example)
                    } else { # target language is L-to-R
                        sub(/\u003cb\u003e/, AnsiCode["negative"] AnsiCode["bold"], example)
                        sub(/\u003c\/b\u003e/, AnsiCode["positive"] AnsiCode["no bold"], example)
                    }
                    r = (i > 0 ? r RS : r) RS ins(1, "- " example, il)
                }
            }
            # See also
            if (isarray(oSeeAlso) && anything(oSeeAlso)) {
                r = r RS RS s(showSeeAlso(hl), hl)
                temp = isRTL(il) ? oSeeAlso[0] : ansi("underline", oSeeAlso[0])
                for (k = 1; k < length(oSeeAlso); k++)
                    temp = temp ", " (isRTL(il) ? oSeeAlso[k] : ansi("underline", oSeeAlso[k]))
                r = r RS ins(1, temp, il)
            }
        }

        if (wShowDictionary) {
            # Display: dictionary entries
            if (r) r = r RS
            for (i = 0; i < length(wordClasses); i++) {
                r = (i > 0 ? r RS : r) RS s(wordClasses[i], hl)
                for (j = 0; j < length(words[i]); j++) {
                    word = words[i][j][0]
                    explanation = join(words[i][j][1], ", ")
                    article = words[i][j][4]

                    r = r RS ansi("bold", ins(1, (article ? "(" article ") " : "") word, tl))
                    r = r RS ins(2, explanation, il)
                }
            }
        }

        if (wShowAlternatives) {
            # Display: alternative translations
            if (r) r = r RS RS
            for (i = 0; i < length(altTranslations); i++) {
                r = (i > 0 ? r RS : r) ansi("underline", show(segments[i]))
                temp = isRTL(tl) ? altTranslations[i][0] : ansi("bold", altTranslations[i][0])
                for (j = 1; j < length(altTranslations[i]); j++)
                    temp = temp ", " (isRTL(tl) ? altTranslations[i][j] : ansi("bold", 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, r, 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 cannot be 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 cannot be 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)
                print replicate("─", 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 {
            r = getTranslation(text, Option["sl"], Option["tl"][i], Option["hl"], Option["verbose"], Option["play"], playlist)
            print r > Option["output"]

            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)
                print replicate("═", Option["width"])

        if (Option["interactive"]) {
            if (line ~ /:(q|quit)/)
                exit
            else if (line ~ /:(s|source)/)
                print Option["sl"]
            else if (line ~ /:(t|target)/) {
                printf "(" Option["tl"][1]
                for (i = 2; i <= length(Option["tl"]); i++)
                    printf ", " Option["tl"][i]
                print ")"
            }

            else {
                translate(line)

                # Interactive verbose mode: newline after each translation
                if (Option["verbose"]) printf RS
            }

            prompt()
        } else
            translate(line)
    }
}

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

# Detect external audio player (mplayer, mpv, mpg123).
function initAudioPlayer() {
    AudioPlayer = !system("mplayer >/dev/null 2>/dev/null") ?
        "mplayer" :
        (!system("mpv >/dev/null 2>/dev/null") ?
         "mpv" :
         (!system("mpg123 >/dev/null 2>/dev/null") ?
          "mpg123" :
          ""))
}

# Detect external speech synthesizer (say, espeak).
function initSpeechSynthesizer() {
    SpeechSynthesizer = !system("say '' >/dev/null 2>/dev/null") ?
        "say" :
        (!system("espeak '' >/dev/null 2>/dev/null") ?
         "espeak" :
         "")
}

# 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, 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

    print "GET " url " HTTP/1.1\n"             \
          "Host: " HttpHost "\n"               \
          "Connection: close\n" |& HttpService
    while ((HttpService |& getline) > 0)
        content = $0
    close(HttpService)

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

# 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"] " '" url "' >/dev/null 2>/dev/null")
}

# Get the translation of a string.
function getTranslation(text, sl, tl, hl,
                        isVerbose, toSpeech, returnPlaylist,
                        ####
                        r,
                        content, tokens, ast,
                        il, ils, isPhonetic,
                        article, example, explanation, ref, word,
                        translation, translations, phonetics,
                        wordClasses, words, segments, altTranslations,
                        original, oPhonetics, oWordClasses, oWords,
                        oRefs, 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 cannot be found.")
    }

    content = getResponse(text, sl, tl, hl)
    plTokenize(tokens, content)
    plParse(ast, tokens)

    # Debug mode
    if (Option["debug"]) {
        d(content)
        da(tokens, "tokens[%s]='%s'")
        da(ast, "ast[%s]='%s'")
    }

    if (!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))
            oWordClasses[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) ?
            join(phonetics) :  # phonetic transcription
            s(translation, tl) # target language

        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 (il == tl && isarray(oWordClasses)) {
            wShowOriginalDictionary = 1
            wShowTranslation = 0
        }
        hasWordClasses = isarray(wordClasses) && anything(wordClasses)
        hasAltTranslations = isarray(altTranslations[0]) && anything(altTranslations[0])
        if (!hasWordClasses) wShowDictionary = 0
        if (hasWordClasses || !hasAltTranslations) wShowAlternatives = 0

        if (wShowOriginal) {
            # Display: original text
            if (r) r = r RS RS
            r = r ansi("negative", ansi("bold", s(join(original), il)))
            if (wShowOriginalPhonetics)
                r = r RS showPhonetics(join(oPhonetics), il)
        }

        if (wShowTranslation) {
            # Display: major translation & phonetics
            if (r) r = r RS RS
            r = r ansi("bold", s(translation, tl))
            if (wShowTranslationPhonetics)
                r = r RS showPhonetics(join(phonetics), tl)
        }

        if (wShowPromptMessage || wShowLanguages)
            if (r) r = r RS
        if (wShowPromptMessage) {
            # Display: prompt message
            if (hasWordClasses) {
                # Definitions of
                if (r) r = r RS
                if (isRTL(hl)) # home language is R-to-L
                    r = r s(showDefinitionsOf(hl, join(original)))
                else # home language is L-to-R
                    r = r showDefinitionsOf(hl, ansi("underline", show(join(original), il)))
            } else if (hasAltTranslations) {
                # Translations of
                if (r) r = r RS
                if (isRTL(hl)) # home language is R-to-L
                    r = r s(showTranslationsOf(hl, join(original)))
                else # home language is L-to-R
                    r = r showTranslationsOf(hl, ansi("underline", show(join(original), il)))
            }
        }
        if (wShowLanguages) {
            # Display: source language -> target language
            if (hasWordClasses || hasAltTranslations) {
                if (r) r = r RS
                r = r s(sprintf("[ %s -> %s ]", getEndonym(il), getEndonym(tl)))
            }
        }

        if (wShowOriginalDictionary) {
            # Display: original dictionary
            if (r) r = r RS
            if (isarray(oWordClasses) && anything(oWordClasses)) {
                for (i = 0; i < length(oWordClasses); i++) {
                    r = (i > 0 ? r RS : r) RS s(oWordClasses[i], hl)
                    if (isarray(oWords[i])) {
                        # Detailed explanation
                        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 ansi("bold", ins(1, explanation, il))
                            if (example)
                                r = r RS ins(2, "- \"" example "\"", il)
                            if (ref && isarray(oRefs[ref])) {
                                temp = showSynonyms(hl) ": " oSynonyms[oRefs[ref][1]][oRefs[ref][2]][0]
                                for (k = 1; k < length(oSynonyms[oRefs[ref][1]][oRefs[ref][2]]); k++)
                                    temp = temp ", " oSynonyms[oRefs[ref][1]][oRefs[ref][2]][k]
                                r = r RS ins(1, temp)
                            }
                        }
                    } else {
                        # Synonyms only
                        for (j = 0; j < length(oSynonyms[i]); j++) {
                            temp = "* " oSynonyms[i][j][0]
                            for (k = 1; k < length(oSynonyms[i][j]); k++)
                                temp = temp ", " oSynonyms[i][j][k]
                            r = r RS ins(1, temp)
                        }
                    }
                }
            }
            # Examples
            if (isarray(oExamples) && anything(oExamples)) {
                r = r RS RS s(showExamples(hl), hl)
                for (i = 0; i < length(oExamples); i++) {
                    example = oExamples[i]

                    if (isRTL(il)) { # target language is R-to-L
                        sub(/\u003cb\u003e/, "", example)
                        sub(/\u003c\/b\u003e/, "", example)
                    } else { # target language is L-to-R
                        sub(/\u003cb\u003e/, AnsiCode["negative"] AnsiCode["bold"], example)
                        sub(/\u003c\/b\u003e/, AnsiCode["positive"] AnsiCode["no bold"], example)
                    }
                    r = (i > 0 ? r RS : r) RS ins(1, "- " example, il)
                }
            }
            # See also
            if (isarray(oSeeAlso) && anything(oSeeAlso)) {
                r = r RS RS s(showSeeAlso(hl), hl)
                temp = isRTL(il) ? oSeeAlso[0] : ansi("underline", oSeeAlso[0])
                for (k = 1; k < length(oSeeAlso); k++)
                    temp = temp ", " (isRTL(il) ? oSeeAlso[k] : ansi("underline", oSeeAlso[k]))
                r = r RS ins(1, temp, il)
            }
        }

        if (wShowDictionary) {
            # Display: dictionary entries
            if (r) r = r RS
            for (i = 0; i < length(wordClasses); i++) {
                r = (i > 0 ? r RS : r) RS s(wordClasses[i], hl)
                for (j = 0; j < length(words[i]); j++) {
                    word = words[i][j][0]
                    explanation = join(words[i][j][1], ", ")
                    article = words[i][j][4]

                    r = r RS ansi("bold", ins(1, (article ? "(" article ") " : "") word, tl))
                    r = r RS ins(2, explanation, il)
                }
            }
        }

        if (wShowAlternatives) {
            # Display: alternative translations
            if (r) r = r RS RS
            for (i = 0; i < length(altTranslations); i++) {
                r = (i > 0 ? r RS : r) ansi("underline", show(segments[i]))
                temp = isRTL(tl) ? altTranslations[i][0] : ansi("bold", altTranslations[i][0])
                for (j = 1; j < length(altTranslations[i]); j++)
                    temp = temp ", " (isRTL(tl) ? altTranslations[i][j] : ansi("bold", 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 cannot be 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 cannot be 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)
                print replicate("─", Option["width"])

        if (inline &&
            startsWithAny(text, UriSchemes) == "file://") {
            fileTranslation(text)
        } else if (inline &&
                   startsWithAny(text, UriSchemes) == "http://" ||
                   startsWithAny(text, UriSchemes) == "https://") {
            webTranslation(text, Option["sl"], Option["tl"][i], Option["hl"])
        } else {
            print getTranslation(text, Option["sl"], Option["tl"][i], Option["hl"], Option["verbose"], Option["play"], playlist) > Option["output"]

            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)
                print replicate("═", Option["width"])

        if (Option["interactive"]) {
            if (line ~ /:(q|quit)/)
                exit
            else if (line ~ /:(s|source)/)
                print Option["sl"]
            else if (line ~ /:(t|target)/) {
                printf "(" Option["tl"][1]
                for (i = 2; i <= length(Option["tl"]); i++)
                    printf ", " Option["tl"][i]
                print ")"
            }

            else {
                translate(line)

                # Interactive verbose mode: newline after each translation
                if (Option["verbose"]) printf RS
            }

            prompt()
        } else
            translate(line)
    }
}


Delete analyze_path.awk

Delete analyze_path.awk

#!/usr/bin/awk -f
# extract `obs_id' and `source name' from path
#
# LIweitiaNux <liweitianux@gmail.com>
# 2013/02/04
#
# input:
#     path that include `oi' and `source name'
#     e.g.:
#
# output:
#

# main part
{
    if (NF==1) {
        ## oi & name
        input=($1 "/")
        if (input ~ /_oi/) {
            ## PATTERN: .../$name_oi$oi/...
            idx_oi = match(input, /oi[0-9]+/) + 2;    # `2' skip the `oi'
            len_oi = RLENGTH - 2;
            oi = substr(input, idx_oi, len_oi);
            idx_name = match(input, /\/[a-zA-Z0-9.+-]+_oi/) + 1;
            len_name = RLENGTH - 4;
            name = substr(input, idx_name, len_name);
        }
        else {
            ## PATTERN: .../$name/$oi/...
            idx_oi = match(input, /\/[0-9]+\//) + 1;
            len_oi = RLENGTH - 2;
            oi = substr(input, idx_oi, len_oi);
            idx_name1 = match(input, /\/[a-zA-Z0-9_.+-]+\/[0-9]+\//);
            len_name1 = RLENGTH;
            name1 = substr(input, idx_name1, len_name1);
            idx_name = match(name1, /\/[a-zA-Z0-9_.+-]+\//) + 1;
            len_name = RLENGTH - 2;
            name = substr(name1, idx_name, len_name);
        }
        ## output
        printf("input: %s\n", input)
        printf("oi: %s\nname: %s\n", oi, name)
    }
    else {
        printf("*** WARNING: invalid input: %s\n", $0)
    }
}
# END


Fix a leftover "iwi_boot" string.

Fix a leftover "iwi_boot" string.

Submitted by:	gallatin

#!/usr/bin/awk -f

#-
# Copyright (c) 2006 Max Laier.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS `AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$

#
# Script to generate module .c file from a list of firmware images
#

function usage ()
{
	print "usage: fw_stub <firmware:name>* [-m modname] [-c outfile]";
	exit 1;
}

#   These are just for convenience ...
function printc(s)
{
	if (opt_c)
		print s > ctmpfilename;
	else
		print s > "/dev/stdout";
}

BEGIN {

#
#   Process the command line.
#

num_files = 0;

for (i = 1; i < ARGC; i++) {
	if (ARGV[i] ~ /^-/) {
		#
		#   awk doesn't have getopt(), so we have to do it ourselves.
		#   This is a bit clumsy, but it works.
		#
		for (j = 2; j <= length(ARGV[i]); j++) {
			o = substr(ARGV[i], j, 1);
			if (o == "c") {
				if (length(ARGV[i]) > j) {
					opt_c = substr(ARGV[i], j + 1);
					break;
				}
				else {
					if (++i < ARGC)
						opt_c = ARGV[i];
					else
						usage();
				}
			} else if (o == "m") {
				if (length(ARGV[i]) > j) {
					opt_m = substr(ARGV[i], j + 1);
					break;
				}
				else {
					if (++i < ARGC)
						opt_m = ARGV[i];
					else
						usage();
				}
			} else
				usage();
		}
	} else {
		split(ARGV[i], curr, ":");
		filenames[num_files] = curr[1];
		if (length(curr[2]) > 0)
			shortnames[num_files] = curr[2];
		else
			shortnames[num_files] = curr[2];
		if (length(curr[3]) > 0)
			versions[num_files] = int(curr[3]);
		else
			versions[num_files] = 0;
		num_files++;
	}
}

if (!num_files || !opt_m)
	usage();

cfilename = opt_c;
ctmpfilename = cfilename ".tmp";

printc("#include <sys/param.h>\
#include <sys/errno.h>\
#include <sys/kernel.h>\
#include <sys/module.h>\
#include <sys/linker.h>\
#include <sys/firmware.h>\n");

for (file_i = 0; file_i < num_files; file_i++) {
	symb = filenames[file_i];
	# '-', '.' and '/' are converted to '_' by ld/objcopy
	gsub(/-|\.|\//, "_", symb);
	printc("extern char _binary_" symb "_start[], _binary_" symb "_end[];");
}

printc("\nstatic int\n"\
opt_m "_fw_modevent(module_t mod, int type, void *unused)\
{\
	struct firmware *fp;\
	switch (type) {\
	case MOD_LOAD:");

for (file_i = 0; file_i < num_files; file_i++) {
	short = shortnames[file_i];
	symb = filenames[file_i];
	version = versions[file_i];
	# '-', '.' and '/' are converted to '_' by ld/objcopy
	gsub(/-|\.|\//, "_", symb);

	if (file_i == 0)
		reg = "\t\tfp = ";
	else
		reg = "\t\t(void)";

	reg = reg "firmware_register(\"" short "\", _binary_" symb "_start , ";
	reg = reg "(size_t)(_binary_" symb "_end - _binary_" symb "_start), ";
	reg = reg version ", ";

	if (file_i == 0)
		reg = reg "NULL);";
	else
		reg = reg "fp);";

	printc(reg);
}

printc("\t\treturn (0);\
	case MOD_UNLOAD:");

for (file_i = 1; file_i < num_files; file_i++) {
	printc("\t\tfirmware_unregister(\"" shortnames[file_i] "\");");
}

printc("\t\tfirmware_unregister(\"" shortnames[0] "\");");

printc("\t\treturn (0);\
	}\
	return (EINVAL);\
}\
\
static moduledata_t " opt_m "_fw_mod = {\
        \"" opt_m "_fw\",\
        " opt_m "_fw_modevent,\
        0\
};\
DECLARE_MODULE(" opt_m "_fw, " opt_m "_fw_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);\
MODULE_VERSION(" opt_m "_fw, 1);\
MODULE_DEPEND(" opt_m "_fw, firmware, 1, 1, 1);\
");

if (opt_c)
	if ((rc = system("mv -f " ctmpfilename " " cfilename))) {
		print "'mv -f " ctmpfilename " " cfilename "' failed: " rc \
		    > "/dev/stderr";
		exit 1;
	}

exit 0;

}

#!/usr/bin/awk -f

#-
# Copyright (c) 2006 Max Laier.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS `AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$

#
# Script to generate module .c file from a list of firmware images
#

function usage ()
{
	print "usage: fw_stub <firmware:name>* [-m modname] [-c outfile]";
	exit 1;
}

#   These are just for convenience ...
function printc(s)
{
	if (opt_c)
		print s > ctmpfilename;
	else
		print s > "/dev/stdout";
}

BEGIN {

#
#   Process the command line.
#

num_files = 0;

for (i = 1; i < ARGC; i++) {
	if (ARGV[i] ~ /^-/) {
		#
		#   awk doesn't have getopt(), so we have to do it ourselves.
		#   This is a bit clumsy, but it works.
		#
		for (j = 2; j <= length(ARGV[i]); j++) {
			o = substr(ARGV[i], j, 1);
			if (o == "c") {
				if (length(ARGV[i]) > j) {
					opt_c = substr(ARGV[i], j + 1);
					break;
				}
				else {
					if (++i < ARGC)
						opt_c = ARGV[i];
					else
						usage();
				}
			} else if (o == "m") {
				if (length(ARGV[i]) > j) {
					opt_m = substr(ARGV[i], j + 1);
					break;
				}
				else {
					if (++i < ARGC)
						opt_m = ARGV[i];
					else
						usage();
				}
			} else
				usage();
		}
	} else {
		split(ARGV[i], curr, ":");
		filenames[num_files] = curr[1];
		if (length(curr[2]) > 0)
			shortnames[num_files] = curr[2];
		else
			shortnames[num_files] = curr[2];
		if (length(curr[3]) > 0)
			versions[num_files] = int(curr[3]);
		else
			versions[num_files] = 0;
		num_files++;
	}
}

if (!num_files || !opt_m)
	usage();

cfilename = opt_c;
ctmpfilename = cfilename ".tmp";

printc("#include <sys/param.h>\
#include <sys/errno.h>\
#include <sys/kernel.h>\
#include <sys/module.h>\
#include <sys/linker.h>\
#include <sys/firmware.h>\n");

for (file_i = 0; file_i < num_files; file_i++) {
	symb = filenames[file_i];
	# '-', '.' and '/' are converted to '_' by ld/objcopy
	gsub(/-|\.|\//, "_", symb);
	printc("extern char _binary_" symb "_start[], _binary_" symb "_end[];");
}

printc("\nstatic int\n"\
opt_m "_fw_modevent(module_t mod, int type, void *unused)\
{\
	struct firmware *fp;\
	switch (type) {\
	case MOD_LOAD:");

for (file_i = 0; file_i < num_files; file_i++) {
	short = shortnames[file_i];
	symb = filenames[file_i];
	version = versions[file_i];
	# '-', '.' and '/' are converted to '_' by ld/objcopy
	gsub(/-|\.|\//, "_", symb);

	if (file_i == 0)
		reg = "\t\tfp = ";
	else
		reg = "\t\t(void)";

	reg = reg "firmware_register(\"" short "\", _binary_" symb "_start , ";
	reg = reg "(size_t)(_binary_" symb "_end - _binary_" symb "_start), ";
	reg = reg version ", ";

	if (file_i == 0)
		reg = reg "NULL);";
	else
		reg = reg "fp);";

	printc(reg);
}

printc("\t\treturn (0);\
	case MOD_UNLOAD:");

for (file_i = 1; file_i < num_files; file_i++) {
	printc("\t\tfirmware_unregister(\"" shortnames[file_i] "\");");
}

printc("\t\tfirmware_unregister(\"" shortnames[0] "\");");

printc("\t\treturn (0);\
	}\
	return (EINVAL);\
}\
\
static moduledata_t " opt_m "_fw_mod = {\
        \"" opt_m "_fw\",\
        " opt_m "_fw_modevent,\
        0\
};\
DECLARE_MODULE(" opt_m "_fw, " opt_m "_fw_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);\
MODULE_VERSION(" opt_m "_fw, 1);\
MODULE_DEPEND(iwi_boot_fw, firmware, 1, 1, 1);\
");

if (opt_c)
	if ((rc = system("mv -f " ctmpfilename " " cfilename))) {
		print "'mv -f " ctmpfilename " " cfilename "' failed: " rc \
		    > "/dev/stderr";
		exit 1;
	}

exit 0;

}


Add single episode warning

Add single episode warning

# Produce a raw data spreadsheet from URLs found in $SHOW_URLS
#
# Note that IN_CANADA affects processing.
#
# INVOCATION:
#    while read -r line; do
#        curl -sS "$line" |
#            awk -v ERRORS=$ERRORS -v RAW_TITLES=$RAW_TITLES -v EPISODE_URLS=$EPISODE_URLS \
#                -v SHORT_SPREADSHEET=$SHORT_SPREADSHEET -f getAcornFrom-showPages.awk >>$UNSORTED
#    done <"$SHOW_URLS"

#   Field Names
#        1 Title (link)    2 Seasons    3 Episodes    4 Duration    5 Description

# Extract the show title
/meta property="og:title/ {
    split ($0,fld,"\"")
    showTitle = fld[4]
    sub (/^Watch /,"",showTitle)
    sub (/ on Acorn TV$/,"",showTitle)
    gsub (/&/,"\\&",showTitle)
    gsub (/"/,"\"\"",showTitle)
    gsub (/'/,"'",showTitle)
    if (match (showTitle, /^The /)) {
        showTitle = substr(showTitle, 5) ", The"
    }
    print showTitle >> RAW_TITLES
    # print "==> showTitle = " showTitle > "/dev/stderr"
    next
}

# Extract the show URL
/meta property="og:url/ {
    split ($0,fld,"\"")
    showURL = fld[4]
    # print "==> showURL = " showURL > "/dev/stderr"
    # Create shorter URL by removing https://
    shortURL = showURL
    sub (/.*acorn\.tv/,"acorn.tv",shortURL)
    next
}

# Extract the number of episodes in the show
/itemprop="numberOfEpisodes"/ {
    episodeLinesFound += 1
    split ($0,fld,"\"")
    if (episodeLinesFound == 1) {
        showEpisodes = fld[4]
        if (showEpisodes == "") {
            printf ("==> Blank showEpisodes in numberOfEpisodes: %s\t%s\n", shortURL,
                    showTitle) >> ERRORS
        }
        # print "==> showEpisodes = " showEpisodes " " shortURL > "/dev/stderr"
    }
    if (episodeLinesFound != 1) {
        seasonEpisodes = seasonEpisodes "+" fld[4]
        if (seasonEpisodes == "") {
            printf ("==> Blank seasonEpisodes in numberOfEpisodes: %s\t%s\n", shortURL,
                    showTitle) >> ERRORS
        }
        # print "==> seasonEpisodes = " seasonEpisodes " " shortURL > "/dev/stderr"
    }
    next
}

# Extract the number of seasons in the show
/itemprop="numberOfSeasons"/ {
    seasonLinesFound += 1
    split ($0,fld,"\"")
    showSeasons = fld[4]
    if (showSeasons == "") {
        printf ("==> Blank showSeasons in numberOfSeasons: %s\t%s\n", shortURL, showTitle) >> ERRORS
    }
    # print "==> showSeasons = " showSeasons " " shortURL > "/dev/stderr"
    next
}

# Extract the show description
/id="franchise-description"/ {
    descriptionLinesFound += 1
    # get rid of boilerplate
    split ($0,fld,"[<>]")
    showDescription = fld[3]
    # fix sloppy input spacing
    gsub (/ \./,".",showDescription)
    gsub (/  */," ",showDescription)
    sub (/^ */,"",showDescription)
    sub (/ *$/,"",showDescription)
    # fix funky HTML characters
    gsub (/&/,"\\&",showDescription)
    gsub (/"/,"\"\"",showDescription)
    gsub (/'/,"'",showDescription)
    # fix unmatched quotes
    numQuotes = gsub(/"/,"\"",showDescription)
    if ((numQuotes % 2) == 1) {
        printf ("==> Changed unmatched quote (%d): %d\t%s\t%s\t%s\n", numQuotes, shortURL,
                showTitle) >> ERRORS
        showDescription = showDescription " \""
    }
    # print "==> showDescription = " showDescription " " shortURL > "/dev/stderr"
    next
}

# Extract the seasonNumber
# Note that Acorn "season" numbers may not correspond to actual season numbers.
# They simply refer to the number of seasons available on Acorn.
/<meta itemprop="seasonNumber" content="/ {
    split ($0,fld,"\"")
    seasonNumber = fld[4]
    # print "==> seasonNumber = " seasonNumber " " shortURL > "/dev/stderr"
    next
}

# Extract the episode URL
/ episodeURL = " episodeURL > "/dev/stderr"
    print episodeURL >> EPISODE_URLS
    shortEpisodeURL = episodeURL
    sub (/.*acorn\.tv/,"acorn.tv",shortEpisodeURL)
    split (episodeURL, part, "/")
    shortSeasonURL = "acorn.tv/" part[4] "/" part[5]
    # extract the episode description
    cmd = "curl -s " episodeURL " | grep '<meta itemprop=\"description\"' | tail -1"
    while ((cmd | getline desc ) > 0) {
        split (desc,fld,"\"")
        episodeDescription = fld[4]
        # fix sloppy input spacing
        gsub (/ \./,".",episodeDescription)
        gsub (/  */," ",episodeDescription)
        sub (/^ */,"",episodeDescription)
        sub (/ *$/,"",episodeDescription)
        # fix funky HTML characters
        gsub (/&/,"\\&",episodeDescription)
        gsub (/"/,"\"\"",episodeDescription)
        gsub (/'/,"'",episodeDescription)
    }
    close cmd
    next
}

# Extract the episode duration
/<meta itemprop="timeRequired"/ {
    durationLinesFound += 1
    split ($0,fld,"\"")
    split (fld[4],tm,/[TMS]/)
    secs = tm[3]
    mins = tm[2] + int(secs / 60)
    hrs =  int(mins / 60)
    secs %= 60; mins %= 60
    #
    showSecs += secs
    showMins += mins + int(showSecs / 60)
    showHrs += hrs + int(showMins / 60)
    showSecs %= 60; showMins %= 60
    #
    episodeDuration = sprintf ("%02d:%02d:%02d", hrs, mins, secs)
    # print "==> episodeDuration = " episodeDuration " " shortEpisodeURL > "/dev/stderr"
    if (episodeDuration == "00:00:00")
        printf ("==> Blank episode duration: %s  %s\n", shortEpisodeURL, showTitle) >> ERRORS
    next
}

# Extract the episode title
/
/ {
    split ($0,fld,"[<>]")
    episodeTitle = fld[3]
    gsub (/&/,"\\&", episodeTitle)
    gsub (/'/,"'",episodeTitle)
    # print "==> episodeTitle = " episodeTitle " " shortEpisodeURL > "/dev/stderr"
    next
}

# Extract episode number
/
.*span itemprop="episodeNumber">/ {
    split ($0,fld,"[<>]")
    episodeNumber = fld[5]
    # print "==> episodeNumber = " episodeNumber " " shortEpisodeURL > "/dev/stderr"
    #
    # Setup showType so shows group/sort properly
    # Default showType to "S"
    showType = "S"
    # If show is a Prequel, use "P" e.g. Doc Martin
    if (episodeURL ~ /\/prequel/)
        showType = "P"
    #
    # Setup episodeType so episodes group/sort properly
    # Default episodeType to "E"
    episodeType = "E"
    # Default bonus and christmas specials episodeType to "X"
    if (episodeURL ~ /\/bonus|christmas[-]?special/)
        episodeType = "X"
    # If episode is a Trailer, set episodeType to "T" - even though not all these have episodes
    if (episodeURL ~ /_cs\//)
        episodeType = "T"
    #
    # cryptoftears, newworlds & newtonslaw bonus seasonNumber should be 1
    if (episodeURL ~ \
        /\/cryptoftears\/bonus\/|\/newworlds\/bonus\/|\/newtonslaw\/bonus\// \
        && seasonNumber != 1) {
            printf ("==> Changed S%02d to S01: %s\n", seasonNumber, shortSeasonURL) >> ERRORS
        seasonNumber = 1
    }
    # Plain christmasspecial, seriesfinale don't increment seasonNumber
    if (episodeURL ~ /\/christmasspecial\/|\/seriesfinale\//) {
        printf ("==> Changed S%02d to S%02d: %s\n", seasonNumber, seasonNumber-1,
                shortSeasonURL) >> ERRORS
        seasonNumber -= 1
    }
    #
    # The season number should match that in the URL
    # Birds of a Feather, Doc Martin, Murdoch, Poirot, Rebus, Vera, and others have problems
    if (episodeURL ~ \
           /\/series[0-9]{1,2}\/|[0-9]{1,2}bonus\/|\/murdoch\/season[0-9]{1,2}/) {
        split (episodeURL, part, "/")
        URLseasonNumber = part[5]
        sub (/bonus/,"",URLseasonNumber)
        # Foyle's War has sets: foyleswar/series9set8bonus/
        sub (/set[0-9]/,"",URLseasonNumber)
        # Midsomer Murders has two parts: midsomermurders/series19parttwo/
        sub (/parttwo/,"",URLseasonNumber)
        sub (/christmasspecial/,"",URLseasonNumber)
        sub (/[[:alpha:]]*/,"",URLseasonNumber)
        if (URLseasonNumber != seasonNumber) {
            printf ("==> Changed S%02d to S%02d: %s\n", seasonNumber, URLseasonNumber,
                    shortSeasonURL) >> ERRORS
            seasonNumber = URLseasonNumber
        }
    }
    #
    # Wrap up this episode
    # =HYPERLINK("https://acorn.tv/1900island/series1/week-one";"1900 Island, S01E01, Week One")
    episodeLink = sprintf ("=HYPERLINK(\"%s\";\"%s, %s%02d%s%02d, %s\"\)", episodeURL, showTitle,
                    showType, seasonNumber, episodeType, episodeNumber, episodeTitle)
    # Print "episode" line to UNSORTED
    # =HYPERLINK("https://acorn.tv/1900island/series1/week-one";"1900 Island, S01E01, Week One") \
    # \t\t\t 00:59:17 \t As they arrive
        printf ("%s\t\t\t%s\t%s\n", episodeLink, episodeDuration, episodeDescription)
    # Make sure there is no carryover
    episodeURL = ""
    shortEpisodeURL = ""
    shortSeasonURL = ""
    episodeType = ""
    episodeTitle = ""
    episodeNumber = ""
    episodeDuration = ""
    next
}

# Wrap up this show
// {
    if (episodeLinesFound == 0) {
        printf ("==> No numberOfEpisodes: %s\t%s\n", shortURL, showTitle) >> ERRORS
    }
    if (seasonLinesFound == 0) {
        printf ("==> No numberOfSeasons: %s\t%s\n", shortURL, showTitle) >> ERRORS
    }
    if (descriptionLinesFound == 0) {
        printf ("==> No franchise-description: %s\t%s\n", shortURL, showTitle) >> ERRORS
    }
    if (durationLinesFound == 0) {
        printf ("==> No durations: %s\t%s\n", shortURL, showTitle) >> ERRORS
    }
    if (showEpisodes == 1)
        printf ("==> Only one episode: %s\t%s\n", shortURL, showTitle) >> ERRORS
    showLink = "=HYPERLINK(\"" showURL "\";\"" showTitle "\")"
    showDuration = sprintf ("%02d:%02d:%02d", showHrs, showMins, showSecs)
    # Print "show" line to SHORT_SPREADSHEET with showDuration
    printf ("%s\t%s\t=%s\t%s\t%s\n", showLink, showSeasons, seasonEpisodes, showDuration, \
            showDescription) >> SHORT_SPREADSHEET
    # Print "show" line to UNSORTED without showDuration unless single episode
    if (showSeasons != 1 || showEpisodes != 1)
        showDuration = ""
    printf ("%s\t%s\t=%s\t%s\t%s\n", showLink, showSeasons, seasonEpisodes,  showDuration, \
            showDescription)
    # Make sure there is no carryover
    showTitle = ""
    showURL = ""
    shortURL = ""
    showLink = ""
    showSeasons = ""
    showEpisodes = ""
    seasonEpisodes = ""
    showSecs = 0
    showMins = 0
    showHrs = 0
    showDuration = ""
    showDescription = ""
    #
    episodeLinesFound = 0
    seasonLinesFound = 0
    descriptionLinesFound  = 0
}

# Produce a raw data spreadsheet from URLs found in $SHOW_URLS
#
# Note that IN_CANADA affects processing.
#
# INVOCATION:
#    while read -r line; do
#        curl -sS "$line" |
#            awk -v ERRORS=$ERRORS -v RAW_TITLES=$RAW_TITLES -v EPISODE_URLS=$EPISODE_URLS \
#                -v SHORT_SPREADSHEET=$SHORT_SPREADSHEET -f getAcornFrom-showPages.awk >>$UNSORTED
#    done <"$SHOW_URLS"

#   Field Names
#        1 Title (link)    2 Seasons    3 Episodes    4 Duration    5 Description

# Extract the show title
/meta property="og:title/ {
    split ($0,fld,"\"")
    showTitle = fld[4]
    sub (/^Watch /,"",showTitle)
    sub (/ on Acorn TV$/,"",showTitle)
    gsub (/&/,"\\&",showTitle)
    gsub (/"/,"\"\"",showTitle)
    gsub (/'/,"'",showTitle)
    if (match (showTitle, /^The /)) {
        showTitle = substr(showTitle, 5) ", The"
    }
    print showTitle >> RAW_TITLES
    # print "==> showTitle = " showTitle > "/dev/stderr"
    next
}

# Extract the show URL
/meta property="og:url/ {
    split ($0,fld,"\"")
    showURL = fld[4]
    # print "==> showURL = " showURL > "/dev/stderr"
    # Create shorter URL by removing https://
    shortURL = showURL
    sub (/.*acorn\.tv/,"acorn.tv",shortURL)
    next
}

# Extract the number of episodes in the show
/itemprop="numberOfEpisodes"/ {
    episodeLinesFound += 1
    split ($0,fld,"\"")
    if (episodeLinesFound == 1) {
        showEpisodes = fld[4]
        if (showEpisodes == "") {
            printf ("==> Blank showEpisodes in numberOfEpisodes: %s\t%s\n", shortURL,
                    showTitle) >> ERRORS
        }
        # print "==> showEpisodes = " showEpisodes " " shortURL > "/dev/stderr"
    }
    if (episodeLinesFound != 1) {
        seasonEpisodes = seasonEpisodes "+" fld[4]
        if (seasonEpisodes == "") {
            printf ("==> Blank seasonEpisodes in numberOfEpisodes: %s\t%s\n", shortURL,
                    showTitle) >> ERRORS
        }
        # print "==> seasonEpisodes = " seasonEpisodes " " shortURL > "/dev/stderr"
    }
    next
}

# Extract the number of seasons in the show
/itemprop="numberOfSeasons"/ {
    seasonLinesFound += 1
    split ($0,fld,"\"")
    showSeasons = fld[4]
    if (showSeasons == "") {
        printf ("==> Blank showSeasons in numberOfSeasons: %s\t%s\n", shortURL, showTitle) >> ERRORS
    }
    # print "==> showSeasons = " showSeasons " " shortURL > "/dev/stderr"
    next
}

# Extract the show description
/id="franchise-description"/ {
    descriptionLinesFound += 1
    # get rid of boilerplate
    split ($0,fld,"[<>]")
    showDescription = fld[3]
    # fix sloppy input spacing
    gsub (/ \./,".",showDescription)
    gsub (/  */," ",showDescription)
    sub (/^ */,"",showDescription)
    sub (/ *$/,"",showDescription)
    # fix funky HTML characters
    gsub (/&/,"\\&",showDescription)
    gsub (/"/,"\"\"",showDescription)
    gsub (/'/,"'",showDescription)
    # fix unmatched quotes
    numQuotes = gsub(/"/,"\"",showDescription)
    if ((numQuotes % 2) == 1) {
        printf ("==> Changed unmatched quote (%d): %d\t%s\t%s\t%s\n", numQuotes, shortURL,
                showTitle) >> ERRORS
        showDescription = showDescription " \""
    }
    # print "==> showDescription = " showDescription " " shortURL > "/dev/stderr"
    next
}

# Extract the seasonNumber
# Note that Acorn "season" numbers may not correspond to actual season numbers.
# They simply refer to the number of seasons available on Acorn.
/<meta itemprop="seasonNumber" content="/ {
    split ($0,fld,"\"")
    seasonNumber = fld[4]
    # print "==> seasonNumber = " seasonNumber " " shortURL > "/dev/stderr"
    next
}

# Extract the episode URL
/ episodeURL = " episodeURL > "/dev/stderr"
    print episodeURL >> EPISODE_URLS
    shortEpisodeURL = episodeURL
    sub (/.*acorn\.tv/,"acorn.tv",shortEpisodeURL)
    split (episodeURL, part, "/")
    shortSeasonURL = "acorn.tv/" part[4] "/" part[5]
    # extract the episode description
    cmd = "curl -s " episodeURL " | grep '<meta itemprop=\"description\"' | tail -1"
    while ((cmd | getline desc ) > 0) {
        split (desc,fld,"\"")
        episodeDescription = fld[4]
        # fix sloppy input spacing
        gsub (/ \./,".",episodeDescription)
        gsub (/  */," ",episodeDescription)
        sub (/^ */,"",episodeDescription)
        sub (/ *$/,"",episodeDescription)
        # fix funky HTML characters
        gsub (/&/,"\\&",episodeDescription)
        gsub (/"/,"\"\"",episodeDescription)
        gsub (/'/,"'",episodeDescription)
    }
    close cmd
    next
}

# Extract the episode duration
/<meta itemprop="timeRequired"/ {
    durationLinesFound += 1
    split ($0,fld,"\"")
    split (fld[4],tm,/[TMS]/)
    secs = tm[3]
    mins = tm[2] + int(secs / 60)
    hrs =  int(mins / 60)
    secs %= 60; mins %= 60
    #
    showSecs += secs
    showMins += mins + int(showSecs / 60)
    showHrs += hrs + int(showMins / 60)
    showSecs %= 60; showMins %= 60
    #
    episodeDuration = sprintf ("%02d:%02d:%02d", hrs, mins, secs)
    # print "==> episodeDuration = " episodeDuration " " shortEpisodeURL > "/dev/stderr"
    if (episodeDuration == "00:00:00")
        printf ("==> Blank episode duration: %s  %s\n", shortEpisodeURL, showTitle) >> ERRORS
    next
}

# Extract the episode title
/
/ {
    split ($0,fld,"[<>]")
    episodeTitle = fld[3]
    gsub (/&/,"\\&", episodeTitle)
    gsub (/'/,"'",episodeTitle)
    # print "==> episodeTitle = " episodeTitle " " shortEpisodeURL > "/dev/stderr"
    next
}

# Extract episode number
/
.*span itemprop="episodeNumber">/ {
    split ($0,fld,"[<>]")
    episodeNumber = fld[5]
    # print "==> episodeNumber = " episodeNumber " " shortEpisodeURL > "/dev/stderr"
    #
    # Setup showType so shows group/sort properly
    # Default showType to "S"
    showType = "S"
    # If show is a Prequel, use "P" e.g. Doc Martin
    if (episodeURL ~ /\/prequel/)
        showType = "P"
    #
    # Setup episodeType so episodes group/sort properly
    # Default episodeType to "E"
    episodeType = "E"
    # Default bonus and christmas specials episodeType to "X"
    if (episodeURL ~ /\/bonus|christmas[-]?special/)
        episodeType = "X"
    # If episode is a Trailer, set episodeType to "T" - even though not all these have episodes
    if (episodeURL ~ /_cs\//)
        episodeType = "T"
    #
    # cryptoftears, newworlds & newtonslaw bonus seasonNumber should be 1
    if (episodeURL ~ \
        /\/cryptoftears\/bonus\/|\/newworlds\/bonus\/|\/newtonslaw\/bonus\// \
        && seasonNumber != 1) {
            printf ("==> Changed S%02d to S01: %s\n", seasonNumber, shortSeasonURL) >> ERRORS
        seasonNumber = 1
    }
    # Plain christmasspecial, seriesfinale don't increment seasonNumber
    if (episodeURL ~ /\/christmasspecial\/|\/seriesfinale\//) {
        printf ("==> Changed S%02d to S%02d: %s\n", seasonNumber, seasonNumber-1,
                shortSeasonURL) >> ERRORS
        seasonNumber -= 1
    }
    #
    # The season number should match that in the URL
    # Birds of a Feather, Doc Martin, Murdoch, Poirot, Rebus, Vera, and others have problems
    if (episodeURL ~ \
           /\/series[0-9]{1,2}\/|[0-9]{1,2}bonus\/|\/murdoch\/season[0-9]{1,2}/) {
        split (episodeURL, part, "/")
        URLseasonNumber = part[5]
        sub (/bonus/,"",URLseasonNumber)
        # Foyle's War has sets: foyleswar/series9set8bonus/
        sub (/set[0-9]/,"",URLseasonNumber)
        # Midsomer Murders has two parts: midsomermurders/series19parttwo/
        sub (/parttwo/,"",URLseasonNumber)
        sub (/christmasspecial/,"",URLseasonNumber)
        sub (/[[:alpha:]]*/,"",URLseasonNumber)
        if (URLseasonNumber != seasonNumber) {
            printf ("==> Changed S%02d to S%02d: %s\n", seasonNumber, URLseasonNumber,
                    shortSeasonURL) >> ERRORS
            seasonNumber = URLseasonNumber
        }
    }
    #
    # Wrap up this episode
    # =HYPERLINK("https://acorn.tv/1900island/series1/week-one";"1900 Island, S01E01, Week One")
    episodeLink = sprintf ("=HYPERLINK(\"%s\";\"%s, %s%02d%s%02d, %s\"\)", episodeURL, showTitle,
                    showType, seasonNumber, episodeType, episodeNumber, episodeTitle)
    # Print "episode" line to UNSORTED
    # =HYPERLINK("https://acorn.tv/1900island/series1/week-one";"1900 Island, S01E01, Week One") \
    # \t\t\t 00:59:17 \t As they arrive
        printf ("%s\t\t\t%s\t%s\n", episodeLink, episodeDuration, episodeDescription)
    # Make sure there is no carryover
    episodeURL = ""
    shortEpisodeURL = ""
    shortSeasonURL = ""
    episodeType = ""
    episodeTitle = ""
    episodeNumber = ""
    episodeDuration = ""
    next
}

# Wrap up this show
// {
    if (episodeLinesFound == 0) {
        printf ("==> No numberOfEpisodes: %s\t%s\n", shortURL, showTitle) >> ERRORS
    }
    if (seasonLinesFound == 0) {
        printf ("==> No numberOfSeasons: %s\t%s\n", shortURL, showTitle) >> ERRORS
    }
    if (descriptionLinesFound == 0) {
        printf ("==> No franchise-description: %s\t%s\n", shortURL, showTitle) >> ERRORS
    }
    if (durationLinesFound == 0) {
        printf ("==> No durations: %s\t%s\n", shortURL, showTitle) >> ERRORS
    }
    showLink = "=HYPERLINK(\"" showURL "\";\"" showTitle "\")"
    showDuration = sprintf ("%02d:%02d:%02d", showHrs, showMins, showSecs)
    # Print "show" line to SHORT_SPREADSHEET with showDuration
    printf ("%s\t%s\t=%s\t%s\t%s\n", showLink, showSeasons, seasonEpisodes, showDuration, \
            showDescription) >> SHORT_SPREADSHEET
    # Print "show" line to UNSORTED without showDuration unless single episode
    if (showSeasons != 1 || showEpisodes != 1)
        showDuration = ""
    printf ("%s\t%s\t=%s\t%s\t%s\n", showLink, showSeasons, seasonEpisodes,  showDuration, \
            showDescription)
    # Make sure there is no carryover
    showTitle = ""
    showURL = ""
    shortURL = ""
    showLink = ""
    showSeasons = ""
    showEpisodes = ""
    seasonEpisodes = ""
    showSecs = 0
    showMins = 0
    showHrs = 0
    showDuration = ""
    showDescription = ""
    #
    episodeLinesFound = 0
    seasonLinesFound = 0
    descriptionLinesFound  = 0
}


uppercase

uppercase

BEGIN {
  slidecount = 0
}

/
/ {
  print ""
  print "
"
  print "  "
  print "  Gabor.Greif@alcatel-lucent.com"
  print "
"
  print ""
  print "
"
  print "  "
  print "
"
  print ""
}

/
/ {
  slidecount++
  if (slidecount == 2)
  {
    sub(/slide/, "slide haskell")
  }
}

{ print }


make-barcodes.awk: Validate input. - Make sure it has the right number of columns. - Make sure + and @ lines begin with those characters.

# The awk code that transforms the one-line fastq record pair into the output that can be sorted
# by barcode.
# Input columns (the 4 FASTQ lines for both reads in a read pair):
#   1: read1 name
#   2: read2 name
#   3: read1 sequence
#   4: read2 sequence
#   5: read1 + line
#   6: read2 + line
#   7: read1 quality
#   8: read2 quality
# Output columns:
#   1: the barcode, put into a canonical form
#   2: the order of the barcode halves ("ab" or "ba")
#   3: read1 name
#   4: sequence of read 1, minus the 12bp barcode and 5bp invariant sequence
#   5: read1 quality scores, minus the same first 17bp
#   6: read2 name
#   7: sequence of read 2, minus the first 17bp
#   8: read2 quality scores, minus the first 17bp
# The canonical form of the barcode is composed of two concatenated tags, one from each read.
# By default, each tag is the first 12bp of the read. The tag from the first read is the "alpha" and
# the tag from the second is the "beta". The barcode is formed by concatenating them in an order
# determined by a string comparison of the two. The lesser tag is first (if they are equal, the
# beta is first, but then you have bigger problems).

BEGIN {
  FS = "\t"
  OFS = "\t"
  # The number of bases from the start of each read that form the two halves of the barcode.
  # (this should be half the size of the full, canonical barcode).
  if (TAG_LEN == "") {
    TAG_LEN = 12
  }
  # The number of bases in the read that are between the barcode and the start of the actual sample
  # sequence (the restriction site in the Loeb 2014 protocol).
  if (INVARIANT == "") {
    INVARIANT = 5
  }
}

# Validate input.
{
  for (i = 1; i <= 8; i++) {
    if (!$i) {
      print "Error on read pair (line) " NR ": Missing or empty columns." > "/dev/stderr"
      exit 1
    }
  }
  if (substr($1, 1, 1) != "@" || substr($2, 1, 1) != "@") {
    print "Error on read pair (line) " NR ": Read name line doesn't begin with '@'." > "/dev/stderr"
    exit 1
  }
  if (substr($5, 1, 1) != "+" || substr($6, 1, 1) != "+") {
    print "Error on read pair (line) " NR ": '+' column does not begin with '+'." > "/dev/stderr"
    exit 1
  }
}

$3 && $4 {
  alpha = substr($3, 1, TAG_LEN)
  beta = substr($4, 1, TAG_LEN)
  if (alpha < beta) {
    barcode = alpha beta
    order = "ab"
  } else {
    barcode = beta alpha
    order = "ba"
  }
  name1 = substr($1, 2)
  name2 = substr($2, 2)
  seq1 = substr($3, TAG_LEN + INVARIANT + 1)
  seq2 = substr($4, TAG_LEN + INVARIANT + 1)
  qual1 = substr($7, TAG_LEN + INVARIANT + 1)
  qual2 = substr($8, TAG_LEN + INVARIANT + 1)
  print barcode, order, name1, seq1, qual1, name2, seq2, qual2
}

# The awk code that transforms the one-line fastq record pair into the output that can be sorted
# by barcode.
# Input columns (the 4 FASTQ lines for both reads in a read pair):
#   1: read1 name
#   2: read2 name
#   3: read1 sequence
#   4: read2 sequence
#   5: read1 + line
#   6: read2 + line
#   7: read1 quality
#   8: read2 quality
# Output columns:
#   1: the barcode, put into a canonical form
#   2: the order of the barcode halves ("ab" or "ba")
#   3: read1 name
#   4: sequence of read 1, minus the 12bp barcode and 5bp invariant sequence
#   5: read1 quality scores, minus the same first 17bp
#   6: read2 name
#   7: sequence of read 2, minus the first 17bp
#   8: read2 quality scores, minus the first 17bp
# The canonical form of the barcode is composed of two concatenated tags, one from each read.
# By default, each tag is the first 12bp of the read. The tag from the first read is the "alpha" and
# the tag from the second is the "beta". The barcode is formed by concatenating them in an order
# determined by a string comparison of the two. The lesser tag is first (if they are equal, the
# beta is first, but then you have bigger problems).

BEGIN {
  FS = "\t"
  OFS = "\t"
  # The number of bases from the start of each read that form the two halves of the barcode.
  # (this should be half the size of the full, canonical barcode).
  if (TAG_LEN == "") {
    TAG_LEN = 12
  }
  # The number of bases in the read that are between the barcode and the start of the actual sample
  # sequence (the restriction site in the Loeb 2014 protocol).
  if (INVARIANT == "") {
    INVARIANT = 5
  }
}

$3 && $4 {
  alpha = substr($3, 1, TAG_LEN)
  beta = substr($4, 1, TAG_LEN)
  if (alpha < beta) {
    barcode = alpha beta
    order = "ab"
  } else {
    barcode = beta alpha
    order = "ba"
  }
  name1 = substr($1, 2)
  name2 = substr($2, 2)
  seq1 = substr($3, TAG_LEN + INVARIANT + 1)
  seq2 = substr($4, TAG_LEN + INVARIANT + 1)
  qual1 = substr($7, TAG_LEN + INVARIANT + 1)
  qual2 = substr($8, TAG_LEN + INVARIANT + 1)
  print barcode, order, name1, seq1, qual1, name2, seq2, qual2
}


duh...actually test the change this time

#!/usr/bin/awk -f
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2003 University of Utah and the Flux Group.
# All rights reserved.
#

#
# "true" pc850 reports:
#    pcib0: <Intel 82443BX host to PCI bridge (AGP disabled)> on motherboard
# upgraded pc600 reports:
#    pcib0: <Intel 82443BX (440 BX) host to PCI bridge> on motherboard
#
/^pcib0: <Intel [0-9][0-9][0-9][0-9][0-9]BX host.*\(AGP disabled\)/ {
    print "BX";
    exit
}
/^pcib0: <Intel [0-9][0-9][0-9][0-9][0-9]BX \(440 BX\) host/ {
    print "BX-AGP";
    exit
}
/^pcib0: <Intel [0-9][0-9][0-9][0-9][0-9]GX / {
    print "GX";
    exit
}
/^pcib0:.*/ {
    print "??";
    exit
}

#!/usr/bin/awk -f
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2003 University of Utah and the Flux Group.
# All rights reserved.
#

#
# "true" pc850 reports:
#    pcib0: <Intel 82443BX host to PCI bridge (AGP disabled)> on motherboard
# upgraded pc600 reports:
#    pcib0: <Intel 82443BX (440 BX) host to PCI bridge> on motherboard
#
/^pcib0: <Intel [0-9][0-9][0-9][0-9][0-9]BX .*(AGP disabled)>/ {
    print "BX";
    exit
}
/^pcib0: <Intel [0-9][0-9][0-9][0-9][0-9]BX / {
    print "BX-AGP";
    exit
}
/^pcib0: <Intel [0-9][0-9][0-9][0-9][0-9]GX / {
    print "GX";
    exit
}
/^pcib0:.*/ {
    print "??";
    exit
}


Update permissions

Update permissions

#!/usr/bin/env awk -f
#
# Annotates a file statistic by prefixing a new column which includes the file type.
#
# Usage: annotate_file_type
#
# Input:
#   - <statistic> <filename>
#
# Output:
#   - <file_type> <statistic> <filename>
#

# When adding file extension patterns, do so in alphabetical order.

# Special cases...
/(LICENSE|NOTICE)$/ {
	print "LICENSE" OFS $1 OFS $2
	next
}
/datapackage\.json$/ {
	print "datapackage.json" OFS $1 OFS $2
	next
}
/package\.json$/ {
	print "package.json" OFS $1 OFS $2
	next
}

# Known file extensions (keep in alphabetical order)...
/\.awk$/ {
	print "AWK" OFS $1 OFS $2
	next
}
/\.bib$/ {
	print "BibTeX" OFS $1 OFS $2
	next
}
/\.c$/ {
	print "C" OFS $1 OFS $2
	next
}
/\.cpp$/ {
	print "C++" OFS $1 OFS $2
	next
}
/\.csl$/ {
	print "CSL" OFS $1 OFS $2
	next
}
/\.css$/ {
	print "CSS" OFS $1 OFS $2
	next
}
/\.csv$/ {
	print "CSV" OFS $1 OFS $2
	next
}
/\.eot$/ {
	print "fonts" OFS $1 OFS $2
	next
}
/\.gif$/ {
	print "gif" OFS $1 OFS $2
	next
}
/\.go$/ {
	print "Go" OFS $1 OFS $2
	next
}
/\.h$/ {
	print "C" OFS $1 OFS $2
	next
}
/\.hpp$/ {
	print "C++" OFS $1 OFS $2
	next
}
/\.html$/ {
	print "HTML" OFS $1 OFS $2
	next
}
/\.jl$/ {
	print "Julia" OFS $1 OFS $2
	next
}
/\.jpg$/ {
	print "JPG" OFS $1 OFS $2
	next
}
/\.js$/ {
	print "JavaScript" OFS $1 OFS $2
	next
}
/\.json$/ {
	print "JSON" OFS $1 OFS $2
	next
}
/Makefile$/ {
	print "make" OFS $1 OFS $2
	next
}
/\.md$/ {
	print "Markdown" OFS $1 OFS $2
	next
}
/\.mk$/ {
	print "make" OFS $1 OFS $2
	next
}
/\.png$/ {
	print "PNG" OFS $1 OFS $2
	next
}
/\.py$/ {
	print "Python" OFS $1 OFS $2
	next
}
/\.R$/ {
	print "R" OFS $1 OFS $2
	next
}
/\.sh$/ {
	print "bash" OFS $1 OFS $2
	next
}
/\.svg$/ {
	print "SVG" OFS $1 OFS $2
	next
}
/\.txt$/ {
	print "plaintext" OFS $1 OFS $2
	next
}
/\.woff$/ {
	print "fonts" OFS $1 OFS $2
	next
}
/\.yml$/ {
	print "YAML" OFS $1 OFS $2
	next
}

# Special cases...
$2 ~ /^\.([A-Za-z])+$|\/\.([A-Za-z])+$/ {
	print "dotfiles" OFS $1 OFS $2
	next
}
$2 ~ /\/([A-Za-z0-9_-])+$/ {
	print "executables" OFS $1 OFS $2
	next
}

# Everything else...
{
	print "OTHER" OFS $1 OFS $2
}