Fix processing of occurances of 'description' and null values (#156)

# This script converts Terraform 0.12 variables/outputs to something suitable for `terraform-docs`
# As of terraform-docs v0.6.0, HCL2 is not supported. This script is a *dirty hack* to get around it.
# https://github.com/segmentio/terraform-docs/
# https://github.com/segmentio/terraform-docs/issues/62

{
  if ( $0 ~ /\{/ ) {
    braceCnt++
  }

  if ( $0 ~ /\}/ ) {
    braceCnt--
  }

  # [START] variable or output block started
  if ($0 ~ /^[[:space:]]*(variable|output)[[:space:]][[:space:]]*"(.*?)"/) {
    # Normalize the braceCnt (should be 1 now)
    braceCnt = 1
    # [CLOSE] "default" block
    if (blockDefCnt > 0) {
      blockDefCnt = 0
    }
    blockCnt++
    print $0
  }

  # [START] multiline default statement started
  if (blockCnt > 0) {
    if ($0 ~ /^[[:space:]][[:space:]]*(default)[[:space:]][[:space:]]*=/) {
      if ($3 ~ "null") {
        print "  default = \"null\""
      } else {
        print $0
        blockDefCnt++
        blockDefStart=1
      }
    }
  }

  # [PRINT] single line "description"
  if (blockCnt > 0) {
    if (blockDefCnt == 0) {
      if ($0 ~ /^[[:space:]][[:space:]]*description[[:space:]][[:space:]]*=/) {
        # [CLOSE] "default" block
        if (blockDefCnt > 0) {
          blockDefCnt = 0
        }
        print $0
      }
    }
  }

  # [PRINT] single line "type"
  if (blockCnt > 0) {
    if ($0 ~ /^[[:space:]][[:space:]]*type[[:space:]][[:space:]]*=/ ) {
      # [CLOSE] "default" block
      if (blockDefCnt > 0) {
        blockDefCnt = 0
      }
      type=$3
      if (type ~ "object") {
        print "  type = \"object\""
      } else {
        print "  type = \"" $3 "\""
      }
    }
  }

  # [CLOSE] variable/output block
  if (blockCnt > 0) {
    if (braceCnt == 0 && blockCnt > 0) {
      blockCnt--
      print $0
    }
  }

  # [PRINT] Multiline "default" statement
  if (blockCnt > 0 && blockDefCnt > 0) {
    if (blockDefStart == 1) {
      blockDefStart = 0
    } else {
      print $0
    }
  }
}

# This script converts Terraform 0.12 variables/outputs to something suitable for `terraform-docs`
# As of terraform-docs v0.6.0, HCL2 is not supported. This script is a *dirty hack* to get around it.
# https://github.com/segmentio/terraform-docs/
# https://github.com/segmentio/terraform-docs/issues/62

{
  if ( /\{/ ) {
    braceCnt++
  }

  if ( /\}/ ) {
    braceCnt--
  }

  # [START] variable or output block started
  if ($0 ~ /(variable|output) "(.*?)"/) {
    # [CLOSE] "default" block
    if (blockDefCnt > 0) {
      blockDefCnt = 0
    }
    blockCnt++
    print $0
  }

  # [START] multiline default statement started
  if (blockCnt > 0) {
    if ($1 == "default") {
      print $0
      if ($NF ~ /[\[\(\{]/) {
        blockDefCnt++
        blockDefStart=1
      }
    }
  }

  # [PRINT] single line "description"
  if (blockDefCnt == 0) {
    if ($1 == "description") {
      # [CLOSE] "default" block
      if (blockDefCnt > 0) {
        blockDefCnt = 0
      }
      print $0
    }
  }

  # [PRINT] single line "type"
  if (blockCnt > 0) {
    if ($1 == "type" ) {
      # [CLOSE] "default" block
      if (blockDefCnt > 0) {
        blockDefCnt = 0
      }
      type=$3
      if (type ~ "object") {
        print "  type = \"object\""
      } else {
        print "  type = \"" $3 "\""
      }
    }
  }

  # [CLOSE] variable/output block
  if (blockCnt > 0) {
    if (braceCnt == 0 && blockCnt > 0) {
      blockCnt--
      print $0
    }
  }

  # [PRINT] Multiline "default" statement
  if (blockCnt > 0 && blockDefCnt > 0) {
    if (blockDefStart == 1) {
      blockDefStart = 0
    } else {
      print $0
    }
  }
}


ci: simplify sphinx expression in note.awk

# extract version notes for version VER

/^[-_0-9a-zA-Z]+ v?[0-9]/ {
    if ($2 == VER) {
        good = 1
        next
    } else {
        good = 0
    }
}

/^(===|---)/ { next }

{
    if (good) {
        # also remove sphinx syntax
        print gensub(/:(\w+):`([^`]+)`/, "``\\2``", "g")
    }
}

# extract version notes for version VER

/^[-_0-9a-zA-Z]+ v?[0-9]/ {
    if ($2 == VER) {
        good = 1
        next
    } else {
        good = 0
    }
}

/^(===|---)/ { next }

{
    if (good) {
        # also remove sphinx syntax
        print gensub(/:(data|class|attr|meth):`([^`]+)`/, "``\\2``", "g")
    }
}


[gpxutil] simplify regex

[gpxutil] simplify regex

#!/usr/bin/env gawk -f

# script to convert garmin gpx files's comment/description field's
# silly DD-MMM-YY format to ISO date format, so native ascii order
# will sort waypoints by the time they are created.
#
# from: <cmt>05-JUL-13 16:34:05</cmt>
#   to: <cmt>2013-07-05 16:34:05</cmt>

BEGIN {
    # use < or > as field separator for XML content
    FS = "[<>]"

    # we are only interested in these lines
    p_line = "<cmt>|<desc>"

    # regex pattern for the timestamp garmin outputs
    # 05-JUL-13 16:34:05
    # the hour can be one digit, so we group it separately
    # p_date = "([0-9]{2})-([a-zA-Z]{3})-([0-9]{2}) ([0-9]{1,2}):([0-9]{2}:[0-9]{2})"
    p_date = "(\d{2})-([a-zA-Z]{3})-(\d{2}) (\d{1,2}):(\d{2}:\d{2})"

    # create the month['jan'] = '01' ... array
    build_month()
}

# print non matching lines (implicit action)
$0 !~ p_line

# matching lines, convert matching timestamp
$0 ~ p_line {
    date = $3  # get the date we are interested in

    if (match(date, p_date, A)) {
        new_date = sprintf("20%s-%s-%s %02d:%s",  # assumes > year 2000
                           A[3],
                           month[tolower(A[2])],
                           A[1],
                           A[4],
                           A[5])
        # print date " -> " new_date
        # replace date with new_date in line
        sub(date, new_date, $0)
        count++
    }
    print
}

END {
    print FILENAME ":", count, "line(s) update." > "/dev/stderr"
}

# create the month['jan'] = '01' ... array
function build_month(   i, n, mlist)
{
    # parameters are used as local vars, no need to pass it in
    n = split("jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec", mlist, "|")
    for (i = 1; i <= n; i++) {
        month[mlist[i]] = sprintf("%02d", i)
    }
}

# var initializes to 0 or "" as appropriate depend on use.
# so basically no need to initialize variables

#!/usr/bin/env gawk -f

# script to convert garmin gpx files's comment/description field's
# silly DD-MMM-YY format to ISO date format, so native ascii order
# will sort waypoints by the time they are created.
#
# from: <cmt>05-JUL-13 16:34:05</cmt>
#   to: <cmt>2013-07-05 16:34:05</cmt>

BEGIN {
    # use < or > as field separator for XML content
    FS = "[<>]"

    # we are only interested in these lines
    p_line = "<cmt>|<desc>"

    # regex pattern for the timestamp garmin outputs
    # 05-JUL-13 16:34:05
    # the hour can be one digit, so we group it separately
    p_date = "([0-9]{2})-([a-zA-Z]{3})-([0-9]{2}) ([0-9]{1,2}):([0-9]{2}:[0-9]{2})"

    # create the month['jan'] = '01' ... array
    build_month()
}

# print non matching lines (implicit action)
$0 !~ p_line

# matching lines, convert matching timestamp
$0 ~ p_line {
    date = $3  # get the date we are interested in

    if (match(date, p_date, A)) {
        new_date = sprintf("20%s-%s-%s %02d:%s",  # assumes > year 2000
                           A[3],
                           month[tolower(A[2])],
                           A[1],
                           A[4],
                           A[5])
        # print date " -> " new_date
        # replace date with new_date in line
        sub(date, new_date, $0)
        count++
    }
    print
}

END {
    print FILENAME ":", count, "line(s) update." > "/dev/stderr"
}

# create the month['jan'] = '01' ... array
function build_month(   i, n, mlist)
{
    # parameters are used as local vars, no need to pass it in
    n = split("jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec", mlist, "|")
    for (i = 1; i <= n; i++) {
        month[mlist[i]] = sprintf("%02d", i)
    }
}

# var initializes to 0 or "" as appropriate depend on use.
# so basically no need to initialize variables


Changed – to en-dash -

Changed – to en-dash -

# Create column files with lists of series titles, descriptions, number
# of seasons, and number of episodes, to be pasted into a spreadsheet.
# Note that IN_CANADA affects processing.
#
# Create a separate file with a line for each episode containing
# seriesNumber, episodeURL, seriesTitle, seasonNumber, episodeNumber,
# episodeTitle, & episodeDuration with the same columns as the primary
# spreadsheet so they can be combined into one.
#
# Create a separate file with a line for each series duration
#
# Return the total duration of all series
#
# INVOCATION:
#       curl -s https://acorn.tv/192/ \
#           | awk -v TITLE_FILE=$TITLE_FILE -v DESCRIPTION_FILE=$DESCRIPTION_FILE \
#           -v NUM_SEASONS_FILE=$NUM_SEASONS_FILE -v NUM_EPISODES_FILE=$NUM_EPISODES_FILE \
#           -v EPISODE_CURL_FILE=$EPISODE_CURL_FILE -v IN_CANADA=$IN_CANADA \
#           -v EPISODE_INFO_FILE=$EPISODE_INFO_FILE -v SERIES_NUMBER=$lastRow \
#           -v DURATION_FILE=$DURATION_FILE -v ERROR_FILE=$ERROR_FILE -f getAcornFrom-seriesPages.awk
#
# INPUT:
#        <title>Empire | Acorn TV</title>
#  ---
#        Empire
#        <meta itemprop="numberOfEpisodes" content="30" />
#        <meta itemprop="numberOfSeasons" content="3" />
#  ---
#        <meta itemprop="numberOfEpisodes" content="10" />
#        <meta itemprop="numberOfEpisodes" content="10" />
#        <meta itemprop="numberOfEpisodes" content="10" />
#  ---
#
"The writing is sublime" (New \
#        York Times) in this anything-but-a-procedural cop drama ... as reluctant partners \
#        patrolling the streets of Montreal. Not Available in Canada. CC Available.

#  ---
#
We're sorry, but Acorn TV is not available in this territory.

#  ---
#        <meta itemprop="seasonNumber" content="1" />
#  ---
#
#  ---
#        <meta itemprop="timeRequired" content="T44M06S" />
#  ---
#
Partners

#  ---
#
Season 1: Episode 1

#
Season 9 Christmas Special: Episode 1

#
#
Series 1: Episode 1

#
Series 1 (Set 1): Episode 1

#
Series 10 (Sets 13 - 14): Episode 1

#
Series 19 (Part One): Episode 1

#
#
Set 1: Episode 1

#
#
Mini Series: Episode 1

#
Miniseries: Episode 1

#
#
Black Widows Season 1: Episode 1

#
#
A Dance to the Music of Time: Episode 1

#
Christmas Special : Episode 1

#
Feature Film: Episode 1

#
#
#
#  OUTPUT:
#       $EPISODE_INFO_FILE
#

# Extract the series title
/<title>/ {
    sub (/.*<title>/,"")
    sub (/ \| Acorn TV<.*/,"")
    gsub (/&/,"\\&")
    gsub (/"/,"\"\"")
    gsub (/'/,"'")
    gsub (/’/,"'")
    gsub (/–/,"-")
    # gsub (/'/,"'")
    if (match ($0, /^The /)) {
        $0 = substr($0, 5) ", The"
    }
    episodeLinesFound = 0
    seasonLinesFound = 0
    descriptionLinesFound = 0
    durationLinesFound = 0
    numEpisodesStr = ""
    seriesTitle = $0
    print seriesTitle >> TITLE_FILE
    next
}

# Extract the number of episodes in the series
/itemprop="numberOfEpisodes"/ {
    episodeLinesFound += 1
    split ($0,fld,"\"")
    if (episodeLinesFound == 1)
        totalEpisodes = fld[4]
    if (episodeLinesFound != 1)
        numEpisodesStr = numEpisodesStr "+" fld[4]
    next
}

# Extract the number of seasons in the series
/itemprop="numberOfSeasons"/ {
    seasonLinesFound += 1
    split ($0,fld,"\"")
    numSeasons = fld[4]
    if ((numSeasons + 0) == 0)
        printf ("==> No seasons: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
    print numSeasons >> NUM_SEASONS_FILE
    next
}

/Acorn TV is not available in this territory/ {
    printf ("==> Not available here: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
}

# Extract the series description
/id="franchise-description"/ {
    descriptionLinesFound += 1
    # get rid of boilerplate
    split ($0,fld,"[<>]")
    description = fld[3]
    # get rid of unnecessary characters and text
    gsub (/\\/,"",description)
    if (IN_CANADA != "yes") {
        sub (/Series [[:digit:]]* not available in Canada\./,"",description)
        sub (/Not [Aa]vailable in Canada\./,"",description)
        sub (/NOT AVAILABLE IN CANADA\./,"",description)
    }
    sub (/CC Available\. CC Available/,"CC Available",description)
    # fix sloppy input spacing
    sub (/\.CC Available/,". CC Available",description)
    gsub (/ \./,".",description)
    gsub (/  */," ",description)
    sub (/^ */,"",description)
    sub (/ *$/,"",description)
    # fix funky HTML characters
    gsub (/"/,"\"\"",description)
    gsub (/'/,"'",description)
    gsub (/'/,"'",description)
    gsub (/’/,"'",description)
    # fix unmatched quotes
    numQuotes = gsub(/"/,"\"",description)
    if ((numQuotes % 2) == 1) {
        printf ("==> Changed unmatched quote (%d): %d\t%s\n", numQuotes, SERIES_NUMBER, \
                seriesTitle) >> ERROR_FILE
        description = description " \""
    }
    print description >> DESCRIPTION_FILE
    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]
    next
}

# Extract the episode URL
/> EPISODE_CURL_FILE
    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
    #
    seriesSecs += secs
    seriesMins += mins + int(seriesSecs / 60)
    seriesHrs += hrs + int(seriesMins / 60)
    seriesSecs %= 60; seriesMins %= 60
    #
    totalSecs += secs
    totalMins += mins + int(totalSecs / 60)
    totalHrs += hrs + int(totalMins / 60)
    totalSecs %= 60; totalMins %= 60
    #
    episodeDuration = sprintf ("%02d:%02d:%02d", hrs, mins, secs)
    if (episodeDuration == "00:00:00")
        printf ("==> No duration: %s  %s\n", shortURL, seriesTitle) >> ERROR_FILE
    next
}

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

# There are a number of variants in the "episodeNumber" string
#
#
Season 1: Episode 1

#
Season 9 Christmas Special: Episode 1

#
#
Series 1: Episode 1

#
Series 1 (Set 1): Episode 1

#
Series 10 (Sets 13 - 14): Episode 1

#
Series 19 (Part One): Episode 1

#
#
Set 1: Episode 1

#
#
Mini Series: Episode 1

#
Miniseries: Episode 1

#
#
Black Widows Season 1: Episode 1

#
#
A Dance to the Music of Time: Episode 1

#
Christmas Special : Episode 1

#
Feature Film: Episode 1

# Extract episode number
/
.*span itemprop="episodeNumber">/ {
    # If show is a Prequel, use "P"
    if (episodeURL ~ /\/prequel/)
        showType = "P"
    # If show is a Miniseries, use "M"
    if (episodeURL ~ /\/miniseries/)
        showType = "M"
    # If we don't know what kind of show it is, use "S"
    if (showType == "")
        showType = "S"
    # Default episodeType to "E"
    episodeType = "E"
    # Default bonus, parttwo, and christmas specials episodeType to "X"
    if (episodeURL ~ /\/bonus|[0-9]{1,2}parttwo\/|christmas[-]?special/)
        episodeType = "X"
    # If episode is a Trailer, set episodeType to "T"
    if (episodeURL ~ /\/trailer/)
        episodeType = "T"
    # jamaicainn, newworlds & newtonslaw bonus or trailer seasonNumber should be 1
    if (episodeURL ~ \
        /\/jamaicainn\/bonus\/|\/jamaicainn\/trailer\/|\/newworlds\/bonus\/|\/newtonslaw\/bonus\// \
        && seasonNumber != 1) {
        split (episodeURL, part, "/")
        printf ("==> Changed S%02d to S01: acorn.tv/%s/%s\n", \
               seasonNumber, part[4], part[5]) >> ERROR_FILE
        seasonNumber = 1
    }
    # Plain christmasspecial, seriesfinale don't increment seasonNumber
    if (episodeURL ~ /\/christmasspecial\/|\/seriesfinale\//) {
        split (episodeURL, part, "/")
        printf ("==> Changed S%02d to S%02d: acorn.tv/%s/%s\n", \
               seasonNumber, seasonNumber-1, part[4], part[5]) >> ERROR_FILE
        seasonNumber -= 1
    }
    #
    split ($0,fld,"[<>]")
    episodeNumber = fld[5]
    # Feature films don't have episodes
    if (episodeURL ~ /\/featurefilm\//)
        next
    # neither should single episode series
    if (totalEpisodes == 1)
        next
    # 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\/|[0-9]{1,2}parttwo\/|\/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: acorn.tv/%s/%s\n", \
               seasonNumber, URLseasonNumber, part[4], part[5]) >> ERROR_FILE
            seasonNumber = URLseasonNumber
        }
    }

    if ((episodeNumber + 0) == 0)
        print "==> Episode number is 00: " shortURL >> ERROR_FILE
    printf ("%d\t=HYPERLINK(\"%s\";\"%s, %s%02d%s%02d, %s\"\)\t\t\t%s\n", \
        SERIES_NUMBER, episodeURL, seriesTitle, showType, seasonNumber, episodeType, episodeNumber, \
        episodeTitle, episodeDuration) >> EPISODE_INFO_FILE
    showType = ""
    next
}

// {
    if (episodeLinesFound == 0) {
        print 0  >> NUM_EPISODES_FILE
        printf ("==> No numberOfEpisodes: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
    } else {
        print (episodeLinesFound == 1 ? "=0" : "=" numEpisodesStr) >> NUM_EPISODES_FILE
    }
    #
    if (seasonLinesFound == 0) {
        print seasonLinesFound >> NUM_SEASONS_FILE
        printf ("==> No numberOfSeasons: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
    }
    #
    if (descriptionLinesFound == 0) {
        print "-- No Description --" >> DESCRIPTION_FILE
        printf ("==> No franchise-description: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
    } else {
        if (description == "")
            printf ("==> No description: %d\t%s  %s\n", SERIES_NUMBER, shortURL, \
                    seriesTitle) >> ERROR_FILE
    }
    description = ""
    #
    if (durationLinesFound == 0) {
        print "00:00:00"  >> DURATION_FILE
        printf ("==> No timeRequired: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
    } else {
        seriesDuration = sprintf ("%02d:%02d:%02d", seriesHrs, seriesMins, seriesSecs)
        print seriesDuration >> DURATION_FILE
        if (seriesDuration == "00:00:00")
            printf ("==> No duration: %d\t%s  %s\n", SERIES_NUMBER, shortURL, i\
                    seriesTitle) >> ERROR_FILE
   }
    seriesSecs = 0
    seriesMins = 0
    seriesHrs = 0
    seriesDuration = ""
    #
    SERIES_NUMBER += 1
}

END {
    # Return the total duration of all series
    printf ("%02d:%02d:%02d\n", totalHrs, totalMins, totalSecs)
}

# Create column files with lists of series titles, descriptions, number
# of seasons, and number of episodes, to be pasted into a spreadsheet.
# Note that IN_CANADA affects processing.
#
# Create a separate file with a line for each episode containing
# seriesNumber, episodeURL, seriesTitle, seasonNumber, episodeNumber,
# episodeTitle, & episodeDuration with the same columns as the primary
# spreadsheet so they can be combined into one.
#
# Create a separate file with a line for each series duration
#
# Return the total duration of all series
#
# INVOCATION:
#       curl -s https://acorn.tv/192/ \
#           | awk -v TITLE_FILE=$TITLE_FILE -v DESCRIPTION_FILE=$DESCRIPTION_FILE \
#           -v NUM_SEASONS_FILE=$NUM_SEASONS_FILE -v NUM_EPISODES_FILE=$NUM_EPISODES_FILE \
#           -v EPISODE_CURL_FILE=$EPISODE_CURL_FILE -v IN_CANADA=$IN_CANADA \
#           -v EPISODE_INFO_FILE=$EPISODE_INFO_FILE -v SERIES_NUMBER=$lastRow \
#           -v DURATION_FILE=$DURATION_FILE -v ERROR_FILE=$ERROR_FILE -f getAcornFrom-seriesPages.awk
#
# INPUT:
#        <title>Empire | Acorn TV</title>
#  ---
#        Empire
#        <meta itemprop="numberOfEpisodes" content="30" />
#        <meta itemprop="numberOfSeasons" content="3" />
#  ---
#        <meta itemprop="numberOfEpisodes" content="10" />
#        <meta itemprop="numberOfEpisodes" content="10" />
#        <meta itemprop="numberOfEpisodes" content="10" />
#  ---
#
"The writing is sublime" (New \
#        York Times) in this anything-but-a-procedural cop drama ... as reluctant partners \
#        patrolling the streets of Montreal. Not Available in Canada. CC Available.

#  ---
#
We're sorry, but Acorn TV is not available in this territory.

#  ---
#        <meta itemprop="seasonNumber" content="1" />
#  ---
#
#  ---
#        <meta itemprop="timeRequired" content="T44M06S" />
#  ---
#
Partners

#  ---
#
Season 1: Episode 1

#
Season 9 Christmas Special: Episode 1

#
#
Series 1: Episode 1

#
Series 1 (Set 1): Episode 1

#
Series 10 (Sets 13 - 14): Episode 1

#
Series 19 (Part One): Episode 1

#
#
Set 1: Episode 1

#
#
Mini Series: Episode 1

#
Miniseries: Episode 1

#
#
Black Widows Season 1: Episode 1

#
#
A Dance to the Music of Time: Episode 1

#
Christmas Special : Episode 1

#
Feature Film: Episode 1

#
#
#
#  OUTPUT:
#       $EPISODE_INFO_FILE
#

# Extract the series title
/<title>/ {
    sub (/.*<title>/,"")
    sub (/ \| Acorn TV<.*/,"")
    gsub (/&/,"\\&")
    gsub (/"/,"\"\"")
    gsub (/'/,"'")
    gsub (/’/,"'")
    # gsub (/'/,"'")
    if (match ($0, /^The /)) {
        $0 = substr($0, 5) ", The"
    }
    episodeLinesFound = 0
    seasonLinesFound = 0
    descriptionLinesFound = 0
    durationLinesFound = 0
    numEpisodesStr = ""
    seriesTitle = $0
    print seriesTitle >> TITLE_FILE
    next
}

# Extract the number of episodes in the series
/itemprop="numberOfEpisodes"/ {
    episodeLinesFound += 1
    split ($0,fld,"\"")
    if (episodeLinesFound == 1)
        totalEpisodes = fld[4]
    if (episodeLinesFound != 1)
        numEpisodesStr = numEpisodesStr "+" fld[4]
    next
}

# Extract the number of seasons in the series
/itemprop="numberOfSeasons"/ {
    seasonLinesFound += 1
    split ($0,fld,"\"")
    numSeasons = fld[4]
    if ((numSeasons + 0) == 0)
        printf ("==> No seasons: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
    print numSeasons >> NUM_SEASONS_FILE
    next
}

/Acorn TV is not available in this territory/ {
    printf ("==> Not available here: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
}

# Extract the series description
/id="franchise-description"/ {
    descriptionLinesFound += 1
    # get rid of boilerplate
    split ($0,fld,"[<>]")
    description = fld[3]
    # get rid of unnecessary characters and text
    gsub (/\\/,"",description)
    if (IN_CANADA != "yes") {
        sub (/Series [[:digit:]]* not available in Canada\./,"",description)
        sub (/Not [Aa]vailable in Canada\./,"",description)
        sub (/NOT AVAILABLE IN CANADA\./,"",description)
    }
    sub (/CC Available\. CC Available/,"CC Available",description)
    # fix sloppy input spacing
    sub (/\.CC Available/,". CC Available",description)
    gsub (/ \./,".",description)
    gsub (/  */," ",description)
    sub (/^ */,"",description)
    sub (/ *$/,"",description)
    # fix funky HTML characters
    gsub (/"/,"\"\"",description)
    gsub (/'/,"'",description)
    gsub (/'/,"'",description)
    gsub (/’/,"'",description)
    # fix unmatched quotes
    numQuotes = gsub(/"/,"\"",description)
    if ((numQuotes % 2) == 1) {
        printf ("==> Changed unmatched quote (%d): %d\t%s\n", numQuotes, SERIES_NUMBER, \
                seriesTitle) >> ERROR_FILE
        description = description " \""
    }
    print description >> DESCRIPTION_FILE
    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]
    next
}

# Extract the episode URL
/> EPISODE_CURL_FILE
    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
    #
    seriesSecs += secs
    seriesMins += mins + int(seriesSecs / 60)
    seriesHrs += hrs + int(seriesMins / 60)
    seriesSecs %= 60; seriesMins %= 60
    #
    totalSecs += secs
    totalMins += mins + int(totalSecs / 60)
    totalHrs += hrs + int(totalMins / 60)
    totalSecs %= 60; totalMins %= 60
    #
    episodeDuration = sprintf ("%02d:%02d:%02d", hrs, mins, secs)
    if (episodeDuration == "00:00:00")
        printf ("==> No duration: %s  %s\n", shortURL, seriesTitle) >> ERROR_FILE
    next
}

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

# There are a number of variants in the "episodeNumber" string
#
#
Season 1: Episode 1

#
Season 9 Christmas Special: Episode 1

#
#
Series 1: Episode 1

#
Series 1 (Set 1): Episode 1

#
Series 10 (Sets 13 - 14): Episode 1

#
Series 19 (Part One): Episode 1

#
#
Set 1: Episode 1

#
#
Mini Series: Episode 1

#
Miniseries: Episode 1

#
#
Black Widows Season 1: Episode 1

#
#
A Dance to the Music of Time: Episode 1

#
Christmas Special : Episode 1

#
Feature Film: Episode 1

# Extract episode number
/
.*span itemprop="episodeNumber">/ {
    # If show is a Prequel, use "P"
    if (episodeURL ~ /\/prequel/)
        showType = "P"
    # If show is a Miniseries, use "M"
    if (episodeURL ~ /\/miniseries/)
        showType = "M"
    # If we don't know what kind of show it is, use "S"
    if (showType == "")
        showType = "S"
    # Default episodeType to "E"
    episodeType = "E"
    # Default bonus, parttwo, and christmas specials episodeType to "X"
    if (episodeURL ~ /\/bonus|[0-9]{1,2}parttwo\/|christmas[-]?special/)
        episodeType = "X"
    # If episode is a Trailer, set episodeType to "T"
    if (episodeURL ~ /\/trailer/)
        episodeType = "T"
    # jamaicainn, newworlds & newtonslaw bonus or trailer seasonNumber should be 1
    if (episodeURL ~ \
        /\/jamaicainn\/bonus\/|\/jamaicainn\/trailer\/|\/newworlds\/bonus\/|\/newtonslaw\/bonus\// \
        && seasonNumber != 1) {
        split (episodeURL, part, "/")
        printf ("==> Changed S%02d to S01: acorn.tv/%s/%s\n", \
               seasonNumber, part[4], part[5]) >> ERROR_FILE
        seasonNumber = 1
    }
    # Plain christmasspecial, seriesfinale don't increment seasonNumber
    if (episodeURL ~ /\/christmasspecial\/|\/seriesfinale\//) {
        split (episodeURL, part, "/")
        printf ("==> Changed S%02d to S%02d: acorn.tv/%s/%s\n", \
               seasonNumber, seasonNumber-1, part[4], part[5]) >> ERROR_FILE
        seasonNumber -= 1
    }
    #
    split ($0,fld,"[<>]")
    episodeNumber = fld[5]
    # Feature films don't have episodes
    if (episodeURL ~ /\/featurefilm\//)
        next
    # neither should single episode series
    if (totalEpisodes == 1)
        next
    # 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\/|[0-9]{1,2}parttwo\/|\/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: acorn.tv/%s/%s\n", \
               seasonNumber, URLseasonNumber, part[4], part[5]) >> ERROR_FILE
            seasonNumber = URLseasonNumber
        }
    }

    if ((episodeNumber + 0) == 0)
        print "==> Episode number is 00: " shortURL >> ERROR_FILE
    printf ("%d\t=HYPERLINK(\"%s\";\"%s, %s%02d%s%02d, %s\"\)\t\t\t%s\n", \
        SERIES_NUMBER, episodeURL, seriesTitle, showType, seasonNumber, episodeType, episodeNumber, \
        episodeTitle, episodeDuration) >> EPISODE_INFO_FILE
    showType = ""
    next
}

// {
    if (episodeLinesFound == 0) {
        print 0  >> NUM_EPISODES_FILE
        printf ("==> No numberOfEpisodes: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
    } else {
        print (episodeLinesFound == 1 ? "=0" : "=" numEpisodesStr) >> NUM_EPISODES_FILE
    }
    #
    if (seasonLinesFound == 0) {
        print seasonLinesFound >> NUM_SEASONS_FILE
        printf ("==> No numberOfSeasons: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
    }
    #
    if (descriptionLinesFound == 0) {
        print "-- No Description --" >> DESCRIPTION_FILE
        printf ("==> No franchise-description: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
    } else {
        if (description == "")
            printf ("==> No description: %d\t%s  %s\n", SERIES_NUMBER, shortURL, \
                    seriesTitle) >> ERROR_FILE
    }
    description = ""
    #
    if (durationLinesFound == 0) {
        print "00:00:00"  >> DURATION_FILE
        printf ("==> No timeRequired: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
    } else {
        seriesDuration = sprintf ("%02d:%02d:%02d", seriesHrs, seriesMins, seriesSecs)
        print seriesDuration >> DURATION_FILE
        if (seriesDuration == "00:00:00")
            printf ("==> No duration: %d\t%s  %s\n", SERIES_NUMBER, shortURL, i\
                    seriesTitle) >> ERROR_FILE
   }
    seriesSecs = 0
    seriesMins = 0
    seriesHrs = 0
    seriesDuration = ""
    #
    SERIES_NUMBER += 1
}

END {
    # Return the total duration of all series
    printf ("%02d:%02d:%02d\n", totalHrs, totalMins, totalSecs)
}


makeDistortosConfiguration.awk: fix handling of strings with '='

Fix handling of string configuration variables that contain '='
character - previously only the part of string up to the first '='
character was printed to distortosConfiguration.h.

#!/bin/awk -f

#
# file: makeDistortosConfiguration.awk
#
# author: Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
#
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
# distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# date: 2015-10-02
#

BEGIN {
	configPattern = "CONFIG_[A-Za-z0-9_]+"
	booleanTrueConfigPattern = "^" configPattern "=y$"
	booleanFalseConfigPattern = "^" configPattern "=n$"
	notSetConfigPattern = "^# " configPattern " is not set$"
	numericConfigPattern = "^" configPattern "=(([[:digit:]]+)|(0x[[:xdigit:]]+))$"
	stringConfigPattern = "^" configPattern "=\"([^\"\\\\]|\\\\.)*\"$"

	print "/**"
	print " * \\file"
	print " * \\brief distortos configuration"
	print " *"
	print " * \\warning"
	print " * Automatically generated file - do not edit!"
	print " *"
	print " * \\date " strftime("%Y-%m-%d %H:%M:%S")
	print " */"
	print ""
	print "#ifndef INCLUDE_DISTORTOS_DISTORTOSCONFIGURATION_H_"
	print "#define INCLUDE_DISTORTOS_DISTORTOSCONFIGURATION_H_"
	print ""
}

$0 ~ booleanTrueConfigPattern {
	split($0, array, "=")
	print "#define " array[1]
}

$0 ~ booleanFalseConfigPattern {
	split($0, array, "=")
	print "#undef " array[1]
}

$0 ~ notSetConfigPattern {
	print "#undef " $2
}

$0 ~ numericConfigPattern {
	split($0, array, "=")
	print "#define " array[1] " " array[2]
}

$0 ~ stringConfigPattern {
	equalsSign = index($0, "=")
	print "#define " substr($0, 1, equalsSign - 1) " " substr($0, equalsSign + 1)
}

END {
	print ""
	print "#endif	/* INCLUDE_DISTORTOS_DISTORTOSCONFIGURATION_H_ */"
}

#!/bin/awk -f

#
# file: makeDistortosConfiguration.awk
#
# author: Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
#
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
# distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# date: 2015-09-25
#

BEGIN {
	configPattern = "CONFIG_[A-Za-z0-9_]+"
	booleanTrueConfigPattern = "^" configPattern "=y$"
	booleanFalseConfigPattern = "^" configPattern "=n$"
	notSetConfigPattern = "^# " configPattern " is not set$"
	numericConfigPattern = "^" configPattern "=(([[:digit:]]+)|(0x[[:xdigit:]]+))$"
	stringConfigPattern = "^" configPattern "=\"([^\"\\\\]|\\\\.)*\"$"

	print "/**"
	print " * \\file"
	print " * \\brief distortos configuration"
	print " *"
	print " * \\warning"
	print " * Automatically generated file - do not edit!"
	print " *"
	print " * \\date " strftime("%Y-%m-%d %H:%M:%S")
	print " */"
	print ""
	print "#ifndef INCLUDE_DISTORTOS_DISTORTOSCONFIGURATION_H_"
	print "#define INCLUDE_DISTORTOS_DISTORTOSCONFIGURATION_H_"
	print ""
}

$0 ~ booleanTrueConfigPattern {
	split($0, array, "=")
	print "#define " array[1]
}

$0 ~ booleanFalseConfigPattern {
	split($0, array, "=")
	print "#undef " array[1]
}

$0 ~ notSetConfigPattern {
	print "#undef " $2
}

$0 ~ numericConfigPattern {
	split($0, array, "=")
	print "#define " array[1] " " array[2]
}

$0 ~ stringConfigPattern {
	split($0, array, "=")
	print "#define " array[1] " " array[2]
}

END {
	print ""
	print "#endif	/* INCLUDE_DISTORTOS_DISTORTOSCONFIGURATION_H_ */"
}


Preparing to chain format calls

Preparing to chain format calls

#! /usr/bin/env awk -f

BEGIN {
    # @start-fragment default-config
    config["SYNTHESIZERS_SHOULD_BE_FORMATTED"]   = 1
    config["SYNTHESIZERS_SHOULD_HAVE_N_COLUMNS"] = 1
    # @end-fragment default-config

    # @include-fragment util.awk config

    if (ARGC <= 1) {
        print "Usage: ./synthesize.awk sourceFile"
        exit 1
    }

    sourceFile = ARGV[1]

    while (getline < sourceFile) {
        print
    }
}
{
    # do nothing
}

# @start-fragment functions

function readSynthesizeBlock(properties, aliases) {
	do {
		foundSemicolon = 0
		do {
			foundSemicolon = extractSynthesizers(properties, aliases)
		}
		while (foundSemicolon == 0 && getline > 0)
	} while (getline > 0 && $0 ~ /@synthesize/)
}

function extractSynthesizers(properties, aliases) {
	foundSemicolon = index($0, ";")

	gsub("@synthesize", "", $0)
	gsub(/[ \t]+/, "", $0)
	sub(/,$/, "", $0)
	sub(/;$/, "", $0)

	split($0, synthesizeStatements, ",")

	for (i = 1; i <= length(synthesizeStatements); i++) {
		split(synthesizeStatements[i], components, "=")

		propertyCount = length(properties)

		properties[propertyCount + 1] = components[1]
		aliases[propertyCount + 1] = components[length(components)]
	}

	return foundSemicolon
}

function formatSynthesizeBlock(properties, aliases) {
	maxLength = 0

	for (i = 1; i <= length(properties); i++) {
		maxLength = max(maxLength, length(properties[i]));
	}

	for (i = 1; i <= length(properties); i++) {
		format = ""

		if (config["SYNTHESIZERS_SHOULD_HAVE_N_COLUMNS"] == 1) {
			format = "@synthesize %s = %s;\n"
		} else {
			format = "@synthesize %-" maxLength "s = %s;\n"
		}

		printf(format, properties[i], aliases[i])
	}
}

# @end-fragment functions

# @include-fragment util.awk functions

#! /usr/bin/env awk -f

BEGIN {
    # @start-fragment default-config
    config["SYNTHESIZERS_SHOULD_BE_FORMATTED"]   = 1
    config["SYNTHESIZERS_SHOULD_HAVE_N_COLUMNS"] = 1
    # @end-fragment default-config

    # @include-fragment util.awk config
}
{
    if ($0 ~ /@synthesize/) {
        # @start-fragment body
		readSynthesizeBlock(properties, aliases)
		formatSynthesizeBlock(properties, aliases)
        # @end-fragment body
    }
}

# @start-fragment functions

function readSynthesizeBlock(properties, aliases) {
	do {
		foundSemicolon = 0
		do {
			foundSemicolon = extractSynthesizers(properties, aliases)
		}
		while (foundSemicolon == 0 && getline > 0)
	} while (getline > 0 && $0 ~ /@synthesize/)
}

function extractSynthesizers(properties, aliases) {
	foundSemicolon = index($0, ";")

	gsub("@synthesize", "", $0)
	gsub(/[ \t]+/, "", $0)
	sub(/,$/, "", $0)
	sub(/;$/, "", $0)

	split($0, synthesizeStatements, ",")

	for (i = 1; i <= length(synthesizeStatements); i++) {
		split(synthesizeStatements[i], components, "=")

		propertyCount = length(properties)

		properties[propertyCount + 1] = components[1]
		aliases[propertyCount + 1] = components[length(components)]
	}

	return foundSemicolon
}

function formatSynthesizeBlock(properties, aliases) {
	maxLength = 0

	for (i = 1; i <= length(properties); i++) {
		maxLength = max(maxLength, length(properties[i]));
	}

	for (i = 1; i <= length(properties); i++) {
		format = ""

		if (config["SYNTHESIZERS_SHOULD_HAVE_N_COLUMNS"] == 1) {
			format = "@synthesize %s = %s;\n"
		} else {
			format = "@synthesize %-" maxLength "s = %s;\n"
		}

		printf(format, properties[i], aliases[i])
	}
}

# @end-fragment functions

# @include-fragment util.awk functions


add empty lines for readablity

add empty lines for readablity

BEGIN {
  slidecount = 0
}

/
/ {
  print ""
  print "
"
  print "  "
  print "
"
  print ""
}

/
/ {
  slidecount++
  if (slidecount == 2)
  {
    print ""
    print "
"
    print "  "
    print "
"
    print ""

    sub(/slide/, "slide haskell")
  }
}

{ print }

BEGIN {
  slidecount = 0
}

/
/ {
  print "
"
  print "  "
  print "
"
  print ""
}

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

{ print }


Improve shell script (thx 'Barmic')

Improve shell script (thx 'Barmic')

BEGIN {
    FS="\n";
    OFS="";
    ORS="\n";
    print "#!/bin/sh";
    print "#";
}
# blank lines
/^$/ {
    next;
}
# record header
$1 ~ /^\*\*\*\*/ {
    next;
}
# summary field
$1 ~ /^[ ]*summary\:/ {
    gsub(/\r/,"");
    idx = match($1, /summary\:(.*)/);
    print "SUMMARY=\"" substr($1, idx + 9) "\"";
    next;
}
# startdate field
$1 ~ /^[ ]*startdate\: / {
    match($1, /startdate\: /);
    print "STARTDATE=\"`date -d '" substr($1, RSTART + RLENGTH) "-000' '+%a %e %b %R'`\"";
    next;
}
# vcalendar start tag
$1 ~ /^[ ]*calendardata\: / {
    gsub(/\r/,"");
    match($1, /calendardata\: /);
    print "ICS=$(mktemp --suffix=.ics)";
    print "cat > \"${ICS}\" <<EOF";
    print substr($1, RSTART + RLENGTH);
    next;
}
# vcalendar end tag
$1 ~ /^END\:VCALENDAR/ {
    gsub(/\r/,"");
    print $1;
    print "EOF";
    print "mpack -s \"$SUMMARY - $STARTDATE\" \"${ICS}\" $1";
    print "#";
    next;
}
# vcalendar body
{
    gsub(/\r/,"");
    print $0;
}

BEGIN {
    FS="\n"
    OFS=""
    ORS="\n"
    print "#!/bin/sh"
    print " "
}
# blank lines
/^$/ { next }
# record header
$1 ~ /^\*\*\*\*/ {
    next
}
# summary field
$1 ~ /^[ ]*summary\:/ {
    gsub(/\r/,"");
    idx = match($1, /summary\:(.*)/)
    print "SUMMARY=\"" substr($1, idx + 9) "\""
    next
}
# startdate field
$1 ~ /^[ ]*startdate\: / {
    match($1, /startdate\: /)
    print "STARTDATE=\"`date -d '" substr($1, RSTART + RLENGTH) "-000' '+%a %e %b %R'`\""
    next
}
# vcalendar start tag
$1 ~ /^[ ]*calendardata\: / {
    gsub(/\r/,"");
    match($1, /calendardata\: /)
    print "echo \"" substr($1, RSTART + RLENGTH) "\" >event.ics"
    next
}
# vcalendar end tag
$1 ~ /^END\:VCALENDAR/ {
    gsub(/\r/,"");
    print "echo \"" $1 "\" >>event.ics"
    print "mpack -s \"$SUMMARY - $STARTDATE\" event.ics $1"
    print ""
    next
}
# vcalendar body
{
    gsub(/\r/,"");
    print "echo \"" $0 "\" >> event.ics"
}


interp_utils.awk	-- down to actually interpolating the vectors

function IU_init(config, interp, name, k_values, k_breaks,   work, n_ary, ary, c_rng, i, nv, v_pat) {

	interp["name"] = name

	# get the values
	work = config["_globals", k_values]
	if(work == ""){
		printf("ERROR: IU_init: no key named \"%s\" in config\n", k_values) > "/dev/stderr"
		return 1
	}
	nv = n_ary = split(work, ary, "|")
	interp["nvalues"] = n_ary
	c_rng = 0
	v_ob_info = ""
	for(i = 1; i <= n_ary; i++){
		work = ary[i]
		if(index(work, ":") != 0){
			c_rng++
			v_pat = v_pat "g"
		}else
			v_pat = v_pat "b"
		sub(/^[ \t]*/, "", work)
		sub(/[ \t]*$/, "", work)
		interp["values", i] = work
	}
	interp["is_grad"] = c_rng > 0

	# get the breaks
	work = config["_globals", k_breaks]
	if(work == ""){
		printf("ERROR: IU_init: no key named \"%s\" in config\n", k_values) > "/dev/stderr"
		return 1
	}
	nv = n_ary = split(work, ary, "|")
	interp["nbreaks"] = n_ary
	for(i = 1; i <= n_ary; i++){
		work = ary[i]
		sub(/^[ \t]*/, "", work)
		sub(/[ \t]*$/, "", work)
		interp["breaks", i] = work + 0	# force numeric
	}
	if(!interp["is_grad"]){
		if(interp["nbreaks"] != interp["nvalues"] - 1){
			printf("ERROR: IU_init: nbreaks (%d) != nvalues - 1 (%d)\n", interp["nbreaks"], interp["nvalues"] - 1) > "/dev/stderr"
			return 1
		}
	}else if(interp["nbreaks"] == interp["nvalues"] + 1){
		if(!match(v_pat, /^gg*$/)){
			printf("ERROR: IU_init: nbreaks (%d) = nvalues + 1 (%d): all values must be gradients\n", interp["nbreaks"], interp["nvalues"] + 1) > "/dev/stderr"
			return 1
		}
		interp["v_ob_info"] = ""
	}else if(interp["nbreaks"] == interp["nvalues"]){
		if(!match(v_pat, /^bgg*$/) && !match(v_pat, /^gg*b$/)){
			printf("ERROR: IU_init: nbreaks (%d) = nvalues (%d): All but first or last, but not both  values must grad\n", inter["nbreaks"], interp["nvalues"]) > "/dev/stderr"
			return 1
		}
		interp["v_ob_info"] = v_pat ~ /^b/ ? "^" : "$"
	}else if(interp["nbreaks"] = interp["nvalues"] - 1){
		printf("DEBUG: IU_init: nb = nv - 1: chk that we have v[1] = bin, v[2:$-1] = grad, v[$] = bin\n") > "/dev/stderr"
		if(!match(v_pat, /^bgg*b$/)){
			printf("ERROR: IU_init: nbreaks (%d) = nvalues - 1 (%d): all values but first and last must be gread\n", interp["nbreaks"], interp["nvalues"] - 1) > "/dev/stderr"
			return 1
		}
		interp["v_ob_info"] = "^$"
	}else{
		printf("ERROR: IU_init: nbreaks (%d) != nvalues +/- 1 (%d, %d, %d)\n", interp["nbreaks"], interp["nvalues"] - 1, interp["nvalues"], interp["nvalues"] + 1) > "/dev/stderr"
		return 1
	}

	# init all counts to 0
	for(i = 0; i <= interp["nbreaks"] + 1; i++)
		interp["counts", i] = 0
	interp["tcounts"] = 0

	return 0
}

function IU_dump(file, interp,   i, keys, nk) {

	printf("interp = {\n") > file
	printf("\tname          = %s\n", interp["name"]) > file
	printf("\tis_grad       = %d\n", interp["is_grad"]) > file
	printf("\tv_ob_info     = %s\n", interp["v_ob_info"]) > file
	printf("\tnvalues       = %d\n", interp["nvalues"]) > file
	printf("\tvalues        = %s", interp["values", 1]) > file
	for(i = 2; i <= interp["nvalues"]; i++)
		printf(" | %s", interp["values", i]) > file
	printf("\n") > file
	printf("\tnbreaks       = %d\n", interp["nbreaks"]) > file
	printf("\tbreaks        = %s", interp["breaks", 1]) > file
	for(i = 2; i <= interp["nbreaks"]; i++)
		printf(" | %s", interp["breaks", i]) > file
	printf("\n") > file
	printf("\ttcounts       = %d\n", interp["tcounts"]) > file
	printf("\tcounts        = %d", interp["counts", 1]) > file
	for(i = 2; i <= interp["nbreaks"] + 1; i++)
		printf(" | %d", interp["counts", i]) > file
	printf("\n") > file
	printf("}\n") > file
}

function IU_interpolate(interp, v,   idx, work, n_ary, ary, i) {

	idx = IU_search(interp, v)
	interp["tcounts"]++
	interp["counts", idx]++
	if(!interp["is_grad"]){
		return interp["values", idx]
	}else{
		# nbreaks = nvalues - 1
		if(interp["v_ob_info"]  == ""){	# ^gg*$, take out of bounds values from first and last grad elemnt
			if(idx == 1){
				work = interp["values", 1]
				n_ary = split(work, ary, ":")
				return ary[1]
			}else if(idx == interp["nbreaks"] + 1){
				work = interp["values", interp["nvalues"]]
				n_ary = split(work, ary, ":")
				return ary[2]
			}else{
				# TODO: do the actual interpolation
				IU_interpolate_vec(interp["values", idx - 1], v, interp["breaks", idx - 1], interp["breaks", idx])
				return interp["values", idx - 1]
			}
		}else if(interp["v_ob_info"] == "^"){	# ^bgg*$, lower out of bounds specified, take higher ob from last grad element
			if(idx == 1)
				return interp["values", 1]
			else if(idx == interp["nbreaks"] + 1){
				work = interp["values", interp["nvalues"]]
				n_ary = split(work, ary, ":")
				return ary[2]
			}else{
				# TODO: do the actual interpolation
				return interp["values", idx]
			}
		}else if(interp["v_ob_info"] == "$"){	# ^gg*b$, upper out of bounds specified, take lower ob from first grad element
			if(idx == 1){
				work = interp["values", 1]
				n_ary = split(work, ary, ":")
				return ary[1]
			}else if(idx == interp["nbreaks"] + 1){
				return interp["values", idx - 1]
			}else{
				# TODO: do the actual interpolation
				return interp["values", idx - 1]
			}
		}else{	# ^bgg*b$, lower & upper ob info specified
			if(idx == 1 || idx == interp["nbreaks"] + 1)
				return interp["values", idx]
			else{
				# TODO: do the actual interpolation
				return interp["values", idx]
			}
		}
		return v_type
	}
}

function IU_interpolate_vec(vec, v, v_min, v_max) {

	printf("DEBUG: IU_interpolate_vec: vec = %s, v = %g, v_min = %g, v_max = %g\n", vec, v, v_min, v_max)
	return ""
}

function IU_search(interp, v,   i, j, k, cv) {

	# deal with out of range values here to simplify the binary interval search
	if(v <= interp["breaks", 1])
		return 1
	else if(v > interp["breaks", interp["nbreaks"]])
		return interp["nbreaks"] + 1

	# binary search on intervals
	i = 1 ; j = interp["nbreaks"];
	for( ; i <= j ; ){
		k = int((i+j)/2)
		if(v == interp["breaks", k]){
			return k
		}else if(v < interp["breaks", k]){
			if(v > interp["breaks", k-1])
				return k
			j = k - 1
		}else{
			if(v <= interp["breaks", k+1])
				return k + 1
			i = k + 1
		}
	}
	# should never get here!
	return 0
}

function IU_init(config, interp, name, k_values, k_breaks,   work, n_ary, ary, c_rng, i, nv) {

	interp["name"] = name

	# get the values
	work = config["_globals", k_values]
	if(work == ""){
		printf("ERROR: IU_init: no key named \"%s\" in config\n", k_values) > "/dev/stderr"
		return 1
	}
	nv = n_ary = split(work, ary, "|")
	interp["nvalues"] = n_ary
	c_rng = 0
	for(i = 1; i <= n_ary; i++){
		work = ary[i]
		if(index(work, ":") != 0)
			c_rng++
		sub(/^[ \t]*/, "", work)
		sub(/[ \t]*$/, "", work)
		interp["values", i] = work
	}
	if(c_rng == 0)
		interp["is_grad"] = 0
	else if(c_rng == n_ary)
		interp["is_grad"] = 1
	else{
		printf("ERROR: IU_init: k_values can not include both gradient and bin values\n") > "/dev/stderr"
		return 1
	}

	# get the breaks
	work = config["_globals", k_breaks]
	if(work == ""){
		printf("ERROR: IU_init: no key named \"%s\" in config\n", k_values) > "/dev/stderr"
		return 1
	}
	nv = n_ary = split(work, ary, "|")
	interp["nbreaks"] = n_ary
	for(i = 1; i <= n_ary; i++){
		work = ary[i]
		sub(/^[ \t]*/, "", work)
		sub(/[ \t]*$/, "", work)
		interp["breaks", i] = work + 0	# force numeric
	}
	if(interp["nbreaks"] != interp["nvalues"] - 1){
		printf("ERROR: IU_init: nbreaks (%d) != nvalues - 1 (%d)\n", interp["nbreaks"], interp["nvalues"] - 1) > "/dev/stderr"
		return 1
	}

	# init all counts to 0
	for(i = 0; i <= interp["nbreaks"] + 1; i++)
		interp["counts", i] = 0
	interp["tcounts"] = 0

	return 0
}

function IU_dump(file, interp,   i, keys, nk) {

	printf("interp = {\n") > file
	printf("\tname          = %s\n", interp["name"]) > file
	printf("\tis_grad       = %d\n", interp["is_grad"]) > file
	printf("\tnvalues       = %d\n", interp["nvalues"]) > file
	printf("\tvalues        = %s", interp["values", 1]) > file
	for(i = 2; i <= interp["nvalues"]; i++)
		printf(" | %s", interp["values", i]) > file
	printf("\n") > file
	printf("\tnbreaks       = %d\n", interp["nbreaks"]) > file
	printf("\tbreaks        = %s", interp["breaks", 1]) > file
	for(i = 2; i <= interp["nbreaks"]; i++)
		printf(" | %s", interp["breaks", i]) > file
	printf("\n") > file
	printf("\ttcounts       = %d\n", interp["tcounts"]) > file
	printf("\tcounts        = %d", interp["counts", 1]) > file
	for(i = 2; i <= interp["nbreaks"] + 1; i++)
		printf(" | %d", interp["counts", i]) > file
	printf("\n") > file
	printf("}\n") > file
}

function IU_interpolate(interp, v,   idx) {

	idx = IU_search(interp, v)
	interp["tcounts"]++
	if(!interp["is_grad"]){
		interp["counts", idx]++
		return interp["values", idx]
	}

	return ""
}

function IU_search(interp, v,   i, j, k, cv) {

	# deal with out of range values here to simplify the binary interval search
	if(v <= interp["breaks", 1])
		return 1
	else if(v > interp["breaks", interp["nbreaks"]])
		return interp["nbreaks"] + 1

	# binary search on intervals
	i = 1 ; j = interp["nbreaks"];
	for( ; i <= j ; ){
		k = int((i+j)/2)
		if(v == interp["breaks", k]){
			return k
		}else if(v < interp["breaks", k]){
			if(v > interp["breaks", k-1])
				return k
			j = k - 1
		}else{
			if(v <= interp["breaks", k+1])
				return k + 1
			i = k + 1
		}
	}
	# should never get here!
	return 0
}


perf.awk: be less strict with the necessary messages to handle serial logs


Update filter_en_uk.awk

Update filter_en_uk.awk

added €

BEGIN {}
/^macro:[:alpha: ]+/ {
    macroLine = ""
    # I check every field from the second one(the first one is "macro:")
    for (x = 2; x <= NF; x++) {
        do {
            # I found a keys being kept pressed or just released, I print it as it is
            # This works because (probably) I don't want to keep pressed keys that need to be translated such as symbols
            if (length($x) > 1 && (substr($x, 1, 1) == "+" || substr($x, 1, 1) == "-")) {
                macroLine = macroLine $x;
                break;
            }
            # An upper-case key, I need to press shift to do it. Using the != toupper I avoid triggering things like "," == tolower(",") and "," == toupper(",")
            if ($x != tolower($x)) {
                macroLine = macroLine "+lshift,+" tolower($x) ",-" tolower($x) ",-lshift";
                break;
            }
            if ($x == "`") {
                macroLine = macroLine "+grave,-grave";
                break;
            }
            if ($x == "¬") {
                macroLine = macroLine "+lshift,+grave,-grave,-lshift";
                break;
            }
            if ($x == "!") {
                macroLine = macroLine "+lshift,+1,-1,-lshift";
                break;
            }
            if ($x == "\"") {
                macroLine = macroLine "+lshift,+2,-2,-lshift";
                break;
            }
            if ($x == "£") {
                macroLine = macroLine "+lshift,+3,-3,-lshift";
                break;
            }
            if ($x == "$") {
                macroLine = macroLine "+lshift,+4,-4,-lshift";
                break;
            }
            if ($x == "€") {
                macroLine = macroLine "+ralt,+4,-4,-ralt";
                break;
            }
            if ($x == "%") {
                macroLine = macroLine "+lshift,+5,-5,-lshift";
                break;
            }
            if ($x == "^") {
                macroLine = macroLine "+lshift,+6,-6,-lshift";
                break;
            }
            if ($x == "&") {
                macroLine = macroLine "+lshift,+7,-7,-lshift";
                break;
            }
            if ($x == "*") {
                macroLine = macroLine "+lshift,+8,-8,-lshift";
                break;
            }
            if ($x == "(") {
                macroLine = macroLine "+lshift,+9,-9,-lshift";
                break;
            }
            if ($x == ")") {
                macroLine = macroLine "+lshift,+0,-0,-lshift";
                break;
            }
            if ($x == "-") {
                macroLine = macroLine "+minus,-minus";
                break;
            }
            if ($x == "_") {
                macroLine = macroLine "+lshift,+minus,-minus,-lshift";
                break;
            }
            if ($x == "=") {
                macroLine = macroLine "+equal,-equal";
                break;
            }
            if ($x == "+") {
                macroLine = macroLine "+lshift,+equal,-equal,-lshift";
                break;
            }
            if ($x == "[") {
                macroLine = macroLine "+lbrace,-lbrace";
                break;
            }
            if ($x == "{") {
                macroLine = macroLine "+lshift,+lbrace,-lbrace,-lshift";
                break;
            }
            if ($x == "]") {
                macroLine = macroLine "+rbrace,-rbrace";
                break;
            }
            if ($x == "}") {
                macroLine = macroLine "+lshift,+rbrace,-rbrace,-lshift";
                break;
            }
            if ($x == "#") {
                macroLine = macroLine "+hash,-hash";
                break;
            }
            if ($x == "~") {
                macroLine = macroLine "+lshift,+hash,-hash,-lshift";
                break;
            }
            if ($x == ";") {
                macroLine = macroLine "+colon,-colon";
                break;
            }
            if ($x == ":") {
                macroLine = macroLine "+lshift,+colon,-colon,-lshift";
                break;
            }
            if ($x == "'") {
                macroLine = macroLine "+quote,-quote";
                break;
            }
            if ($x == "@") {
                macroLine = macroLine "+lshift,+quote,-quote,-lshift";
                break;
            }
            if ($x == ",") {
                macroLine = macroLine "+comma,-comma";
                break;
            }
            if ($x == "\\") {
                macroLine = macroLine "+bslash_iso,-bslash_iso";
                break;
            }
            if ($x == "|") {
                macroLine = macroLine "+lshift,+bslash_iso,-bslash_iso,-lshift";
                break;
            }
            if ($x == "<") {
                macroLine = macroLine "+lshift,+comma,-comma,-lshift";
                break;
            }
            if ($x == ".") {
                macroLine = macroLine "+dot,-dot";
                break;
            }
            if ($x == ">") {
                macroLine = macroLine "+lshift,+dot,-dot,-lshift";
                break;
            }
            if ($x == "/") {
                macroLine = macroLine "+slash,-slash";
                break;
            }
            if ($x == "?") {
                macroLine = macroLine "+lshift,+slash,-slash,-lshift";
                break;
            }

            # A normal key, press and release it, catches all the remaining cases
            macroLine = macroLine "+" $x ",-" $x
        }
        while (0)
        # 0 means it runs only once instead of endlessly looping in case of errors

        # I add a comma between this key and the next unless I 'm at the last one
        if (x < NF)
            macroLine = macroLine ","
        else
            # Finished reading the macro, sending it macro to the output
            print macroLine
    }
}
END {}

BEGIN {}
/^macro:[:alpha: ]+/ {
    macroLine = ""
    # I check every field from the second one(the first one is "macro:")
    for (x = 2; x <= NF; x++) {
        do {
            # I found a keys being kept pressed or just released, I print it as it is
            # This works because (probably) I don't want to keep pressed keys that need to be translated such as symbols
            if (length($x) > 1 && (substr($x, 1, 1) == "+" || substr($x, 1, 1) == "-")) {
                macroLine = macroLine $x;
                break;
            }
            # An upper-case key, I need to press shift to do it. Using the != toupper I avoid triggering things like "," == tolower(",") and "," == toupper(",")
            if ($x != tolower($x)) {
                macroLine = macroLine "+lshift,+" tolower($x) ",-" tolower($x) ",-lshift";
                break;
            }
            if ($x == "`") {
                macroLine = macroLine "+grave,-grave";
                break;
            }
            if ($x == "¬") {
                macroLine = macroLine "+lshift,+grave,-grave,-lshift";
                break;
            }
            if ($x == "!") {
                macroLine = macroLine "+lshift,+1,-1,-lshift";
                break;
            }
            if ($x == "\"") {
                macroLine = macroLine "+lshift,+2,-2,-lshift";
                break;
            }
            if ($x == "£") {
                macroLine = macroLine "+lshift,+3,-3,-lshift";
                break;
            }
            if ($x == "$") {
                macroLine = macroLine "+lshift,+4,-4,-lshift";
                break;
            }
            if ($x == "%") {
                macroLine = macroLine "+lshift,+5,-5,-lshift";
                break;
            }
            if ($x == "^") {
                macroLine = macroLine "+lshift,+6,-6,-lshift";
                break;
            }
            if ($x == "&") {
                macroLine = macroLine "+lshift,+7,-7,-lshift";
                break;
            }
            if ($x == "*") {
                macroLine = macroLine "+lshift,+8,-8,-lshift";
                break;
            }
            if ($x == "(") {
                macroLine = macroLine "+lshift,+9,-9,-lshift";
                break;
            }
            if ($x == ")") {
                macroLine = macroLine "+lshift,+0,-0,-lshift";
                break;
            }
            if ($x == "-") {
                macroLine = macroLine "+minus,-minus";
                break;
            }
            if ($x == "_") {
                macroLine = macroLine "+lshift,+minus,-minus,-lshift";
                break;
            }
            if ($x == "=") {
                macroLine = macroLine "+equal,-equal";
                break;
            }
            if ($x == "+") {
                macroLine = macroLine "+lshift,+equal,-equal,-lshift";
                break;
            }
            if ($x == "[") {
                macroLine = macroLine "+lbrace,-lbrace";
                break;
            }
            if ($x == "{") {
                macroLine = macroLine "+lshift,+lbrace,-lbrace,-lshift";
                break;
            }
            if ($x == "]") {
                macroLine = macroLine "+rbrace,-rbrace";
                break;
            }
            if ($x == "}") {
                macroLine = macroLine "+lshift,+rbrace,-rbrace,-lshift";
                break;
            }
            if ($x == "#") {
                macroLine = macroLine "+hash,-hash";
                break;
            }
            if ($x == "~") {
                macroLine = macroLine "+lshift,+hash,-hash,-lshift";
                break;
            }
            if ($x == ";") {
                macroLine = macroLine "+colon,-colon";
                break;
            }
            if ($x == ":") {
                macroLine = macroLine "+lshift,+colon,-colon,-lshift";
                break;
            }
            if ($x == "'") {
                macroLine = macroLine "+quote,-quote";
                break;
            }
            if ($x == "@") {
                macroLine = macroLine "+lshift,+quote,-quote,-lshift";
                break;
            }
            if ($x == ",") {
                macroLine = macroLine "+comma,-comma";
                break;
            }
            if ($x == "\\") {
                macroLine = macroLine "+bslash_iso,-bslash_iso";
                break;
            }
            if ($x == "|") {
                macroLine = macroLine "+lshift,+bslash_iso,-bslash_iso,-lshift";
                break;
            }
            if ($x == "<") {
                macroLine = macroLine "+lshift,+comma,-comma,-lshift";
                break;
            }
            if ($x == ".") {
                macroLine = macroLine "+dot,-dot";
                break;
            }
            if ($x == ">") {
                macroLine = macroLine "+lshift,+dot,-dot,-lshift";
                break;
            }
            if ($x == "/") {
                macroLine = macroLine "+slash,-slash";
                break;
            }
            if ($x == "?") {
                macroLine = macroLine "+lshift,+slash,-slash,-lshift";
                break;
            }

            # A normal key, press and release it, catches all the remaining cases
            macroLine = macroLine "+" $x ",-" $x
        }
        while (0)
        # 0 means it runs only once instead of endlessly looping in case of errors

        # I add a comma between this key and the next unless I 'm at the last one
        if (x < NF)
            macroLine = macroLine ","
        else
            # Finished reading the macro, sending it macro to the output
            print macroLine
    }
}
END {}


use SGR code 22 instead of 21

use SGR code 22 instead of 21

* fix #38

####################################################################
# Commons.awk                                                      #
#                                                                  #
# Commonly used functions for string and array operations, logging.#
####################################################################

# Initialize `UrlEncoding`.
# See: <https://en.wikipedia.org/wiki/Percent-encoding>
function initUrlEncoding() {
    UrlEncoding["\n"] = "%0A"
    UrlEncoding[" "]  = "%20"
    UrlEncoding["!"]  = "%21"
    UrlEncoding["\""] = "%22"
    UrlEncoding["#"]  = "%23"
    UrlEncoding["$"]  = "%24"
    UrlEncoding["%"]  = "%25"
    UrlEncoding["&"]  = "%26"
    UrlEncoding["'"]  = "%27"
    UrlEncoding["("]  = "%28"
    UrlEncoding[")"]  = "%29"
    UrlEncoding["*"]  = "%2A"
    UrlEncoding["+"]  = "%2B"
    UrlEncoding[","]  = "%2C"
    UrlEncoding["-"]  = "%2D"
    UrlEncoding["."]  = "%2E"
    UrlEncoding["/"]  = "%2F"
    UrlEncoding[":"]  = "%3A"
    UrlEncoding[";"]  = "%3B"
    UrlEncoding["<"]  = "%3C"
    UrlEncoding["="]  = "%3D"
    UrlEncoding[">"]  = "%3E"
    UrlEncoding["?"]  = "%3F"
    UrlEncoding["@"]  = "%40"
    UrlEncoding["["]  = "%5B"
    UrlEncoding["\\"] = "%5C"
    UrlEncoding["]"]  = "%5D"
    UrlEncoding["^"]  = "%5E"
    UrlEncoding["_"]  = "%5F"
    UrlEncoding["`"]  = "%60"
    UrlEncoding["{"]  = "%7B"
    UrlEncoding["|"]  = "%7C"
    UrlEncoding["}"]  = "%7D"
    UrlEncoding["~"]  = "%7E"
}

# Return the real character represented by an escape sequence.
# Example: escapeChar("n") returns "\n".
# See: <https://en.wikipedia.org/wiki/Escape_character>
#      <https://en.wikipedia.org/wiki/Escape_sequences_in_C>
function escapeChar(char) {
    switch (char) {
    case "b":
        return "\b" # Backspace
    case "f":
        return "\f" # Formfeed
    case "n":
        return "\n" # Newline (Line Feed)
    case "r":
        return "\r" # Carriage Return
    case "t":
        return "\t" # Horizontal Tab
    case "v":
        return "\v" # Vertical Tab
    default:
        return char
    }
}

# Convert a literal-formatted string into its original string.
function literal(string,
                 ####
                 c, escaping, i, s) {
    if (string !~ /^".*"$/)
        return string

    split(string, s, "")
    string = ""
    escaping = 0
    for (i = 2; i < length(s); i++) {
        c = s[i]
        if (escaping) {
            string = string escapeChar(c)
            escaping = 0 # escape ends
        } else {
            if (c == "\\")
                escaping = 1 # escape begins
            else
                string = string c
        }
    }
    return string
}

# Return the escaped string.
function escape(string) {
    gsub(/"/, "\\\"", string)
    gsub(/\\/, "\\\\", string)
    return string
}

# Return the escaped, quoted string.
function parameterize(string, quotationMark) {
    if (!quotationMark)
        quotationMark = "'"

    if (quotationMark == "'") {
        gsub(/'/, "'\\''", string)
        return "'" string "'"
    } else {
        return "\"" escape(string) "\""
    }
}

# Return the URL-encoded string.
function quote(string,    i, r, s) {
    r = ""
    split(string, s, "")
    for (i = 1; i <= length(s); i++)
        r = r (s[i] in UrlEncoding ? UrlEncoding[s[i]] : s[i])
    return r
}

# Replicate a string.
function replicate(string, len,
                   ####
                   i, temp) {
    temp = ""
    for (i = 0; i < len; i++)
        temp = temp string
    return temp
}

# Squeeze a source line of AWK code.
function squeeze(line) {
    # Remove preceding spaces
    gsub(/^[[:space:]]+/, "", line)
    # Remove in-line comment
    gsub(/#[^"]*$/, "", line)
    # Remove trailing spaces
    gsub(/[[:space:]]+$/, "", line)

    return line
}

# Return 1 if the array contains anything; otherwise return 0.
function anything(array,
                  ####
                  i) {
    for (i in array)
        if (array[i]) return 1
    return 0
}

# Append an element into an array (zero-based).
function append(array, element) {
    array[anything(array) ? length(array) : 0] = element
}

# Return an element if it belongs to the array;
# Otherwise, return a null string.
function belongsTo(element, array,
                   ####
                   i) {
    for (i in array)
        if (element == array[i]) return element
    return ""
}

# Return one of the substrings if the string starts with it;
# Otherwise, return a null string.
function startsWithAny(string, substrings,
                       ####
                       i) {
    for (i in substrings)
        if (index(string, substrings[i]) == 1) return substrings[i]
    return ""
}

# Return one of the patterns if the string matches this pattern at the beginning;
# Otherwise, return a null string.
function matchesAny(string, patterns,
                    ####
                    i) {
    for (i in patterns)
        if (string ~ "^" patterns[i]) return patterns[i]
    return ""
}

# Join an array into one string;
# Return the string.
function join(array, separator, sortedIn, preserveNull,
              ####
              i, j, saveSortedIn, temp) {
    # Default parameters
    if (!separator)
        separator = " "
    if (!sortedIn)
        sortedIn = "@ind_num_asc"

    temp = ""
    j = 0
    saveSortedIn = PROCINFO["sorted_in"]
    if (length(array)) {
        PROCINFO["sorted_in"] = sortedIn
        for (i in array)
            if (preserveNull || array[i] != "")
                temp = j++ ? temp separator array[i] : array[i]
        PROCINFO["sorted_in"] = saveSortedIn
    } else
        temp = array # array == ""

    return temp
}

# Initialize ANSI escape codes (ANSI X3.64 Standard Control Sequences).
# See: <https://en.wikipedia.org/wiki/ANSI_escape_code>
function initAnsiCode() {
    # Dumb terminal: no ANSI escape code whatsoever
    if (ENVIRON["TERM"] == "dumb") return

    AnsiCode["reset"]         = AnsiCode[0] = "\33[0m"

    AnsiCode["bold"]          = "\33[1m"
    AnsiCode["underline"]     = "\33[4m"
    AnsiCode["negative"]      = "\33[7m"
    AnsiCode["no bold"]       = "\33[22m" # SGR code 21 (bold off) not widely supported
    AnsiCode["no underline"]  = "\33[24m"
    AnsiCode["positive"]      = "\33[27m"

    AnsiCode["black"]         = "\33[30m"
    AnsiCode["red"]           = "\33[31m"
    AnsiCode["green"]         = "\33[32m"
    AnsiCode["yellow"]        = "\33[33m"
    AnsiCode["blue"]          = "\33[34m"
    AnsiCode["magenta"]       = "\33[35m"
    AnsiCode["cyan"]          = "\33[36m"
    AnsiCode["gray"]          = "\33[37m"

    AnsiCode["default"]       = "\33[39m"

    AnsiCode["dark gray"]     = "\33[90m"
    AnsiCode["light red"]     = "\33[91m"
    AnsiCode["light green"]   = "\33[92m"
    AnsiCode["light yellow"]  = "\33[93m"
    AnsiCode["light blue"]    = "\33[94m"
    AnsiCode["light magenta"] = "\33[95m"
    AnsiCode["light cyan"]    = "\33[96m"
    AnsiCode["white"]         = "\33[97m"
}

# Print warning message.
function w(text) {
    print AnsiCode["yellow"] text AnsiCode[0] > "/dev/stderr"
}

# Print error message.
function e(text) {
    print AnsiCode["red"] AnsiCode["bold"] text AnsiCode[0] > "/dev/stderr"
}

# Print debugging message.
function d(text) {
    print AnsiCode["gray"] text AnsiCode[0] > "/dev/stderr"
}

# Log an array.
function da(array, formatString, sortedIn,
            ####
            i, j, saveSortedIn) {
    # Default parameters
    if (!formatString)
        formatString = "_[%s]='%s'"
    if (!sortedIn)
        sortedIn = "@ind_num_asc"

    saveSortedIn = PROCINFO["sorted_in"]
    PROCINFO["sorted_in"] = sortedIn
    for (i in array) {
        split(i, j, SUBSEP)
        d(sprintf(formatString, join(j, ","), array[i]))
    }
    PROCINFO["sorted_in"] = saveSortedIn
}

# Naive assertion.
function assert(x, message) {
    if (!message)
        message = "[ERROR] Assertion failed."

    if (x)
        return x
    else
        e(message)
}

# Return non-zero if file exists, otherwise return 0.
function fileExists(file) {
    return !system("test -f " file)
}

# Initialize `UriSchemes`.
function initUriSchemes() {
    UriSchemes[0] = "file"
    UriSchemes[1] = "http"
    UriSchemes[2] = "https"
}

BEGIN {
    initUrlEncoding()
    initAnsiCode()
    initUriSchemes()
}

####################################################################
# Commons.awk                                                      #
#                                                                  #
# Commonly used functions for string and array operations, logging.#
####################################################################

# Initialize `UrlEncoding`.
# See: <https://en.wikipedia.org/wiki/Percent-encoding>
function initUrlEncoding() {
    UrlEncoding["\n"] = "%0A"
    UrlEncoding[" "]  = "%20"
    UrlEncoding["!"]  = "%21"
    UrlEncoding["\""] = "%22"
    UrlEncoding["#"]  = "%23"
    UrlEncoding["$"]  = "%24"
    UrlEncoding["%"]  = "%25"
    UrlEncoding["&"]  = "%26"
    UrlEncoding["'"]  = "%27"
    UrlEncoding["("]  = "%28"
    UrlEncoding[")"]  = "%29"
    UrlEncoding["*"]  = "%2A"
    UrlEncoding["+"]  = "%2B"
    UrlEncoding[","]  = "%2C"
    UrlEncoding["-"]  = "%2D"
    UrlEncoding["."]  = "%2E"
    UrlEncoding["/"]  = "%2F"
    UrlEncoding[":"]  = "%3A"
    UrlEncoding[";"]  = "%3B"
    UrlEncoding["<"]  = "%3C"
    UrlEncoding["="]  = "%3D"
    UrlEncoding[">"]  = "%3E"
    UrlEncoding["?"]  = "%3F"
    UrlEncoding["@"]  = "%40"
    UrlEncoding["["]  = "%5B"
    UrlEncoding["\\"] = "%5C"
    UrlEncoding["]"]  = "%5D"
    UrlEncoding["^"]  = "%5E"
    UrlEncoding["_"]  = "%5F"
    UrlEncoding["`"]  = "%60"
    UrlEncoding["{"]  = "%7B"
    UrlEncoding["|"]  = "%7C"
    UrlEncoding["}"]  = "%7D"
    UrlEncoding["~"]  = "%7E"
}

# Return the real character represented by an escape sequence.
# Example: escapeChar("n") returns "\n".
# See: <https://en.wikipedia.org/wiki/Escape_character>
#      <https://en.wikipedia.org/wiki/Escape_sequences_in_C>
function escapeChar(char) {
    switch (char) {
    case "b":
        return "\b" # Backspace
    case "f":
        return "\f" # Formfeed
    case "n":
        return "\n" # Newline (Line Feed)
    case "r":
        return "\r" # Carriage Return
    case "t":
        return "\t" # Horizontal Tab
    case "v":
        return "\v" # Vertical Tab
    default:
        return char
    }
}

# Convert a literal-formatted string into its original string.
function literal(string,
                 ####
                 c, escaping, i, s) {
    if (string !~ /^".*"$/)
        return string

    split(string, s, "")
    string = ""
    escaping = 0
    for (i = 2; i < length(s); i++) {
        c = s[i]
        if (escaping) {
            string = string escapeChar(c)
            escaping = 0 # escape ends
        } else {
            if (c == "\\")
                escaping = 1 # escape begins
            else
                string = string c
        }
    }
    return string
}

# Return the escaped string.
function escape(string) {
    gsub(/"/, "\\\"", string)
    gsub(/\\/, "\\\\", string)
    return string
}

# Return the escaped, quoted string.
function parameterize(string, quotationMark) {
    if (!quotationMark)
        quotationMark = "'"

    if (quotationMark == "'") {
        gsub(/'/, "'\\''", string)
        return "'" string "'"
    } else {
        return "\"" escape(string) "\""
    }
}

# Return the URL-encoded string.
function quote(string,    i, r, s) {
    r = ""
    split(string, s, "")
    for (i = 1; i <= length(s); i++)
        r = r (s[i] in UrlEncoding ? UrlEncoding[s[i]] : s[i])
    return r
}

# Replicate a string.
function replicate(string, len,
                   ####
                   i, temp) {
    temp = ""
    for (i = 0; i < len; i++)
        temp = temp string
    return temp
}

# Squeeze a source line of AWK code.
function squeeze(line) {
    # Remove preceding spaces
    gsub(/^[[:space:]]+/, "", line)
    # Remove in-line comment
    gsub(/#[^"]*$/, "", line)
    # Remove trailing spaces
    gsub(/[[:space:]]+$/, "", line)

    return line
}

# Return 1 if the array contains anything; otherwise return 0.
function anything(array,
                  ####
                  i) {
    for (i in array)
        if (array[i]) return 1
    return 0
}

# Append an element into an array (zero-based).
function append(array, element) {
    array[anything(array) ? length(array) : 0] = element
}

# Return an element if it belongs to the array;
# Otherwise, return a null string.
function belongsTo(element, array,
                   ####
                   i) {
    for (i in array)
        if (element == array[i]) return element
    return ""
}

# Return one of the substrings if the string starts with it;
# Otherwise, return a null string.
function startsWithAny(string, substrings,
                       ####
                       i) {
    for (i in substrings)
        if (index(string, substrings[i]) == 1) return substrings[i]
    return ""
}

# Return one of the patterns if the string matches this pattern at the beginning;
# Otherwise, return a null string.
function matchesAny(string, patterns,
                    ####
                    i) {
    for (i in patterns)
        if (string ~ "^" patterns[i]) return patterns[i]
    return ""
}

# Join an array into one string;
# Return the string.
function join(array, separator, sortedIn, preserveNull,
              ####
              i, j, saveSortedIn, temp) {
    # Default parameters
    if (!separator)
        separator = " "
    if (!sortedIn)
        sortedIn = "@ind_num_asc"

    temp = ""
    j = 0
    saveSortedIn = PROCINFO["sorted_in"]
    if (length(array)) {
        PROCINFO["sorted_in"] = sortedIn
        for (i in array)
            if (preserveNull || array[i] != "")
                temp = j++ ? temp separator array[i] : array[i]
        PROCINFO["sorted_in"] = saveSortedIn
    } else
        temp = array # array == ""

    return temp
}

# Initialize ANSI escape codes (ANSI X3.64 Standard Control Sequences).
# See: <https://en.wikipedia.org/wiki/ANSI_escape_code>
function initAnsiCode() {
    # Dumb terminal: no ANSI escape code whatsoever
    if(ENVIRON["TERM"] == "dumb") return

    AnsiCode["reset"]         = AnsiCode[0] = "\33[0m"

    AnsiCode["bold"]          = "\33[1m"
    AnsiCode["underline"]     = "\33[4m"
    AnsiCode["negative"]      = "\33[7m"
    AnsiCode["no bold"]       = "\33[21m"
    AnsiCode["no underline"]  = "\33[24m"
    AnsiCode["positive"]      = "\33[27m"

    AnsiCode["black"]         = "\33[30m"
    AnsiCode["red"]           = "\33[31m"
    AnsiCode["green"]         = "\33[32m"
    AnsiCode["yellow"]        = "\33[33m"
    AnsiCode["blue"]          = "\33[34m"
    AnsiCode["magenta"]       = "\33[35m"
    AnsiCode["cyan"]          = "\33[36m"
    AnsiCode["gray"]          = "\33[37m"

    AnsiCode["default"]       = "\33[39m"

    AnsiCode["dark gray"]     = "\33[90m"
    AnsiCode["light red"]     = "\33[91m"
    AnsiCode["light green"]   = "\33[92m"
    AnsiCode["light yellow"]  = "\33[93m"
    AnsiCode["light blue"]    = "\33[94m"
    AnsiCode["light magenta"] = "\33[95m"
    AnsiCode["light cyan"]    = "\33[96m"
    AnsiCode["white"]         = "\33[97m"
}

# Print warning message.
function w(text) {
    print AnsiCode["yellow"] text AnsiCode[0] > "/dev/stderr"
}

# Print error message.
function e(text) {
    print AnsiCode["red"] AnsiCode["bold"] text AnsiCode[0] > "/dev/stderr"
}

# Print debugging message.
function d(text) {
    print AnsiCode["gray"] text AnsiCode[0] > "/dev/stderr"
}

# Log an array.
function da(array, formatString, sortedIn,
            ####
            i, j, saveSortedIn) {
    # Default parameters
    if (!formatString)
        formatString = "_[%s]='%s'"
    if (!sortedIn)
        sortedIn = "@ind_num_asc"

    saveSortedIn = PROCINFO["sorted_in"]
    PROCINFO["sorted_in"] = sortedIn
    for (i in array) {
        split(i, j, SUBSEP)
        d(sprintf(formatString, join(j, ","), array[i]))
    }
    PROCINFO["sorted_in"] = saveSortedIn
}

# Naive assertion.
function assert(x, message) {
    if (!message)
        message = "[ERROR] Assertion failed."

    if (x)
        return x
    else
        e(message)
}

# Return non-zero if file exists, otherwise return 0.
function fileExists(file) {
    return !system("test -f " file)
}

# Initialize `UriSchemes`.
function initUriSchemes() {
    UriSchemes[0] = "file"
    UriSchemes[1] = "http"
    UriSchemes[2] = "https"
}

BEGIN {
    initUrlEncoding()
    initAnsiCode()
    initUriSchemes()
}


handle one line struct {}, multiline comments, whitespaces when parsing header for structs

git-svn-id: 88e1a00e4eab3b0bb0c09889526309a3e2f5bd8a@187 c26eb9a1-5813-0410-bd6c-c2e55f420ca7

#! /usr/bin/awk -f
# vim:ts=4:sw=4
BEGIN {
	brace = 0;
	incomment = 0;
	buffer_len = 0;
}

# multiline comment handling
{
	# removes one line comment
	gsub(/\/\*(.+?)\*\//, " ");
}
/\*\// {
	if (incomment) {
		sub(/.*\*\//, "");
		incomment = 0;
	}
}
incomment {
	next;
}
/\/\*/ {
	sub(/\/\*.*/, "");
	incomment = 1;
	# fall through
}

# skip file/line mark here to be faster
/^#/ {
	next;
}

/^}.*;/ {
	if (instruct) {
		sub(";", "");
		if (instruct == 1 && $2) {
			instruct = $2;
		}
		if (instruct in typedefs) {
			instruct = typedefs[instruct];
		}
		sizeinfo = "";
		elms = "";
		for (i = 0; i in buffer; i ++) {
			if (i) {
				sizeinfo = sizeinfo " + ";
			}
			sizeinfo = sizeinfo "sizeof(((" instruct "*)NULL)->" buffer[i] ")";

			if (i == 0) {
				elms = buffer[i];
			}
			else {
				elms = elms "," buffer[i];
			}
		}
		printf "define(`ELEMENTSOF_%s', `%s')\n", instruct, elms;
		printf "define(`COUNTOF_%s', `%s')\n", instruct, i;
		printf "define(`SIZEOF_%s', `(  %s  )')\n", instruct, sizeinfo;
		print "\n";
		for (i in buffer) {
			delete buffer[i];
		}
		buffer_len = 0;
		instruct = 0;
	}
	next;
}

/.\{/ {
	brace = brace + 1;
}
/.}/ {
	brace = brace - 1;
}

{
	if (brace == 1 && instruct) {
		gsub(/(^[\t ]+|[\t ]+$)/, ""); # trim whitespaces
		sub(/.*[{}]/, "");
		gsub(/\[[^\]]+\]/, ""); # ignore [...]
		gsub(/:[0-9]+/, ""); # ignore struct bit
		if (match($0, /^[^(]*\([ ]*\*([^)]+)\)/)) {
			sub(/ +/, "")
			sub(/^[^(]*\(\*/, "");
			sub(/\).*/, "");
			# function pointer
			buffer[buffer_len] = $0;
			buffer_len ++;
		}
		else {
			# process normal variables

			# ignore any ()s
			while (gsub(/(\([^)]*\))/, "")) {
			}
			if (match($0, /[()]/)) {
				next;
			}
			# unsigned int *a,  b; int c;
			gsub(/[*]/, " ");
			# unsigned int a,  b; int c;
			gsub(/ +/, " ");
			# unsigned int a, b; int c;
			gsub(/ *[,;]/, ";");
			# unsigned int a; b; int c;
			if (!match($0, /;/)) {
				next;
			}
			# print "=DEBUG=" $0 "==";
			split($0, chunks, ";");
			# [unsigned int a, b, c]

			for (i = 1; i in chunks; i ++) {
				if (chunks[i] == "") {
					delete chunks[i];
					continue;
				}
				split(chunks[i], pieces, " ");
				# [unsigned, int, a]
				# [b]
				# [c]

				last_piece = "";
				for (j = 1; j in pieces; j ++) {
					last_piece = pieces[j];
					delete pieces[j];
				}
				if (last_piece == "") {
					# print "=ERROR=" chunks[i] "==";
					delete chunks[i];
					continue;
				}
				# a
				# b
				# c

				buffer[buffer_len] = last_piece;
				buffer_len ++;
				delete chunks[i]
			}
			last_piece = "";
		}
		next;
	}
}

/^typedef struct [^{]*;/ {
	sub(";", "");
	typedefs[$3] = $4;
	next;
}
/^typedef struct .*\{[^}]*$/ {
	brace = 1;
	instruct = 1;
	next;
}

/^struct .*\{/ {
	instruct = $2;
	brace = 1;
	next;
}

#! /usr/bin/awk -f
# vim:ts=4:sw=4
BEGIN {
	brace = 0;
	buffer_len = 0;
}
/^#/ {
	next;
}
/^}.*;/ {
	if (instruct) {
		sub(";", "");
		if (instruct == 1 && $2) {
			instruct = $2;
		}
		if (instruct in typedefs) {
			instruct = typedefs[instruct];
		}
		sizeinfo = "";
		elms = "";
		for (i = 0; i in buffer; i ++) {
			if (i) {
				sizeinfo = sizeinfo " + ";
			}
			sizeinfo = sizeinfo "sizeof(((" instruct "*)NULL)->" buffer[i] ")";

			if (i == 0) {
				elms = buffer[i];
			}
			else {
				elms = elms "," buffer[i];
			}
		}
		printf "define(`ELEMENTSOF_%s', `%s')\n", instruct, elms;
		printf "define(`COUNTOF_%s', `%s')\n", instruct, i;
		printf "define(`SIZEOF_%s', `(  %s  )')\n", instruct, sizeinfo;
		print "\n";
		for (i in buffer) {
			delete buffer[i];
		}
		buffer_len = 0;
		instruct = 0;
	}
	next;
}

/.\{/ {
	brace = brace + 1;
}
/.}/ {
	brace = brace - 1;
}

{
	if (brace == 1 && instruct) {
		gsub(/\/\*(.+?)\*\//, " "); # removes one line comment
		sub(/.*[{}]/, "");
		gsub(/\[[^\]]+\]/, ""); # ignore [...]
		gsub(/:[0-9]+/, ""); # ignore struct bit
		if (match($0, /^[^(]*\([ ]*\*([^)]+)\)/)) {
			sub(/ +/, "")
			sub(/^[^(]*\(\*/, "");
			sub(/\).*/, "");
			# function pointer
			buffer[buffer_len] = $0;
			buffer_len ++;
		}
		else {
			# ignore any ()s
			while (gsub(/(\([^)]*\))/, "")) {
			}
			if (match($0, /[()]/)) {
				next;
			}
			# unsigned int *a,  b; int c;
			gsub(/[*]/, " ");
			# unsigned int a,  b; int c;
			gsub(/ +/, " ");
			# unsigned int a, b; int c;
			gsub(/ *[,;]/, ";");
			# unsigned int a; b; int c;
			if (!match($0, /;/)) {
				next;
			}
			split($0, chunks, ";");
			# [unsigned int a, b, c]

			for (i = 1; i in chunks; i ++) {
				if (chunks[i] == "") {
					delete chunks[i];
					continue;
				}
				split(chunks[i], pieces, " ");
				# [unsigned, int, a]
				# [b]
				# [c]

				last_piece = "";
				for (j = 1; j in pieces; j ++) {
					last_piece = pieces[j];
					delete pieces[j];
				}
				if (last_piece == "") {
					print "=====" chunks[i];
				}
				# a
				# b
				# c

				buffer[buffer_len] = last_piece;
				buffer_len ++;
				delete chunks[i]
			}
			last_piece = "";
		}
		next;
	}
}

/^typedef struct [^{]*;/ {
	sub(";", "");
	typedefs[$3] = $4;
	next;
}
/^typedef struct .*\{/ {
	brace = 1;
	instruct = 1;
	next;
}

/^struct .*\{/ {
	instruct = $2;
	brace = 1;
	next;
}