blob: 2f3a5bcfec90e23ae7b9241cd97ba004fad9f042 [file] [log] [blame]
(* ProgramArgs.mod implement the ISO ProgramArgs specification.
Copyright (C) 2008-2025 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.