############################################################################
#
#	File:     delam.icn
#
#	Subject:  Program to delaminate file
#
#	Author:   Thomas R. Hicks
#
#	Date:     June 10, 1988
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#  
#     This program delaminates standard input into several output
#  files according to the specified fields.  It writes the fields in
#  each line to the corresponding output files as individual lines.
#  If no data occurs in the specified position for a given input
#  line an empty output line is written. This insures that all out-
#  put files contain the same number of lines as the input file.
#  
#     If - is used for the input file, the standard input is read.
#  If - is used as an output file name, the corresponding field is
#  written to the standard output.
#  
#     The fields are defined by a list of field specifications,
#  separated by commas or colons, of the following form:
#  
#          n    the character in column n
#          n-m  the characters in columns n through m
#          n+m  m characters beginning at column n
#  
#  where the columns in a line are numbered from 1 to the length of
#  the line.
#  
#     The use of delam is illustrated by the following examples.
#  The command
#  
#          delam 1-10,5 x.txt y.txt
#  
#  reads standard input and writes characters 1 through 10 to file
#  x.txt and character 5 to file y.txt.  The command
#  
#          delam 10+5:1-10:1-10:80 mid x1 x2 end
#  
#  writes characters 10 through 14 to mid, 1 through 10 to x1 and
#  x2, and character 80 to end.  The command
#  
#          delam 1-80,1-80 - -
#  
#  copies standard input to standard output, replicating the first
#  eighty columns of each line twice.
#  
############################################################################
#
#  Links: usage
#
############################################################################

link usage

procedure main(a)
   local fylist, ranges
   if any(&digits,a[1]) then
      ranges := fldecode(a[1])
   else
      {
      write(&errout,"Bad argument to delam: ",a[1])
      Usage("delam fieldlist {outputfile | -} ...")
      }
   if not a[2] then
      Usage("delam fieldlist {outputfile | -} ...")
   fylist := doutfyls(a,2)
   if *fylist ~= *ranges then
      stop("Unequal number of field args and output files")
   delamr(ranges,fylist)
end

# delamr - do actual division of input file
#
procedure delamr(ranges,fylist)
   local i, j, k, line
   while line := read() do
      {
      i := 1
      while i <= *fylist do
         {
         j := ranges[i][1]
         k := ranges[i][2]
         if k > 0 then
            write(fylist[i][2],line[j+:k] | line[j:0] | "")
         i +:= 1
         }
      }
end

# doutfyls - process the output file arguments; return list
#
procedure doutfyls(a,i)
   local lst, x
   lst := []
   while \a[i] do
      {
      if x := llu(a[i],lst) then		# already in list
         lst |||:= [[a[i],lst[x][2]]]
      else					# not in list
         if a[i] == "-" then			# standard out
            lst |||:= [[a[i],&output]]
         else					# new file
            if not (x := open(a[i],"w")) then
               stop("Cannot open ",a[i]," for output")
            else
               lst |||:= [[a[i],x]]
      i +:= 1
      }
   return lst

end

# fldecode - decode the fieldlist argument
#
procedure fldecode(fldlst)
   local fld, flst, poslst, m, n, x
   poslst := []
   flst := str2lst(fldlst,':,')
   every fld := !flst do
      {
      if x := upto('-+',fld) then
         {
         if not (m := integer(fld[1:x])) then
            stop("bad argument in field list; ",fld)
         if not (n := integer(fld[x+1:0])) then
            stop("bad argument in field list; ",fld)
         if upto('-',fld) then
            {
            if n < m then
               n := 0
            else
               n := (n - m) + 1
            }
         }
      else {
         if not (m := integer(fld)) then
            stop("bad argument in field list; ",fld)
         n := 1
         }
      poslst |||:= [[m,n]]
      }
   return poslst
end

# llu - lookup file name in output file list
#
procedure llu(str,lst)
   local i
   i := 1
   while \lst[i] do
      {
      if \lst[i][1] == str then
         return i
      i +:= 1
      }
end

# str2lst - create a list from a delimited string
#
procedure str2lst(str,delim)
   local lst, f
   lst := []
   str ? {
      while f := (tab(upto(delim))) do
      {
      lst |||:= [f]
      move(1)
      }
        if "" ~== (f := tab(0)) then
        lst |||:= [f]
        }
   return lst
end
