summaryrefslogtreecommitdiff
path: root/ipl/progs/fset.icn
blob: 8f0f37ea2313485e581cc9f99e65ce92bdfe8a2a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
############################################################################
#
#	File:     fset.icn
#
#	Subject:  Program to do set operations on file specifications
#
#	Author:   Thomas R. Hicks
#
#	Date:     June 10, 1988
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#   The UNIX shell provides for the specification of filenames
#  using ``wildcards''.  Each wildcard specification may be
#  thought of as defining a set of names (that is, those that
#  match the specification).  Fset allows the user to apply the
#  set operations of intersection, union, and difference to
#  these filename sets. The resultant list may then be used as
#  an argument to other shell commands.
#
#  Fset's argument is an expression composed of legal UNIX file
#  specifications, parenthesis, and the following set opera-
#  tors:
#
#	&&   intersection
#	++   union
#	--   difference
#
#  Because characters that have special meaning to the shell
#  occur frequently in the arguments used for fset, it is
#  advisable to quote the arguments consistently.
#
#  The use of fset is illustrated by the following examples:
#
#	fset 'g*--*.icn'
#
#  produces the list (set) of filenames for files beginning
#  with g, excluding those ending with .icn.
#
#  Similarly,
#
#	fset '*'
#
#  produces all files in the current directory excluding the .
#  and .. files.
#
#	fset '((*--*.icn)++c*)'
#  and
#
#	fset '(*--*.icn)++c*'
#
#  produces the complement of all filenames ending with .icn in
#  addition to all filenames beginning with c.
#
#	fset '(((c? && c*)))'
#
#  is a redundant, but legal, specification for all two-
#  character filenames that begin with c, while
#
#	fset '.*'
#
#  produces the set of filenames for all hidden files, exclud-
#  ing the . and ..  files.
#
#  Limitations:
#
#  Multiple command line arguments, formed by omitting the
#  quotes around the file set expression, are permitted.  Their
#  use is limited, however, since parentheses do not get past
#  the shell's command-line expansion.
#
#  Almost any legal file specification will work when enclosed
#  in quotes except that the simple grammar that is used cannot
#  handle blanks adjacent to parentheses.
#
#  File names that begin or end in ``questionable'' characters
#  such as *, ?, +, -, and &, probably will not work.
#
#  A file specification that, when interpreted by the shell,
#  produces no matching filename will be placed (unchanged) in
#  the result.
#
############################################################################
#
#  See also:  gcomp.icn
#
############################################################################
#
#  Requires:  UNIX
#
############################################################################

procedure main(args)
   local i, fyls, arglist
   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