1(*	$Id: ProgramArgs.Mod,v 1.6 2002/11/17 13:57:45 mva Exp $	*)
2MODULE ProgramArgs;
3(*  Provides access to a program invokation's command line arguments.
4    Copyright (C) 1997-2000  Michael van Acken
5
6    This module is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public License
8    as published by the Free Software Foundation; either version 2 of
9    the License, or (at your option) any later version.
10
11    This module is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with OOC. If not, write to the Free Software Foundation,
18    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19*)
20
21(**This module provides access to the command line arguments passed to the
22   program's invocation.  They are mapped onto a standard channel, with each
23   argument transformed to a single line of text.  Newline characters embedded
24   in arguments are converted to spaces.  Interpreting the arguments is usually
25   done by applying @oproc{*TextRider.Reader} or @oproc{*TextRider.Scanner} to
26   the argument channel.
27
28   The number of arguments can be determined by calling @oproc{args.ArgNumber}.
29   If the invokation was something like @samp{foo bar 42}, where @samp{foo} is
30   the command's name, then the channel's contents would look like this:
31
32   @example
33   foo
34   bar
35   42
36   @end example
37
38   For the given example @oproc{args.ArgNumber} would return @samp{2}.  *)
39<* ConformantMode := FALSE *>  (* for NewReader/NewWriter *)
40
41IMPORT
42  SYSTEM, RT0, Ch := Channel, CharClass, Time, Msg;
43
44
45CONST
46  (* NOTE: refer to module Channel for the meaning of the various codes *)
47
48  (* the following values may appear in the `res' field of `Channel', `Reader',
49     or `Writer': *)
50  done* = Ch.done;
51
52  (* symbolic values for `Reader.res' resp. `Writer.res': *)
53  outOfRange* = Ch.outOfRange;
54  readAfterEnd* = Ch.readAfterEnd;
55  channelClosed* = Ch.channelClosed;
56
57  (* symbolic values for `Ch.res': *)
58  noWriteAccess* = Ch.noWriteAccess;
59  noModTime* = Ch.noModTime;
60
61TYPE
62  Channel* = POINTER TO ChannelDesc;
63  ChannelDesc = RECORD
64    (Ch.ChannelDesc)
65  END;
66  Reader = POINTER TO ReaderDesc;
67  ReaderDesc = RECORD
68    (Ch.ReaderDesc)
69    pos0, pos1: LONGINT
70  END;
71
72VAR
73  args-: Channel;
74
75
76TYPE
77  ErrorContext = POINTER TO ErrorContextDesc;
78  ErrorContextDesc* = RECORD
79    (Ch.ErrorContextDesc)
80  END;
81
82VAR
83  errorContext: ErrorContext;
84
85PROCEDURE GetError (code: Msg.Code): Msg.Msg;
86  BEGIN
87    RETURN Msg.New (errorContext, code)
88  END GetError;
89
90
91PROCEDURE (r: Reader) Pos*(): LONGINT;
92  VAR
93    i, j, count: LONGINT;
94  BEGIN
95    IF (r. pos0 = RT0.argc) THEN
96      (* some trickery to get Pos(SetPos(x))=x for x>=ch.Length *)
97      RETURN r. pos1
98    ELSE
99      i := 0; j := 0; count := 0;
100      WHILE (i # r. pos0) OR (j # r. pos1) DO
101        IF (RT0.argv[i][j] = 0X) THEN
102          INC (i); j := 0
103        ELSE
104          INC (j)
105        END;
106        INC (count)
107      END;
108      RETURN count
109    END
110  END Pos;
111
112PROCEDURE (r: Reader) Available*(): LONGINT;
113  VAR
114    i: LONGINT;
115  BEGIN
116    IF r. base. open THEN
117      i := r. base. Length() - r. Pos();
118      IF (i < 0) THEN
119        RETURN 0
120      ELSE
121        RETURN i
122      END
123    ELSE
124      RETURN -1
125    END
126  END Available;
127
128PROCEDURE (r: Reader) SetPos* (newPos: LONGINT);
129  VAR
130    i, j, count: LONGINT;
131  BEGIN
132    IF (r. res = done) THEN
133      IF (newPos < 0) THEN
134        r. res := GetError (outOfRange)
135      ELSIF r. base. open THEN
136        i := 0; j := 0; count := 0;
137        WHILE (i < RT0.argc) & (count # newPos) DO
138          IF (RT0.argv[i][j] = 0X) THEN
139            INC (i); j := 0
140          ELSE
141            INC (j)
142          END;
143          INC (count)
144        END;
145        r. pos0 := i;
146        IF (i = RT0.argc) THEN
147          (* some trickery to get Pos(SetPos(x))=x for x>=ch.Length *)
148          r. pos1 := newPos
149        ELSE
150          r. pos1 := j
151        END
152      ELSE  (* channel has been closed *)
153        r. res := GetError (channelClosed)
154      END
155    END
156  END SetPos;
157
158PROCEDURE (r: Reader) ReadByte* (VAR x: SYSTEM.BYTE);
159  BEGIN
160    IF (r. res = done) THEN
161      IF r. base. open THEN
162        IF (r. pos0 = RT0.argc) THEN
163          r. res := GetError (readAfterEnd)
164        ELSIF (RT0.argv[r. pos0][r. pos1] = 0X) THEN
165          x := CharClass.eol;
166          INC (r. pos0);
167          IF (r. pos0 = RT0.argc) THEN
168            (* some trickery to get Pos(SetPos(x))=x for x>=ch.Length *)
169            r. pos1 := r. base. Length()
170          ELSE
171            r. pos1 := 0
172          END
173        ELSIF (RT0.argv[r. pos0][r. pos1] = CharClass.eol) THEN
174          (* newline character is turned into space *)
175          x := " ";
176          INC (r. pos1)
177        ELSE
178          x := RT0.argv[r. pos0][r. pos1];
179          INC (r. pos1)
180        END
181      ELSE  (* channel has been closed *)
182        r. res := GetError (channelClosed);
183        r. bytesRead := 0
184      END
185    ELSE
186      r. bytesRead := 0
187    END
188  END ReadByte;
189
190PROCEDURE (r: Reader) ReadBytes* (VAR x: ARRAY OF SYSTEM.BYTE;
191                                  start, n: LONGINT);
192  VAR
193    i: LONGINT;
194  BEGIN
195    (* no need for fancy footwork here; simply delegate everything
196       to ReadByte *)
197    i := 0;
198    WHILE (i < n) & (r. res = done) DO
199      r. ReadByte (x[start+i]);
200      IF (r. res = done) THEN
201        INC (i)
202      END
203    END;
204    r. bytesRead := i
205  END ReadBytes;
206
207
208
209(* Channel methods
210   ------------------------------------------------------------------------ *)
211
212PROCEDURE (ch: Channel) Length*(): LONGINT;
213  VAR
214    i, j, len: LONGINT;
215  BEGIN
216    i := 0; len := 0;
217    WHILE (i < RT0.argc) DO
218      j := 0;
219      WHILE (RT0.argv[i][j] # 0X) DO
220        INC (j)
221      END;
222      INC (len, j+1);
223      INC (i)
224    END;
225    RETURN len
226  END Length;
227
228PROCEDURE (ch: Channel) ArgNumber* (): LONGINT;
229(**Returns the number of command line arguments (excluding the program name
230   itself) passed to the program.  *)
231  BEGIN
232    RETURN RT0.argc-1
233  END ArgNumber;
234
235PROCEDURE (ch: Channel) GetModTime* (VAR mtime: Time.TimeStamp);
236(**Since the argument channel has no modification time, this procedure will
237   always signal a `noModTime' error.  *)
238  BEGIN
239    ch. res := GetError (Ch.noModTime)
240  END GetModTime;
241
242PROCEDURE (ch: Channel) NewReader*(): Reader;
243  VAR
244    r: Reader;
245  BEGIN
246    IF ch. open THEN
247      NEW (r);
248      r. base := ch;
249      r. ClearError;
250      r. bytesRead := 1;
251      r. positionable := TRUE;
252      r. pos0 := 0;
253      r. pos1 := 0;
254      ch. ClearError
255    ELSE
256      r := NIL;
257      ch. res := GetError (channelClosed)
258    END;
259    RETURN r
260  END NewReader;
261
262PROCEDURE (ch: Channel) Flush*;
263  BEGIN
264    IF ~ch. open THEN
265      ch. res := GetError (channelClosed)
266    END
267  END Flush;
268
269PROCEDURE (ch: Channel) Close*;
270  BEGIN
271    ch. open := FALSE
272  END Close;
273
274BEGIN
275  NEW (errorContext);
276  Msg.InitContext (errorContext, "OOC:Core:ProgramArgs");
277
278  NEW (args);
279  args. ClearError;
280  args. readable := TRUE;
281  args. writable := FALSE;
282  args. open := TRUE
283END ProgramArgs.
284