############################################################################
#
#	File:     patch.icn
#
#	Subject:  Procedures for UNIX-like patch(1)
#
#	Author:   Rich Morin
#
#	Date:     June 18, 1990
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#  This procedure produces a sequence of edited items, reading a source
#  stream (from) and a stream of difference records (diffs), as generated
#  by dif.icn.
#
#  An optional parameter (rev) causes the edits to be made in reverse.
#  This allows an old stream to be regenerated from a new stream and an
#  appropriate stream of difference records.
#
#  The original patch(1) utility was written by Larry Wall, and is used
#  widely in the UNIX community.  See also diffu.icn and patchu.icn, the
#  utility program versions of dif.icn and patch.icn.
#
#  Usage:	patch(old, diff)	# patch old to new via diff
#  		patch(new, diff, rev)	# patch new to old via diff
#
############################################################################
#
#  Requires: co-expressions
#
############################################################################

procedure patch(from, diff, rev)
  local c_diff, c_from, cnte, cnti, i, item, ldr, o

  initial {
    i := 1
    o := 2
    if \rev then
      i :=: o
      
    c_diff := create !diff
    c_from := create !from

    cnti := item := 0
    ldr  := @c_diff
    cnte := ldr[i].pos
  }

  repeat {

    while /ldr | cnti < cnte-1 do {		# copy old items
      cnti +:= 1
      if item := @c_from then
        suspend item
      else {
        item := &null
        break
      }
    }

    if \ldr then {				# still have edits
      every 1 to *ldr[i].diffs do {		# discard items
        cnti +:= 1
        @c_from | zot_patch("unexpected end of stream")
      }

      if *ldr[o].diffs > 0 then			# copy new items
        suspend !ldr[o].diffs

      if ldr := @c_diff then			# get next edit
        cnte := ldr[i].pos
      else
        ldr := &null
    }

    if /item & /ldr then
      fail
  }

end


procedure zot_patch(msg)			# exit w/ message
  write(&errout, "patch: ", msg)
  exit(1)
end
