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