(* ProgramArgs.mod implement the ISO ProgramArgs specification. Copyright (C) 2008-2023 Free Software Foundation, Inc. Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>. This file is part of GNU Modula-2. GNU Modula-2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Modula-2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Under Section 7 of GPL version 3, you are granted additional permissions described in the GCC Runtime Library Exception, version 3.1, as published by the Free Software Foundation. You should have received a copy of the GNU General Public License and a copy of the GCC Runtime Library Exception along with this program; see the files COPYING3 and COPYING.RUNTIME respectively. If not, see <http://www.gnu.org/licenses/>. *) IMPLEMENTATION MODULE ProgramArgs ; FROM RTgen IMPORT ChanDev, InitChanDev, DeviceType, doLook, doSkip, doSkipLook, doReadText, doReadLocs ; FROM SYSTEM IMPORT ADDRESS, ADR ; FROM UnixArgs IMPORT GetArgC, GetArgV ; FROM RTgenif IMPORT GenDevIF, InitGenDevIF ; FROM RTdata IMPORT ModuleId, MakeModuleId, InitData, GetData ; FROM IOLink IMPORT DeviceId, DeviceTablePtr, DeviceTablePtrValue, AllocateDeviceId, MakeChan, RAISEdevException ; FROM IOChan IMPORT ChanExceptions ; FROM IOConsts IMPORT ReadResults ; FROM ChanConsts IMPORT read, text ; FROM Storage IMPORT ALLOCATE, DEALLOCATE ; FROM ASCII IMPORT nul, lf ; TYPE PtrToChar = POINTER TO CHAR ; ArgInfo = POINTER TO RECORD currentPtr: PtrToChar ; currentPos: CARDINAL ; currentArg: CARDINAL ; argLength : CARDINAL ; argc : CARDINAL ; END ; VAR mid : ModuleId ; did : DeviceId ; cid : ChanId ; ArgData : PtrToChar ; ArgLength: CARDINAL ; gen : GenDevIF ; dev : ChanDev ; PROCEDURE look (d: DeviceTablePtr; VAR ch: CHAR; VAR r: ReadResults) ; BEGIN doLook(dev, d, ch, r) END look ; PROCEDURE skip (d: DeviceTablePtr) ; BEGIN doSkip(dev, d) END skip ; PROCEDURE skiplook (d: DeviceTablePtr; VAR ch: CHAR; VAR r: ReadResults) ; BEGIN doSkipLook(dev, d, ch, r) END skiplook ; PROCEDURE textread (d: DeviceTablePtr; to: ADDRESS; maxChars: CARDINAL; VAR charsRead: CARDINAL) ; BEGIN doReadText(dev, d, to, maxChars, charsRead) END textread ; PROCEDURE rawread (d: DeviceTablePtr; to: ADDRESS; maxLocs: CARDINAL; VAR locsRead: CARDINAL) ; BEGIN doReadLocs(dev, d, to, maxLocs, locsRead) END rawread ; PROCEDURE getname (d: DeviceTablePtr; VAR a: ARRAY OF CHAR) ; BEGIN d^.doGetName(d, a) END getname ; PROCEDURE flush (d: DeviceTablePtr) ; BEGIN END flush ; PROCEDURE handlefree (d: DeviceTablePtr) ; BEGIN END handlefree ; PROCEDURE reset (d: DeviceTablePtr) ; VAR a : ArgInfo ; BEGIN a := GetData(d, mid) ; WITH a^ DO currentPtr := ArgData ; currentPos := 0 ; currentArg := 0 ; argLength := strlen(currentPtr)+1 ; argc := GetArgC () END END reset ; (* doreadchar - returns a CHAR from the file associated with, g. *) PROCEDURE doreadchar (g: GenDevIF; d: DeviceTablePtr) : CHAR ; VAR a : ArgInfo ; ch: CHAR ; BEGIN d := DeviceTablePtrValue(cid, did) ; a := GetData(d, mid) ; WITH a^ DO IF currentPos<argLength THEN ch := currentPtr^ ; INC(currentPtr) ; INC(currentPos) ; d^.result := allRight ; RETURN( ch ) ELSE d^.result := endOfInput ; RETURN( nul ) END END END doreadchar ; (* dounreadchar - pushes a CHAR back onto the file associated with, g. *) PROCEDURE dounreadchar (g: GenDevIF; d: DeviceTablePtr; ch: CHAR) : CHAR ; VAR a: ArgInfo ; BEGIN d := DeviceTablePtrValue(cid, did) ; a := GetData(d, mid) ; WITH a^ DO IF currentPos>0 THEN DEC(currentPtr) ; DEC(currentPos) END END ; RETURN( ch ) END dounreadchar ; (* dogeterrno - returns the errno relating to the generic device. *) PROCEDURE dogeterrno (g: GenDevIF; d: DeviceTablePtr) : INTEGER ; BEGIN RETURN 0 END dogeterrno ; (* dorbytes - reads upto, max, bytes setting, actual, and returning FALSE if an error (not due to eof) occurred. *) PROCEDURE dorbytes (g: GenDevIF; d: DeviceTablePtr; to: ADDRESS; max: CARDINAL; VAR actual: CARDINAL) : BOOLEAN ; VAR p: PtrToChar ; i: CARDINAL ; BEGIN WITH d^ DO p := to ; i := 0 ; WHILE (i<max) AND ((result=notKnown) OR (result=allRight) OR (result=endOfLine)) DO p^ := doreadchar(g, d) ; INC(i) ; INC(p) END ; RETURN( TRUE ) END END dorbytes ; (* dowbytes - *) PROCEDURE dowbytes (g: GenDevIF; d: DeviceTablePtr; from: ADDRESS; nBytes: CARDINAL; VAR actual: CARDINAL) : BOOLEAN ; BEGIN RAISEdevException(cid, did, notAvailable, 'ProgramArgs.dowbytes: not allowed to write to this channel') ; RETURN( FALSE ) END dowbytes ; (* dowriteln - attempt to write an end of line marker to the file and returns TRUE if successful. *) PROCEDURE dowriteln (g: GenDevIF; d: DeviceTablePtr) : BOOLEAN ; BEGIN RAISEdevException(cid, did, notAvailable, 'ProgramArgs.dowbytes: not allowed to write to this channel') ; RETURN( FALSE ) END dowriteln ; (* iseof - returns TRUE if end of file is seen. *) PROCEDURE iseof (g: GenDevIF; d: DeviceTablePtr) : BOOLEAN ; VAR a: ArgInfo ; BEGIN d := DeviceTablePtrValue(cid, did) ; a := GetData(d, mid) ; WITH a^ DO RETURN( currentPos=ArgLength ) END END iseof ; (* iseoln - returns TRUE if end of line is seen. *) PROCEDURE iseoln (g: GenDevIF; d: DeviceTablePtr) : BOOLEAN ; VAR ch: CHAR ; BEGIN IF iseof(g, d) THEN RETURN( FALSE ) ELSE ch := doreadchar(g, d) ; IF ch#dounreadchar(g, d, ch) THEN RAISEdevException(cid, did, hardDeviceError, 'ProgramArgs.iseoln: internal inconsistancy error') END ; RETURN( ch=lf ) END END iseoln ; (* iserror - returns TRUE if an error was seen on the device. *) PROCEDURE iserror (g: GenDevIF; d: DeviceTablePtr) : BOOLEAN ; BEGIN RETURN( FALSE ) END iserror ; (* strlen - returns the number characters in string at this point. *) PROCEDURE strlen (p: PtrToChar) : CARDINAL ; VAR n: CARDINAL ; BEGIN n := 0 ; WHILE p^#nul DO INC(n) ; INC(p) END ; RETURN( n ) END strlen ; (* ArgChan - returns a value that identifies a channel for reading program arguments. *) PROCEDURE ArgChan () : ChanId ; BEGIN RETURN( cid ) END ArgChan ; (* IsArgPresent - tests if there is a current argument to read from. If not, read <= IOChan.CurrentFlags() will be FALSE, and attempting to read from the argument channel will raise the exception notAvailable. *) PROCEDURE IsArgPresent () : BOOLEAN ; VAR d: DeviceTablePtr ; a: ArgInfo ; BEGIN d := DeviceTablePtrValue(cid, did) ; a := GetData(d, mid) ; WITH a^ DO RETURN( currentArg<argc ) END END IsArgPresent ; (* NextArg - if there is another argument, causes subsequent input from the argument device to come from the start of the next argument. Otherwise there is no argument to read from, and a call of IsArgPresent will return FALSE. *) PROCEDURE NextArg ; VAR d: DeviceTablePtr ; a: ArgInfo ; p: PtrToChar ; BEGIN d := DeviceTablePtrValue(cid, did) ; a := GetData(d, mid) ; WITH a^ DO IF currentArg<argc THEN INC(currentArg) ; WHILE (currentPos<argLength) AND (currentPtr^#nul) DO INC(currentPos) ; INC(currentPtr) END ; INC(currentPtr) ; (* move over nul onto first char of next arg *) argLength := strlen(currentPtr)+1 ; currentPos := 0 END END END NextArg ; (* collectArgs - *) PROCEDURE collectArgs ; VAR i : INTEGER ; n : CARDINAL ; pp : POINTER TO PtrToChar ; p, q: PtrToChar ; BEGIN (* count the number of bytes necessary to remember all arg data *) n := 0 ; i := 0 ; pp := GetArgV () ; WHILE i < GetArgC () DO p := pp^ ; WHILE p^#nul DO INC(p) ; INC(n) END ; INC(n) ; INC(pp, SIZE(ADDRESS)) ; INC(i) END ; ArgLength := n ; (* now allocate correct amount of memory and copy the data *) ALLOCATE(ArgData, ArgLength) ; i := 0 ; pp := GetArgV () ; q := ArgData ; WHILE i < GetArgC () DO p := pp^ ; WHILE p^#nul DO q^ := p^ ; INC(q) ; INC(p) END ; q^ := p^ ; INC(q) ; INC(pp, SIZE(ADDRESS)) ; INC(i) END END collectArgs ; (* freeData - deallocates, a. *) PROCEDURE freeData (a: ArgInfo) ; BEGIN DISPOSE(a) END freeData ; (* Init - *) PROCEDURE Init ; VAR d: DeviceTablePtr ; a: ArgInfo ; BEGIN MakeModuleId(mid) ; AllocateDeviceId(did) ; MakeChan(did, cid) ; collectArgs ; NEW(a) ; WITH a^ DO currentPtr := ArgData ; currentPos := 0 ; currentArg := 0 ; argLength := strlen(currentPtr)+1 ; argc := GetArgC () END ; d := DeviceTablePtrValue(cid, did) ; InitData(d, mid, a, freeData) ; gen := InitGenDevIF(did, doreadchar, dounreadchar, dogeterrno, dorbytes, dowbytes, dowriteln, iseof, iseoln, iserror) ; dev := InitChanDev(programargs, did, gen) ; WITH d^ DO flags := read + text ; errNum := 0 ; doLook := look ; doSkip := skip ; doSkipLook := skiplook ; doTextRead := textread ; doRawRead := rawread ; doGetName := getname ; doReset := reset ; doFlush := flush ; doFree := handlefree END END Init ; BEGIN Init END ProgramArgs.