#!/usr/bin/gawk -f
# vim: fdm=marker:

# =============================================================================
# Text line formatter for findnrun source pluging taps.
# Version=4.0.0
# author: Copyright (C)2016-2019 step
# license: GNU GPL applies
# source: https://github.com/step-/find-n-run
# forum: http://www.murga-linux.com/puppy/viewtopic.php?t=102811
# =============================================================================

# Usage: source | findnrun-formatter [-S source-id] [-I icon-filename] [-T title] [-O options] [-v verbosity-level]
# This program assumes that plugin invocation environment variables are defined.
# This program transforms input data as described in section "Encodings" below.

# Source plugin formatter script {{{1
BEGIN {
  Version = "4.0.0"
  Program = "findnrun-formatter"
  Opterr = 1    # default is to diagnose
  Optind = 1    # skip ARGV[0]

  while ((_go_c = getopt(ARGC, ARGV, "S:I:T:O:hv:")) != -1)
    opt[_go_c] = Optarg
#  printf("non-option arguments:\n")
#  for (; Optind < ARGC; Optind++)
#    printf("\tARGV[%d] = <%s>\n", Optind, ARGV[Optind])

  # Clear out all option args so they aren't interpreted as filenames.
  for(i = 0; i <= Optind; i++) delete ARGV[i]

  if("h" in opt) {
    printf("%s %s\nusage: <command> | %s -- [ <options> ]\n<options>:\n", \
           Program, Version, Program)
    print "  -h\t\t\tprint this message and exit\n" \
          "  -I icon-filepath\toverrides plugin declared icon\n" \
          "  -O options\t\ta string of letters:\n" \
          "     's' (s)ingle field input\n" \
          "     'u' sort -(u)nique output, implies 's'\n" \
          "  -S source-id\n" \
          "  -T source-title\n" \
          "  -v level\t\tprint progress info to stderr, integer level 1..4"
    exit
  }
  if(opt["v"] > 2) {
    printf("\nfindnrun-formatter: -Source '%s' -Icon '%s' -Ttitle '%s' -Options '%s'\n",
           opt["S"], opt["I"], opt["T"], opt["O"]) >"/dev/stderr"
  }
  # Optimize well-known sources and single-field case.
  if(index(opt["O"], "s")) {
    RS="^$" # slurp whole input file
    FS = "\n"
  }
  else
  {
    FS="|"
  }
  SEP="\x08"
  default_icon = get_default_icon()
  verbose = ("v" in opt) ?opt["v"] :0
}
# Optimized single-field case. {{{2
index(opt["O"], "s") {
  if(! index(opt["O"], "u")) {
    for(i=1; i <= NF; i++)
      if($(i)) print substr(default_icon "|" $(i) "||" SEP SEP $(i) SEP, 1, 510)
      # 510 works around gtkdialog tree widget buffer overflow limit.
    if(verbose > 3)
      if($(i)) print default_icon "||" $(i) > "/dev/stderr"
  } else { # sort -unique
    split($0, a)
    asort(a)
    a0=""
    for(i=1; i <= NF; i++) {
      ai = a[i]
      if(a0 != ai) {
        a0 = ai
        print substr(default_icon "|" ai "||" SEP SEP ai SEP, 1, 510)
        if(verbose > 3)
          print default_icon "|" ai "|" > "/dev/stderr"
      }
    }
  }
  exit
}
# Multi-field case. {{{2
{
  # Encodings {{{
  # In:   <icon-filename> '|' <tap-reserved> '|' <label> '|' <tap-data> '|' <comment> '|' <categories>
  # Out:  Icon|Label|FNR-Reserved|PackedValues
  # PackedValues:     for SEP=,
  #   plugin tap: <icon-filename>,<tap-reserved>,<label>,<tap-data>,<comment>,<categories>
  #   FNRstart:   icon,filename,name,exec,comment,category
  # Reference In : /usr/share/doc/findnrun/plugin-dev.md section "Tap-record".
  # Reference Out: findnrun::$AWKB::format_item(); PackedValues: format_item()
  #}}}
  # The third column below, FNR-Reserved, is always empty and it's reserved for future expansion.
  # The tree widget does display its value.
  print substr(($1 ?$1 :default_icon) "|" $3 "||" $2 SEP $3 SEP $4 SEP $5 SEP $6, 1, 510)
  # 510 works around gtkdialog tree widget buffer overflow limit
  if(verbose > 3)
    print ($1 ?$1 :default_icon) "|" $3 "||", $2 "**", $3, "**", $4, "**", $5, "**", $6 > "/dev/stderr"
}

function get_default_icon(   a, na, iconref) # {{{2
{
  # Note: The file path of an icon passed with option -I must be
  # included in freedesktop.org's icon search path for the icon to be
  # actually displayed. More info [ANCHOR_ICON_PATH].
  if("I" in opt) return opt["I"]

  # The icon reference of a plugin source $ICON is guaranteed to be visible.
  # This code assumes a proper plugin invocation environment.
  if(iconref = ENVIRON["ICON"]) {
    na = split(iconref, a, /\//)
    na = split(ENVIRON["FNRTMP"]"/icons/"a[na], a, /\//)
    iconref = a[na]
    if(match(iconref, /.+\./)) # trim file extension
      iconref = substr(iconref, 1, RLENGTH - 1)
    return iconref
  }

  return ""
}

# getopt.awk --- Do C library getopt(3) function in awk {{{1
#
# Arnold Robbins, arnold@skeeve.com, Public Domain
#
# Initial version: March, 1991
# Revised: May, 1993

# External variables:
#    Optind -- index in ARGV of first nonoption argument
#    Optarg -- string value of argument to current option
#    Opterr -- if nonzero, print our own diagnostic
#    Optopt -- current option letter

# Returns:
#    -1     at end of options
#    "?"    for unrecognized option
#    <c>    a character representing the current option

# Private Data:
#    _opti  -- index in multiflag option, e.g., -abc
function getopt(argc, argv, options,    thisopt, i)
{
    if (length(options) == 0)    # no options given
        return -1

    if (argv[Optind] == "--") {  # all done
        Optind++
        _opti = 0
        return -1
    } else if (argv[Optind] !~ /^-[^:[:space:]]/) {
        _opti = 0
        return -1
    }
    if (_opti == 0)
        _opti = 2
    thisopt = substr(argv[Optind], _opti, 1)
    Optopt = thisopt
    i = index(options, thisopt)
    if (i == 0) {
        if (Opterr)
            printf("%c -- invalid option\n", thisopt) > "/dev/stderr"
        if (_opti >= length(argv[Optind])) {
            Optind++
            _opti = 0
        } else
            _opti++
        return "?"
    }
    if (substr(options, i + 1, 1) == ":") {
        # get option argument
        if (length(substr(argv[Optind], _opti + 1)) > 0)
            Optarg = substr(argv[Optind], _opti + 1)
        else
            Optarg = argv[++Optind]
        _opti = 0
    } else
        Optarg = ""
    if (_opti == 0 || _opti >= length(argv[Optind])) {
        Optind++
        _opti = 0
    } else
        _opti++
    return thisopt
}

