blob: b5a8eb72b8ce66f943ddf3909a12e5eea13db65f [file] [log] [blame]
(* varargs.mod provides a basic vararg facility for GNU Modula-2.
Copyright (C) 2015-2023 Free Software Foundation, Inc.
Contributed by Gaius Mulley <gaius@glam.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.
You should have received a copy of the GNU General Public License
along with GNU Modula-2; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. *)
IMPLEMENTATION MODULE varargs ;
FROM Storage IMPORT ALLOCATE, DEALLOCATE ;
FROM libc IMPORT memcpy ;
FROM SYSTEM IMPORT ADDRESS, TSIZE, ADR, BYTE ;
CONST
MaxArg = 4 ;
TYPE
vararg = POINTER TO RECORD
nArgs : CARDINAL ;
i : CARDINAL ;
contents: ADDRESS ;
size : CARDINAL ;
arg : ARRAY [0..MaxArg] OF argDesc ;
END ;
argDesc = RECORD
ptr: ADDRESS ;
len: CARDINAL ;
END ;
ptrToByte = POINTER TO BYTE ;
(*
arg - fills in, a, with the next argument. The size of, a, must be an exact
match with the original vararg parameter.
*)
PROCEDURE arg (v: vararg; VAR a: ARRAY OF BYTE) ;
VAR
p: POINTER TO BYTE ;
j: CARDINAL ;
BEGIN
WITH v^ DO
IF i=nArgs
THEN
HALT (* too many calls to arg. *)
ELSE
IF HIGH(a)+1=arg[i].len
THEN
p := arg[i].ptr ;
j := 0 ;
WHILE j<=HIGH (a) DO
a[j] := p^ ;
INC (p) ;
INC (j)
END
ELSE
HALT (* parameter mismatch. *)
END ;
INC (i)
END
END
END arg ;
(*
nargs - returns the number of arguments wrapped in, v.
*)
PROCEDURE nargs (v: vararg) : CARDINAL ;
BEGIN
RETURN v^.nArgs
END nargs ;
(*
copy - returns a copy of, v.
*)
PROCEDURE copy (v: vararg) : vararg ;
VAR
c : vararg ;
j,
offset: CARDINAL ;
BEGIN
NEW (c) ;
WITH c^ DO
i := v^.i ;
nArgs := v^.nArgs ;
size := v^.size ;
ALLOCATE (contents, size) ;
contents := memcpy (contents, v^.contents, size) ;
FOR j := 0 TO nArgs DO
offset := VAL (CARDINAL, VAL (ptrToByte, v^.contents) - VAL (ptrToByte, v^.arg[j].ptr)) ;
arg[j].ptr := VAL (ptrToByte, VAL (ptrToByte, contents) + offset) ;
arg[j].len := v^.arg[j].len ;
END
END ;
RETURN c
END copy ;
(*
replace - fills the next argument with, a. The size of, a,
must be an exact match with the original vararg
parameter.
*)
PROCEDURE replace (v: vararg; VAR a: ARRAY OF BYTE) ;
VAR
p: POINTER TO BYTE ;
j: CARDINAL ;
BEGIN
WITH v^ DO
IF i=nArgs
THEN
HALT (* too many calls to arg. *)
ELSE
IF HIGH(a)+1=arg[i].len
THEN
p := arg[i].ptr ;
j := 0 ;
WHILE j<=HIGH (a) DO
p^ := a[j] ;
INC (p) ;
INC (j)
END
ELSE
HALT (* parameter mismatch. *)
END
END
END
END replace ;
(*
next - assigns the next arg to be collected as, i.
*)
PROCEDURE next (v: vararg; i: CARDINAL) ;
BEGIN
v^.i := i
END next ;
(*
end - destructor for vararg, v.
*)
PROCEDURE end (VAR v: vararg) ;
BEGIN
IF v#NIL
THEN
DEALLOCATE (v^.contents, TSIZE (vararg)) ;
DISPOSE (v)
END
END end ;
(*
start1 - wraps up argument, a, into a vararg.
*)
PROCEDURE start1 (a: ARRAY OF BYTE) : vararg ;
VAR
v: vararg ;
BEGIN
NEW (v) ;
WITH v^ DO
i := 0 ;
nArgs := 1 ;
size := HIGH (a) + 1;
ALLOCATE (contents, size) ;
contents := memcpy (contents, ADR (a), size) ;
arg[0].ptr := contents ;
arg[0].len := size
END ;
RETURN v
END start1 ;
(*
start2 - wraps up arguments, a, b, into a vararg.
*)
PROCEDURE start2 (a, b: ARRAY OF BYTE) : vararg ;
VAR
v: vararg ;
p: POINTER TO BYTE ;
BEGIN
NEW (v) ;
WITH v^ DO
i := 0 ;
nArgs := 2 ;
size := HIGH (a) + HIGH (b) + 2 ;
ALLOCATE (contents, size) ;
p := memcpy (contents, ADR (a), HIGH (a) + 1) ;
arg[0].ptr := p ;
arg[0].len := HIGH (a) + 1 ;
INC (p, arg[0].len) ;
p := memcpy (p, ADR (b), HIGH (b) + 1) ;
arg[1].ptr := p ;
arg[1].len := HIGH (b) + 1
END ;
RETURN v
END start2 ;
(*
start3 - wraps up arguments, a, b, c, into a vararg.
*)
PROCEDURE start3 (a, b, c: ARRAY OF BYTE) : vararg ;
VAR
v: vararg ;
p: POINTER TO BYTE ;
BEGIN
NEW (v) ;
WITH v^ DO
i := 0 ;
nArgs := 3 ;
size := HIGH (a) + HIGH (b) + HIGH (c) + 3 ;
ALLOCATE (contents, size) ;
p := memcpy (contents, ADR (a), HIGH (a) + 1) ;
arg[0].ptr := p ;
arg[0].len := HIGH (a) + 1 ;
INC (p, arg[0].len) ;
p := memcpy (p, ADR (b), HIGH (b) + 1) ;
arg[1].ptr := p ;
arg[1].len := HIGH (b) + 1 ;
INC (p, arg[1].len) ;
p := memcpy (p, ADR (c), HIGH (c) + 1) ;
arg[2].ptr := p ;
arg[2].len := HIGH (c) + 1
END ;
RETURN v
END start3 ;
(*
start4 - wraps up arguments, a, b, c, d, into a vararg.
*)
PROCEDURE start4 (a, b, c, d: ARRAY OF BYTE) : vararg ;
VAR
v: vararg ;
p: POINTER TO BYTE ;
BEGIN
NEW (v) ;
WITH v^ DO
i := 0 ;
nArgs := 4 ;
size := HIGH (a) + HIGH (b) + HIGH (c) + HIGH (d) + 4 ;
ALLOCATE (contents, size) ;
p := memcpy (contents, ADR (a), HIGH (a) + 1) ;
arg[0].len := HIGH (a) + 1 ;
INC (p, arg[0].len) ;
p := memcpy (p, ADR (b), HIGH (b) + 1) ;
arg[1].ptr := p ;
arg[1].len := HIGH (b) + 1 ;
INC (p, arg[1].len) ;
p := memcpy (p, ADR (c), HIGH (c) + 1) ;
arg[2].ptr := p ;
arg[2].len := HIGH (c) + 1 ;
INC (p, arg[2].len) ;
p := memcpy (p, ADR (c), HIGH (c) + 1) ;
arg[3].ptr := p ;
arg[3].len := HIGH (c) + 1
END ;
RETURN v
END start4 ;
END varargs.