From af6746cd0f34c0b43461d5ba653c6e402805fcec Mon Sep 17 00:00:00 2001 From: Norayr Chilingarian Date: Wed, 16 Oct 2013 19:22:58 +0400 Subject: [PATCH] ulmTypes.Mod and ulmStreams.Mod modules ported. --- makefile | 4 +- makefile.gnuc.armv6j | 4 +- makefile.gnuc.armv6j_hardfp | 4 +- makefile.gnuc.armv7a_hardfp | 4 +- makefile.gnuc.x86 | 4 +- makefile.gnuc.x86_64 | 188 +++ ocat | Bin 208976 -> 208976 bytes showdef | Bin 432776 -> 432776 bytes src/lib/ulm/armv6j/ulmTypes.Mod | 125 ++ src/lib/ulm/armv6j_hardfp/ulmTypes.Mod | 125 ++ src/lib/ulm/armv7a_hardfp/ulmTypes.Mod | 125 ++ src/lib/ulm/ulmStreams.Mod | 2149 ++++++++++++++++++++++++ src/lib/ulm/x86/ulmTypes.Mod | 125 ++ src/lib/ulm/x86_64/ulmTypes.Mod | 125 ++ voc | Bin 1325020 -> 1325020 bytes 15 files changed, 2972 insertions(+), 10 deletions(-) create mode 100644 makefile.gnuc.x86_64 create mode 100644 src/lib/ulm/armv6j/ulmTypes.Mod create mode 100644 src/lib/ulm/armv6j_hardfp/ulmTypes.Mod create mode 100644 src/lib/ulm/armv7a_hardfp/ulmTypes.Mod create mode 100644 src/lib/ulm/ulmStreams.Mod create mode 100644 src/lib/ulm/x86/ulmTypes.Mod create mode 100644 src/lib/ulm/x86_64/ulmTypes.Mod diff --git a/makefile b/makefile index eb60e77b..1382a97d 100644 --- a/makefile +++ b/makefile @@ -9,7 +9,7 @@ RELEASE = 1.0 INCLUDEPATH = -Isrc/lib/system/$(CCOMP)/$(TARCH) -SETPATH = CFLAGS=$(INCLUDEPATH) PATH=.:/bin:/usr/bin MODULES=.:src/lib:src/lib/v4:src/lib/system:src/lib/system/$(CCOMP):src/lib/system/$(CCOMP)/$(TARCH):src/lib/ulm:src/lib/ulm/gnuc:src/lib/ooc2:src/lib/ooc2/gnuc:src/lib/ooc:src/lib/ooc/lowlevel:src/voc:src/voc/gnuc:src/voc/gnuc/$(TARCH):src/tools/ocat:src/tools/browser:src/tools/vocparam:src/tools/coco:src/test +SETPATH = CFLAGS=$(INCLUDEPATH) PATH=.:/bin:/usr/bin MODULES=.:src/lib:src/lib/v4:src/lib/system:src/lib/system/$(CCOMP):src/lib/system/$(CCOMP)/$(TARCH):src/lib/ulm:src/lib/ulm/$(CCOMP):src/lib/ulm/$(TARCH):src/lib/ooc2:src/lib/ooc2/$(CCOMP):src/lib/ooc:src/lib/ooc/$(CCOMP):src/voc:src/voc/$(CCOMP):src/voc/$(CCOMP)/$(TARCH):src/tools/ocat:src/tools/browser:src/tools/vocparam:src/tools/coco:src/test VOC = voc VOCSTATIC = $(SETPATH) ./vocstatic @@ -137,7 +137,7 @@ stage6: $(VOCSTATIC) -sP ulmASCII.Mod ulmSets.Mod $(VOCSTATIC) -sP ulmObjects.Mod ulmDisciplines.Mod $(VOCSTATIC) -sP ulmPriorities.Mod ulmServices.Mod ulmEvents.Mod ulmResources.Mod ulmForwarders.Mod ulmRelatedEvents.Mod - $(VOCSTATIC) -sP ulmIO.Mod ulmProcess.Mod + $(VOCSTATIC) -sP ulmIO.Mod ulmProcess.Mod ulmTypes.Mod ulmStreams.Mod stage7: #objects := $(wildcard *.o) diff --git a/makefile.gnuc.armv6j b/makefile.gnuc.armv6j index ebce4f74..adf4893b 100644 --- a/makefile.gnuc.armv6j +++ b/makefile.gnuc.armv6j @@ -9,7 +9,7 @@ RELEASE = 1.0 INCLUDEPATH = -Isrc/lib/system/$(CCOMP)/$(TARCH) -SETPATH = CFLAGS=$(INCLUDEPATH) PATH=.:/bin:/usr/bin MODULES=.:src/lib:src/lib/v4:src/lib/system:src/lib/system/$(CCOMP):src/lib/system/$(CCOMP)/$(TARCH):src/lib/ulm:src/lib/ulm/gnuc:src/lib/ooc2:src/lib/ooc2/gnuc:src/lib/ooc:src/lib/ooc/lowlevel:src/voc:src/voc/gnuc:src/voc/gnuc/$(TARCH):src/tools/ocat:src/tools/browser:src/tools/vocparam:src/tools/coco:src/test +SETPATH = CFLAGS=$(INCLUDEPATH) PATH=.:/bin:/usr/bin MODULES=.:src/lib:src/lib/v4:src/lib/system:src/lib/system/$(CCOMP):src/lib/system/$(CCOMP)/$(TARCH):src/lib/ulm:src/lib/ulm/$(CCOMP):src/lib/ulm/$(TARCH):src/lib/ooc2:src/lib/ooc2/$(CCOMP):src/lib/ooc:src/lib/ooc/$(CCOMP):src/voc:src/voc/$(CCOMP):src/voc/$(CCOMP)/$(TARCH):src/tools/ocat:src/tools/browser:src/tools/vocparam:src/tools/coco:src/test VOC = voc VOCSTATIC = $(SETPATH) ./vocstatic @@ -137,7 +137,7 @@ stage6: $(VOCSTATIC) -sP ulmASCII.Mod ulmSets.Mod $(VOCSTATIC) -sP ulmObjects.Mod ulmDisciplines.Mod $(VOCSTATIC) -sP ulmPriorities.Mod ulmServices.Mod ulmEvents.Mod ulmResources.Mod ulmForwarders.Mod ulmRelatedEvents.Mod - $(VOCSTATIC) -sP ulmIO.Mod ulmProcess.Mod + $(VOCSTATIC) -sP ulmIO.Mod ulmProcess.Mod ulmTypes.Mod ulmStreams.Mod stage7: #objects := $(wildcard *.o) diff --git a/makefile.gnuc.armv6j_hardfp b/makefile.gnuc.armv6j_hardfp index bcbe36b9..1e0d4646 100644 --- a/makefile.gnuc.armv6j_hardfp +++ b/makefile.gnuc.armv6j_hardfp @@ -9,7 +9,7 @@ RELEASE = 1.0 INCLUDEPATH = -Isrc/lib/system/$(CCOMP)/$(TARCH) -SETPATH = CFLAGS=$(INCLUDEPATH) PATH=.:/bin:/usr/bin MODULES=.:src/lib:src/lib/v4:src/lib/system:src/lib/system/$(CCOMP):src/lib/system/$(CCOMP)/$(TARCH):src/lib/ulm:src/lib/ulm/gnuc:src/lib/ooc2:src/lib/ooc2/gnuc:src/lib/ooc:src/lib/ooc/lowlevel:src/voc:src/voc/gnuc:src/voc/gnuc/$(TARCH):src/tools/ocat:src/tools/browser:src/tools/vocparam:src/tools/coco:src/test +SETPATH = CFLAGS=$(INCLUDEPATH) PATH=.:/bin:/usr/bin MODULES=.:src/lib:src/lib/v4:src/lib/system:src/lib/system/$(CCOMP):src/lib/system/$(CCOMP)/$(TARCH):src/lib/ulm:src/lib/ulm/$(CCOMP):src/lib/ulm/$(TARCH):src/lib/ooc2:src/lib/ooc2/$(CCOMP):src/lib/ooc:src/lib/ooc/$(CCOMP):src/voc:src/voc/$(CCOMP):src/voc/$(CCOMP)/$(TARCH):src/tools/ocat:src/tools/browser:src/tools/vocparam:src/tools/coco:src/test VOC = voc VOCSTATIC = $(SETPATH) ./vocstatic @@ -137,7 +137,7 @@ stage6: $(VOCSTATIC) -sP ulmASCII.Mod ulmSets.Mod $(VOCSTATIC) -sP ulmObjects.Mod ulmDisciplines.Mod $(VOCSTATIC) -sP ulmPriorities.Mod ulmServices.Mod ulmEvents.Mod ulmResources.Mod ulmForwarders.Mod ulmRelatedEvents.Mod - $(VOCSTATIC) -sP ulmIO.Mod ulmProcess.Mod + $(VOCSTATIC) -sP ulmIO.Mod ulmProcess.Mod ulmTypes.Mod ulmStreams.Mod stage7: #objects := $(wildcard *.o) diff --git a/makefile.gnuc.armv7a_hardfp b/makefile.gnuc.armv7a_hardfp index 1d5da59b..810d4222 100644 --- a/makefile.gnuc.armv7a_hardfp +++ b/makefile.gnuc.armv7a_hardfp @@ -9,7 +9,7 @@ RELEASE = 1.0 INCLUDEPATH = -Isrc/lib/system/$(CCOMP)/$(TARCH) -SETPATH = CFLAGS=$(INCLUDEPATH) PATH=.:/bin:/usr/bin MODULES=.:src/lib:src/lib/v4:src/lib/system:src/lib/system/$(CCOMP):src/lib/system/$(CCOMP)/$(TARCH):src/lib/ulm:src/lib/ulm/gnuc:src/lib/ooc2:src/lib/ooc2/gnuc:src/lib/ooc:src/lib/ooc/lowlevel:src/voc:src/voc/gnuc:src/voc/gnuc/$(TARCH):src/tools/ocat:src/tools/browser:src/tools/vocparam:src/tools/coco:src/test +SETPATH = CFLAGS=$(INCLUDEPATH) PATH=.:/bin:/usr/bin MODULES=.:src/lib:src/lib/v4:src/lib/system:src/lib/system/$(CCOMP):src/lib/system/$(CCOMP)/$(TARCH):src/lib/ulm:src/lib/ulm/$(CCOMP):src/lib/ulm/$(TARCH):src/lib/ooc2:src/lib/ooc2/$(CCOMP):src/lib/ooc:src/lib/ooc/$(CCOMP):src/voc:src/voc/$(CCOMP):src/voc/$(CCOMP)/$(TARCH):src/tools/ocat:src/tools/browser:src/tools/vocparam:src/tools/coco:src/test VOC = voc VOCSTATIC = $(SETPATH) ./vocstatic @@ -137,7 +137,7 @@ stage6: $(VOCSTATIC) -sP ulmASCII.Mod ulmSets.Mod $(VOCSTATIC) -sP ulmObjects.Mod ulmDisciplines.Mod $(VOCSTATIC) -sP ulmPriorities.Mod ulmServices.Mod ulmEvents.Mod ulmResources.Mod ulmForwarders.Mod ulmRelatedEvents.Mod - $(VOCSTATIC) -sP ulmIO.Mod ulmProcess.Mod + $(VOCSTATIC) -sP ulmIO.Mod ulmProcess.Mod ulmTypes.Mod ulmStreams.Mod stage7: #objects := $(wildcard *.o) diff --git a/makefile.gnuc.x86 b/makefile.gnuc.x86 index 00b734c9..4e8bf28a 100644 --- a/makefile.gnuc.x86 +++ b/makefile.gnuc.x86 @@ -9,7 +9,7 @@ RELEASE = 1.0 INCLUDEPATH = -Isrc/lib/system/$(CCOMP)/$(TARCH) -SETPATH = CFLAGS=$(INCLUDEPATH) PATH=.:/bin:/usr/bin MODULES=.:src/lib:src/lib/v4:src/lib/system:src/lib/system/$(CCOMP):src/lib/system/$(CCOMP)/$(TARCH):src/lib/ulm:src/lib/ulm/gnuc:src/lib/ooc2:src/lib/ooc2/gnuc:src/lib/ooc:src/lib/ooc/lowlevel:src/voc:src/voc/gnuc:src/voc/gnuc/$(TARCH):src/tools/ocat:src/tools/browser:src/tools/vocparam:src/tools/coco:src/test +SETPATH = CFLAGS=$(INCLUDEPATH) PATH=.:/bin:/usr/bin MODULES=.:src/lib:src/lib/v4:src/lib/system:src/lib/system/$(CCOMP):src/lib/system/$(CCOMP)/$(TARCH):src/lib/ulm:src/lib/ulm/$(CCOMP):src/lib/ulm/$(TARCH):src/lib/ooc2:src/lib/ooc2/$(CCOMP):src/lib/ooc:src/lib/ooc/$(CCOMP):src/voc:src/voc/$(CCOMP):src/voc/$(CCOMP)/$(TARCH):src/tools/ocat:src/tools/browser:src/tools/vocparam:src/tools/coco:src/test VOC = voc VOCSTATIC = $(SETPATH) ./vocstatic @@ -137,7 +137,7 @@ stage6: $(VOCSTATIC) -sP ulmASCII.Mod ulmSets.Mod $(VOCSTATIC) -sP ulmObjects.Mod ulmDisciplines.Mod $(VOCSTATIC) -sP ulmPriorities.Mod ulmServices.Mod ulmEvents.Mod ulmResources.Mod ulmForwarders.Mod ulmRelatedEvents.Mod - $(VOCSTATIC) -sP ulmIO.Mod ulmProcess.Mod + $(VOCSTATIC) -sP ulmIO.Mod ulmProcess.Mod ulmTypes.Mod ulmStreams.Mod stage7: #objects := $(wildcard *.o) diff --git a/makefile.gnuc.x86_64 b/makefile.gnuc.x86_64 new file mode 100644 index 00000000..1382a97d --- /dev/null +++ b/makefile.gnuc.x86_64 @@ -0,0 +1,188 @@ +#SHELL := /bin/bash +BUILDID=$(shell date +%Y/%m/%d) +TOS = linux +TARCH = x86_64 +#TARCH = x86 x86_64 armv6j armv6j_hardfp armv7a_hardfp +CCOMP = gnuc +RELEASE = 1.0 + + +INCLUDEPATH = -Isrc/lib/system/$(CCOMP)/$(TARCH) + +SETPATH = CFLAGS=$(INCLUDEPATH) PATH=.:/bin:/usr/bin MODULES=.:src/lib:src/lib/v4:src/lib/system:src/lib/system/$(CCOMP):src/lib/system/$(CCOMP)/$(TARCH):src/lib/ulm:src/lib/ulm/$(CCOMP):src/lib/ulm/$(TARCH):src/lib/ooc2:src/lib/ooc2/$(CCOMP):src/lib/ooc:src/lib/ooc/$(CCOMP):src/voc:src/voc/$(CCOMP):src/voc/$(CCOMP)/$(TARCH):src/tools/ocat:src/tools/browser:src/tools/vocparam:src/tools/coco:src/test + +VOC = voc +VOCSTATIC = $(SETPATH) ./vocstatic +VOCPARAM = $(shell ./vocparam > voc.par) +VERSION = GNU_Linux_$(TARCH) +LIBNAME = VishapOberon +LIBRARY = lib$(LIBNAME) + +ifndef PREFIX +PREFIX = /opt/voc-$(RELEASE) +endif + +CCOPT = -fPIC $(INCLUDEPATH) -g + +CC = cc $(CCOPT) -c +CL = cc $(CCOPT) +LD = cc -shared -o $(LIBRARY).so +# s is necessary to create index inside a archive +ARCHIVE = ar rcs $(LIBRARY).a + +#%.c: %.Mod +#%.o: %.c +# $(CC) $(input) + +all: stage2 stage3 stage4 stage5 stage6 stage7 + +# when porting to new platform: +# * put corresponding .par file into current directory. it can be generated on the target platform by compiling vocparam (stage0) and running (stage1) +# * run make port0 - this will generate C source files for the target architecture +# * move the source tree to the target machine, and compile (or compile here via crosscompiler) (port1) +port0: stage2 stage3 stage4 + +# now compile C source files for voc, showdef and ocat on target machine (or by using crosscompiler) +port1: stage5 +# after you have "voc" compiled for target architecture. replace vocstatic with it and run make on target platform to get everything compiled + +# this builds binary which generates voc.par +stage0: src/tools/vocparam/vocparam.c + $(CL) -I src/lib -o vocparam src/tools/vocparam/vocparam.c + +# this creates voc.par for a host architecture. +# comment this out if you need to build a compiler for a different architecture. +stage1: + #rm voc.par + #$(shell "./vocparam > voc.par") + #./vocparam > voc.par + $(VOCPARAM) + +# this copies necessary voc.par to the current directory. +# skip this if you are building compiler for the host architecture. +stage2: + cp src/par/voc.par.$(CCOMP).$(TARCH) voc.par +# cp src/par/voc.par.gnu.x86_64 voc.par +# cp src/par/voc.par.gnu.x86 voc.par +# cp src/par/voc.par.gnu.armv6 voc.par +# cp src/par/voc.par.gnu.armv7 voc.par + +# this prepares modules necessary to build the compiler itself +stage3: + + $(VOCSTATIC) -siapxPS SYSTEM.Mod + $(VOCSTATIC) -sPS Args.Mod Console.Mod Unix.Mod + $(VOCSTATIC) -sPS oocOakStrings.Mod architecture.Mod version.Mod Kernel.Mod Modules.Mod + $(VOCSTATIC) -sxPS Files.Mod + $(VOCSTATIC) -sPS Reals.Mod CmdlnTexts.Mod errors.Mod + +# build the compiler +stage4: + $(VOCSTATIC) -sPS extTools.Mod + $(VOCSTATIC) -sPS OPM.cmdln.Mod + $(VOCSTATIC) -sxPS OPS.Mod + $(VOCSTATIC) -sPS OPT.Mod OPC.Mod OPV.Mod OPB.Mod OPP.Mod + $(VOCSTATIC) -smPS voc.Mod + $(VOCSTATIC) -smPS BrowserCmd.Mod + $(VOCSTATIC) -smPS OCatCmd.Mod + +#this is to build the compiler from C sources. +#this is a way to create a bootstrap binary. +stage5: + $(CC) SYSTEM.c Args.c Console.c Modules.c Unix.c \ + oocOakStrings.c architecture.c version.c Kernel.c Files.c Reals.c CmdlnTexts.c \ + version.c extTools.c \ + OPM.c OPS.c OPT.c OPC.c OPV.c OPB.c OPP.c errors.c + + $(CL) -static voc.c -o voc \ + SYSTEM.o Args.o Console.o Modules.o Unix.o \ + oocOakStrings.o architecture.o version.o Kernel.o Files.o Reals.o CmdlnTexts.o \ + extTools.o \ + OPM.o OPS.o OPT.o OPC.o OPV.o OPB.o OPP.o errors.o + $(CL) BrowserCmd.c -o showdef \ + SYSTEM.o Args.o Console.o Modules.o Unix.o oocOakStrings.o architecture.o version.o Kernel.o Files.o Reals.o CmdlnTexts.o \ + OPM.o OPS.o OPT.o OPV.o OPC.o errors.o + + $(CL) OCatCmd.c -o ocat \ + SYSTEM.o Args.o Console.o Modules.o Unix.o oocOakStrings.o architecture.o version.o Kernel.o Files.o Reals.o CmdlnTexts.o + + + +# build all library files +stage6: + $(VOCSTATIC) -sP oocAscii.Mod + $(VOCSTATIC) -sP oocStrings.Mod + $(VOCSTATIC) -sP oocStrings2.Mod + $(VOCSTATIC) -sP oocCharClass.Mod + $(VOCSTATIC) -sP oocConvTypes.Mod + $(VOCSTATIC) -sP oocIntConv.Mod + $(VOCSTATIC) -sP oocIntStr.Mod + $(VOCSTATIC) -sP oocSysClock.Mod + $(VOCSTATIC) -sP oocTime.Mod +# $(VOCSTATIC) -s oocLongStrings.Mod +# $(CC) oocLongStrings.c +# $(VOCSTATIC) -s oocMsg.Mod +# $(CC) oocMsg.c + + + $(VOCSTATIC) -sP ooc2Strings.Mod + $(VOCSTATIC) -sP ooc2Ascii.Mod + $(VOCSTATIC) -sP ooc2CharClass.Mod + $(VOCSTATIC) -sP ooc2ConvTypes.Mod + $(VOCSTATIC) -sP ooc2IntConv.Mod + $(VOCSTATIC) -sP ooc2IntStr.Mod + $(VOCSTATIC) -sP ooc2Real0.Mod + $(VOCSTATIC) -sP oocwrapperlibc.Mod + $(VOCSTATIC) -sP ulmSYSTEM.Mod + $(VOCSTATIC) -sP ulmASCII.Mod ulmSets.Mod + $(VOCSTATIC) -sP ulmObjects.Mod ulmDisciplines.Mod + $(VOCSTATIC) -sP ulmPriorities.Mod ulmServices.Mod ulmEvents.Mod ulmResources.Mod ulmForwarders.Mod ulmRelatedEvents.Mod + $(VOCSTATIC) -sP ulmIO.Mod ulmProcess.Mod ulmTypes.Mod ulmStreams.Mod + +stage7: + #objects := $(wildcard *.o) + #$(LD) objects + $(ARCHIVE) *.o + #$(ARCHIVE) objects + $(LD) *.o + echo "$(PREFIX)/lib" > 05vishap.conf + +clean: +# rm_objects := rm $(wildcard *.o) +# objects + rm *.o + rm *.sym + rm *.h + rm *.c + rm *.a + rm *.so + +coco: + $(JET) Sets.Mod Oberon.Mod CRS.Mod CRT.Mod CRA.Mod CRX.Mod CRP.Mod Coco.Mod -m + $(CC) Sets.c Oberon.c CRS.c CRT.c CRA.c CRX.c CRP.c + $(CL) -static -o Coco Coco.c Sets.o Oberon.o CRS.o CRT.o CRA.o CRX.o CRP.o CmdlnTexts.o SYSTEM.o Files.o -L. -lOberon -L/usr/lib -ldl + +install: + test -d $(PREFIX)/bin | mkdir -p $(PREFIX)/bin + cp voc $(PREFIX)/bin/ + cp showdef $(PREFIX)/bin/ + cp ocat $(PREFIX)/bin/ + cp -a src $(PREFIX)/ + + test -d $(PREFIX)/lib/voc | mkdir -p $(PREFIX)/lib/voc + test -d $(PREFIX)/lib/voc/ | mkdir -p $(PREFIX)/lib/voc + test -d $(PREFIX)/lib/voc/obj | mkdir -p $(PREFIX)/lib/voc/obj + test -d $(PREFIX)/lib/voc/sym | mkdir -p $(PREFIX)/lib/voc/sym + + cp $(LIBRARY).so $(PREFIX)/lib + cp $(LIBRARY).a $(PREFIX)/lib + cp *.c $(PREFIX)/lib/voc/obj/ + cp *.h $(PREFIX)/lib/voc/obj/ + cp *.sym $(PREFIX)/lib/voc/sym/ + + cp 05vishap.conf /etc/ld.so.conf.d/ + ldconfig + +# cp *.o $(PREFIX)/lib/voc/$(RELEASE)/obj/ +uninstall: + rm -rf $(PREFIX) diff --git a/ocat b/ocat index ed9d6dde1b1c8258907309c66b9a945cec83b0a6..993ef40b212ab46f31580f1912fab5751c22b818 100755 GIT binary patch delta 91 zcmcccfak&ko`x-qf12C>H#2Vk-^^4Pw>|d}Q!*Q)%k+ycn9LbjrgOezvS%!r?g^sK kPwxa#`=_4-QNN~hz5-Gf(>*~{#Pm)O^*~{^z=>;^?LisS4{1$0IjGe>Hq)$ diff --git a/showdef b/showdef index 178817a94acd3bf196f05bcb739ee068c1004f3b..6c2495d4cb8fe87ff3e48b6638dbd330de9f64ea 100755 GIT binary patch delta 118 zcmeC!D%G)7s-cCkg=q`(mag{gUCcntvVD6Ot7iB1yP|B#Y>Y0`9c9?ex%PB1Gl0MZ z=IMQ{tYXs*0+~6cFOXrAVDy>3Q-;l+@!|BJAc}dqr7VzoF})H*J)OQ2L>-;}6GVk> Kx0GY^uK@sF?;c5iF;5DaYnt0|0uyBLDyZ diff --git a/src/lib/ulm/armv6j/ulmTypes.Mod b/src/lib/ulm/armv6j/ulmTypes.Mod new file mode 100644 index 00000000..3b4a8e19 --- /dev/null +++ b/src/lib/ulm/armv6j/ulmTypes.Mod @@ -0,0 +1,125 @@ +(* Ulm's Oberon Library + Copyright (C) 1989-2000 by University of Ulm, SAI, D-89069 Ulm, Germany + ---------------------------------------------------------------------------- + Ulm's Oberon Library is free software; you can redistribute it + and/or modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either version + 2 of the License, or (at your option) any later version. + + Ulm's Oberon Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + ---------------------------------------------------------------------------- + E-mail contact: oberon@mathematik.uni-ulm.de + ---------------------------------------------------------------------------- + $Id: Types.om,v 1.5 2000/12/13 10:03:00 borchert Exp $ + ---------------------------------------------------------------------------- + $Log: Types.om,v $ + Revision 1.5 2000/12/13 10:03:00 borchert + SetInt type used in msb constant + + Revision 1.4 2000/12/13 09:51:57 borchert + constants and types for the relationship of INTEGER and SET added + + Revision 1.3 1998/09/25 15:23:09 borchert + Real32..Real128 added + + Revision 1.2 1994/07/01 11:08:04 borchert + IntAddress, Int8/16/32, ToInt8/16/32 and bit/little endian stuff added + + Revision 1.1 1994/02/22 20:12:14 borchert + Initial revision + + ---------------------------------------------------------------------------- + AFB 9/93 + ---------------------------------------------------------------------------- +*) + +MODULE ulmTypes; + + (* compiler-dependent type definitions; + this version works for Ulm's Oberon Compilers on + following architectures: m68k and sparc + *) + + IMPORT SYS := SYSTEM; + + TYPE + Address* = LONGINT (*SYS.ADDRESS*); + UntracedAddress* = LONGINT; (*SYS.UNTRACEDADDRESS;*) + Count* = LONGINT; + Size* = Count; + Byte* = SYS.BYTE; + IntAddress* = LONGINT; + Int8* = SHORTINT; + Int16* = INTEGER; + Int32* = LONGINT; + Real32* = REAL; + Real64* = LONGREAL; + + CONST + bigEndian* = 0; (* SPARC, M68K etc *) + littleEndian* = 1; (* Intel 80x86, VAX etc *) + byteorder* = littleEndian; (* machine-dependent constant *) + TYPE + ByteOrder* = SHORTINT; (* bigEndian or littleEndian *) + + (* following constants and type definitions try to make + conversions from INTEGER to SET and vice versa more portable + to allow for bit operations on INTEGER values + *) + TYPE + SetInt* = LONGINT; (* INTEGER type that corresponds to SET *) + VAR msb* : SET; + msbIsMax*, msbIs0*: SHORTINT; + msbindex*, lsbindex*, nofbits*: LONGINT; + + PROCEDURE ToInt8*(int: LONGINT) : Int8; + BEGIN + RETURN SHORT(SHORT(int)) + END ToInt8; + + PROCEDURE ToInt16*(int: LONGINT) : Int16; + BEGIN + RETURN SYS.VAL(Int16, int) + END ToInt16; + + PROCEDURE ToInt32*(int: LONGINT) : Int32; + BEGIN + RETURN int + END ToInt32; + + PROCEDURE ToReal32*(real: LONGREAL) : Real32; + BEGIN + RETURN SHORT(real) + END ToReal32; + + PROCEDURE ToReal64*(real: LONGREAL) : Real64; + BEGIN + RETURN real + END ToReal64; + +BEGIN + msb := SYS.VAL(SET, MIN(SetInt)); + (* most significant bit, converted to a SET *) + (* we expect msbIsMax XOR msbIs0 to be 1; + this is checked for by an assertion + *) + msbIsMax := SYS.VAL(SHORTINT, (msb = {MAX(SET)})); + (* is 1, if msb equals {MAX(SET)} *) + msbIs0 := SYS.VAL(SHORTINT, (msb = {0})); + (* is 0, if msb equals {0} *) + msbindex := msbIsMax * MAX(SET); + (* set element that corresponds to the most-significant-bit *) + lsbindex := MAX(SET) - msbindex; + (* set element that corresponds to the lowest-significant-bit *) + nofbits := MAX(SET) + 1; + (* number of elements in SETs *) + + ASSERT((msbIs0 = 1) & (msbIsMax = 0) OR (msbIs0 = 0) & (msbIsMax = 1)); +END ulmTypes. diff --git a/src/lib/ulm/armv6j_hardfp/ulmTypes.Mod b/src/lib/ulm/armv6j_hardfp/ulmTypes.Mod new file mode 100644 index 00000000..3b4a8e19 --- /dev/null +++ b/src/lib/ulm/armv6j_hardfp/ulmTypes.Mod @@ -0,0 +1,125 @@ +(* Ulm's Oberon Library + Copyright (C) 1989-2000 by University of Ulm, SAI, D-89069 Ulm, Germany + ---------------------------------------------------------------------------- + Ulm's Oberon Library is free software; you can redistribute it + and/or modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either version + 2 of the License, or (at your option) any later version. + + Ulm's Oberon Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + ---------------------------------------------------------------------------- + E-mail contact: oberon@mathematik.uni-ulm.de + ---------------------------------------------------------------------------- + $Id: Types.om,v 1.5 2000/12/13 10:03:00 borchert Exp $ + ---------------------------------------------------------------------------- + $Log: Types.om,v $ + Revision 1.5 2000/12/13 10:03:00 borchert + SetInt type used in msb constant + + Revision 1.4 2000/12/13 09:51:57 borchert + constants and types for the relationship of INTEGER and SET added + + Revision 1.3 1998/09/25 15:23:09 borchert + Real32..Real128 added + + Revision 1.2 1994/07/01 11:08:04 borchert + IntAddress, Int8/16/32, ToInt8/16/32 and bit/little endian stuff added + + Revision 1.1 1994/02/22 20:12:14 borchert + Initial revision + + ---------------------------------------------------------------------------- + AFB 9/93 + ---------------------------------------------------------------------------- +*) + +MODULE ulmTypes; + + (* compiler-dependent type definitions; + this version works for Ulm's Oberon Compilers on + following architectures: m68k and sparc + *) + + IMPORT SYS := SYSTEM; + + TYPE + Address* = LONGINT (*SYS.ADDRESS*); + UntracedAddress* = LONGINT; (*SYS.UNTRACEDADDRESS;*) + Count* = LONGINT; + Size* = Count; + Byte* = SYS.BYTE; + IntAddress* = LONGINT; + Int8* = SHORTINT; + Int16* = INTEGER; + Int32* = LONGINT; + Real32* = REAL; + Real64* = LONGREAL; + + CONST + bigEndian* = 0; (* SPARC, M68K etc *) + littleEndian* = 1; (* Intel 80x86, VAX etc *) + byteorder* = littleEndian; (* machine-dependent constant *) + TYPE + ByteOrder* = SHORTINT; (* bigEndian or littleEndian *) + + (* following constants and type definitions try to make + conversions from INTEGER to SET and vice versa more portable + to allow for bit operations on INTEGER values + *) + TYPE + SetInt* = LONGINT; (* INTEGER type that corresponds to SET *) + VAR msb* : SET; + msbIsMax*, msbIs0*: SHORTINT; + msbindex*, lsbindex*, nofbits*: LONGINT; + + PROCEDURE ToInt8*(int: LONGINT) : Int8; + BEGIN + RETURN SHORT(SHORT(int)) + END ToInt8; + + PROCEDURE ToInt16*(int: LONGINT) : Int16; + BEGIN + RETURN SYS.VAL(Int16, int) + END ToInt16; + + PROCEDURE ToInt32*(int: LONGINT) : Int32; + BEGIN + RETURN int + END ToInt32; + + PROCEDURE ToReal32*(real: LONGREAL) : Real32; + BEGIN + RETURN SHORT(real) + END ToReal32; + + PROCEDURE ToReal64*(real: LONGREAL) : Real64; + BEGIN + RETURN real + END ToReal64; + +BEGIN + msb := SYS.VAL(SET, MIN(SetInt)); + (* most significant bit, converted to a SET *) + (* we expect msbIsMax XOR msbIs0 to be 1; + this is checked for by an assertion + *) + msbIsMax := SYS.VAL(SHORTINT, (msb = {MAX(SET)})); + (* is 1, if msb equals {MAX(SET)} *) + msbIs0 := SYS.VAL(SHORTINT, (msb = {0})); + (* is 0, if msb equals {0} *) + msbindex := msbIsMax * MAX(SET); + (* set element that corresponds to the most-significant-bit *) + lsbindex := MAX(SET) - msbindex; + (* set element that corresponds to the lowest-significant-bit *) + nofbits := MAX(SET) + 1; + (* number of elements in SETs *) + + ASSERT((msbIs0 = 1) & (msbIsMax = 0) OR (msbIs0 = 0) & (msbIsMax = 1)); +END ulmTypes. diff --git a/src/lib/ulm/armv7a_hardfp/ulmTypes.Mod b/src/lib/ulm/armv7a_hardfp/ulmTypes.Mod new file mode 100644 index 00000000..3b4a8e19 --- /dev/null +++ b/src/lib/ulm/armv7a_hardfp/ulmTypes.Mod @@ -0,0 +1,125 @@ +(* Ulm's Oberon Library + Copyright (C) 1989-2000 by University of Ulm, SAI, D-89069 Ulm, Germany + ---------------------------------------------------------------------------- + Ulm's Oberon Library is free software; you can redistribute it + and/or modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either version + 2 of the License, or (at your option) any later version. + + Ulm's Oberon Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + ---------------------------------------------------------------------------- + E-mail contact: oberon@mathematik.uni-ulm.de + ---------------------------------------------------------------------------- + $Id: Types.om,v 1.5 2000/12/13 10:03:00 borchert Exp $ + ---------------------------------------------------------------------------- + $Log: Types.om,v $ + Revision 1.5 2000/12/13 10:03:00 borchert + SetInt type used in msb constant + + Revision 1.4 2000/12/13 09:51:57 borchert + constants and types for the relationship of INTEGER and SET added + + Revision 1.3 1998/09/25 15:23:09 borchert + Real32..Real128 added + + Revision 1.2 1994/07/01 11:08:04 borchert + IntAddress, Int8/16/32, ToInt8/16/32 and bit/little endian stuff added + + Revision 1.1 1994/02/22 20:12:14 borchert + Initial revision + + ---------------------------------------------------------------------------- + AFB 9/93 + ---------------------------------------------------------------------------- +*) + +MODULE ulmTypes; + + (* compiler-dependent type definitions; + this version works for Ulm's Oberon Compilers on + following architectures: m68k and sparc + *) + + IMPORT SYS := SYSTEM; + + TYPE + Address* = LONGINT (*SYS.ADDRESS*); + UntracedAddress* = LONGINT; (*SYS.UNTRACEDADDRESS;*) + Count* = LONGINT; + Size* = Count; + Byte* = SYS.BYTE; + IntAddress* = LONGINT; + Int8* = SHORTINT; + Int16* = INTEGER; + Int32* = LONGINT; + Real32* = REAL; + Real64* = LONGREAL; + + CONST + bigEndian* = 0; (* SPARC, M68K etc *) + littleEndian* = 1; (* Intel 80x86, VAX etc *) + byteorder* = littleEndian; (* machine-dependent constant *) + TYPE + ByteOrder* = SHORTINT; (* bigEndian or littleEndian *) + + (* following constants and type definitions try to make + conversions from INTEGER to SET and vice versa more portable + to allow for bit operations on INTEGER values + *) + TYPE + SetInt* = LONGINT; (* INTEGER type that corresponds to SET *) + VAR msb* : SET; + msbIsMax*, msbIs0*: SHORTINT; + msbindex*, lsbindex*, nofbits*: LONGINT; + + PROCEDURE ToInt8*(int: LONGINT) : Int8; + BEGIN + RETURN SHORT(SHORT(int)) + END ToInt8; + + PROCEDURE ToInt16*(int: LONGINT) : Int16; + BEGIN + RETURN SYS.VAL(Int16, int) + END ToInt16; + + PROCEDURE ToInt32*(int: LONGINT) : Int32; + BEGIN + RETURN int + END ToInt32; + + PROCEDURE ToReal32*(real: LONGREAL) : Real32; + BEGIN + RETURN SHORT(real) + END ToReal32; + + PROCEDURE ToReal64*(real: LONGREAL) : Real64; + BEGIN + RETURN real + END ToReal64; + +BEGIN + msb := SYS.VAL(SET, MIN(SetInt)); + (* most significant bit, converted to a SET *) + (* we expect msbIsMax XOR msbIs0 to be 1; + this is checked for by an assertion + *) + msbIsMax := SYS.VAL(SHORTINT, (msb = {MAX(SET)})); + (* is 1, if msb equals {MAX(SET)} *) + msbIs0 := SYS.VAL(SHORTINT, (msb = {0})); + (* is 0, if msb equals {0} *) + msbindex := msbIsMax * MAX(SET); + (* set element that corresponds to the most-significant-bit *) + lsbindex := MAX(SET) - msbindex; + (* set element that corresponds to the lowest-significant-bit *) + nofbits := MAX(SET) + 1; + (* number of elements in SETs *) + + ASSERT((msbIs0 = 1) & (msbIsMax = 0) OR (msbIs0 = 0) & (msbIsMax = 1)); +END ulmTypes. diff --git a/src/lib/ulm/ulmStreams.Mod b/src/lib/ulm/ulmStreams.Mod new file mode 100644 index 00000000..2f35f58b --- /dev/null +++ b/src/lib/ulm/ulmStreams.Mod @@ -0,0 +1,2149 @@ +(* Ulm's Oberon Library + Copyright (C) 1989-2001 by University of Ulm, SAI, D-89069 Ulm, Germany + ---------------------------------------------------------------------------- + Ulm's Oberon Library is free software; you can redistribute it + and/or modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either version + 2 of the License, or (at your option) any later version. + + Ulm's Oberon Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + ---------------------------------------------------------------------------- + E-mail contact: oberon@mathematik.uni-ulm.de + ---------------------------------------------------------------------------- + $Id: Streams.om,v 1.13 2005/02/14 23:36:35 borchert Exp $ + ---------------------------------------------------------------------------- + $Log: Streams.om,v $ + Revision 1.13 2005/02/14 23:36:35 borchert + bug fix: WritePart called InternalFlush without considering + that s.pos may be implicitly changed + (this assumption was wrong since revision 1.11) + + Revision 1.12 2004/05/20 09:52:43 borchert + performance improvements: + - WritePart and Write take now the buffer by reference + - ReadByteFromBuf replaced by ReadBytesFromBuf + (contributed by Christian Ehrhardt) + + Revision 1.11 2001/05/03 15:17:58 borchert + InternalFlush adapted for unidirectional pipelines to avoid + unintentional flushes due to buffer boundaries + + Revision 1.10 2000/04/25 21:41:47 borchert + Streams.ReadPart loops now for unbuffered streams to collect input + until cnt is reached + + Revision 1.9 1998/03/31 11:13:05 borchert + bug fix: NotificationHandler just reacted on Resources.unreferenced + but not on Resources.terminated + + Revision 1.8 1998/03/24 22:58:28 borchert + bug fix in Copy: left was computed incorrectly in case of + copies with fixed length (# -1) + + Revision 1.7 1997/04/02 07:50:05 borchert + Copy replaced by a slightly more efficient variant + + Revision 1.6 1996/09/18 07:43:51 borchert + qualified references to own module (i.e. Streams.XXX) removed + + Revision 1.5 1996/01/04 16:43:57 borchert + some bug fixes in the updates of read and write regions + + Revision 1.4 1995/10/11 09:46:41 borchert + - closeEvent re-introduced (because it gets raised *before* + the actual close) + - bug fix: s.write was diminished in ReadPart but the write region + not properly adjusted + - bug fix: InternalSeek was setting s.left to negative values in + a special case + + Revision 1.3 1995/04/18 12:17:12 borchert + - Streams.Stream is now an extension of Services.Object + - Library variant of assertions replaced by ASSERT + - support of Resources added + - EnableClose, PreventClose & closeEvent removed + + Revision 1.2 1994/07/05 12:45:57 borchert + some minor bug fixes & enhancements: + - ReadPacket added + - streams which don't require cleanup are now subject to the GC + even if Close will never be called for them + - line buffered streams w/o bufio/addrio capability fill now buffer + up to the next line terminator only instead of trying to fill the + whole buffer + - ReadPart didn't set count correctly in all cases + - Touch calls now the flush interface procedure + + Revision 1.1 1994/02/22 20:10:45 borchert + Initial revision + + ---------------------------------------------------------------------------- + AFB 6/89 + Major Revision: AFB 1/92: bufpool + ---------------------------------------------------------------------------- +*) + +MODULE ulmStreams; + + IMPORT Events := ulmEvents, Objects := ulmObjects, Priorities := ulmPriorities, Process := ulmProcess, RelatedEvents := ulmRelatedEvents, Resources := ulmResources, + Services := ulmServices, SYS := ulmSYSTEM, SYSTEM, Types := ulmTypes; + + CONST + (* 3rd parameter of Seek *) + (* Whence = (fromStart, fromPos, fromEnd); *) + fromStart* = 0; fromPos* = 1; fromEnd* = 2; + + (* capabilities of a stream *) + (* Capability = (read, write, addrio, bufio, seek, tell, trunc, close, + holes, handler); + *) + read* = 0; write* = 1; addrio* = 2; bufio* = 3; seek* = 4; tell* = 5; + trunc* = 6; flush* = 7; close* = 8; holes* = 9; handler* = 10; + + (* BufMode = (nobuf, linebuf, onebuf, bufpool); *) + nobuf* = 0; linebuf* = 1; onebuf* = 2; bufpool* = 3; + + (* ErrorCode = (NoHandlerDefined, CannotRead, CannotSeek, CloseFailed, + NotLineBuffered, SeekFailed, TellFailed, BadWhence, + CannotTell, WriteFailed, CannotWrite, ReadFailed, + Unbuffered, BadParameters, CannotTrunc, TruncFailed, + NestedCall, FlushFailed); + *) + NoHandlerDefined* = 0; (* no handler defined *) + CannotRead* = 1; (* stream is write only *) + CannotSeek* = 2; (* stream is not capable of seeking *) + CloseFailed* = 3; (* Flush or Close failed *) + NotLineBuffered* = 4; (* LineTerm must not be called *) + SeekFailed* = 5; (* seek operation failed *) + TellFailed* = 6; (* tell operation failed *) + BadWhence* = 7; (* whence value out of [fromStart..fromEnd] *) + CannotTell* = 8; (* stream does not have a current position *) + WriteFailed* = 9; (* write error *) + CannotWrite* = 10; (* stream is read only *) + ReadFailed* = 11; (* read error *) + Unbuffered* = 12; (* operation isn't valid for unbuff'd streams *) + BadParameters* = 13; (* e.g. wrong count or offset values *) + CannotTrunc* = 14; (* stream is not capable of truncating *) + TruncFailed* = 15; (* trunc operation failed *) + NestedCall* = 16; (* nested stream operation *) + FlushFailed* = 17; (* flush operation failed *) + errorcodes* = 18; (* number of error codes *) + + (* === private constants ======================================= *) + bufsize = 8192; (* should be the file system block size *) + defaulttermch = 0AX; (* default line terminator (for linebuf) *) + + TYPE + Address* = Types.Address; + Count* = Types.Count; + Byte* = Types.Byte; + Whence* = SHORTINT; (* Whence = (fromStart, fromPos, fromEnd); *) + CapabilitySet* = SET; (* OF Capability; *) + BufMode* = SHORTINT; + ErrorCode* = SHORTINT; + Stream* = POINTER TO StreamRec; + Message* = RECORD (Objects.ObjectRec) END; + + (* the buffering system: + + buffers are always on bufsize-boundaries + + ok: the other components are defined + pos: file position of cont[0] (pos MOD bufsize = 0) + cont: valid data: cont[rbegin]..cont[rend-1] (read-region) + written data: cont[wbegin]..cont[wend-1] (write-region) + + both regions are maintained (even for non-rw streams) + *) + Buffer = POINTER TO BufferRec; + BufferRec = + RECORD + ok: BOOLEAN; (* TRUE if other components are valid *) + pos: Count; (* file position which corresponds to cont[0] *) + rbegin: Count; (* read-region: starting index *) + rend: Count; (* read-region: ending index *) + wbegin: Count; (* write-region: starting index of dirty region *) + wend: Count; (* write-region: ending index *) + cont: ARRAY bufsize OF Byte; (* buffer contents *) + nextfree: Buffer; (* only needed for released buffers *) + (* components for buffers which are members of a buffer pool *) + prevh, nexth: Buffer; (* next buffer with same the hash value *) + preva, nexta: Buffer; (* sorted list of buffers (access time) *) + END; + + CONST + hashtabsize = 128; (* size of bucket table *) + TYPE + BucketTable = ARRAY hashtabsize OF Buffer; + BufferPool = POINTER TO BufferPoolRec; + BufferPoolRec = + RECORD + maxbuf: INTEGER; (* maximal number of buffers to be used *) + nbuf: INTEGER; (* number of buffers in use *) + bucket: BucketTable; + (* list of all buffers sorted after the last access time; + tail points to the buffer most recently accessed + *) + head, tail: Buffer; + END; + + TYPE + AddrIOProc* = PROCEDURE (s: Stream; ptr: Address; cnt: Count) : Count; + BufIOProc* = PROCEDURE (s: Stream; VAR buf: ARRAY OF Byte; + off, cnt: Count) : Count; + SeekProc* = PROCEDURE (s: Stream; cnt: Count; whence: Whence) : BOOLEAN; + TellProc* = PROCEDURE (s: Stream; VAR cnt: Count) : BOOLEAN; + ReadProc* = PROCEDURE (s: Stream; VAR byte: Byte) : BOOLEAN; + WriteProc* = PROCEDURE (s: Stream; byte: Byte) : BOOLEAN; + TruncProc* = PROCEDURE (s: Stream; cnt: Count) : BOOLEAN; + FlushProc* = PROCEDURE (s: Stream) : BOOLEAN; + CloseProc* = PROCEDURE (s: Stream) : BOOLEAN; + HandlerProc* = PROCEDURE (s: Stream; VAR msg: Message); + + Interface* = POINTER TO InterfaceRec; + InterfaceRec* = + RECORD + (Objects.ObjectRec) + addrread*: AddrIOProc; (* read, addrio *) + addrwrite*: AddrIOProc; (* write, addrio *) + bufread*: BufIOProc; (* read, bufio *) + bufwrite*: BufIOProc; (* write, bufio *) + read*: ReadProc; (* read *) + write*: WriteProc; (* write *) + seek*: SeekProc; (* seek *) + tell*: TellProc; (* tell *) + trunc*: TruncProc; (* trunc *) + flush*: FlushProc; (* flush *) + close*: CloseProc; (* close *) + handler*: HandlerProc; (* handler *) + END; + + StreamRec* = + RECORD + (Services.ObjectRec) + (* following components are set after i/o-operations *) + count*: Count; (* resulting count of last operation *) + errors*: INTEGER; (* incremented for each error; may be set to 0 *) + error*: BOOLEAN; (* last operation successful? *) + lasterror*: ErrorCode; (* error code of last error *) + eof*: BOOLEAN; (* last read-operation with count=0 returned *) + (* === private part ============================================ *) + prev, next: Stream; (* list of open streams *) + if: Interface; caps: CapabilitySet; + bufmode: BufMode; (* buffering mode *) + bidirect: BOOLEAN; (* bidirectional buffering? *) + termch: Byte; (* flush on termch (linebuf only) *) + inlist: BOOLEAN; (* member of the list of opened streams? *) + tiedStream: Stream; (* to be flushed before read operations *) + buf: Buffer; (* current buffer; = NIL for unbuffered streams *) + wbuf: Buffer; (* buffer for writing (only if bidirect = TRUE) *) + bufpool: BufferPool; (* only if bufmode = bufpool *) + validpos: BOOLEAN; (* pos valid? *) + pos: Count; (* current position in stream *) + maxpos: Count; (* maximal position until now (only if buf # NIL) *) + left: Count; (* number of bytes left in buf (after pos) *) + write: Count; (* number of bytes which can be written in buf *) + rpos: Count; (* current position of if.tell *) + wextensible: BOOLEAN; (* write region extensible? *) + eofFound: BOOLEAN; (* eof seen yet? temporary use only *) + lock: BOOLEAN; (* avoid recursive operations *) + flushEvent: Events.EventType; (* valid if # NIL *) + closeEvent: Events.EventType; (* valid if # NIL *) + END; + VAR + type: Services.Type; + + TYPE + (* each error causes an event; + the error number is stored in event.errorcode; + the associated text can be taken from event.message + *) + Event* = POINTER TO EventRec; + EventRec* = + RECORD + (Events.EventRec) + stream*: Stream; + errorcode*: ErrorCode; + END; + + VAR + null*: Stream; (* accepts any output; does not return input *) + (* these streams are set by other modules; + after initialization of Streams they equal `null'; + so, connections with the standard UNIX streams must be + done by other modules + *) + stdin*, stdout, stderr: Stream; + errormsg*: ARRAY errorcodes OF Events.Message; + error*: Events.EventType; + + (* === private variables ========================================== *) + + opened: Stream; (* list of opened streams *) + (* this list has been reduced to the set of streams which + need to be cleaned up explicitly; + all other streams are subject to the garbage collection + even if Close has never been called for them + *) + freelist: Buffer; (* list of free buffers *) + nullif: Interface; (* interface of null-devices *) + + (* === private procedures ========================================= *) + + PROCEDURE NewStream(s: Stream); + BEGIN + IF s.inlist THEN + s.prev := NIL; + s.next := opened; + IF opened # NIL THEN + opened.prev := s; + END; + opened := s; + END; + END NewStream; + + PROCEDURE OldStream(s: Stream); + BEGIN + IF s.inlist THEN + IF s.prev # NIL THEN + s.prev.next := s.next; + ELSE + opened := s.next; + END; + IF s.next # NIL THEN + s.next.prev := s.prev; + END; + END; + END OldStream; + + PROCEDURE NewBuffer(VAR b: Buffer); + BEGIN + IF freelist # NIL THEN + b := freelist; + freelist := freelist.nextfree; + ELSE + NEW(b); + END; + b.nextfree := NIL; + b.ok := FALSE; + END NewBuffer; + + PROCEDURE OldBuffer(VAR b: Buffer); + BEGIN + b.nextfree := freelist; + freelist := b; + b := NIL; + END OldBuffer; + + PROCEDURE Error(s: Stream; code: ErrorCode); + VAR + event: Event; + BEGIN + IF s # NIL THEN + INC(s.errors); + s.error := TRUE; + s.lasterror := code; + + (* generate error event *) + NEW(event); + event.type := error; + event.message := errormsg[code]; + event.stream := s; + event.errorcode := code; + RelatedEvents.Raise(s, event); + END; + END Error; + + PROCEDURE ^ InternalFlush(s: Stream) : BOOLEAN; + + (* ===== management of buffer pool ================================== *) + + PROCEDURE InitBufPool(s: Stream); + VAR + index: INTEGER; + BEGIN + s.bufpool.maxbuf := 16; (* default size *) + s.bufpool.nbuf := 0; (* currently, no buffers are allocated *) + s.bufpool.head := NIL; s.bufpool.tail := NIL; + index := 0; + WHILE index < hashtabsize DO + s.bufpool.bucket[index] := NIL; + INC(index); + END; + END InitBufPool; + + PROCEDURE HashValue(pos: Count) : INTEGER; + (* HashValue returns a hash value for pos *) + BEGIN + RETURN SHORT(pos DIV bufsize) MOD hashtabsize + END HashValue; + + PROCEDURE FindBuffer(s: Stream; pos: Count; VAR buf: Buffer) : BOOLEAN; + VAR + index: INTEGER; + bp: Buffer; + BEGIN + index := HashValue(pos); + bp := s.bufpool.bucket[index]; + WHILE bp # NIL DO + IF bp.pos = pos THEN + buf := bp; RETURN TRUE + END; + bp := bp.nexth; (* next buffer with same hash value *) + END; + buf := NIL; + RETURN FALSE + END FindBuffer; + + PROCEDURE GetBuffer(s: Stream); + (* look for buffer for s.pos and make it to the current buffer; + set s.left and s.write in dependance of s.pos + *) + VAR + buf: Buffer; + pos: Count; (* buffer boundary for s.pos *) + posindex: Count; (* buf[posindex] corresponds to s.pos *) + index: INTEGER; (* index into bucket table of the buffer pool *) + + PROCEDURE InitBuf(buf: Buffer); + VAR + index: INTEGER; (* of bucket table *) + BEGIN + buf.ok := TRUE; + buf.pos := pos; + buf.rbegin := posindex; buf.rend := posindex; s.left := 0; + buf.wbegin := posindex; buf.wend := posindex; + s.write := bufsize - posindex; + buf.nextfree := NIL; + + (* insert buf into hash list *) + index := HashValue(pos); + buf.prevh := NIL; + buf.nexth := s.bufpool.bucket[index]; + IF buf.nexth # NIL THEN + buf.nexth.prevh := buf; + END; + s.bufpool.bucket[index] := buf; + + (* buf is already at the end of the sorted list if we + re-use an old buffer + *) + IF s.bufpool.tail # buf THEN + (* append buf to the sorted list *) + buf.nexta := NIL; + IF s.bufpool.tail = NIL THEN + s.bufpool.head := buf; + buf.preva := NIL; + ELSE + s.bufpool.tail.nexta := buf; + buf.preva := s.bufpool.tail; + END; + s.bufpool.tail := buf; + END; + END InitBuf; + + PROCEDURE UseBuffer(s: Stream; buf: Buffer); + (* make buf to the current buffer of s *) + BEGIN + IF s.buf # buf THEN + (* remove buf from sorted list *) + IF buf.preva # NIL THEN + buf.preva.nexta := buf.nexta; + ELSE + s.bufpool.head := buf.nexta; + END; + IF buf.nexta # NIL THEN + buf.nexta.preva := buf.preva; + ELSE + s.bufpool.tail := buf.preva; + END; + + (* append buf to sorted list *) + buf.nexta := NIL; + IF s.bufpool.tail = NIL THEN + s.bufpool.head := buf; + buf.preva := NIL; + ELSE + s.bufpool.tail.nexta := buf; + buf.preva := s.bufpool.tail; + END; + s.bufpool.tail := buf; + + (* set current buf of s to buf *) + s.buf := buf; + + (* update s.left and s.write *) + IF buf.rbegin = buf.rend THEN + buf.rbegin := posindex; buf.rend := posindex; s.left := 0; + ELSIF (posindex >= buf.rbegin) & (posindex < buf.rend) THEN + s.left := buf.rend - posindex; + ELSE + s.left := 0; + END; + IF buf.wbegin = buf.wend THEN + buf.wbegin := posindex; buf.wend := posindex; + s.write := bufsize - posindex; + ELSIF (posindex >= buf.wbegin) & (posindex < buf.wend) THEN + s.write := bufsize - posindex; + ELSE + s.write := 0; + END; + END; + END UseBuffer; + + BEGIN (* GetBuffer *) + posindex := s.pos MOD bufsize; + pos := s.pos - posindex; + + IF ~s.buf.ok THEN + (* init first allocated buffer which has not been used until now *) + InitBuf(s.buf); + INC(s.bufpool.nbuf); + ELSIF s.buf.pos # pos THEN + IF FindBuffer(s, pos, buf) THEN + UseBuffer(s, buf); + ELSE + IF s.bufpool.nbuf >= s.bufpool.maxbuf THEN + (* re-use already allocated buffer *) + buf := s.bufpool.head; + UseBuffer(s, buf); + IF buf.wbegin # buf.wend THEN + IF ~InternalFlush(s) THEN END; + END; + + (* remove buf from hash list *) + IF buf.prevh # NIL THEN + buf.prevh.nexth := buf.nexth; + ELSE + index := HashValue(buf.pos); + s.bufpool.bucket[index] := buf.nexth; + END; + IF buf.nexth # NIL THEN + buf.nexth.prevh := buf.prevh; + END; + + InitBuf(buf); + ELSE + (* allocate and initialize new buffer *) + NewBuffer(buf); + InitBuf(buf); + INC(s.bufpool.nbuf); + END; + s.buf := buf; + END; + END; + END GetBuffer; + + PROCEDURE FlushBufPool(s: Stream) : BOOLEAN; + VAR + buf: Buffer; + ok: BOOLEAN; + BEGIN + ok := TRUE; + IF s.bufpool.nbuf > 0 THEN + buf := s.bufpool.head; + WHILE buf # NIL DO + s.buf := buf; + ok := InternalFlush(s) & ok; + buf := buf.nexta; + END; + END; + RETURN ok + END FlushBufPool; + + PROCEDURE ReleaseBufPool(s: Stream); + (* precondition: all buffers are flushed *) + VAR + buf: Buffer; + BEGIN + IF s.bufpool.nbuf > 0 THEN + buf := s.bufpool.head; + WHILE buf # NIL DO + s.buf := buf; + OldBuffer(s.buf); + buf := buf.nexta; + END; + END; + NewBuffer(s.buf); + InitBufPool(s); + END ReleaseBufPool; + + (* ================================================================== *) + + PROCEDURE GetBufMode*(s: Stream) : BufMode; + BEGIN + RETURN s.bufmode + END GetBufMode; + + PROCEDURE LineTerm*(s: Stream; termch: Byte); + (* set line terminator of `s' (linebuf) to `termch' *) + BEGIN + s.error := FALSE; + IF s.bufmode = linebuf THEN + s.termch := termch; + ELSE + Error(s, NotLineBuffered); + END; + END LineTerm; + + PROCEDURE Tie*(in, out: Stream); + (* PRE: `in' is an line buffered input stream, + `out' an output stream, + and `in' # `out'; + causes `out' to be flushed before reading from `in'; + `out' may be NIL to undo the effect + *) + BEGIN + in.error := FALSE; + IF in.bufmode # linebuf THEN + Error(in, NotLineBuffered); RETURN + END; + IF (in = out) OR ~(read IN in.caps) OR + (out # NIL) & ~(write IN out.caps) THEN + Error(in, BadParameters); RETURN + END; + in.tiedStream := out; + END Tie; + + PROCEDURE SetBufferPoolSize*(s: Stream; nbuf: INTEGER); + BEGIN + s.error := FALSE; + IF SYS.TAS(s.lock) THEN + Error(s, NestedCall); RETURN + END; + IF (s.bufmode = bufpool) & (nbuf >= 1) THEN + s.bufpool.maxbuf := nbuf; + END; + s.lock := FALSE; + END SetBufferPoolSize; + + PROCEDURE GetBufferPoolSize*(s: Stream; VAR nbuf: INTEGER); + BEGIN + s.error := FALSE; + CASE s.bufmode OF + | nobuf: nbuf := 0; + | linebuf: nbuf := 1; + | onebuf: nbuf := 1; + | bufpool: nbuf := s.bufpool.maxbuf; + END; + END GetBufferPoolSize; + + PROCEDURE Capabilities*(s: Stream) : CapabilitySet; + BEGIN + s.error := FALSE; + RETURN s.caps + END Capabilities; + + PROCEDURE GetFlushEvent*(s: Stream; VAR type: Events.EventType); + (* `type' will be raised BEFORE every flush operation *) + BEGIN + s.error := FALSE; + IF s.flushEvent = NIL THEN + Events.Define(s.flushEvent); + END; + type := s.flushEvent; + END GetFlushEvent; + + PROCEDURE GetCloseEvent*(s: Stream; VAR type: Events.EventType); + (* `type' will be raised BEFORE the stream gets closed; + that means write operations etc. are legal + *) + BEGIN + s.error := FALSE; + IF s.closeEvent = NIL THEN + Events.Define(s.closeEvent); + END; + type := s.closeEvent; + END GetCloseEvent; + + PROCEDURE Close*(s: Stream) : BOOLEAN; + VAR + event: Event; + type: Events.EventType; + otherStream: Stream; + BEGIN + s.error := FALSE; + + IF (s.closeEvent # NIL) & ~SYS.TAS(s.lock) THEN + type := s.closeEvent; s.closeEvent := NIL; + s.lock := FALSE; + Events.SetPriority(type, Events.GetPriority() + 1); + NEW(event); + event.type := type; + event.message := "close event of Streams"; + event.stream := s; + Events.Raise(event); + END; + + IF ~SYS.TAS(s.lock) THEN + IF write IN s.caps THEN + IF s.bufmode = bufpool THEN + IF ~FlushBufPool(s) THEN END; + ELSE + IF ~InternalFlush(s) THEN END; + END; + END; + IF close IN s.caps THEN + IF ~s.if.close(s) THEN + Error(s, CloseFailed); + END; + END; + IF s.buf # NIL THEN + IF s.bufmode = bufpool THEN + ReleaseBufPool(s); + END; + OldBuffer(s.buf); + END; + OldStream(s); + + (* check if this stream has been tied to another stream *) + otherStream := opened; + WHILE otherStream # NIL DO + IF otherStream.tiedStream = s THEN + otherStream.tiedStream := NIL; (* undo tie operation *) + END; + otherStream := otherStream.next; + END; + (* s.lock remains TRUE to prevent further operations *) + Resources.Notify(s, Resources.terminated); + RETURN ~s.error + ELSE + Error(s, NestedCall); + RETURN FALSE + END; + END Close; + + PROCEDURE Release*(s: Stream); + BEGIN + IF ~Close(s) THEN END; + END Release; + + PROCEDURE CloseAll*; + BEGIN + WHILE opened # NIL DO + (* that's no endless loop; see Close/OldStream *) + Release(opened); + END; + END CloseAll; + + PROCEDURE NotificationHandler(event: Events.Event); + VAR + s: Stream; + BEGIN + IF ~(event IS Resources.Event) THEN RETURN END; + WITH event: Resources.Event DO + IF ~(event.resource IS Stream) THEN RETURN END; + s := event.resource(Stream); + IF event.change IN {Resources.unreferenced, Resources.terminated} THEN + IF ~s.lock THEN + Release(s); + END; + END; + END; + END NotificationHandler; + + PROCEDURE Init*(s: Stream; if: Interface; caps: CapabilitySet; + bufmode: BufMode); + + VAR + eventType: Events.EventType; + type: Services.Type; + + PROCEDURE InitBidirectionalBuffering(s: Stream); + BEGIN + s.validpos := TRUE; + s.pos := 0; + NewBuffer(s.wbuf); + s.buf.ok := TRUE; s.buf.rbegin := 0; s.buf.rend := 0; s.buf.pos := 0; + s.wbuf.ok := TRUE; s.wbuf.wbegin := 0; s.wbuf.wend := 0; + s.wbuf.pos := 0; + s.left := 0; s.write := bufsize; + END InitBidirectionalBuffering; + + BEGIN + ASSERT((s # NIL) & (if # NIL) & ({read, write} * caps # {})); + Services.GetType(s, type); ASSERT(type # NIL); + s.inlist := (close IN caps) OR (bufmode # nobuf) & (write IN caps); + NewStream(s); + (* initialize public part *) + s.count := 0; + s.errors := 0; + s.error := FALSE; + s.lasterror := 0; + s.eof := FALSE; + (* private part *) + s.if := if; s.caps := caps; + s.bufmode := bufmode; + s.validpos := FALSE; + s.left := 0; s.write := 0; + s.tiedStream := NIL; + IF bufmode IN {linebuf, onebuf, bufpool} THEN + NewBuffer(s.buf); + IF (bufmode = bufpool) & ~(seek IN caps) THEN + bufmode := onebuf; + END; + CASE bufmode OF + | linebuf: s.termch := defaulttermch; + | bufpool: NEW(s.bufpool); InitBufPool(s); + ELSE + END; + s.maxpos := 0; + s.wextensible := {read, write, seek, tell, holes} * caps = + {read, write, seek, tell}; + s.bidirect := {read, write, seek, tell, trunc} * caps = {read, write}; + IF s.bidirect THEN + InitBidirectionalBuffering(s); + ELSE + s.wbuf := NIL; + END; + ELSE + s.buf := NIL; + s.wbuf := NIL; + s.wextensible := FALSE; + s.bidirect := FALSE; + END; + s.flushEvent := NIL; + s.closeEvent := NIL; + Resources.TakeInterest(s, eventType); + Events.Handler(eventType, NotificationHandler); + s.lock := FALSE; + END Init; + + PROCEDURE Send*(s: Stream; VAR message: Message); + BEGIN + IF ~SYS.TAS(s.lock) THEN + IF handler IN s.caps THEN + s.if.handler(s, message); + ELSE + Error(s, NoHandlerDefined); + END; + s.lock := FALSE; + ELSE + Error(s, NestedCall); + END; + END Send; + + (* === private i/o procedures ================================= *) + + PROCEDURE ValidPos(s: Stream); + BEGIN + IF ~s.validpos THEN + IF tell IN s.caps THEN + IF ~s.if.tell(s, s.pos) OR (s.pos < 0) THEN + Error(s, TellFailed); + s.pos := 0; + END; + ELSE + s.pos := 0; + END; + s.rpos := s.pos; + s.validpos := TRUE; + s.left := 0; + s.write := 0; + END; + END ValidPos; + + PROCEDURE InitBuf(s: Stream); + BEGIN + IF s.bufmode = bufpool THEN + GetBuffer(s); + ELSE + s.buf.pos := s.pos - s.pos MOD bufsize; + s.buf.wbegin := s.pos MOD bufsize; + s.write := bufsize - s.buf.wbegin; + s.buf.wend := s.buf.wbegin; + s.buf.rbegin := s.buf.wbegin; + s.buf.rend := s.buf.wbegin; + s.left := 0; + s.buf.ok := TRUE; + END; + END InitBuf; + + PROCEDURE FillBuf(s: Stream) : BOOLEAN; + (* return FALSE on EOF or errors *) + VAR + offset, count: Count; + posindex: Count; (* s.pos MOD bufsize *) + + PROCEDURE Fill(s: Stream; VAR offset, count: Count) : BOOLEAN; + (* try to fill buf.cont[offset]..buf.cont[offset+count-1]; + return FALSE on EOF; + Fill always extends a read region: + s.buf.rend is set to offset + the number of bytes read + *) + VAR + linetermseen: BOOLEAN; + byte: Byte; + BEGIN + IF s.eofFound THEN + RETURN FALSE + END; + IF addrio IN s.caps THEN + s.buf.rend := s.if.addrread(s, SYSTEM.ADR(s.buf.cont[offset]), count) + + offset; + ELSIF bufio IN s.caps THEN + s.buf.rend := s.if.bufread(s, s.buf.cont, offset, count) + offset; + ELSIF s.bufmode = linebuf THEN + s.buf.rend := offset; linetermseen := FALSE; + WHILE ~linetermseen & (s.buf.rend < offset+count) & + s.if.read(s, byte) DO + s.buf.cont[s.buf.rend] := byte; INC(s.buf.rend); + linetermseen := byte = s.termch; + END; + s.eofFound := ~linetermseen & + (s.buf.rend < offset+count); (* s.if.read failed? *) + ELSE + s.buf.rend := offset; + WHILE (s.buf.rend < offset+count) & + s.if.read(s, s.buf.cont[s.buf.rend]) DO + INC(s.buf.rend); + END; + s.eofFound := s.buf.rend < offset+count; (* s.if.read failed? *) + END; + (* negative counts of addrread or bufread indicate read errors *) + IF s.buf.rend < offset THEN + (* note error and recover s.buf.rend *) + Error(s, ReadFailed); + s.buf.rend := offset; + END; + INC(s.rpos, s.buf.rend - offset); + IF s.buf.rend > offset THEN + DEC(count, s.buf.rend - offset); + offset := s.buf.rend; + RETURN TRUE + ELSE + s.eofFound := TRUE; + RETURN FALSE + END; + END Fill; + + BEGIN (* FillBuf *) + ValidPos(s); + posindex := s.pos MOD bufsize; + s.eofFound := FALSE; + + (* flush associated output streams (line buffered streams only) *) + IF s.bufmode = linebuf THEN + IF write IN s.caps THEN + IF ~InternalFlush(s) THEN END; + END; + IF (s.tiedStream # NIL) & ~SYS.TAS(s.tiedStream.lock) THEN + IF ~InternalFlush(s.tiedStream) THEN END; + s.tiedStream.lock := FALSE; + END; + END; + + (* get a valid buffer and set + offset and count to the buffer range which is to be filled; + on default, we want to fill the whole buffer + *) + offset := 0; count := bufsize; (* default *) + IF ~s.buf.ok THEN + InitBuf(s); + ELSIF s.bidirect THEN + s.buf.rbegin := 0; s.buf.rend := 0; s.pos := 0; posindex := 0; + ELSE + IF s.bufmode = bufpool THEN + GetBuffer(s); + IF s.left > 0 THEN + (* buffer is already filled *) + s.eof := FALSE; RETURN TRUE + END; + ELSIF s.buf.pos # s.pos - posindex THEN + (* reuse filled buffer *) + IF write IN s.caps THEN + IF ~InternalFlush(s) THEN END; + END; + InitBuf(s); + END; + IF s.buf.rbegin # s.buf.rend THEN + IF (write IN s.caps) & + (s.buf.wbegin <= posindex) & (s.buf.wend > posindex) THEN + (* set read region to write region *) + s.buf.rbegin := s.buf.wbegin; s.buf.rend := s.buf.wend; + s.left := s.buf.wend - posindex; + s.eof := FALSE; RETURN TRUE + ELSIF s.buf.rend = posindex THEN + (* stream position equals end of read region *) + offset := s.buf.rend; count := bufsize - offset; + END; + END; + + (* take care of the write region by limiting count; + note that s.pos does *not* point into the write region; + this is guaranteed by WritePart and other operations + which would have extended the read region in such a case + *) + IF (write IN s.caps) & (s.buf.wbegin # s.buf.wend) THEN + IF s.buf.wbegin >= offset THEN + IF s.buf.wbegin > posindex THEN + (* write-region behind current position *) + count := s.buf.wbegin - offset; + ELSE + (* write-region before current position *) + offset := s.buf.wend; count := bufsize - offset; + END; + END; + IF (s.buf.pos + s.buf.wbegin = s.rpos) & ~(seek IN s.caps) THEN + (* flush if the start of write region corresponds to real + file position and we are not able to change the position + *) + IF ~InternalFlush(s) THEN END; + END; + END; + END; + + (* set the real position to the position we want to read from *) + IF ~s.bidirect & (s.buf.pos + offset # s.rpos) THEN + IF (seek IN s.caps) & s.if.seek(s, s.buf.pos+offset, fromStart) THEN + s.rpos := s.buf.pos + offset; + ELSIF s.pos = s.rpos THEN + DEC(count, posindex - offset); + offset := posindex; + ELSIF seek IN s.caps THEN + Error(s, SeekFailed); RETURN FALSE + ELSE + Error(s, CannotSeek); RETURN FALSE + END; + END; + + (* try to fill buf[offset..offset+count-1]; + and set s.buf.rbegin & s.buf.rend to the new read region + *) + IF s.buf.rend # offset THEN + (* forget old read region if we cannot extend it *) + s.buf.rbegin := offset; s.buf.rend := offset; + END; + WHILE Fill(s, offset, count) & (posindex >= s.buf.rend) DO END; + + IF posindex >= s.buf.rend THEN + (* read operation failed *) + IF (s.pos > s.rpos) & + (seek IN s.caps) & s.if.seek(s, s.pos, fromStart) THEN + s.rpos := s.pos; + (* second try: we were not able to fill the whole buffer + but perhaps we are able to read what we were requested for + *) + DEC(count, posindex - offset); + offset := posindex; + s.buf.rbegin := offset; s.buf.rend := offset; + s.eofFound := FALSE; (* retry it *) + s.eof := ~Fill(s, offset, count); + ELSE + s.eof := TRUE; + END; + ELSE + s.eof := FALSE; + END; + + IF s.eof THEN + s.left := 0; + ELSE + s.left := s.buf.rend - posindex; + END; + + RETURN ~s.eof + END FillBuf; + + + (* ==== i/o operations ============================================== *) + + PROCEDURE ReadPart*(s: Stream; VAR buf: ARRAY OF Byte; + off, cnt: Count) : BOOLEAN; + (* fill buf[off..off+cnt-1] *) + + VAR + pos: Count; + partcnt: Count; + + PROCEDURE ReadBytesFromBuf(s: Stream; + VAR to: ARRAY OF Byte; + off, cnt: Count) : BOOLEAN; + VAR + bytes, max, spos: Count; + BEGIN + IF s.left = 0 THEN + IF s.eofFound OR ~FillBuf(s) THEN RETURN FALSE END; + END; + spos := s.pos MOD bufsize; + max := s.left; + IF max > cnt THEN + max := cnt; + END; + bytes := 0; + WHILE bytes < max DO + to[off] := s.buf.cont[spos]; + INC(off); INC(spos); INC(bytes); + END; + INC(s.pos, bytes); DEC(s.left, bytes); INC(s.count, bytes); + IF ~s.bidirect THEN + IF s.write >= bytes THEN + DEC(s.write, bytes); + ELSE + s.write := 0; + END; + END; + RETURN TRUE + END ReadBytesFromBuf; + + BEGIN (* ReadPart *) + IF SYS.TAS(s.lock) THEN + Error(s, NestedCall); + RETURN FALSE + END; + s.error := FALSE; s.count := 0; + IF ~(read IN s.caps) THEN + s.lock := FALSE; Error(s, CannotRead); RETURN FALSE + ELSIF (off < 0) OR (off+cnt > LEN(buf)) OR (cnt < 0) THEN + s.lock := FALSE; Error(s, BadParameters); RETURN FALSE + END; + IF cnt = 0 THEN s.lock := FALSE; RETURN TRUE END; + IF s.buf # NIL THEN + s.eofFound := FALSE; + WHILE (s.count < cnt) & + ReadBytesFromBuf(s, buf, s.count + off, cnt - s.count) DO + (* s.count is already incremented by ReadBytesFromBuf *) + END; + (* extend write region, if necessary *) + IF ~s.bidirect THEN + pos := s.pos MOD bufsize; + IF (s.write > 0) & (s.buf.wend < pos) THEN + IF s.buf.wbegin = s.buf.wend THEN + s.buf.wbegin := pos; + END; + s.buf.wend := pos; + END; + END; + ELSE + IF addrio IN s.caps THEN + s.count := s.if.addrread(s, SYSTEM.ADR(buf[off]), cnt); + IF (s.count > 0) & (s.count < cnt) THEN + LOOP + partcnt := s.if.addrread(s, + SYSTEM.ADR(buf[off + s.count]), cnt - s.count); + IF (partcnt < 0) OR (partcnt = 0) THEN EXIT END; + ASSERT(partcnt <= cnt - s.count); + INC(s.count, partcnt); + IF s.count = cnt THEN EXIT END; + END; + END; + ELSIF bufio IN s.caps THEN + s.count := s.if.bufread(s, buf, off, cnt); + IF (s.count > 0) & (s.count < cnt) THEN + LOOP + partcnt := s.if.bufread(s, buf, off + s.count, cnt - s.count); + IF (partcnt < 0) OR (partcnt = 0) THEN EXIT END; + ASSERT(partcnt <= cnt - s.count); + INC(s.count, partcnt); + IF s.count = cnt THEN EXIT END; + END; + END; + ELSE + s.count := 0; + WHILE (s.count < cnt) & s.if.read(s, buf[s.count+off]) DO + INC(s.count); + END; + END; + IF s.count < 0 THEN + s.count := 0; + Error(s, ReadFailed); + ELSE + s.eof := s.count = 0; + END; + END; + s.lock := FALSE; + RETURN s.count = cnt + END ReadPart; + + PROCEDURE Read*(s: Stream; VAR buf: ARRAY OF Byte) : BOOLEAN; + BEGIN + RETURN ReadPart(s, buf, 0, LEN(buf)) + END Read; + + PROCEDURE ReadByte*(s: Stream; VAR byte: Byte) : BOOLEAN; + VAR + ok: BOOLEAN; + pos: Count; + BEGIN + IF SYS.TAS(s.lock) THEN + Error(s, NestedCall); RETURN FALSE + END; + s.error := FALSE; + IF s.left = 0 THEN + IF ~(read IN s.caps) THEN + s.lock := FALSE; Error(s, CannotRead); s.count := 0; RETURN FALSE + END; + IF s.buf # NIL THEN + IF ~FillBuf(s) THEN + (* FillBuf sets s.eof *) + s.lock := FALSE; + s.count := 0; + RETURN FALSE + END; + ELSE + ok := s.if.read(s, byte); + IF ok THEN + s.count := 1; + ELSE + s.count := 0; + END; + s.eof := ~ok; + s.lock := FALSE; + RETURN ok + END; + END; + (* s.left > 0 *) + s.count := 1; + byte := s.buf.cont[s.pos MOD bufsize]; + INC(s.pos); DEC(s.left); + IF ~s.bidirect & (s.write # 0) THEN + DEC(s.write); + pos := s.pos MOD bufsize; + IF s.buf.wend < pos THEN + IF s.buf.wbegin = s.buf.wend THEN + s.buf.wbegin := pos; + END; + s.buf.wend := pos; + END; + END; + (* s.eof has been set by FillBuf *) + s.lock := FALSE; + RETURN TRUE + END ReadByte; + + PROCEDURE ReadPacket*(s: Stream; VAR buf: ARRAY OF Byte; + off, maxcnt: Count) : Count; + (* fill buf[off..] with next packet *) + BEGIN + IF s.left > 0 THEN + IF maxcnt > s.left THEN + maxcnt := s.left; + END; + IF ReadPart(s, buf, off, maxcnt) THEN END; + RETURN s.count + END; + + IF SYS.TAS(s.lock) THEN + Error(s, NestedCall); + s.count := 0; + RETURN 0 + END; + s.error := FALSE; s.count := 0; + IF ~(read IN s.caps) THEN + s.lock := FALSE; Error(s, CannotRead); s.count := 0; RETURN 0 + ELSIF (off < 0) OR (off+maxcnt > LEN(buf)) OR (maxcnt < 0) THEN + s.lock := FALSE; Error(s, BadParameters); s.count := 0; RETURN 0 + END; + IF maxcnt = 0 THEN s.lock := FALSE; RETURN 0 END; + + IF s.buf # NIL THEN + (* s.left = 0 *) + IF ~FillBuf(s) THEN + (* FillBuf sets s.eof *) + s.lock := FALSE; + RETURN 0 + END; + s.lock := FALSE; + IF maxcnt > s.left THEN + maxcnt := s.left; + END; + IF ReadPart(s, buf, off, maxcnt) THEN END; + RETURN s.count + END; + + (* s.buf = NIL *) + IF addrio IN s.caps THEN + s.count := s.if.addrread(s, SYSTEM.ADR(buf[off]), maxcnt); + ELSIF bufio IN s.caps THEN + s.count := s.if.bufread(s, buf, off, maxcnt); + ELSE + s.count := 0; + WHILE (s.count < maxcnt) & s.if.read(s, buf[s.count+off]) DO + INC(s.count); + END; + END; + IF s.count < 0 THEN + s.count := 0; + Error(s, ReadFailed); + ELSE + s.eof := s.count = 0; + END; + s.lock := FALSE; + RETURN s.count + END ReadPacket; + + PROCEDURE WritePart*(s: Stream; + (* read-only *) VAR buf: ARRAY OF Byte; + off, cnt: Count) : BOOLEAN; + (* write buf[off..off+cnt-1] to s *) + VAR + posindex: Count; + + PROCEDURE NewBuffer(s: Stream) : BOOLEAN; + (* flush and get new buffer *) + BEGIN + IF s.pos - posindex # s.buf.pos THEN + IF s.bufmode # bufpool THEN + IF ~InternalFlush(s) THEN RETURN FALSE END; + END; + InitBuf(s); + IF s.write # 0 THEN RETURN TRUE END; + END; + IF s.buf.wbegin = s.buf.wend THEN + (* nothing written into this buffer until now *) + s.buf.wbegin := posindex; s.buf.wend := posindex; + s.write := bufsize - posindex; + ELSIF s.wextensible & (s.buf.rbegin # s.buf.rend) THEN + (* check if the write region may be extended + over parts of the read region + *) + IF s.buf.wend < posindex THEN + (* write region before current position *) + IF (s.buf.rbegin <= s.buf.wend) & (s.buf.rend >= posindex) THEN + s.buf.wend := posindex; + s.write := bufsize - posindex; + END; + ELSE (* s.wbegin > posindex *) + (* write region after current position *) + IF (s.buf.rbegin <= posindex) & (s.buf.rend >= s.buf.wbegin) THEN + s.buf.wbegin := posindex; + s.write := bufsize - posindex; + END; + END; + END; + IF (* still *) s.write = 0 THEN + (* Flush necessary *) + IF ~InternalFlush(s) THEN RETURN FALSE END; + s.buf.wbegin := posindex; s.buf.wend := posindex; + s.write := bufsize - posindex; + END; + RETURN TRUE + END NewBuffer; + + PROCEDURE UpdateReadRegion(s: Stream); + BEGIN + (* update s.left and extend read region, if possible *) + IF s.buf.rbegin = s.buf.rend THEN + (* set read region to write region *) + s.buf.rbegin := s.buf.wbegin; s.buf.rend := s.buf.wend; + s.left := s.buf.rend - posindex; + ELSIF (s.buf.rbegin < s.buf.wbegin) & (s.buf.rend >= s.buf.wbegin) THEN + (* forward extension of read region possible *) + IF s.buf.rend < s.buf.wend THEN + s.buf.rend := s.buf.wend; + END; + s.left := s.buf.rend - posindex; + ELSIF (s.buf.rbegin <= s.buf.wend) & (s.buf.rend > s.buf.wend) THEN + (* backward extension of read region possible *) + IF s.buf.rbegin > s.buf.wbegin THEN + s.buf.rbegin := s.buf.wend; + END; + s.left := s.buf.rend - posindex; + ELSE + (* posindex does not fall into [s.buf.rbegin..s.buf.rend-1] *) + s.left := 0; + END; + IF s.pos = s.buf.pos + bufsize THEN + s.left := 0; + END; + END UpdateReadRegion; + + BEGIN + IF SYS.TAS(s.lock) THEN + Error(s, NestedCall); RETURN FALSE + END; + s.error := FALSE; s.count := 0; + IF ~(write IN s.caps) THEN + s.lock := FALSE; Error(s, CannotWrite); RETURN FALSE + ELSIF (off < 0) OR (off+cnt > LEN(buf)) OR (cnt < 0) THEN + s.lock := FALSE; Error(s, BadParameters); RETURN FALSE + ELSIF cnt = 0 THEN + s.lock := FALSE; RETURN TRUE + END; + + IF s.buf # NIL THEN + IF s.bidirect THEN + WHILE s.count < cnt DO + IF (s.write = 0) & ~InternalFlush(s) THEN + s.lock := FALSE; RETURN FALSE + END; + s.wbuf.cont[s.wbuf.wend] := buf[off + s.count]; + INC(s.wbuf.wend); INC(s.count); DEC(s.write); + IF (s.bufmode = linebuf) & + (buf[s.count+off-1] = s.termch) THEN + IF ~InternalFlush(s) THEN + s.lock := FALSE; RETURN FALSE + END; + END; + END; + ELSE + ValidPos(s); + posindex := s.pos MOD bufsize; + IF ~s.buf.ok THEN + InitBuf(s); + END; + + (* copy from buf to s.buf *) + WHILE s.count < cnt DO + IF s.write = 0 THEN + posindex := s.pos MOD bufsize; + IF s.count > 0 THEN + UpdateReadRegion(s); + END; + IF ~NewBuffer(s) THEN + s.lock := FALSE; RETURN FALSE + END; + END; + s.buf.cont[posindex] := buf[off + s.count]; + IF s.buf.wend = posindex THEN + INC(s.buf.wend); + END; + INC(s.count); INC(s.pos); DEC(s.write); INC(posindex); + IF (s.bufmode = linebuf) & + (buf[s.count+off-1] = s.termch) THEN + UpdateReadRegion(s); + IF ~InternalFlush(s) THEN + s.lock := FALSE; RETURN FALSE + END; + (* s.pos can be changed by InternalFlush *) + posindex := s.pos MOD bufsize; + END; + END; + UpdateReadRegion(s); + END; + ELSE (* unbuffered stream *) + IF addrio IN s.caps THEN + s.count := s.if.addrwrite(s, SYSTEM.ADR(buf[off]), cnt); + ELSIF bufio IN s.caps THEN + s.count := s.if.bufwrite(s, buf, off, cnt); + ELSE + s.count := 0; + WHILE (s.count < cnt) & s.if.write(s, buf[off+s.count]) DO + INC(s.count); + END; + END; + IF s.count # cnt THEN + Error(s, WriteFailed); + END; + END; + s.lock := FALSE; + RETURN s.count = cnt + END WritePart; + + PROCEDURE Write*(s: Stream; + (* read-only *) VAR buf: ARRAY OF Byte) : BOOLEAN; + BEGIN + RETURN WritePart(s, buf, 0, LEN(buf)) + END Write; + + PROCEDURE WritePartC*(s: Stream; buf: ARRAY OF Byte; + off, cnt: Count) : BOOLEAN; + (* write buf[off..off+cnt-1] to s *) + BEGIN + RETURN WritePart(s, buf, off, cnt) + END WritePartC; + + PROCEDURE WriteC*(s: Stream; buf: ARRAY OF Byte) : BOOLEAN; + BEGIN + RETURN WritePart(s, buf, 0, LEN(buf)) + END WriteC; + + PROCEDURE WriteByte*(s: Stream; byte: Byte) : BOOLEAN; + VAR + posindex: Count; + BEGIN + IF (s.write > 0) & ~SYS.TAS(s.lock) THEN + s.error := FALSE; s.count := 1; + + IF s.bidirect THEN + s.wbuf.cont[s.wbuf.wend] := byte; INC(s.wbuf.wend); DEC(s.write); + ELSE + (* put byte into s.buf *) + posindex := s.pos MOD bufsize; + s.buf.cont[posindex] := byte; + IF s.buf.wend = posindex THEN + INC(s.buf.wend); + END; + DEC(s.write); + + (* update s.buf.rend and s.left, if necessary *) + IF s.buf.rend = posindex THEN + INC(s.buf.rend); + END; + IF s.left # 0 THEN + DEC(s.left); + ELSIF s.buf.rbegin = s.buf.rend THEN + (* set read-region to write-region *) + s.buf.rbegin := s.buf.wbegin; s.buf.rend := s.buf.wend; + s.left := s.buf.wend - posindex; + END; + + INC(s.pos); + END; + + IF (s.bufmode = linebuf) & (byte = s.termch) THEN + IF ~InternalFlush(s) THEN + s.lock := FALSE; RETURN FALSE + END; + IF ~s.bidirect THEN + s.buf.wbegin := s.pos MOD bufsize; + END; + END; + + s.lock := FALSE; RETURN TRUE + ELSE + RETURN WritePart(s, byte, 0, 1) + END; + END WriteByte; + + PROCEDURE InternalSeek(s: Stream; offset: Count; whence: Whence) : BOOLEAN; + VAR + oldpos: Count; pos: Count; + BEGIN + s.error := FALSE; + IF s.bidirect THEN + Error(s, CannotSeek); RETURN FALSE + ELSIF s.buf = NIL THEN + IF ~(seek IN s.caps) THEN + Error(s, CannotSeek); RETURN FALSE + ELSIF ~s.if.seek(s, offset, whence) THEN + Error(s, SeekFailed); RETURN FALSE + END; + ELSE + IF ~s.validpos & (seek IN s.caps) THEN + IF (write IN s.caps) & ~InternalFlush(s) THEN END; + IF ~s.if.seek(s, offset, whence) THEN + Error(s, SeekFailed); RETURN FALSE + END; + IF whence = fromStart THEN + s.validpos := TRUE; + s.pos := offset; s.rpos := offset; + END; + ELSE + ValidPos(s); oldpos := s.pos; + IF s.pos > s.maxpos THEN + s.maxpos := s.pos; + END; + CASE whence OF + | fromStart: IF offset < 0 THEN + Error(s, SeekFailed); RETURN FALSE + END; + s.pos := offset; + | fromPos: IF s.pos + offset < 0 THEN + Error(s, SeekFailed); RETURN FALSE + END; + INC(s.pos, offset); + | fromEnd: IF (write IN s.caps) & ~InternalFlush(s) THEN END; + IF ~(seek IN s.caps) OR + ~s.if.seek(s, offset, whence) THEN + Error(s, SeekFailed); RETURN FALSE + END; + s.validpos := FALSE; ValidPos(s); + ELSE + Error(s, BadWhence); RETURN FALSE + END; + IF ~(holes IN s.caps) & (s.pos > s.maxpos) THEN + (* if holes are not permitted + we need to check the new position + *) + IF ~(seek IN s.caps) THEN + Error(s, CannotSeek); RETURN FALSE + ELSIF s.if.seek(s, s.pos, fromStart) THEN + s.rpos := s.pos; s.maxpos := s.pos; + ELSE + Error(s, SeekFailed); RETURN FALSE + END; + END; + IF s.buf.ok & (s.pos # oldpos) THEN + (* set s.left and s.write *) + IF (s.pos < s.buf.pos) OR (s.pos >= s.buf.pos + bufsize) THEN + s.left := 0; s.write := 0; + ELSE + pos := s.pos MOD bufsize; + IF s.buf.rbegin = s.buf.rend THEN + s.buf.rbegin := pos; s.buf.rend := pos; + END; + IF s.buf.wbegin = s.buf.wend THEN + s.buf.wbegin := pos; s.buf.wend := pos; + END; + IF s.pos > oldpos THEN + IF (pos >= s.buf.rbegin) & (pos < s.buf.rend) THEN + s.left := s.buf.rend - pos; + ELSE + s.left := 0; + END; + IF (pos >= s.buf.wbegin) & (pos <= s.buf.wend) THEN + s.write := bufsize - pos; + ELSE + s.write := 0; + END; + IF s.wextensible & + (s.write < s.left) & (s.buf.wbegin # s.buf.wend) THEN + (* s.write = 0 (else s.write >= s.left); + try to extend write-region to avoid + an unnecessary flush operation + *) + IF (s.buf.wbegin < pos) & + (s.buf.wend >= s.buf.rbegin) THEN + (* write-region is followed by read-region *) + s.buf.wend := pos; s.write := bufsize - pos; + ELSIF (pos < s.buf.wbegin) & + (s.buf.wbegin >= s.buf.rend) THEN + (* read-region is followed by write-region *) + s.buf.wbegin := pos; s.write := bufsize - pos; + END; + END; + ELSE (* s.pos < oldpos *) + IF (pos < s.buf.rbegin) OR (pos > s.buf.rend) THEN + s.left := 0; + ELSE + s.left := s.buf.rend - pos; + END; + IF (pos < s.buf.wbegin) OR (pos > s.buf.wend) THEN + s.write := 0; + ELSE + s.write := bufsize - pos; + END; + END; + END; + END; + END; + END; + IF s.left > 0 THEN + s.eof := FALSE; + END; + RETURN TRUE + END InternalSeek; + + PROCEDURE Seek*(s: Stream; offset: Count; whence: Whence) : BOOLEAN; + VAR + rval: BOOLEAN; + BEGIN + IF ~SYS.TAS(s.lock) THEN + rval := InternalSeek(s, offset, whence); + s.lock := FALSE; + RETURN rval + ELSE + Error(s, NestedCall); + RETURN FALSE + END; + END Seek; + + PROCEDURE Tell*(s: Stream; VAR offset: Count) : BOOLEAN; + BEGIN + IF ~SYS.TAS(s.lock) THEN + s.error := FALSE; + IF tell IN s.caps THEN + IF s.buf # NIL THEN + IF s.validpos THEN + offset := s.pos; + ELSIF s.if.tell(s, s.rpos) THEN + s.validpos := TRUE; + s.pos := s.rpos; + offset := s.pos; + ELSE + s.lock := FALSE; + Error(s, TellFailed); + END; + ELSIF ~s.if.tell(s, offset) THEN + s.lock := FALSE; + Error(s, TellFailed); + END; + ELSE + s.lock := FALSE; + Error(s, CannotTell); + END; + s.lock := FALSE; + ELSE + Error(s, NestedCall); + END; + RETURN ~s.error + END Tell; + + PROCEDURE GetPos*(s: Stream; VAR offset: Count); + (* IF ~Tell(s, offset) THEN offset := internal position END; *) + BEGIN + IF ~Tell(s, offset) THEN + IF SYS.TAS(s.lock) THEN + Error(s, NestedCall); + ELSE + ValidPos(s); + offset := s.pos; + s.lock := FALSE; + END; + END; + END GetPos; + + PROCEDURE SetPos*(s: Stream; offset: Count); + (* IF ~Seek(s, offset, fromStart) THEN END; *) + BEGIN + IF ~Seek(s, offset, fromStart) THEN END; + END SetPos; + + PROCEDURE ^ Touch*(s: Stream); + + PROCEDURE Trunc*(s: Stream; length: Count) : BOOLEAN; + (* truncate `s' to a total length of `length'; + following holds if holes are permitted: + (1) the current position remains unchanged + (2) the contents between `length' and + the current position is undefined + this call fails if holes are not permitted and the + current position is beyond `length' + *) + VAR + ok: BOOLEAN; + BEGIN + IF ~SYS.TAS(s.lock) THEN + IF (trunc IN s.caps) & (length >= 0) THEN + s.error := FALSE; ok := TRUE; + IF s.buf # NIL THEN + ValidPos(s); + IF ~(holes IN s.caps) & (s.pos > length) THEN + ok := FALSE; + ELSIF (s.bufmode = bufpool) OR s.buf.ok & + (s.buf.pos DIV bufsize >= length DIV bufsize) THEN + Touch(s); + END; + END; + IF ~ok OR ~s.if.trunc(s, length) THEN + s.lock := FALSE; Error(s, TruncFailed); + END; + ELSE + s.lock := FALSE; Error(s, CannotTrunc); + END; + s.lock := FALSE; + ELSE + Error(s, NestedCall); + END; + RETURN ~s.error + END Trunc; + + PROCEDURE Back*(s: Stream) : BOOLEAN; + (* undo last read operation (one byte); + because of the delayed buffer filling + Back is always successful for buffered streams + immediately after read-operations + *) + VAR + rval: BOOLEAN; + BEGIN + IF ~SYS.TAS(s.lock) THEN + s.error := FALSE; + IF read IN s.caps THEN + IF seek IN s.caps THEN + (* fails if s.pos = 0 *) + rval := InternalSeek(s, -1, 1) + ELSIF s.bidirect & s.buf.ok THEN + IF s.pos > 0 THEN + DEC(s.pos); INC(s.left); + rval := TRUE; + ELSE + rval := FALSE; + END; + ELSIF (s.buf # NIL) & s.buf.ok THEN + rval := InternalSeek(s, -1, 1) & (s.left > 0) + ELSE + rval := FALSE + END; + ELSE + s.lock := FALSE; Error(s, CannotRead); + rval := FALSE + END; + s.lock := FALSE; + RETURN rval + ELSE + Error(s, NestedCall); + RETURN FALSE + END; + END Back; + + PROCEDURE Insert*(s: Stream; byte: Byte) : BOOLEAN; + (* return `byte' on next read-operation *) + BEGIN + IF ~SYS.TAS(s.lock) THEN + s.error := FALSE; + IF read IN s.caps THEN + IF s.buf # NIL THEN + (* seek in buffer possible? *) + IF s.bidirect THEN + IF s.pos > 0 THEN + DEC(s.pos); s.buf.cont[s.pos] := byte; + RETURN TRUE + ELSE + RETURN FALSE + END; + ELSIF s.buf.ok & + (s.pos > s.buf.pos+s.buf.rbegin) & + (s.pos < s.buf.pos+s.buf.rend) & + InternalSeek(s, -1, 1) THEN + s.buf.cont[s.pos MOD bufsize] := byte; + s.lock := FALSE; + RETURN TRUE + ELSE + s.lock := FALSE; + RETURN FALSE + END; + ELSE + s.lock := FALSE; Error(s, Unbuffered); RETURN FALSE + END; + ELSE + s.lock := FALSE; Error(s, CannotRead); RETURN FALSE + END; + ELSE + Error(s, NestedCall); + RETURN FALSE + END; + END Insert; + + PROCEDURE InternalFlush(s: Stream) : BOOLEAN; + + PROCEDURE Write(s: Stream; buf: Buffer) : BOOLEAN; + + VAR + count: Count; + BEGIN + IF addrio IN s.caps THEN + count := s.if.addrwrite(s, SYSTEM.ADR(buf.cont[buf.wbegin]), + buf.wend-buf.wbegin); + ELSIF bufio IN s.caps THEN + count := s.if.bufwrite(s, buf.cont, + buf.wbegin, buf.wend-buf.wbegin); + ELSIF s.if.write(s, buf.cont[buf.wbegin]) THEN + count := 1; + ELSE + count := 0; + END; + IF count < 0 THEN + count := 0; + END; + INC(buf.wbegin, count); INC(s.rpos, count); + RETURN count > 0 + END Write; + + PROCEDURE FlushEvent; + VAR + event: Event; + BEGIN + IF s.flushEvent # NIL THEN + NEW(event); + event.type := s.flushEvent; + event.message := "flush event of Streams"; + event.stream := s; + Events.Raise(event); + END; + END FlushEvent; + + BEGIN + s.error := FALSE; + IF (write IN s.caps) & (s.buf # NIL) & s.buf.ok THEN + IF s.bidirect & (s.wbuf.wend > s.wbuf.wbegin) THEN + FlushEvent; + WHILE (s.wbuf.wend > s.wbuf.wbegin) & Write(s, s.wbuf) DO END; + IF s.wbuf.wend > s.wbuf.wbegin THEN + s.wbuf.wbegin := 0; s.wbuf.wend := 0; s.write := bufsize; + Error(s, WriteFailed); RETURN FALSE + END; + s.wbuf.wbegin := 0; s.wbuf.wend := 0; s.write := bufsize; + ELSIF ~s.bidirect & (s.buf.wend > s.buf.wbegin) THEN + FlushEvent; + ValidPos(s); + IF s.buf.pos + s.buf.wbegin # s.rpos THEN + IF ~(seek IN s.caps) THEN + Error(s, CannotSeek); + (* write in this case at the current position + else there is no easy way to write anyhow + *) + ELSIF ~s.if.seek(s, s.buf.pos + s.buf.wbegin, fromStart) THEN + s.buf.wend := s.buf.wbegin; s.write := 0; + Error(s, SeekFailed); RETURN FALSE + END; + s.rpos := s.buf.pos + s.buf.wbegin; + END; + WHILE (s.buf.wend > s.buf.wbegin) & Write(s, s.buf) DO END; + IF s.buf.wend > s.buf.wbegin THEN + s.buf.wend := s.buf.wbegin; s.write := bufsize - s.buf.wbegin; + Error(s, WriteFailed); RETURN FALSE + END; + IF {seek, tell, trunc} * s.caps = {} THEN + (* unidirectional pipeline; reset s.pos to avoid + unintentional flushes due to buffer boundaries + *) + s.pos := 0; s.rpos := 0; s.buf.pos := 0; + s.buf.wbegin := 0; s.buf.wend := 0; s.write := bufsize; + ELSE + IF (s.pos >= s.buf.pos) & (s.pos < s.buf.pos + bufsize) THEN + s.buf.wbegin := s.pos MOD bufsize; + s.buf.wend := s.buf.wbegin; + s.write := bufsize - s.buf.wbegin; + ELSE + s.write := 0; + END; + END; + END; + END; + RETURN TRUE + END InternalFlush; + + PROCEDURE Flush*(s: Stream) : BOOLEAN; + VAR + ok: BOOLEAN; + BEGIN + IF ~SYS.TAS(s.lock) THEN + IF s.bufmode = bufpool THEN + ok := FlushBufPool(s); + ELSE + ok := InternalFlush(s); + END; + IF ok & (flush IN s.caps) THEN + ok := s.if.flush(s); + IF ~ok THEN + Error(s, FlushFailed); + END; + END; + s.lock := FALSE; + RETURN ok + ELSE + Error(s, NestedCall); + RETURN FALSE + END; + END Flush; + + PROCEDURE InputInBuffer*(s: Stream) : BOOLEAN; + (* returns TRUE if the next byte to be read is buffered *) + VAR + buf: Buffer; + pos: Count; + BEGIN + IF s.bufmode = bufpool THEN + IF ~s.buf.ok THEN RETURN FALSE END; + pos := s.pos - s.pos MOD bufsize; + IF s.buf.pos # pos THEN + IF ~FindBuffer(s, pos, buf) THEN + RETURN FALSE + END; + pos := s.pos - buf.pos; + RETURN (pos >= buf.rbegin) & (pos < buf.rend) + END; + ELSIF s.bidirect THEN + RETURN s.left > 0 + END; + pos := s.pos MOD bufsize; + RETURN (read IN s.caps) & (s.buf # NIL) & s.buf.ok & + ((s.left > 0) OR + (write IN s.caps) & (s.buf.wbegin <= pos) & (s.buf.wend > pos)) + END InputInBuffer; + + PROCEDURE OutputInBuffer*(s: Stream) : BOOLEAN; + (* returns TRUE if Flush would lead to a write-operation *) + VAR + buf: Buffer; + BEGIN + IF s.bufmode = bufpool THEN + buf := s.bufpool.head; + WHILE buf # NIL DO + IF buf.wbegin # buf.wend THEN RETURN TRUE END; + buf := buf.nexta; + END; + RETURN FALSE + ELSIF s.bidirect THEN + RETURN s.wbuf.wend > s.wbuf.wbegin + ELSE + RETURN (write IN s.caps) & (s.buf # NIL) & s.buf.ok & + (s.buf.wend > s.buf.wbegin) + END; + END OutputInBuffer; + + PROCEDURE OutputWillBeBuffered*(s: Stream) : BOOLEAN; + (* returns TRUE if the next written byte will be buffered *) + VAR + buf: Buffer; + pos: Count; + BEGIN + IF s.bufmode = bufpool THEN + IF s.bufpool.nbuf < s.bufpool.maxbuf THEN RETURN TRUE END; + pos := s.pos - s.pos MOD bufsize; + IF s.buf.pos # pos THEN + IF ~FindBuffer(s, pos, buf) THEN RETURN FALSE END; + IF s.buf.wbegin = s.buf.wend THEN RETURN TRUE END; + pos := s.pos - buf.pos; + RETURN (pos >= buf.wbegin) & (pos <= buf.wend) OR + (buf.wbegin > 0) & (pos + 1 = buf.wbegin) + END; + ELSIF s.bidirect THEN + RETURN s.write > 0 + END; + RETURN (write IN s.caps) & (s.buf # NIL) & + ((s.write > 0) OR ~s.buf.ok) + END OutputWillBeBuffered; + + PROCEDURE Touch*(s: Stream); + (* forget any buffer contents *) + BEGIN + IF ~SYS.TAS(s.lock) THEN + s.error := FALSE; + IF write IN s.caps THEN + IF s.bufmode = bufpool THEN + IF ~FlushBufPool(s) THEN END; + ReleaseBufPool(s); + ELSE + IF ~InternalFlush(s) THEN END; + END; + END; + IF flush IN s.caps THEN + IF ~s.if.flush(s) THEN + Error(s, FlushFailed); + END; + END; + IF s.bidirect THEN + s.buf.rbegin := 0; s.buf.rend := 0; s.left := 0; + ELSE + s.validpos := FALSE; + IF s.buf # NIL THEN + s.buf.ok := FALSE; + s.left := 0; + s.write := 0; + s.eofFound := FALSE; + END; + END; + s.lock := FALSE; + ELSE + Error(s, NestedCall); + END; + END Touch; + + PROCEDURE Copy*(source, dest: Stream; maxcnt: Count) : BOOLEAN; + (* more efficient variants are possible *) + VAR + left, count, copied, read, written: Count; + buffer: ARRAY bufsize OF Byte; + ok: BOOLEAN; + BEGIN + IF maxcnt >= 0 THEN + read := 0; written := 0; ok := TRUE; + left := maxcnt; + LOOP + IF left = 0 THEN + EXIT + END; + ASSERT(left > 0); + IF left > bufsize THEN + count := bufsize; + ELSE + count := left; + END; + + ok := ReadPacket(source, buffer, 0, count) > 0; + ASSERT(source.count <= count); + INC(read, source.count); + IF ~ok THEN EXIT END; + + ok := WritePart(dest, buffer, 0, source.count); + ASSERT(dest.count <= source.count); + INC(written, dest.count); + IF ~ok THEN EXIT END; + + DEC(left, dest.count); + END; + source.count := read; dest.count := written; + RETURN ok + ELSE + copied := 0; + WHILE (ReadPacket(source, buffer, 0, bufsize) > 0) & + WritePart(dest, buffer, 0, source.count) DO + INC(copied, source.count); + END; + source.count := copied; dest.count := copied; + RETURN ~source.error & ~dest.error + END; + END Copy; + + (* === nulldev procedures ========================================== *) + + PROCEDURE NulldevRead(s: Stream; VAR byte: Byte) : BOOLEAN; + BEGIN + byte := 0X; + RETURN FALSE + END NulldevRead; + + PROCEDURE NulldevWrite(s: Stream; byte: Byte) : BOOLEAN; + BEGIN + RETURN TRUE + END NulldevWrite; + + PROCEDURE InitNullIf(VAR nullif: Interface); + BEGIN + NEW(nullif); + nullif.read := NulldevRead; + nullif.write := NulldevWrite; + END InitNullIf; + + PROCEDURE OpenNulldev(VAR s: Stream); + BEGIN + NEW(s); + Services.Init(s, type); + Init(s, nullif, {read, write}, nobuf); + END OpenNulldev; + + PROCEDURE ExitHandler(event: Events.Event); + (* flush all streams on exit; + we do not close them to allow output by other exit event handlers + *) + VAR s: Stream; + BEGIN + s := opened; + WHILE s # NIL DO + IF (s.bufmode # nobuf) & (write IN s.caps) THEN + IF ~Flush(s) THEN END; + END; + s := s.next; + END; + END ExitHandler; + + PROCEDURE FreeHandler(event: Events.Event); + (* set all free lists to NIL to return the associated storage + to the garbage collector + *) + BEGIN + freelist := NIL; + END FreeHandler; + +BEGIN + Services.CreateType(type, "Streams.Stream", ""); + + errormsg[NoHandlerDefined] := "no handler defined"; + errormsg[CannotRead] := "not opened for reading"; + errormsg[CannotSeek] := "not capable of seeking"; + errormsg[CloseFailed] := "close operation failed"; + errormsg[NotLineBuffered] := "stream is not line buffered"; + errormsg[SeekFailed] := "seek operation failed"; + errormsg[TellFailed] := "tell operation failed"; + errormsg[BadWhence] := "bad value of whence parameter"; + errormsg[CannotTell] := "not capable of telling current position"; + errormsg[WriteFailed] := "write operation failed"; + errormsg[CannotWrite] := "not opened for writing"; + errormsg[ReadFailed] := "read operation failed"; + errormsg[Unbuffered] := "operation is not valid for unbuffered streams"; + errormsg[BadParameters] := "bad parameter values"; + errormsg[CannotTrunc] := "not capable of truncating"; + errormsg[TruncFailed] := "trunc operation failed"; + errormsg[NestedCall] := "nested stream operation"; + errormsg[FlushFailed] := "flush operation failed"; + + Events.Define(error); Events.SetPriority(error, Priorities.liberrors); + Events.Ignore(error); + + opened := NIL; + InitNullIf(nullif); + OpenNulldev(null); stdin := null; stdout := null; stderr := null; + + Events.Handler(Process.termination, ExitHandler); + Events.Handler(Process.startOfGarbageCollection, FreeHandler); +END ulmStreams. diff --git a/src/lib/ulm/x86/ulmTypes.Mod b/src/lib/ulm/x86/ulmTypes.Mod new file mode 100644 index 00000000..3b4a8e19 --- /dev/null +++ b/src/lib/ulm/x86/ulmTypes.Mod @@ -0,0 +1,125 @@ +(* Ulm's Oberon Library + Copyright (C) 1989-2000 by University of Ulm, SAI, D-89069 Ulm, Germany + ---------------------------------------------------------------------------- + Ulm's Oberon Library is free software; you can redistribute it + and/or modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either version + 2 of the License, or (at your option) any later version. + + Ulm's Oberon Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + ---------------------------------------------------------------------------- + E-mail contact: oberon@mathematik.uni-ulm.de + ---------------------------------------------------------------------------- + $Id: Types.om,v 1.5 2000/12/13 10:03:00 borchert Exp $ + ---------------------------------------------------------------------------- + $Log: Types.om,v $ + Revision 1.5 2000/12/13 10:03:00 borchert + SetInt type used in msb constant + + Revision 1.4 2000/12/13 09:51:57 borchert + constants and types for the relationship of INTEGER and SET added + + Revision 1.3 1998/09/25 15:23:09 borchert + Real32..Real128 added + + Revision 1.2 1994/07/01 11:08:04 borchert + IntAddress, Int8/16/32, ToInt8/16/32 and bit/little endian stuff added + + Revision 1.1 1994/02/22 20:12:14 borchert + Initial revision + + ---------------------------------------------------------------------------- + AFB 9/93 + ---------------------------------------------------------------------------- +*) + +MODULE ulmTypes; + + (* compiler-dependent type definitions; + this version works for Ulm's Oberon Compilers on + following architectures: m68k and sparc + *) + + IMPORT SYS := SYSTEM; + + TYPE + Address* = LONGINT (*SYS.ADDRESS*); + UntracedAddress* = LONGINT; (*SYS.UNTRACEDADDRESS;*) + Count* = LONGINT; + Size* = Count; + Byte* = SYS.BYTE; + IntAddress* = LONGINT; + Int8* = SHORTINT; + Int16* = INTEGER; + Int32* = LONGINT; + Real32* = REAL; + Real64* = LONGREAL; + + CONST + bigEndian* = 0; (* SPARC, M68K etc *) + littleEndian* = 1; (* Intel 80x86, VAX etc *) + byteorder* = littleEndian; (* machine-dependent constant *) + TYPE + ByteOrder* = SHORTINT; (* bigEndian or littleEndian *) + + (* following constants and type definitions try to make + conversions from INTEGER to SET and vice versa more portable + to allow for bit operations on INTEGER values + *) + TYPE + SetInt* = LONGINT; (* INTEGER type that corresponds to SET *) + VAR msb* : SET; + msbIsMax*, msbIs0*: SHORTINT; + msbindex*, lsbindex*, nofbits*: LONGINT; + + PROCEDURE ToInt8*(int: LONGINT) : Int8; + BEGIN + RETURN SHORT(SHORT(int)) + END ToInt8; + + PROCEDURE ToInt16*(int: LONGINT) : Int16; + BEGIN + RETURN SYS.VAL(Int16, int) + END ToInt16; + + PROCEDURE ToInt32*(int: LONGINT) : Int32; + BEGIN + RETURN int + END ToInt32; + + PROCEDURE ToReal32*(real: LONGREAL) : Real32; + BEGIN + RETURN SHORT(real) + END ToReal32; + + PROCEDURE ToReal64*(real: LONGREAL) : Real64; + BEGIN + RETURN real + END ToReal64; + +BEGIN + msb := SYS.VAL(SET, MIN(SetInt)); + (* most significant bit, converted to a SET *) + (* we expect msbIsMax XOR msbIs0 to be 1; + this is checked for by an assertion + *) + msbIsMax := SYS.VAL(SHORTINT, (msb = {MAX(SET)})); + (* is 1, if msb equals {MAX(SET)} *) + msbIs0 := SYS.VAL(SHORTINT, (msb = {0})); + (* is 0, if msb equals {0} *) + msbindex := msbIsMax * MAX(SET); + (* set element that corresponds to the most-significant-bit *) + lsbindex := MAX(SET) - msbindex; + (* set element that corresponds to the lowest-significant-bit *) + nofbits := MAX(SET) + 1; + (* number of elements in SETs *) + + ASSERT((msbIs0 = 1) & (msbIsMax = 0) OR (msbIs0 = 0) & (msbIsMax = 1)); +END ulmTypes. diff --git a/src/lib/ulm/x86_64/ulmTypes.Mod b/src/lib/ulm/x86_64/ulmTypes.Mod new file mode 100644 index 00000000..017a6ca7 --- /dev/null +++ b/src/lib/ulm/x86_64/ulmTypes.Mod @@ -0,0 +1,125 @@ +(* Ulm's Oberon Library + Copyright (C) 1989-2000 by University of Ulm, SAI, D-89069 Ulm, Germany + ---------------------------------------------------------------------------- + Ulm's Oberon Library is free software; you can redistribute it + and/or modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either version + 2 of the License, or (at your option) any later version. + + Ulm's Oberon Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + ---------------------------------------------------------------------------- + E-mail contact: oberon@mathematik.uni-ulm.de + ---------------------------------------------------------------------------- + $Id: Types.om,v 1.5 2000/12/13 10:03:00 borchert Exp $ + ---------------------------------------------------------------------------- + $Log: Types.om,v $ + Revision 1.5 2000/12/13 10:03:00 borchert + SetInt type used in msb constant + + Revision 1.4 2000/12/13 09:51:57 borchert + constants and types for the relationship of INTEGER and SET added + + Revision 1.3 1998/09/25 15:23:09 borchert + Real32..Real128 added + + Revision 1.2 1994/07/01 11:08:04 borchert + IntAddress, Int8/16/32, ToInt8/16/32 and bit/little endian stuff added + + Revision 1.1 1994/02/22 20:12:14 borchert + Initial revision + + ---------------------------------------------------------------------------- + AFB 9/93 + ---------------------------------------------------------------------------- +*) + +MODULE ulmTypes; + + (* compiler-dependent type definitions; + this version works for Ulm's Oberon Compilers on + following architectures: m68k and sparc + *) + + IMPORT SYS := SYSTEM; + + TYPE + Address* = LONGINT (*SYS.ADDRESS*); + UntracedAddress* = LONGINT; (*SYS.UNTRACEDADDRESS;*) + Count* = LONGINT; + Size* = Count; + Byte* = SYS.BYTE; + IntAddress* = LONGINT; + Int8* = SHORTINT; + Int16* = INTEGER; (* we don't have 16 bit integer in x86_64 version of voc *) + Int32* = INTEGER; + Real32* = REAL; + Real64* = LONGREAL; + + CONST + bigEndian* = 0; (* SPARC, M68K etc *) + littleEndian* = 1; (* Intel 80x86, VAX etc *) + byteorder* = littleEndian; (* machine-dependent constant *) + TYPE + ByteOrder* = SHORTINT; (* bigEndian or littleEndian *) + + (* following constants and type definitions try to make + conversions from INTEGER to SET and vice versa more portable + to allow for bit operations on INTEGER values + *) + TYPE + SetInt* = LONGINT; (* INTEGER type that corresponds to SET *) + VAR msb* : SET; + msbIsMax*, msbIs0*: SHORTINT; + msbindex*, lsbindex*, nofbits*: LONGINT; + + PROCEDURE ToInt8*(int: LONGINT) : Int8; + BEGIN + RETURN SHORT(SHORT(int)) + END ToInt8; + + PROCEDURE ToInt16*(int: LONGINT) : Int16; + BEGIN + RETURN SYS.VAL(Int16, int) + END ToInt16; + + PROCEDURE ToInt32*(int: LONGINT) : Int32; + BEGIN + RETURN SHORT(int) + END ToInt32; + + PROCEDURE ToReal32*(real: LONGREAL) : Real32; + BEGIN + RETURN SHORT(real) + END ToReal32; + + PROCEDURE ToReal64*(real: LONGREAL) : Real64; + BEGIN + RETURN real + END ToReal64; + +BEGIN + msb := SYS.VAL(SET, MIN(SetInt)); + (* most significant bit, converted to a SET *) + (* we expect msbIsMax XOR msbIs0 to be 1; + this is checked for by an assertion + *) + msbIsMax := SYS.VAL(SHORTINT, (msb = {MAX(SET)})); + (* is 1, if msb equals {MAX(SET)} *) + msbIs0 := SYS.VAL(SHORTINT, (msb = {0})); + (* is 0, if msb equals {0} *) + msbindex := msbIsMax * MAX(SET); + (* set element that corresponds to the most-significant-bit *) + lsbindex := MAX(SET) - msbindex; + (* set element that corresponds to the lowest-significant-bit *) + nofbits := MAX(SET) + 1; + (* number of elements in SETs *) + + ASSERT((msbIs0 = 1) & (msbIsMax = 0) OR (msbIs0 = 0) & (msbIsMax = 1)); +END ulmTypes. diff --git a/voc b/voc index 9cdb3e113e99a63f2f5f5240dcbcea3d535979c1..c9ceee10d830635889976280eea64e5e92dc2234 100755 GIT binary patch delta 65726 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 t