4.3BSD/usr/contrib/icon/src/cmd/fset.icn

#	FSET(1)
#
#	Perform set operations on file specifications
#
#	Thomas R. Hicks
#
#	Last modified 8/14/84
#

procedure main(args)
   local i, fyls, arglist
   if not find("sets",&options) then
      stop("Set extensions must be enabled to run this program.")
   if *args = 0 then return
   if *args > 1 then
      every i := 2 to *args do
         args[1] ||:= (" " || args[i])
   (arglist := parse(args[1])) |
      stop("Invalid file specification expression")
   case type(arglist) of {
      "string"	: fyls := mkfset(arglist)
      "list"	: fyls := exec(arglist)
      default	: stop("Main: bad type -can't happen")
      }
   fyls := sort(fyls)
   every write(!fyls," ")
end

procedure Exp()			# file spec expression parser
   local a
   suspend (a := [Factor(),=Op(),Factor()] & [a[2],a[1],a[3]]) |
      Factor() |
      (a := [="(",Exp(),=")"] & .a[2])
end

procedure Factor()		# file spec expression parser
   local a
   suspend (a := [Term(),=Op(),Term()] & [a[2],a[1],a[3]]) |
      Term() |
      (a := [="(",Factor(),=")"] & .a[2])
end

procedure Name()		# file spec name matcher
   static valid
   initial valid := ~'()'
   suspend (any(~valid) || fail) | tab(find(Op()) | many(valid))
end

procedure Non()			# file spec expression parser
   local a
   suspend a := [Name(),=Op(),Name()] & [a[2],a[1],a[3]]
end

procedure Op()			# file spec operation matcher
   suspend !["++","--","&&"]
end

procedure Term()		# file spec expression parser
   local a
   suspend (a := [="(",Non(),=")"] & .a[2]) |
           Name()
end

procedure bldfset(arg)		# build file set, excluding . and ..
   local line
   static dotfiles
   initial dotfiles := set([".",".."])
   line := read(open("echo " || arg,"rp"))
   return str2set(line,' ') -- dotfiles
end

procedure exec(lst)		# process file spec list recursively
   return setops(lst[1])(exec2(lst[2]),exec2(lst[3]))
end

procedure exec2(arg)		# helping procedure for exec
   case type(arg) of {
      "string"	: return mkfset(arg)
      "list"	: return exec(arg)
      default	: stop("exec2: can't happen")
      }
end

procedure mkfset(fspec)		# make file list from specification
   if fspec == "*" then
      fspec := "* .*"
   return bldfset(fspec)
end

procedure parse(str)		# top level of parsing procedures
   local res
   str ? (res := Exp() & pos(0)) | fail
   return res
end

procedure sdiff(f1,f2)		# set difference
   return f1 -- f2
end

procedure setops(op)		# return correct set operaton
   case op of {
      "++"	: return sunion
      "&&"	: return sinter
      "--"	: return sdiff
      }
end

procedure sinter(f1,f2)		# set intersection
   return f1 ** f2
end

procedure str2set(str,delim)	# convert delimited string into a set
   local fset, f
   fset := set([])
   str ? {
      while f := (tab(upto(delim))) do {
         insert(fset,f)
         move(1)
         }
      if "" ~== (f := tab(0)) then
         insert(fset,f)
      }
   return fset
end

procedure sunion(f1,f2)		# set union
   return f1 ++ f2
end