#! /usr/bin/gawk -f
# Last edited on 2000-02-02 02:27:56 by stolfi

BEGIN {
  usage = ( ARGV[0] "\\\n" \
    "  [-v columns=COLUMNS | -v pagewidth=PAGEWIDTH] \\\n" \
    "  [-v colwidth=COLWIDTH] \\\n" \
    "  [-v lines=LINES] \\\n" \
    "  [-v sep='string'] \\\n" \
    "  [-v filler='string'] \\\n" \
    "  [-v left='string'] [-v right='string'] \\\n" \
    "  [-v head='string'] [-v foot='string'] \\\n" \
    "  < INFILE > OUTFILE" \
  );
  
  # Like "pr" (bleech!) but does the right thing. Prints INFILE in
  # COLUMNS columns, LINES lines per page.

  # The columns in the last page are always balanced. If LINES is 0 or
  # omitted, it defaults to infinity, i.e. there will be a single page
  # with just enough lines to fit balanced columns.  Otherwise
  # all pages (even the last one) will have LINES lines of data.
  
  # The FILLER string is used to complete short columns, and,
  # if LINES > 0, to complete the last page. It defaults to empty.
  
  # If COLWIDTH is not specified, it defaults to 0. The actual width
  # of each column is the maximum between COLWIDTH, the length of
  # the the widest line in INFILE, and the length of the FILLER string.
  
  # If PAGEWIDTH and COLUMNS are not specified or zero, they default to
  # infinity. At least one of them must be specified. In any case,
  # COLUMNS will be reduced as needed so that the output lines will not
  # exceed the specified PAGEWIDTH. On the other hand, at least
  # one column will be printed, even if the PAGEWIDTH gets exceeded.
  
  abort = -1;
    
  if (columns == "")
    { columns = 0; }
  else if ((columns !~ /^[0-9]+$/) || (columns < 0))
    { arg_error("bad \"columns\""); }
    
  if (pagewidth == "")
    { pagewidth = 0; }
  else if ((pagewidth !~ /^[0-9]+$/) || (pagewidth < 0))
    { arg_error("bad \"pagewidth\""); }
    
  if ((pagewidth == 0) && (columns == 0)) 
    { arg_error("you must specify \"pagewidth\" or \"columns\""); }
  
  if (lines == "")
    { lines = 0; }
  else if ((lines !~ /^[0-9]+$/) || (lines < 0))
    { arg_error("bad \"lines\""); }
  
  if (colwidth == "")
    { colwidth = 0; }
  else if ( colwidth !~ /^[0-9]+$/ )
    { arg_error("bad \"colwidth\""); }
  
  nrecs = 0;
  maxwidth = length(filler);
  if (colwidth > maxwidth) { maxwidth = colwidth; }
  split("", rec);
  
}

# Gobble up lines

(abort >= 0) { exit abort; }

// {
  rec[nrecs] = $0;
  nrecs++;
  m = length($0); if (m > maxwidth) { maxwidth = m; maxline = $0; }
}

END {
  if (abort >= 0) { exit abort; }
  
  # printf "maxwidth = %d  maxline = [%s]\n", maxwidth, maxline;
  
  # Adjust number of columns to fit:
  if (columns == 0) { columns = nrecs; }
  cw = maxwidth + length(sep);  
  if ((cw > 0) && (pagewidth != 0))
    { avail = pagewidth - length(left) - length(right);
      maxcols = int((avail + length(sep)) / cw);
      if (maxcols < columns) { columns = maxcols; }
    }
  if (columns == 0) { columns = 1; }

  # Adjust page size and number of pages:
  if (lines > 0)
    { nrecspp = lines*columns;
      npages = int((nrecs + nrecspp - 1)/ nrecspp);
    }
  else
    { nrecspp = nrecs;
      lines = int((nrecspp + columns - 1) / columns);
      npages = 1;
    }
    
  # Print it:
  
  for (ip = 0; ip < npages; ip++)
    { 
      if (head != "") { printf "%s\n", head; }
      # Compute range and number of records in this page:
      ini = ip*nrecspp;
      fin = ini + nrecspp;
      if (fin > nrecs) { fin = nrecs + 1;}
      nr = (fin - ini);
      # Compute number of records per column:
      nrpc = int((nr + columns - 1) / columns);
      for (jl = 0; jl < lines; jl++)
        { printf "%s", left;
          for (kc = 0; kc < columns; kc++)
            { if (kc > 0) { printf "%s", sep; }
              if (jl < nrpc)
                { ir = ip*nrecspp + kc*nrpc + jl;
                  r = ( ir < nrecs ? rec[ir] : filler );
                }
              else
                { r = filler; }
              printf "%-*s", maxwidth, r;
            }
          printf "%s\n", right;
        }
      if (foot != "") { printf "%s\n", foot; }
    }
}

function arg_error(msg)
  {
    printf "*** %s\n", msg > "/dev/stderr"; 
    abort = 1;
    exit abort;
  }