| (* 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. |