#!/usr/bin/mawk -We
# *********************************************************************
# tabletolist: convert from NoSQL 'table' to 'list' format.
# Copyright (c) 1998,2006 Carlo Strozzi
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 dated June, 1991.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# *********************************************************************

BEGIN {
  _NULL = ""; FS = OFS = sep = "\t"; unescape = 1; cont = "\n\t"

  # Get local settings.
  nosql_install = ENVIRON["NOSQL_INSTALL"]
  stdout = ENVIRON["NOSQL_STDOUT"]
  stderr = ENVIRON["NOSQL_STDERR"]

  # Set default values if necessary.
  if (nosql_install == _NULL) nosql_install = "/usr/local/nosql"
  if (stdout == _NULL) stdout = "/dev/stdout"
  if (stderr == _NULL) stderr = "/dev/stderr"

  while (ARGV[++i] != _NULL) {
     if (ARGV[i] == "-p" || ARGV[i] == "--prefix") pfx = ARGV[++i]
     else if (ARGV[i] == "-n" || ARGV[i] == "--no-unescape") unescape = 0
     else if (ARGV[i] == "-C" || ARGV[i] == "--chop") no_hdr = 1
     else if (ARGV[i] == "-N" || ARGV[i] == "--no-header") no_hdr = 1
     else if (ARGV[i] == "-c" || ARGV[i] == "--chomp") no_foot = 1
     else if (ARGV[i] == "-F" || ARGV[i] == "--no-footer") no_foot = 1
     else if (ARGV[i] == "-e" || ARGV[i] == "--no-empty") no_empty = 1
     else if (ARGV[i] == "-j" || ARGV[i] == "--justify") {
	justify = 1
	sep = OFS
     }
     else if (ARGV[i] == "-t" || ARGV[i] == "--trim") trim = 1
     else if (ARGV[i] == "-u" || ARGV[i] == "--unique") no_dups = 1
     else if (ARGV[i] == "-s" || ARGV[i] == "--separator") {
	sep = ARGV[++i]
	justify = 0
     }
     else if (ARGV[i] == "-i" || ARGV[i] == "--input") i_file = ARGV[++i]
     else if (ARGV[i] == "-o" || ARGV[i] == "--output") o_file = ARGV[++i]
     else if (ARGV[i] == "-m" || ARGV[i] == "--mux") cont = "\n@"
     else if (ARGV[i] == "-l" || ARGV[i] == "--ldif") {

	# It looks like that for some slapd(8) setups to correctly
	# encode to Base64 those fields that contain newlines, the
	# safe bet is to prepend each newline with a non-printable
	# character.

	cont = "\001\n "
	sep = ": "
	justify = 0
	no_empty = no_hdr = no_foot = no_dups = 1
     }
     else if (ARGV[i] == "-h" || ARGV[i] == "--help") {
	system("grep -v '^#' " nosql_install "/help/tabletolist.txt")
	exit(rc=1)
     }
     else if (ARGV[i] == "--show-copying") {
	system("cat " nosql_install "/doc/COPYING")
	exit(rc=1)
     }
     else if (ARGV[i] == "--show-warranty") {
	system("cat " nosql_install "/doc/WARRANTY")
	exit(rc=1)
     }
     else argv[++j] = ARGV[i]
  }

  ARGC = 1					# Fix argv[]

  if (o_file == _NULL) o_file = stdout
  if (i_file != _NULL) { ARGV[1] = i_file; ARGC = 2 }

  # Move argv[] back onto ARGV[].

  while (++k <= j) ARGV[ARGC++] = argv[k]
}

FILENAME != old_filename {
   NR = 1
   old_filename = FILENAME
}

NR == 1 {
   gsub(/\001/, "")			# Remove SOH markers
   gsub(/ +/, "")			# trim blanks in names
   ncol = split($0, C)
   i = 0
   while (++i <= ncol)
	if (length(C[i]) > max_width) max_width = length(C[i])
   next
}

{ gsub(/ +\t/,"\t"); sub(/ +$/,_NULL) }	# trim trailing blanks.

{
  if (NR == 2) {
     if (!no_hdr) print _NULL > o_file
  }  else print _NULL > o_file

  delete seen

  for (i = 1; i <= ncol; i++) {

    if (no_dups && seen[C[i]] == C[i]) continue

    seen[C[i]] = C[i]

    # Unescape tabs and newlines first.

    if (unescape) $i = _unesctbl($i)

    # Add the proper trailing char tab to newlines,
    # unless '-j' was requested.

    if (!justify) gsub(/\n/, cont, $i)
    else {
       pad = " "
       for (j=1; j<=max_width; j++) pad = pad " "
       gsub(/\n/, "\n" pad, $i)
    }

    if (trim) {
       sub(/^ +/, _NULL, C[i]); sub(/ +$/, _NULL, C[i])
       sub(/^ +/, _NULL, $i); sub(/ +$/, _NULL, $i)
    }
    if (no_empty && $i == _NULL) continue
    if (justify) {
       printf("%s%s", pfx, C[i]) > o_file
       for (j=length(C[i]); j<max_width; j++) printf(" ") > o_file
       printf(" %s\n", $i) > o_file
    }
    else printf("%s%s%s%s\n", pfx, C[i], sep, $i) > o_file
  }
}

END {
  if (rc) exit(rc)
  # Handle empty input table.
  if (NR == 1) {
    if (!no_hdr) print _NULL > o_file
    if (!no_empty) {
       for (i = 1; i <= ncol; i++) {
	  printf("%s%s%s\n", pfx, C[i], sep) > o_file
       }
    }
  }
  if (!no_foot) print _NULL > o_file
}

# =====================================================================
# string _unesctbl(string s)
#
# Un-escape NoSQL special sequences \t, \n and \\
# Note: global variables, like RSTART and RLENGTH cannot be localised
# with their original names, or things won't work.
# =====================================================================

function _unesctbl(s,		S,rstart,rlength) {

   while (match(s,/\\+t/)) {

      rstart=RSTART
      rlength=RLENGTH

      if (!(rlength % 2))
	 S = S substr(s,1,rstart+rlength-3) "\t"
      else
	 S = S substr(s,1,rstart+rlength-1)

      s = substr(s,rstart+rlength)
   }

   s = S s
   S = ""

   while (match(s,/\\+n/)) {

      rstart=RSTART
      rlength=RLENGTH

      if (!(rlength % 2))
	 S = S substr(s,1,rstart+rlength-3) "\n"
      else
	 S = S substr(s,1,rstart+rlength-1)

      s = substr(s,rstart+rlength)
   }

   s = S s
   S = ""

   while (match(s,/[\\][\\]/)) {

      rstart=RSTART; rlength=RLENGTH

      S = S substr(s,1,rstart)
      s = substr(s,rstart+2)
   }

   return S s
}

# EOF
