Initial revision
authorclaus
Fri, 16 Jul 1993 11:39:45 +0200
changeset 1 a27a279701f8
parent 0 aa2498ef6470
child 2 6526dde5f3ac
Initial revision
ArithVal.st
ArithmeticValue.st
ArrColl.st
Array.st
ArrayedCollection.st
Assoc.st
Association.st
Autoload.st
BContext.st
Bag.st
Behavior.st
Block.st
BlockContext.st
Boolean.st
ByteArray.st
CCReader.st
Character.st
Class.st
ClassCategoryReader.st
ClassDescr.st
ClassDescription.st
Coll.st
Collection.st
Context.st
Date.st
Dict.st
Dictionary.st
DirStr.st
DirectoryStream.st
DoubleArray.st
Exception.st
ExtStream.st
ExternalStream.st
False.st
FileDir.st
FileDirectory.st
FileStr.st
FileStream.st
Filename.st
Float.st
FloatArray.st
Fraction.st
IdDict.st
IdSet.st
IdentityDictionary.st
IdentitySet.st
Integer.st
Interval.st
LargeInt.st
LargeInteger.st
Link.st
LinkList.st
LinkedList.st
Magnitude.st
Make.proto
Message.st
Metaclass.st
Method.st
MiniDebug.st
MiniDebugger.st
MiniIns.st
MiniInspector.st
NPExtStr.st
NonPositionableExternalStream.st
Number.st
ObjMem.st
Object.st
ObjectMemory.st
OrdColl.st
OrderedCollection.st
PipeStr.st
PipeStream.st
Point.st
PosStream.st
PositionableStream.st
ProcSched.st
Process.st
ProcessorScheduler.st
Project.st
RWStream.st
ReadStr.st
ReadStream.st
ReadWriteStream.st
Rectangle.st
Registry.st
Semaphore.st
SeqColl.st
SequenceableCollection.st
Set.st
Signal.st
SignalSet.st
SmallInt.st
SmallInteger.st
Smalltalk.st
SortColl.st
SortedCollection.st
Stream.st
String.st
StringCollection.st
Symbol.st
Time.st
True.st
TwoByteString.st
UIBytes.st
UndefObj.st
UndefinedObject.st
UninterpretedBytes.st
Unix.st
WriteStr.st
WriteStream.st
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ArithVal.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,613 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Magnitude subclass:#ArithmeticValue
+       instanceVariableNames:''
+       classVariableNames:'DivisionByZeroSignal DomainErrorSignal
+                           OverflowSignal UnderflowSignal
+			   AnyArithmeticSignal'
+       poolDictionaries:''
+       category:'Magnitude-Numbers'
+!
+
+ArithmeticValue comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ArithmeticValue is an abstract superclass for all things responding to
+arithmetic messages. It was inserted into the hierarchy, to allow things
+like matrices, functions etc. share the arithmetic methods defined here.
+
+(In the old hierarchy these had to be Numbers to do that 
+ - which is not quite correct)
+
+%W% %E%
+'!
+
+!ArithmeticValue class methodsFor:'initialization' !
+
+initialize
+    "setup the signals"
+
+    DomainErrorSignal := (Signal new) mayProceed:false.
+    DomainErrorSignal notifierString:'domain error'.
+
+    DivisionByZeroSignal := (Signal new) mayProceed:false.
+    DivisionByZeroSignal notifierString:'division by zero'.
+
+    OverflowSignal := (Signal new) mayProceed:false.
+    OverflowSignal notifierString:'overflow'.
+
+    UnderflowSignal := (Signal new) mayProceed:false.
+    UnderflowSignal notifierString:'underflow'.
+
+    AnyArithmeticSignal := SignalSet with:DomainErrorSignal
+				     with:DivisionByZeroSignal
+				     with:OverflowSignal
+				     with:UnderflowSignal.
+! !
+
+!ArithmeticValue class methodsFor:'signal access' !
+
+domainErrorSignal
+    "return the signal which is raised on math errors
+     (such as log of 0 etc.)"
+
+    ^ DomainErrorSignal
+!
+
+divisionByZeroSignal
+    "return the signal which is raised on division by zero"
+
+    ^ DivisionByZeroSignal
+!
+
+overflowSignal
+    "return the signal which is raised on overflow conditions (in floats)"
+
+    ^ OverflowSignal
+!
+
+underflowSignal
+    "return the signal which is raised on underflow conditions (in floats)"
+
+    ^ UnderflowSignal
+!
+
+anyArithmeticSignal
+    "return a signalSet with all possible arithmetic signals"
+
+    ^ AnyArithmeticSignal
+! !
+
+!ArithmeticValue methodsFor:'converting' !
+
+degreesToRadians
+    "interpreting the receiver as radians, return the degrees"
+
+    ^ self asFloat degreesToRadians
+!
+
+radiansToDegrees
+    "interpreting the receiver as degrees, return the radians"
+
+    ^ self asFloat radiansToDegrees
+!
+
+asInteger
+    "return an integer with same value - might truncate"
+
+    ^ self truncated
+!
+
+asFloat
+    "return a float with same value"
+
+   ^ self subclassResponsibility
+!
+
+asFraction
+    "return a fraction with same value"
+
+   ^ self subclassResponsibility
+!
+
+coerce:aNumber
+    "convert aNumber into an instance of the receivers class and return it."
+
+    ^ self subclassResponsibility
+!
+
+generality
+    "return a number giving the receivers generality, that number is
+     used to convert one of the arguments in a mixed expression. 
+     The generality has to be defined in subclasses,
+     such that gen(a) > gen(b) iff, conversion of b into a's class 
+     does not cut precision. For example, Integer has 40, Float has 80,
+     meaning that if we convert a Float to an Integer, some precision may
+     be lost. The generality is used by ArithmeticValue>>retry:cuercing:"
+      
+    ^ self subclassResponsibility
+!
+
+retry:aSymbol coercing:aNumber
+    "arithmetic represented by the binary operator, aSymbol,
+    could not be performed with the receiver and the argument, aNumber, 
+    because of the differences in representation.  
+    Coerce either the receiver or the argument, depending on which has higher 
+    generality, and try again.  
+    If the operation is compare for same value (=), return false if
+    the argument is not a Number. 
+    If the generalities are the same, create an error message, since this
+    means that a subclass has not been fully implemented."
+
+    |myGenerality otherGenerality|
+
+    (aSymbol == #=) ifTrue:[
+        (aNumber respondsTo:#generality) ifFalse:[^ false]
+    ] ifFalse:[
+        (aNumber respondsTo:#generality) ifFalse:[
+            self error:'retry:coercing: argument is not a number'.
+            ^ self
+        ]
+    ].
+    myGenerality := self generality.
+    otherGenerality := aNumber generality.
+    (myGenerality > otherGenerality) ifTrue:[
+        ^ self perform:aSymbol with:(self coerce:aNumber)
+    ].
+    (myGenerality < otherGenerality) ifTrue:[
+        ^ (aNumber coerce:self) perform:aSymbol with:aNumber
+    ].
+    self error:'retry:coercing: oops - same generality'
+! !
+
+!ArithmeticValue methodsFor:'queries' !
+
+respondsToArithmetic
+    "return true, if the receiver responds to arithmetic messages"
+
+    ^ true
+! !
+
+!ArithmeticValue methodsFor:'arithmetic' !
+
++ something
+    "return the sum of the receiver and the argument"
+
+    ^ self subclassResponsibility
+!
+
+- something
+    "return the difference of the receiver and the argument"
+
+    ^ self subclassResponsibility
+!
+
+* something
+    "return the product of the receiver and the argument"
+
+    ^ self subclassResponsibility
+!
+
+/ something
+    "return the quotient of the receiver and the argument"
+
+    ^ self subclassResponsibility
+!
+
+// something
+    "return the integer quotient of the receiver and the argument"
+
+    ^ (self / something) floor
+!
+
+\\ something
+    "return the integer modulu of the receiver and the argument"
+
+    ^ self - ((self // something) * something)
+!
+
+quo:something
+    "Return the integer quotient of dividing the receiver by the argument
+     with truncation towards zero."
+
+    ^ (self / something) truncated
+!
+
+rem:something
+    "Return the integer remainder of dividing the receiver by the argument
+     with truncation towards zero.
+     The remainder has the same sign as the receiver."
+
+    ^ self - ((self quo:something) * something)
+!
+
+abs
+    "return the absolute value of the receiver"
+
+    (self negative) ifTrue:[^ self negated].
+    ^ self
+!
+
+negated
+    "return the receiver negated"
+
+    ^ self class zero - self
+!
+
+reciprocal
+    "return the receivers reciprocal"
+
+    ^ self class unity / self
+! !
+
+!ArithmeticValue methodsFor:'comparing'!
+
+>= something
+    "return true, if the argument is less or equal than the receiver"
+
+    ^ (self < something) not
+!
+
+> something
+    "return true, if the argument is less than the receiver"
+
+    ^ something < self
+!
+
+<= something
+    "return true, if the argument is greater or equal than the receiver"
+
+    ^ (something < self) not
+!
+
+< something
+    "return true, if the argument is greater than the receiver"
+
+    ^ self subclassResponsibility
+!
+
+compare:arg ifLess:lessBlock ifEqual:equalBlock ifGreater:greaterBlock
+    "three-way compare - thanks to Self for this idea.
+     Can be redefined in subclasses to do it with a single comparison if
+     comparison is expensive."
+
+    self < arg ifTrue:[
+        ^ lessBlock value
+    ].
+    self = arg ifTrue:[
+        ^ equalBlock value
+    ].
+    ^ greaterBlock value
+! !
+
+!ArithmeticValue methodsFor:'truncation and rounding'!
+
+ceiling
+    "return the integer nearest the receiver towards positive infinity."
+
+    |anInteger|
+
+    anInteger := self // 1.       "truncates towards negative infinity"
+    anInteger = self ifTrue:[^ anInteger].
+    ^ anInteger + 1
+!
+
+floor
+    "return the receiver truncated towards negative infinity"
+
+    ^ self // 1
+!
+
+truncated
+    "return the receiver truncated towards zero"
+
+    ^ self floor asInteger
+!
+
+truncateTo:aNumber
+    "return the receiver truncated to multiples of aNumber"
+
+    ^ ((self / aNumber) floor * aNumber) asInteger
+!
+
+rounded
+    "return the integer nearest the receiver"
+
+    ^ (self + 0.5) floor asInteger
+!
+
+roundTo:aNumber
+    "return the receiver rounded to multiples of aNumber"
+
+    ^ (self / aNumber) rounded * aNumber
+! !
+
+!ArithmeticValue methodsFor:'double dispatching'!
+
+sumFromInteger:anInteger
+    "the receiver does not know how to add an integer -
+     retry the operation by coercing to higher generality"
+
+    ^ anInteger retry:#+ coercing:self
+!
+
+sumFromFloat:aFloat
+    "the receiver does not know how to add a float -
+     retry the operation by coercing to higher generality"
+
+    ^ aFloat retry:#+ coercing:self
+!
+
+sumFromFraction:aFraction
+    "the receiver does not know how to add a fraction -
+     retry the operation by coercing to higher generality"
+
+    ^ aFraction retry:#+ coercing:self
+!
+
+differenceFromInteger:anInteger
+    "the receiver does not know how to subtract from an integer -
+     retry the operation by coercing to higher generality"
+
+    ^ anInteger retry:#- coercing:self
+!
+
+differenceFromFloat:aFloat
+    "the receiver does not know how to subtract from a float -
+     retry the operation by coercing to higher generality"
+
+    ^ aFloat retry:#- coercing:self
+!
+
+differenceFromFraction:aFraction
+    "the receiver does not know how to subtract from a fraction -
+     retry the operation by coercing to higher generality"
+
+    ^ aFraction retry:#- coercing:self
+!
+
+productFromInteger:anInteger
+    "the receiver does not know how to multiply an integer -
+     retry the operation by coercing to higher generality"
+
+    ^ anInteger retry:#* coercing:self
+!
+
+productFromFloat:aFloat
+    "the receiver does not know how to multiply a float -
+     retry the operation by coercing to higher generality"
+
+    ^ aFloat retry:#* coercing:self
+!
+
+productFromFraction:aFraction
+    "the receiver does not know how to multiply a fraction -
+     retry the operation by coercing to higher generality"
+
+    ^ aFraction retry:#* coercing:self
+!
+
+quotientFromInteger:anInteger
+    "the receiver does not know how to divide an integer -
+     retry the operation by coercing to higher generality"
+
+    ^ anInteger retry:#/ coercing:self
+!
+
+quotientFromFloat:aFloat
+    "the receiver does not know how to divide a float -
+     retry the operation by coercing to higher generality"
+
+    ^ aFloat retry:#/ coercing:self
+!
+
+quotientFromFraction:aFraction
+    "the receiver does not know how to divide a fraction -
+     retry the operation by coercing to higher generality"
+
+    ^ aFraction retry:#/ coercing:self
+!
+
+lessFromInteger:anInteger
+    "the receiver does not know how to compare to an integer -
+     retry the operation by coercing to higher generality"
+
+    ^ anInteger retry:#< coercing:self
+!
+
+lessFromFloat:aFloat
+    "the receiver does not know how to compare to a float -
+     retry the operation by coercing to higher generality"
+
+    ^ aFloat retry:#< coercing:self
+!
+
+lessFromFraction:aFraction
+    "the receiver does not know how to compare to a fraction -
+     retry the operation by coercing to higher generality"
+
+    ^ aFraction retry:#< coercing:self
+! !
+
+!ArithmeticValue methodsFor:'misc math'!
+
+squared
+    "return receiver * receiver"
+
+    ^ self * self
+!
+
+exp
+    "return e ^ receiver"
+
+    ^ self asFloat exp
+!
+
+ln
+    "return the natural logarithm of the receiver"
+
+    ^ self asFloat ln
+!
+
+log
+    "return log base 10 of the receiver"
+
+    ^ self log:10
+!
+
+log:aNumber
+    "return log base aNumber of the receiver"
+
+    ^ self ln / aNumber ln
+!
+
+sqrt
+    "return the square root of the receiver"
+
+    ^ self asFloat sqrt
+!
+
+floorLog:radix
+    "return the logarithm truncated as an integer"
+
+    ^ (self log:radix) floor
+!
+
+raisedTo:aNumber
+    "return the receiver raised to aNumber"
+
+    aNumber = 0 ifTrue:[^ 1].
+    aNumber = 1 ifTrue:[^ self].
+    aNumber isInteger ifTrue:[
+        ^ self raisedToInteger:aNumber
+    ].
+    ^ self asFloat raisedTo:aNumber
+!
+
+raisedToInteger:anInteger
+    "return the receiver raised to anInteger"
+
+    |count result|
+
+    result := self coerce:1.
+    count := anInteger abs.
+    count timesRepeat:[result := result * self].
+    (anInteger < 0) ifTrue:[
+        ^ 1 / result
+    ].
+    ^ result
+! !
+
+!ArithmeticValue methodsFor:'trigonometric'!
+
+sin
+    "return the sine of the receiver (interpreted as radians)"
+
+    ^ self asFloat sin
+!
+
+cos
+    "return the cosine of the receiver (interpreted as radians)"
+
+    ^ self asFloat cos
+!
+
+tan
+    "return the tangens of the receiver (interpreted as radians)"
+
+    ^ self asFloat tan
+!
+
+arcCos
+    "return the arccosine of the receiver (in radians)"
+
+    ^ self asFloat arcCos
+!
+
+arcSin
+    "return the arcsine of the receiver (in radians)"
+
+    ^ self asFloat arcSin
+!
+
+arcTan
+    "return the arctangens of the receiver (in radians)"
+
+    ^ self asFloat arcTan
+! !
+
+!ArithmeticValue methodsFor:'error handling'!
+
+divideByZeroError
+    "report a division by zero error"
+
+    DivisionByZeroSignal raise
+    "self error:'division by zero'"
+! !
+
+!ArithmeticValue methodsFor:'testing'!
+
+negative
+    "return true, if the receiver is < 0"
+
+    " this would lead to infinite recursion ...
+    ^ (self < 0)
+    "
+    ^ self subclassResponsibility
+!
+
+positive
+    "return true, if the receiver is >= 0"
+
+    ^ self negative not
+!
+
+strictlyPositive
+    "return true, if the receiver is > 0"
+
+    ^ (self > 0)
+!
+
+sign
+    "return the sign of the receiver"
+
+    (self < 0) ifTrue:[^ -1].
+    (self > 0) ifTrue:[^ 1].
+    ^ 0
+!
+
+even
+    "return true if the receiver is divisible by 2"
+
+    ^ self truncated asInteger even
+!
+
+odd
+    "return true if the receiver is not divisible by 2"
+
+    ^ self even not
+!
+
+denominator
+    "return the denominator of the receiver"
+
+    ^ 1
+!
+
+numerator
+    "return the numerator of the receiver."
+
+    ^ self
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ArithmeticValue.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,613 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Magnitude subclass:#ArithmeticValue
+       instanceVariableNames:''
+       classVariableNames:'DivisionByZeroSignal DomainErrorSignal
+                           OverflowSignal UnderflowSignal
+			   AnyArithmeticSignal'
+       poolDictionaries:''
+       category:'Magnitude-Numbers'
+!
+
+ArithmeticValue comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ArithmeticValue is an abstract superclass for all things responding to
+arithmetic messages. It was inserted into the hierarchy, to allow things
+like matrices, functions etc. share the arithmetic methods defined here.
+
+(In the old hierarchy these had to be Numbers to do that 
+ - which is not quite correct)
+
+%W% %E%
+'!
+
+!ArithmeticValue class methodsFor:'initialization' !
+
+initialize
+    "setup the signals"
+
+    DomainErrorSignal := (Signal new) mayProceed:false.
+    DomainErrorSignal notifierString:'domain error'.
+
+    DivisionByZeroSignal := (Signal new) mayProceed:false.
+    DivisionByZeroSignal notifierString:'division by zero'.
+
+    OverflowSignal := (Signal new) mayProceed:false.
+    OverflowSignal notifierString:'overflow'.
+
+    UnderflowSignal := (Signal new) mayProceed:false.
+    UnderflowSignal notifierString:'underflow'.
+
+    AnyArithmeticSignal := SignalSet with:DomainErrorSignal
+				     with:DivisionByZeroSignal
+				     with:OverflowSignal
+				     with:UnderflowSignal.
+! !
+
+!ArithmeticValue class methodsFor:'signal access' !
+
+domainErrorSignal
+    "return the signal which is raised on math errors
+     (such as log of 0 etc.)"
+
+    ^ DomainErrorSignal
+!
+
+divisionByZeroSignal
+    "return the signal which is raised on division by zero"
+
+    ^ DivisionByZeroSignal
+!
+
+overflowSignal
+    "return the signal which is raised on overflow conditions (in floats)"
+
+    ^ OverflowSignal
+!
+
+underflowSignal
+    "return the signal which is raised on underflow conditions (in floats)"
+
+    ^ UnderflowSignal
+!
+
+anyArithmeticSignal
+    "return a signalSet with all possible arithmetic signals"
+
+    ^ AnyArithmeticSignal
+! !
+
+!ArithmeticValue methodsFor:'converting' !
+
+degreesToRadians
+    "interpreting the receiver as radians, return the degrees"
+
+    ^ self asFloat degreesToRadians
+!
+
+radiansToDegrees
+    "interpreting the receiver as degrees, return the radians"
+
+    ^ self asFloat radiansToDegrees
+!
+
+asInteger
+    "return an integer with same value - might truncate"
+
+    ^ self truncated
+!
+
+asFloat
+    "return a float with same value"
+
+   ^ self subclassResponsibility
+!
+
+asFraction
+    "return a fraction with same value"
+
+   ^ self subclassResponsibility
+!
+
+coerce:aNumber
+    "convert aNumber into an instance of the receivers class and return it."
+
+    ^ self subclassResponsibility
+!
+
+generality
+    "return a number giving the receivers generality, that number is
+     used to convert one of the arguments in a mixed expression. 
+     The generality has to be defined in subclasses,
+     such that gen(a) > gen(b) iff, conversion of b into a's class 
+     does not cut precision. For example, Integer has 40, Float has 80,
+     meaning that if we convert a Float to an Integer, some precision may
+     be lost. The generality is used by ArithmeticValue>>retry:cuercing:"
+      
+    ^ self subclassResponsibility
+!
+
+retry:aSymbol coercing:aNumber
+    "arithmetic represented by the binary operator, aSymbol,
+    could not be performed with the receiver and the argument, aNumber, 
+    because of the differences in representation.  
+    Coerce either the receiver or the argument, depending on which has higher 
+    generality, and try again.  
+    If the operation is compare for same value (=), return false if
+    the argument is not a Number. 
+    If the generalities are the same, create an error message, since this
+    means that a subclass has not been fully implemented."
+
+    |myGenerality otherGenerality|
+
+    (aSymbol == #=) ifTrue:[
+        (aNumber respondsTo:#generality) ifFalse:[^ false]
+    ] ifFalse:[
+        (aNumber respondsTo:#generality) ifFalse:[
+            self error:'retry:coercing: argument is not a number'.
+            ^ self
+        ]
+    ].
+    myGenerality := self generality.
+    otherGenerality := aNumber generality.
+    (myGenerality > otherGenerality) ifTrue:[
+        ^ self perform:aSymbol with:(self coerce:aNumber)
+    ].
+    (myGenerality < otherGenerality) ifTrue:[
+        ^ (aNumber coerce:self) perform:aSymbol with:aNumber
+    ].
+    self error:'retry:coercing: oops - same generality'
+! !
+
+!ArithmeticValue methodsFor:'queries' !
+
+respondsToArithmetic
+    "return true, if the receiver responds to arithmetic messages"
+
+    ^ true
+! !
+
+!ArithmeticValue methodsFor:'arithmetic' !
+
++ something
+    "return the sum of the receiver and the argument"
+
+    ^ self subclassResponsibility
+!
+
+- something
+    "return the difference of the receiver and the argument"
+
+    ^ self subclassResponsibility
+!
+
+* something
+    "return the product of the receiver and the argument"
+
+    ^ self subclassResponsibility
+!
+
+/ something
+    "return the quotient of the receiver and the argument"
+
+    ^ self subclassResponsibility
+!
+
+// something
+    "return the integer quotient of the receiver and the argument"
+
+    ^ (self / something) floor
+!
+
+\\ something
+    "return the integer modulu of the receiver and the argument"
+
+    ^ self - ((self // something) * something)
+!
+
+quo:something
+    "Return the integer quotient of dividing the receiver by the argument
+     with truncation towards zero."
+
+    ^ (self / something) truncated
+!
+
+rem:something
+    "Return the integer remainder of dividing the receiver by the argument
+     with truncation towards zero.
+     The remainder has the same sign as the receiver."
+
+    ^ self - ((self quo:something) * something)
+!
+
+abs
+    "return the absolute value of the receiver"
+
+    (self negative) ifTrue:[^ self negated].
+    ^ self
+!
+
+negated
+    "return the receiver negated"
+
+    ^ self class zero - self
+!
+
+reciprocal
+    "return the receivers reciprocal"
+
+    ^ self class unity / self
+! !
+
+!ArithmeticValue methodsFor:'comparing'!
+
+>= something
+    "return true, if the argument is less or equal than the receiver"
+
+    ^ (self < something) not
+!
+
+> something
+    "return true, if the argument is less than the receiver"
+
+    ^ something < self
+!
+
+<= something
+    "return true, if the argument is greater or equal than the receiver"
+
+    ^ (something < self) not
+!
+
+< something
+    "return true, if the argument is greater than the receiver"
+
+    ^ self subclassResponsibility
+!
+
+compare:arg ifLess:lessBlock ifEqual:equalBlock ifGreater:greaterBlock
+    "three-way compare - thanks to Self for this idea.
+     Can be redefined in subclasses to do it with a single comparison if
+     comparison is expensive."
+
+    self < arg ifTrue:[
+        ^ lessBlock value
+    ].
+    self = arg ifTrue:[
+        ^ equalBlock value
+    ].
+    ^ greaterBlock value
+! !
+
+!ArithmeticValue methodsFor:'truncation and rounding'!
+
+ceiling
+    "return the integer nearest the receiver towards positive infinity."
+
+    |anInteger|
+
+    anInteger := self // 1.       "truncates towards negative infinity"
+    anInteger = self ifTrue:[^ anInteger].
+    ^ anInteger + 1
+!
+
+floor
+    "return the receiver truncated towards negative infinity"
+
+    ^ self // 1
+!
+
+truncated
+    "return the receiver truncated towards zero"
+
+    ^ self floor asInteger
+!
+
+truncateTo:aNumber
+    "return the receiver truncated to multiples of aNumber"
+
+    ^ ((self / aNumber) floor * aNumber) asInteger
+!
+
+rounded
+    "return the integer nearest the receiver"
+
+    ^ (self + 0.5) floor asInteger
+!
+
+roundTo:aNumber
+    "return the receiver rounded to multiples of aNumber"
+
+    ^ (self / aNumber) rounded * aNumber
+! !
+
+!ArithmeticValue methodsFor:'double dispatching'!
+
+sumFromInteger:anInteger
+    "the receiver does not know how to add an integer -
+     retry the operation by coercing to higher generality"
+
+    ^ anInteger retry:#+ coercing:self
+!
+
+sumFromFloat:aFloat
+    "the receiver does not know how to add a float -
+     retry the operation by coercing to higher generality"
+
+    ^ aFloat retry:#+ coercing:self
+!
+
+sumFromFraction:aFraction
+    "the receiver does not know how to add a fraction -
+     retry the operation by coercing to higher generality"
+
+    ^ aFraction retry:#+ coercing:self
+!
+
+differenceFromInteger:anInteger
+    "the receiver does not know how to subtract from an integer -
+     retry the operation by coercing to higher generality"
+
+    ^ anInteger retry:#- coercing:self
+!
+
+differenceFromFloat:aFloat
+    "the receiver does not know how to subtract from a float -
+     retry the operation by coercing to higher generality"
+
+    ^ aFloat retry:#- coercing:self
+!
+
+differenceFromFraction:aFraction
+    "the receiver does not know how to subtract from a fraction -
+     retry the operation by coercing to higher generality"
+
+    ^ aFraction retry:#- coercing:self
+!
+
+productFromInteger:anInteger
+    "the receiver does not know how to multiply an integer -
+     retry the operation by coercing to higher generality"
+
+    ^ anInteger retry:#* coercing:self
+!
+
+productFromFloat:aFloat
+    "the receiver does not know how to multiply a float -
+     retry the operation by coercing to higher generality"
+
+    ^ aFloat retry:#* coercing:self
+!
+
+productFromFraction:aFraction
+    "the receiver does not know how to multiply a fraction -
+     retry the operation by coercing to higher generality"
+
+    ^ aFraction retry:#* coercing:self
+!
+
+quotientFromInteger:anInteger
+    "the receiver does not know how to divide an integer -
+     retry the operation by coercing to higher generality"
+
+    ^ anInteger retry:#/ coercing:self
+!
+
+quotientFromFloat:aFloat
+    "the receiver does not know how to divide a float -
+     retry the operation by coercing to higher generality"
+
+    ^ aFloat retry:#/ coercing:self
+!
+
+quotientFromFraction:aFraction
+    "the receiver does not know how to divide a fraction -
+     retry the operation by coercing to higher generality"
+
+    ^ aFraction retry:#/ coercing:self
+!
+
+lessFromInteger:anInteger
+    "the receiver does not know how to compare to an integer -
+     retry the operation by coercing to higher generality"
+
+    ^ anInteger retry:#< coercing:self
+!
+
+lessFromFloat:aFloat
+    "the receiver does not know how to compare to a float -
+     retry the operation by coercing to higher generality"
+
+    ^ aFloat retry:#< coercing:self
+!
+
+lessFromFraction:aFraction
+    "the receiver does not know how to compare to a fraction -
+     retry the operation by coercing to higher generality"
+
+    ^ aFraction retry:#< coercing:self
+! !
+
+!ArithmeticValue methodsFor:'misc math'!
+
+squared
+    "return receiver * receiver"
+
+    ^ self * self
+!
+
+exp
+    "return e ^ receiver"
+
+    ^ self asFloat exp
+!
+
+ln
+    "return the natural logarithm of the receiver"
+
+    ^ self asFloat ln
+!
+
+log
+    "return log base 10 of the receiver"
+
+    ^ self log:10
+!
+
+log:aNumber
+    "return log base aNumber of the receiver"
+
+    ^ self ln / aNumber ln
+!
+
+sqrt
+    "return the square root of the receiver"
+
+    ^ self asFloat sqrt
+!
+
+floorLog:radix
+    "return the logarithm truncated as an integer"
+
+    ^ (self log:radix) floor
+!
+
+raisedTo:aNumber
+    "return the receiver raised to aNumber"
+
+    aNumber = 0 ifTrue:[^ 1].
+    aNumber = 1 ifTrue:[^ self].
+    aNumber isInteger ifTrue:[
+        ^ self raisedToInteger:aNumber
+    ].
+    ^ self asFloat raisedTo:aNumber
+!
+
+raisedToInteger:anInteger
+    "return the receiver raised to anInteger"
+
+    |count result|
+
+    result := self coerce:1.
+    count := anInteger abs.
+    count timesRepeat:[result := result * self].
+    (anInteger < 0) ifTrue:[
+        ^ 1 / result
+    ].
+    ^ result
+! !
+
+!ArithmeticValue methodsFor:'trigonometric'!
+
+sin
+    "return the sine of the receiver (interpreted as radians)"
+
+    ^ self asFloat sin
+!
+
+cos
+    "return the cosine of the receiver (interpreted as radians)"
+
+    ^ self asFloat cos
+!
+
+tan
+    "return the tangens of the receiver (interpreted as radians)"
+
+    ^ self asFloat tan
+!
+
+arcCos
+    "return the arccosine of the receiver (in radians)"
+
+    ^ self asFloat arcCos
+!
+
+arcSin
+    "return the arcsine of the receiver (in radians)"
+
+    ^ self asFloat arcSin
+!
+
+arcTan
+    "return the arctangens of the receiver (in radians)"
+
+    ^ self asFloat arcTan
+! !
+
+!ArithmeticValue methodsFor:'error handling'!
+
+divideByZeroError
+    "report a division by zero error"
+
+    DivisionByZeroSignal raise
+    "self error:'division by zero'"
+! !
+
+!ArithmeticValue methodsFor:'testing'!
+
+negative
+    "return true, if the receiver is < 0"
+
+    " this would lead to infinite recursion ...
+    ^ (self < 0)
+    "
+    ^ self subclassResponsibility
+!
+
+positive
+    "return true, if the receiver is >= 0"
+
+    ^ self negative not
+!
+
+strictlyPositive
+    "return true, if the receiver is > 0"
+
+    ^ (self > 0)
+!
+
+sign
+    "return the sign of the receiver"
+
+    (self < 0) ifTrue:[^ -1].
+    (self > 0) ifTrue:[^ 1].
+    ^ 0
+!
+
+even
+    "return true if the receiver is divisible by 2"
+
+    ^ self truncated asInteger even
+!
+
+odd
+    "return true if the receiver is not divisible by 2"
+
+    ^ self even not
+!
+
+denominator
+    "return the denominator of the receiver"
+
+    ^ 1
+!
+
+numerator
+    "return the numerator of the receiver."
+
+    ^ self
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ArrColl.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,181 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+SequenceableCollection subclass:#ArrayedCollection
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Abstract'
+!
+
+ArrayedCollection comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+             All Rights Reserved
+
+ArrayedCollections are collections where the elements can be accessed via an integer index.
+
+%W% %E%
+written spring 89 by claus
+'!
+
+!ArrayedCollection class methodsFor:'instance creation'!
+
+with:element
+    "return a new SequenceableCollection with one element:anObject"
+
+    |newCollection|
+
+    newCollection := self new:1.
+    newCollection at:1 put:element.
+    ^newCollection
+!
+
+with:first with:second
+    "return a new SequenceableCollection with two elements"
+
+    |newCollection|
+
+    newCollection := self new:2.
+    newCollection at:1 put:first.
+    newCollection at:2 put:second.
+    ^newCollection
+!
+
+with:first with:second with:third
+    "return a new SequenceableCollection with three elements"
+
+    |newCollection|
+
+    newCollection := self new:3.
+    newCollection at:1 put:first.
+    newCollection at:2 put:second.
+    newCollection at:3 put:third.
+    ^newCollection
+!
+
+with:first with:second with:third with:forth
+    "return a new SequenceableCollection with four elements"
+
+    |newCollection|
+
+    newCollection := self new:4.
+    newCollection at:1 put:first.
+    newCollection at:2 put:second.
+    newCollection at:3 put:third.
+    newCollection at:4 put:forth.
+    ^newCollection
+!
+
+with:one with:two with:three with:four with:five
+    "return a new SequenceableCollection with five elements"
+
+    |newCollection|
+
+    newCollection := self new:5.
+    newCollection at:1 put:one.
+    newCollection at:2 put:two.
+    newCollection at:3 put:three.
+    newCollection at:4 put:four.
+    newCollection at:5 put:five.
+    ^newCollection
+!
+
+withAll:aCollection
+    "return a new Collection with all elements taken from the argument,
+     aCollection"
+
+    |newCollection newSize
+     index "{ Class: SmallInteger }" |
+
+    newSize := aCollection size.
+    newCollection := self new:newSize.
+    (aCollection isKindOf:SequenceableCollection) ifTrue:[
+        "aCollection has indexed elements"
+        newCollection replaceFrom:1 to:newSize with:aCollection startingAt:1
+    ] ifFalse:[
+        "must enumerate the elements"
+        index := 1.
+        aCollection do:[:element |
+            newCollection at:index put:element.
+            index := index + 1
+        ]
+    ].
+    ^ newCollection
+! !
+
+!ArrayedCollection methodsFor:'error handling'!
+
+indexMustBeInteger
+    "report an error that index must be Integer"
+
+    ^ self error:'index must be integer'
+!
+
+indexOutOfRange:theIndex
+    "report an error that index is out of range"
+
+    ^ self error:'index is out of range'
+!
+
+fixedSizeError
+    "report an error that size of the collection cannot be changed"
+
+    ^ self error:'cannot change size'
+! !
+
+!ArrayedCollection methodsFor:'accessing'!
+
+size
+    "return the ArrayedCollections size - redefined since SequenceableCollection
+     does it in a slow way"
+
+    ^ self basicSize
+!
+
+at:index
+    "return the index's element of the collection"
+
+    ^ self basicAt:index
+!
+
+at:index put:anObject
+    "put the argument as index's element into the collection"
+
+    ^ self basicAt:index put:anObject
+! !
+
+!ArrayedCollection methodsFor:'storing'!
+
+storeOn:aStream
+    "output a printed representation (which can be re-read)
+     onto the argument aStream"
+
+    |index "{ Class: SmallInteger }"|
+
+    aStream nextPutAll:'('.
+    aStream nextPutAll:self class name.
+    aStream nextPutAll:' new:'.
+    self size printOn:aStream.
+    aStream nextPutAll:')'.
+    index := 1.
+    self do:[:element |
+        aStream nextPutAll:' at:'.
+        index printOn:aStream.
+        aStream nextPutAll:' put:'.
+        element storeOn:aStream.
+        aStream nextPut:$;.
+        index := index + 1
+    ].
+    index > 1 ifTrue:[aStream nextPutAll:' yourself'].
+    aStream nextPut:$)
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Array.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,626 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+ArrayedCollection variableSubclass:#Array
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Indexed'
+!
+
+Array comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+              All Rights Reserved
+
+Arrays store general objects; the size is fixed, so add/remove is not
+allowed. Access to the elements is via an Integer index. Since Arrays
+are used very often in the system, some methods have been tuned by
+reimplementation as primitive.
+
+%W% %E%
+
+written spring 89 by claus
+'!
+
+!Array methodsFor:'resizing'!
+
+grow:newSize
+    (newSize ~~ self size) ifTrue:[
+        self fixedSizeError
+    ]
+! !
+
+!Array methodsFor:'accessing'!
+
+size
+    "return the number of indexed elements in the receiver"
+
+%{  /* NOCONTEXT */
+
+    RETURN ( _MKSMALLINT(_arraySize(self) - _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) ));
+%}
+!
+
+at:index
+    "return the indexed instance variable with index, anInteger
+     - added here for speed"
+
+%{  /* NOCONTEXT */
+
+    REGISTER int indx;
+    REGISTER int nIndex;
+
+    if (_isSmallInteger(index)) {
+        indx = _intVal(index) - 1;
+	if (indx >= 0) {
+            nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+            indx += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+            if (indx < nIndex) {
+                RETURN ( _InstPtr(self)->i_instvars[indx] );
+            }
+        }
+    }
+%}
+.
+    ^ super at:index
+!
+
+at:index put:anObject
+    "store the 2nd arg, anObject as indexed instvar with index, anInteger.
+     - added here for speed"
+
+%{  /* NOCONTEXT */
+
+    REGISTER int indx;
+    REGISTER int nIndex;
+
+    if (_isSmallInteger(index)) {
+        indx = _intVal(index) - 1;
+        if (indx >= 0) {
+            nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+            indx += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+            if (indx < nIndex) {
+                _InstPtr(self)->i_instvars[indx] = anObject;
+                __STORE(self, anObject);
+                RETURN ( anObject );
+            }
+        }
+    }
+%}
+.
+    ^ super at:index put:anObject
+! !
+
+!Array methodsFor:'copying'!
+
+copyWith:something
+    "reimplemented for speed if receiver is an Array"
+%{
+    OBJ nObj;
+    int mySize;
+    int i, nIndex;
+    OBJ *op;
+    extern int newSpace;
+
+    if (_qClass(self) == Array) {
+        mySize = _qSize(self);
+        _qAlignedNew(nObj, mySize + sizeof(OBJ), __context);
+        _InstPtr(nObj)->o_class = Array;
+
+	nIndex = (mySize - OHDR_SIZE) / sizeof(OBJ);
+        /* created object is usually in newspace */
+        if (_qSpace(nObj) == newSpace) {
+            /* dont care for store */
+#ifdef bcopy4
+	    bcopy4(_ArrayInstPtr(self)->a_element, _ArrayInstPtr(nObj)->a_element, nIndex);
+#else
+            bcopy(_ArrayInstPtr(self)->a_element, _ArrayInstPtr(nObj)->a_element, mySize - OHDR_SIZE);
+#endif
+            _ArrayInstPtr(nObj)->a_element[nIndex] = something;
+        } else {
+            /* must take care of stores ... */
+            op = _ArrayInstPtr(self)->a_element;
+            for (i=0; i<nIndex; i++) {
+                _ArrayInstPtr(nObj)->a_element[i] = *op;
+               __STORE(nObj, *op);
+               op++;
+            }
+            _ArrayInstPtr(nObj)->a_element[i] = something;
+            __STORE(nObj, something);
+	}
+        RETURN ( nObj );
+    }
+%}
+.
+    ^ super copyWith:something
+! !
+
+!Array methodsFor:'filling & replacing'!
+
+from:index1 to:index2 put:anObject
+    "reimplemented for speed if receiver is an Array"
+
+%{  /* NOCONTEXT */
+
+    REGISTER int index;
+    int nIndex;
+    int endIndex;
+    REGISTER OBJ *dst;
+
+    if ((_qClass(self) == Array)
+     && _isSmallInteger(index1)
+     && _isSmallInteger(index2)) {
+        index = _intVal(index1) - 1;
+        if (index >= 0) {
+            nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+            endIndex = _intVal(index2) - 1;
+            if (endIndex < nIndex) {
+                dst = &(_InstPtr(self)->i_instvars[index]);
+#ifdef memset4
+                memset4(dst, anObject, (endIndex-index+1));
+                __STORE(self, anObject);
+#else
+                if ((INT)anObject == 0) {
+                    memset(dst, 0, (endIndex-index+1) * sizeof(OBJ));
+                } else {
+                    for (; index <= endIndex; index++) {
+                        *dst++ = anObject;
+                    }
+                    __STORE(self, anObject);
+                }
+#endif
+                RETURN ( self );
+            }
+        }
+    }
+%}
+.
+    ^ super from:index1 to:index2 put:anObject
+!
+
+replaceFrom:start to:stop with:aCollection startingAt:repStart
+    "reimplemented for speed if both receiver and aCollection are Arrays"
+
+%{  /* NOCONTEXT */
+
+    int nIndex, repNIndex;
+    int startIndex, stopIndex;
+    REGISTER OBJ *src;
+    REGISTER OBJ *dst;
+    int repStopIndex;
+    REGISTER int repStartIndex;
+    REGISTER OBJ t;
+    REGISTER int count;
+    extern int newSpace;
+
+    if ((_qClass(self) == Array)
+     && (_Class(aCollection) == Array)
+     && _isSmallInteger(start)
+     && _isSmallInteger(stop)
+     && _isSmallInteger(repStart)) {
+        startIndex = _intVal(start) - 1;
+        if (startIndex >= 0) {
+            nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+            stopIndex = _intVal(stop) - 1;
+            count = stopIndex - startIndex + 1;
+            if ((count > 0) && (stopIndex < nIndex)) {
+                repStartIndex = _intVal(repStart) - 1;
+                if (repStartIndex >= 0) {
+                    repNIndex = (_qSize(aCollection) - OHDR_SIZE) / sizeof(OBJ);
+                    repStopIndex = repStartIndex + (stopIndex - startIndex);
+                    if (repStopIndex < repNIndex) {
+                        src = &(_InstPtr(aCollection)->i_instvars[repStartIndex]);
+                        dst = &(_InstPtr(self)->i_instvars[startIndex]);
+                        if (aCollection == self) {
+                            /* no need to check stores */
+                            /* take care of overlapping copy */
+                            if (src < dst) {
+                                /* must do a reverse copy */
+                                src += count;
+                                dst += count;
+                                while (count-- > 0) {
+                                    *--dst = *--src;
+                                }
+                                RETURN ( self );
+                            }
+#ifdef bcopy4
+			    bcopy4(src, dst, count);
+#else
+# ifdef FAST_MEMCPY
+                            bcopy(src, dst, count*sizeof(OBJ));
+# else
+			    while (count >= 4) {
+				MOVE4LONGS(src, dst);
+				count -= 4;
+			    }
+                            while (count--) {
+                                *dst++ = *src++;
+                            }
+# endif
+#endif
+                        } else {
+                            /*
+			     * no need for store-check, if dst is in newspace
+                             */
+                            if (_qSpace(self) == newSpace) {
+#ifdef bcopy4
+				bcopy4(src, dst, count);
+#else
+# ifdef FAST_MEMCPY
+                                bcopy(src, dst, count*sizeof(OBJ));
+# else
+			        while (count >= 4) {
+				    MOVE4LONGS(src, dst);
+				    count -= 4;
+			        }
+                                while (count--) {
+                                    *dst++ = *src++;
+                                }
+# endif
+#endif
+                            } else {
+                                while (count-- > 0) {
+                                    t = *src++;
+                                    *dst++ = t;
+                                    __STORE(self, t);
+                                }
+                            }
+                        }
+                        RETURN ( self );
+                    }
+                }
+            }
+        }
+    }
+%}
+.
+    ^ super replaceFrom:start to:stop with:aCollection startingAt:repStart
+! !
+
+!Array methodsFor:'testing'!
+
+includes:anObject
+    "return true, if the argument, anObject is contained in the array
+     - reimplemented for speed"
+
+    |element|
+
+%{  /* NOCONTEXT */
+
+    REGISTER int index;
+    REGISTER OBJ o;
+    int nIndex;
+
+    nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+    index = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+
+    /* quick check using == */
+    o = anObject;
+    while (index < nIndex) {
+        if (_InstPtr(self)->i_instvars[index++] == o) {
+            RETURN ( true );
+	}
+    }
+    if (o == nil) {
+	RETURN ( false );
+    }
+%}
+.
+%{
+    REGISTER int index;
+    int nIndex;
+    extern OBJ __eq;
+    static struct inlineCache eq = _ILC1;
+
+    nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+    index = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+
+    /* slow check using = */
+
+    while (index < nIndex) {
+        element = _InstPtr(self)->i_instvars[index++];
+	if (element != nil) {
+#ifdef PASS_ARG_REF
+            if ((*eq.ilc_func)(anObject,__eq, CON_COMMA nil,&eq,&element)==true)
+#else
+            if ((*eq.ilc_func)(anObject,__eq, CON_COMMA nil,&eq,element)==true)
+#endif
+	    {
+                RETURN ( true );
+	    }
+	}
+    }
+%}
+.
+    ^ false
+!
+
+indexOf:anElement startingAt:start
+    "search the array for anElement; return index if found, 0 otherwise
+     - reimplemented for speed"
+
+    |element|
+%{
+    REGISTER int index;
+    int nIndex, nInsts;
+    extern OBJ __eq;
+    static struct inlineCache eq = _ILC1;
+
+    if (_isSmallInteger(start)) {
+        index = _intVal(start) - 1;
+        if (index >= 0) {
+            nInsts = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+            index += nInsts;
+            nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+	    if (anElement != nil) {
+                while (index < nIndex) {
+                    element = _InstPtr(self)->i_instvars[index++];
+		    if (element != nil) {
+                        if ((element == anElement) 
+#ifdef PASS_ARG_REF
+                         || ((*eq.ilc_func)(anElement,__eq, CON_COMMA nil,&eq,&element)
+#else
+                         || ((*eq.ilc_func)(anElement,__eq, CON_COMMA nil,&eq,element)
+#endif
+                                                                            == true)) {
+                            RETURN ( _MKSMALLINT(index - nInsts) );
+                        }
+                    }
+                }
+	    } else {
+		/* search for nil */
+                while (index < nIndex) {
+                    if (_InstPtr(self)->i_instvars[index++] == nil) {
+                        RETURN ( _MKSMALLINT(index - nInsts) );
+                    }
+                }
+            }
+        }
+    }
+%}
+.
+    ^ 0
+!
+
+identityIndexOf:anElement startingAt:start
+    "search the array for anElement; return index if found, 0 otherwise
+     - reimplemented for speed"
+
+%{  /* NOCONTEXT */
+
+    REGISTER int index;
+    REGISTER OBJ el;
+    REGISTER OBJ *op;
+    REGISTER int nIndex;
+    int nInsts;
+
+    if (_isSmallInteger(start)) {
+        index = _intVal(start) - 1;
+        if (index >= 0) {
+            nInsts = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+            index += nInsts;
+            nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+            el = anElement;
+            op = & (_InstPtr(self)->i_instvars[index]);
+            while (index++ < nIndex) {
+                if (*op++ == el) {
+                    RETURN ( _MKSMALLINT(index - nInsts) );
+                }
+            }
+	    RETURN ( _MKSMALLINT(0) );
+        }
+    }
+%}
+.
+    ^ super identityIndexOf:anElement startingAt:start
+! !
+
+!Array methodsFor:'enumeration'!
+
+do:aBlock
+    "evaluate the argument, aBlock for each element in the collection.
+     - reimplemented for speed"
+
+    |home element|
+%{
+    REGISTER OBJFUNC codeVal;
+    REGISTER int index;
+    int nIndex;
+    extern OBJ _value_, Block;
+    static struct inlineCache val = _ILC1;
+
+    index = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+    nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+    if (_isBlock(aBlock)
+     && ((codeVal = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
+     && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
+        home = _BlockInstPtr(aBlock)->b_home;
+        for (; index < nIndex; index++) {
+	    if (InterruptPending != nil) interruptL(__LINE__ COMMA_CON);
+
+            element = _InstPtr(self)->i_instvars[index];
+#ifdef PASS_ARG_REF
+            (*codeVal)(home, CON_COMMA  &element);
+#else
+            (*codeVal)(home, CON_COMMA  element);
+#endif
+        } 
+    } else {
+        for (; index < nIndex; index++) {
+	    if (InterruptPending != nil) interruptL(__LINE__ COMMA_CON);
+
+            element = _InstPtr(self)->i_instvars[index];
+#ifdef PASS_ARG_REF
+            (*val.ilc_func)(aBlock, _value_, CON_COMMA  nil, &val, &element);
+#else
+            (*val.ilc_func)(aBlock, _value_, CON_COMMA  nil, &val, element);
+#endif
+        } 
+    }
+%}
+.
+    ^ self
+!
+
+reverseDo:aBlock
+    "evaluate the argument, aBlock for each element in the collection in reverse order.
+     - reimplemented for speed"
+
+    |home element|
+%{
+    REGISTER OBJFUNC codeVal;
+    REGISTER int index;
+    int nIndex, endIndex;
+    extern OBJ _value_, Block;
+    static struct inlineCache val = _ILC1;
+
+    endIndex = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+    nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+    if (_isBlock(aBlock)
+     && ((codeVal = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
+     && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
+        home = _BlockInstPtr(aBlock)->b_home;
+        for (index=nIndex-1; index >= endIndex; index--) {
+	    if (InterruptPending != nil) interruptL(__LINE__ COMMA_CON);
+
+            element = _InstPtr(self)->i_instvars[index];
+#ifdef PASS_ARG_REF
+            (*codeVal)(home, CON_COMMA  &element);
+#else
+            (*codeVal)(home, CON_COMMA  element);
+#endif
+        } 
+    } else {
+        for (index=nIndex=1; index >= endIndex; index--) {
+	    if (InterruptPending != nil) interruptL(__LINE__ COMMA_CON);
+
+            element = _InstPtr(self)->i_instvars[index];
+#ifdef PASS_ARG_REF
+            (*val.ilc_func)(aBlock, _value_, CON_COMMA  nil, &val, &element);
+#else
+            (*val.ilc_func)(aBlock, _value_, CON_COMMA  nil, &val, element);
+#endif
+        } 
+    }
+%}
+.
+    ^ self
+!
+
+from:start to:stop do:aBlock
+    "evaluate the argument, aBlock for the elements starting at index start
+     up to (and including) stop in the collection.
+     - reimplemented for speed"
+
+    |home element|
+%{
+    REGISTER OBJFUNC codeVal;
+    REGISTER int index;
+    int nIndex, nInsts;
+    extern OBJ _value_, Block;
+    static struct inlineCache val = _ILC1;
+    int indexLow, indexHigh;
+
+    if (_isSmallInteger(start) && _isSmallInteger(stop)) {
+        indexLow = _intVal(start);
+        if (indexLow > 0) {
+            indexHigh = _intVal(stop);
+            nInsts = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+            indexLow += nInsts;
+            indexHigh += nInsts;
+            nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+            if (indexHigh <= nIndex) {
+                indexLow--;
+                indexHigh--;
+                if (_isBlock(aBlock)
+                 && ((codeVal = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
+                 && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
+                    home = _BlockInstPtr(aBlock)->b_home;
+                    for (index=indexLow; index <= indexHigh; index++) {
+	    	        if (InterruptPending != nil) interruptL(__LINE__ COMMA_CON);
+
+#ifdef PASS_ARG_REF
+                        element = _InstPtr(self)->i_instvars[index];
+                        (*codeVal)(home, CON_COMMA  &element);
+#else
+                        (*codeVal)(home, CON_COMMA  _InstPtr(self)->i_instvars[index]);
+#endif
+                    } 
+                } else {
+                    for (index=indexLow; index <= indexHigh; index++) {
+	    	        if (InterruptPending != nil) interruptL(__LINE__ COMMA_CON);
+
+                        element = _InstPtr(self)->i_instvars[index];
+#ifdef PASS_ARG_REF
+                        (*val.ilc_func)
+                            (aBlock, _value_, CON_COMMA  nil, &val, &element);
+#else
+                        (*val.ilc_func)
+                            (aBlock, _value_, CON_COMMA  nil, &val, element);
+#endif
+                    } 
+                }
+                RETURN ( self );
+            }
+        }
+    }
+%}
+.
+    ^ super from:start to:stop do:aBlock
+!
+
+nonNilElementsDo:aBlock
+    "evaluate the argument, aBlock for each non-nil element"
+
+    |home element|
+%{
+    REGISTER OBJFUNC codeVal;
+    REGISTER int index;
+    int nIndex;
+    extern OBJ _value_, Block;
+    static struct inlineCache val = _ILC1;
+
+    index = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars);
+    nIndex = (_qSize(self) - OHDR_SIZE) / sizeof(OBJ);
+    if (_isBlock(aBlock)
+     && ((codeVal = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
+     && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
+        home = _BlockInstPtr(aBlock)->b_home;
+        for (; index < nIndex; index++) {
+	    if (InterruptPending != nil) interrupt(CONARG);
+
+            element = _InstPtr(self)->i_instvars[index];
+	    if (element != nil)
+#ifdef PASS_ARG_REF
+                (*codeVal)(home, CON_COMMA  &element);
+#else
+                (*codeVal)(home, CON_COMMA  element);
+#endif
+        } 
+    } else {
+        for (; index < nIndex; index++) {
+	    if (InterruptPending != nil) interrupt(CONARG);
+
+            element = _InstPtr(self)->i_instvars[index];
+	    if (element != nil)
+#ifdef PASS_ARG_REF
+                (*val.ilc_func)(aBlock, _value_, CON_COMMA  nil, &val, &element);
+#else
+                (*val.ilc_func)(aBlock, _value_, CON_COMMA  nil, &val, element);
+#endif
+        } 
+    }
+%}
+.
+    ^ self
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ArrayedCollection.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,181 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+SequenceableCollection subclass:#ArrayedCollection
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Abstract'
+!
+
+ArrayedCollection comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+             All Rights Reserved
+
+ArrayedCollections are collections where the elements can be accessed via an integer index.
+
+%W% %E%
+written spring 89 by claus
+'!
+
+!ArrayedCollection class methodsFor:'instance creation'!
+
+with:element
+    "return a new SequenceableCollection with one element:anObject"
+
+    |newCollection|
+
+    newCollection := self new:1.
+    newCollection at:1 put:element.
+    ^newCollection
+!
+
+with:first with:second
+    "return a new SequenceableCollection with two elements"
+
+    |newCollection|
+
+    newCollection := self new:2.
+    newCollection at:1 put:first.
+    newCollection at:2 put:second.
+    ^newCollection
+!
+
+with:first with:second with:third
+    "return a new SequenceableCollection with three elements"
+
+    |newCollection|
+
+    newCollection := self new:3.
+    newCollection at:1 put:first.
+    newCollection at:2 put:second.
+    newCollection at:3 put:third.
+    ^newCollection
+!
+
+with:first with:second with:third with:forth
+    "return a new SequenceableCollection with four elements"
+
+    |newCollection|
+
+    newCollection := self new:4.
+    newCollection at:1 put:first.
+    newCollection at:2 put:second.
+    newCollection at:3 put:third.
+    newCollection at:4 put:forth.
+    ^newCollection
+!
+
+with:one with:two with:three with:four with:five
+    "return a new SequenceableCollection with five elements"
+
+    |newCollection|
+
+    newCollection := self new:5.
+    newCollection at:1 put:one.
+    newCollection at:2 put:two.
+    newCollection at:3 put:three.
+    newCollection at:4 put:four.
+    newCollection at:5 put:five.
+    ^newCollection
+!
+
+withAll:aCollection
+    "return a new Collection with all elements taken from the argument,
+     aCollection"
+
+    |newCollection newSize
+     index "{ Class: SmallInteger }" |
+
+    newSize := aCollection size.
+    newCollection := self new:newSize.
+    (aCollection isKindOf:SequenceableCollection) ifTrue:[
+        "aCollection has indexed elements"
+        newCollection replaceFrom:1 to:newSize with:aCollection startingAt:1
+    ] ifFalse:[
+        "must enumerate the elements"
+        index := 1.
+        aCollection do:[:element |
+            newCollection at:index put:element.
+            index := index + 1
+        ]
+    ].
+    ^ newCollection
+! !
+
+!ArrayedCollection methodsFor:'error handling'!
+
+indexMustBeInteger
+    "report an error that index must be Integer"
+
+    ^ self error:'index must be integer'
+!
+
+indexOutOfRange:theIndex
+    "report an error that index is out of range"
+
+    ^ self error:'index is out of range'
+!
+
+fixedSizeError
+    "report an error that size of the collection cannot be changed"
+
+    ^ self error:'cannot change size'
+! !
+
+!ArrayedCollection methodsFor:'accessing'!
+
+size
+    "return the ArrayedCollections size - redefined since SequenceableCollection
+     does it in a slow way"
+
+    ^ self basicSize
+!
+
+at:index
+    "return the index's element of the collection"
+
+    ^ self basicAt:index
+!
+
+at:index put:anObject
+    "put the argument as index's element into the collection"
+
+    ^ self basicAt:index put:anObject
+! !
+
+!ArrayedCollection methodsFor:'storing'!
+
+storeOn:aStream
+    "output a printed representation (which can be re-read)
+     onto the argument aStream"
+
+    |index "{ Class: SmallInteger }"|
+
+    aStream nextPutAll:'('.
+    aStream nextPutAll:self class name.
+    aStream nextPutAll:' new:'.
+    self size printOn:aStream.
+    aStream nextPutAll:')'.
+    index := 1.
+    self do:[:element |
+        aStream nextPutAll:' at:'.
+        index printOn:aStream.
+        aStream nextPutAll:' put:'.
+        element storeOn:aStream.
+        aStream nextPut:$;.
+        index := index + 1
+    ].
+    index > 1 ifTrue:[aStream nextPutAll:' yourself'].
+    aStream nextPut:$)
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Assoc.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,112 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Association
+       instanceVariableNames:'key value'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Support'
+!
+
+Association comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+Associations are a key-value pair; they are the elements of Dictionaries -
+storing value under the key. By itself, an Association is not very
+useful.
+
+Instance variables:
+
+key             <Object>        the key
+value           <Object>        the value
+
+%W% %E%
+'!
+
+!Association class methodsFor:'instance creation'!
+
+key:aKey
+    "return a new Association."
+
+    ^ self basicNew key:aKey
+!
+
+key:aKey value:aValue
+    "return a new Association"
+
+    ^ self basicNew key:aKey value:aValue
+! !
+
+!Association methodsFor:'accessing'!
+
+key
+    "return the key of the association"
+
+    ^ key
+!
+
+key:anObject
+    "set the key of the receiver to be anObject.
+     Return the receiver"
+
+    key := anObject
+!
+
+value
+    "return the value of the association"
+
+    ^ value
+!
+
+value:anObject
+    "set the value of the receiver to be anObject.
+     Return the receiver"
+
+    value := anObject
+!
+
+key:aKey value:aValue
+    "set both the key and value of the receiver.
+     Return the receiver"
+
+    key := aKey.
+    value := aValue
+! !
+
+!Association methodsFor:'comparing'!
+
+= anAssociation
+    (anAssociation isKindOf:Association) ifTrue:[
+        (anAssociation key = key) ifTrue:[
+            ^ anAssociation value = value
+        ]
+    ].
+    ^ false
+! !
+
+!Association methodsFor:'printing & storing'!
+
+printString
+    "return a string containing a printable representation
+     of the receiver"
+
+    ^ key printString , '->' , value printString
+!
+
+displayString
+    "return a string containing a printable representation
+     of the receiver for displaying"
+
+    ^ key displayString , '->' , value displayString
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Association.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,112 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Association
+       instanceVariableNames:'key value'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Support'
+!
+
+Association comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+Associations are a key-value pair; they are the elements of Dictionaries -
+storing value under the key. By itself, an Association is not very
+useful.
+
+Instance variables:
+
+key             <Object>        the key
+value           <Object>        the value
+
+%W% %E%
+'!
+
+!Association class methodsFor:'instance creation'!
+
+key:aKey
+    "return a new Association."
+
+    ^ self basicNew key:aKey
+!
+
+key:aKey value:aValue
+    "return a new Association"
+
+    ^ self basicNew key:aKey value:aValue
+! !
+
+!Association methodsFor:'accessing'!
+
+key
+    "return the key of the association"
+
+    ^ key
+!
+
+key:anObject
+    "set the key of the receiver to be anObject.
+     Return the receiver"
+
+    key := anObject
+!
+
+value
+    "return the value of the association"
+
+    ^ value
+!
+
+value:anObject
+    "set the value of the receiver to be anObject.
+     Return the receiver"
+
+    value := anObject
+!
+
+key:aKey value:aValue
+    "set both the key and value of the receiver.
+     Return the receiver"
+
+    key := aKey.
+    value := aValue
+! !
+
+!Association methodsFor:'comparing'!
+
+= anAssociation
+    (anAssociation isKindOf:Association) ifTrue:[
+        (anAssociation key = key) ifTrue:[
+            ^ anAssociation value = value
+        ]
+    ].
+    ^ false
+! !
+
+!Association methodsFor:'printing & storing'!
+
+printString
+    "return a string containing a printable representation
+     of the receiver"
+
+    ^ key printString , '->' , value printString
+!
+
+displayString
+    "return a string containing a printable representation
+     of the receiver for displaying"
+
+    ^ key displayString , '->' , value displayString
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Autoload.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,121 @@
+"
+ COPYRIGHT (c) 1991-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Autoload
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Kernel-Classes'
+!
+
+Autoload comment:'
+
+COPYRIGHT (c) 1991-93 by Claus Gittinger
+              All Rights Reserved
+
+In memory limited systems (as my 8Mb 386 is) all seldom-used classes are made
+subclasses of this class. Autoload catches all messages and
+files-In the corresponding code when first used. Then the cought message
+is resent to the (now existing) class.
+
+%W% %E%
+written fall 91 by claus
+'!
+
+!Autoload class methodsFor:'loading'!
+
+autoload
+    "use this to force loading
+     - it is defined a noop in all non-autoloading clases"
+
+    |mySelf myName mySym newClass|
+
+    mySelf := self.
+    myName := self name.
+    mySym := myName asSymbol.
+
+    "remove myself - to avoid recompilation"
+    Smalltalk at:mySym put:nil.
+
+    "load"
+    Transcript showCr:('autoloading ', myName , ' ...').
+
+    Smalltalk fileInClass:myName.
+
+    "did it work ?"
+    newClass := Smalltalk at:mySym.
+    Smalltalk at:mySym put:mySelf.   "will be undone by become:"
+
+    newClass isNil ifTrue:[
+        "no - reinstall myself"
+        self warn:('autoload of ' , myName , ' failed').
+        ^ nil
+    ].
+
+    "wow - it did"
+
+    self become:newClass.
+    ^ self
+! !
+
+!Autoload class methodsFor:'message catching'!
+
+doesNotUnderstand:aMessage
+    "cought a message; load class and retry"
+
+    |newClass|
+
+    newClass := self autoload.
+    newClass notNil ifTrue:[
+        ^ newClass perform:(aMessage selector)
+             withArguments:(aMessage arguments)
+    ].
+    super doesNotUnderstand:aMessage
+!
+
+new
+    "catch new"
+
+    ^ self doesNotUnderstand:(Message selector:#new)
+!
+
+basicNew
+    "catch basicNew"
+
+    ^ self doesNotUnderstand:(Message selector:#basicew)
+!
+
+new:arg
+    "catch new:"
+
+    ^ self doesNotUnderstand:(Message selector:#new: with:arg)
+!
+
+basicNew:arg
+    "catch basicNew:"
+
+    ^ self doesNotUnderstand:(Message selector:#basicNew: with:arg)
+!
+
+subclass:a1 instanceVariableNames:a2 classVariableNames:a3 poolDictionaries:a4 category:a5
+    |newClass|
+
+    (self == Autoload) ifTrue:[
+        ^ super subclass:a1 instanceVariableNames:a2 classVariableNames:a3 poolDictionaries:a4 category:a5
+    ].
+    newClass := self autoload.
+    newClass notNil ifTrue:[
+        ^ newClass perform:(thisContext selector)
+             withArguments:(thisContext args)
+    ].
+    ^ nil
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BContext.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,106 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Context subclass:#BlockContext
+       instanceVariableNames:''  "do not add instvars here"
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Kernel-Methods'
+!
+
+BlockContext comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+BlockContexts represent the stack context objects of blocks. The layout is the same
+as for other contexts - this class has been added to avoid a flag in an instance variable.
+(has become necessary with cheap blocks, which have no home).
+
+Warning: layout and size known by compiler and runtime system - do not change.
+
+%W% %E%
+'!
+
+!BlockContext methodsFor:'accessing'!
+
+isBlockContext
+    "return true, iff the receiver is a BlockContext, false otherwise"
+
+    ^ true
+!
+
+methodHome
+    "return the method-home for block contexts"
+
+    |con h|
+
+    home isNil ifTrue:[^ nil]. "XXX will change soon"
+    home isContext ifFalse:[^ nil]. "copying blocks have no method home"
+
+    con := self.
+    h := home.
+    [h notNil] whileTrue:[
+        con := h.
+        h := con home
+    ].
+    ^ con
+!
+
+home
+    "return the immediate home of the receiver.
+     normally this is the methodcontext, where the block was created,
+     for nested block contexts, this is the surrounding blocks context."
+
+    home isContext ifFalse:[^ nil]. "copying blocks have no home"
+    ^ home
+!
+
+selector
+    "return the selector of the context - which is one of the value
+     selectors"
+
+    |nargs|
+
+    nargs := self nargs.
+    (nargs == 0) ifTrue:[^ #value].
+    (nargs == 1) ifTrue:[^ #value:].
+    (nargs == 2) ifTrue:[^ #value:value:].
+    (nargs == 3) ifTrue:[^ #value:value:value:].
+    (nargs == 4) ifTrue:[^ #value:value:value:value:].
+    (nargs == 5) ifTrue:[^ #value:value:value:value:value:].
+    ^ nil
+! !
+
+!BlockContext methodsFor:'printing'!
+
+receiverPrintString
+    home isNil ifTrue:[
+	^ '[] optimized'
+    ].
+    home isContext ifFalse:[
+	"a copying block"
+
+	"receiverClassName := home selfValue class name."
+	^ '[] optimized'
+    ].
+
+    ^ '[] in ' , receiver class name , '-' , self methodHome selector
+!
+
+printReceiver
+    self receiverPrintString print
+!
+    
+printString
+    ^ self receiverPrintString , ' ' , self selector printString
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Bag.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,144 @@
+"
+ COPYRIGHT (c) 1991-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Collection subclass:#Bag
+       instanceVariableNames:'contents'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Unordered'
+!
+
+Bag comment:'
+
+COPYRIGHT (c) 1991-93 by Claus Gittinger
+              All Rights Reserved
+
+Bag implements collections whose elements are unordered and have no
+external keys. Elements may occur more than once.
+
+Instance variables:
+
+contents        <Dictionary>        for each element, the number of occurrences
+
+%W% %E%
+written jun 91 by claus
+'!
+
+!Bag class methodsFor:'instance creation'!
+
+new
+    "return a new empty Bag"
+
+    ^ super new initContents
+!
+
+new:size
+    "return a new empty Bag with initial space for size elements"
+
+    ^ super new initContents:size
+! !
+
+!Bag methodsFor:'private'!
+
+initContents
+    "set the contents to be an empty Dictionary"
+
+    contents := Dictionary new
+!
+
+initContents:size
+    "set the contents to be an empty Dictionary with initial size"
+
+    contents := Dictionary new:size
+! !
+
+!Bag methodsFor:'accessing'!
+
+at:index
+    "report an error: at: is not allowed for Bags"
+
+    ^ self errorNotKeyed
+!
+
+at:index put:anObject
+    "report an error: at:put: is not allowed for Bags"
+
+    ^ self errorNotKeyed
+! !
+
+!Bag methodsFor:'testing'!
+
+size
+    "return the number of bag elements"
+
+    |count|
+
+    count := 0.
+    contents do:[:element | count := count + element].
+    ^ count
+!
+
+occurrencesOf:anObject
+    "return how many times anObject is in the receiver"
+
+    ^ contents at:anObject ifAbsent:[0]
+!
+
+includes:anObject
+    "return true, if anObject is in the receiver"
+
+    ^ contents includesKey:anObject
+! !
+
+!Bag methodsFor:'adding & removing'!
+
+add:anObject
+    "add the argument, anObject to the receiver"
+
+    ^ self add:anObject withOccurences:1
+!
+
+add:newObject withOccurences:anInteger
+    "add the argument, anObject anInteger times to the receiver"
+
+    contents at:newObject
+            put:(self occurrencesOf:newObject) + anInteger.
+    ^ newObject
+!
+
+remove:oldObject ifAbsent:anExceptionBlock
+    "Remove oldObject from the collection and return it
+     - if it was not present, return the value of the exceptionBlock."
+
+    |count|
+
+    count := self occurrencesOf:oldObject.
+    (count == 0) ifTrue:[^ anExceptionBlock value].
+    (count == 1) ifTrue:[
+        contents removeKey:oldObject
+    ] ifFalse:[ 
+        contents at:oldObject put:(count - 1)
+    ].
+    ^ oldObject
+! !
+
+!Bag methodsFor:'enumerating'!
+
+do:aBlock
+    "Perform the block for all members in the collection."
+
+    contents associationsDo:[:assoc |
+        assoc value timesRepeat:[
+            aBlock value:(assoc key)
+        ]
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Behavior.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,733 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Behavior
+       instanceVariableNames:'superclass selectors methods
+                              instSize flags'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Kernel-Classes'
+!
+
+Behavior comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+this class provides minimum support for all classes - additional stuff is
+found in Class; Behaviors stuff has been extracted to make generation of
+compact binaries possible.
+(these do not need all of the functionality in Class)
+
+Instance variables:
+
+superclass  <Class>           the classes superclass
+selectors   <Array>           the selectors for which inst-methods are defined here
+methods     <Array>           the inst-methods corresponding to the selectors
+instSize    <SmallInteger>    the number of instance variables
+flags       <SmallInteger>    special flag bits coded in a number
+
+NOTICE: layout known by compiler and runtime system; be careful when changing
+
+%W% %E%
+written Dec 88 by claus
+'!
+
+!Behavior class methodsFor:'creating new classes'!
+
+new
+    "creates and return a new class"
+
+    |newClass|
+
+    newClass := self basicNew.
+    newClass setSuperclass:Object
+                 selectors:(Array new:0)
+                   methods:(Array new:0)
+                  instSize:0
+                     flags:0.
+    ^ newClass
+! !
+
+!Behavior methodsFor:'initialization'!
+
+initialize
+    "to catch initialize for classes which do not"
+
+    ^ self
+!
+
+reinitialize
+    "to catch reinitialize for classes which do not"
+
+    ^ self
+! !
+
+!Behavior methodsFor:'creating an instance of myself'!
+
+uninitializedNew
+    "same as new"
+
+    ^ self basicNew
+!
+
+uninitializedNew:anInteger
+    "same as new:anInteger"
+
+    ^ self basicNew:anInteger
+!
+
+new
+    "return an instance of myself without indexed variables"
+
+    ^ self basicNew
+!
+
+new:anInteger
+    "return an instance of myself with anInteger indexed variables"
+
+    ^ self basicNew:anInteger
+!
+
+basicNew
+    "return an instance of myself without indexed variables
+     If the receiver-class has indexed instvars, the new object will have
+     a basicSize of zero.
+     ** Do not redefine this method in any class **"
+
+%{  /* NOCONTEXT */
+
+    extern char *newNextPtr, *newEndPtr;
+    OBJ newobj;
+    int instsize;
+    REGISTER int nInstVars;
+#if !defined(memset4)
+# if !defined(FAST_MEMSET) || defined(NEGATIVE_ADDRESSES)
+    REGISTER OBJ *op;
+# endif
+#endif
+
+    nInstVars = _intVal(_INST(instSize));
+    instsize = OHDR_SIZE + nInstVars * sizeof(OBJ);
+    PROTECT(self);
+    _qAlignedNew(newobj, instsize, SENDER);
+    UNPROTECT(self);
+    _InstPtr(newobj)->o_class = self;
+
+    if (nInstVars) {
+#if defined(memset4)
+        memset4(_InstPtr(newobj)->i_instvars, nil, nInstVars);
+#else
+# if defined(FAST_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
+        /*
+         * knowing that nil is 0
+         */
+        memset(_InstPtr(newobj)->i_instvars, 0, instsize - OHDR_SIZE);
+# else
+        op = _InstPtr(newobj)->i_instvars;
+        do {
+            *op++ = nil;
+        } while (--nInstVars);
+# endif
+#endif
+    }
+    RETURN ( newobj );
+%}
+!
+
+basicNew:anInteger
+    "return an instance of myself with anInteger indexed variables.
+     If the receiver-class has no indexed instvars, this is only allowed
+     if the argument, anInteger is zero.
+     ** Do not redefine this method in any class **"
+
+%{  /* NOCONTEXT */
+
+    OBJ newobj;
+    INT instsize, nInstVars, nindexedinstvars;
+    INT flags;
+#if ! defined(FAST_MEMSET) || defined(NEGATIVE_ADDRESSES)
+    REGISTER char *cp;
+    short *sp;
+    long *lp;
+#endif
+    REGISTER OBJ *op;
+    float *fp;
+    double *dp;
+    extern OBJ new();
+
+    if (_isSmallInteger(anInteger)) {
+        nindexedinstvars = _intVal(anInteger);
+        if (nindexedinstvars >= 0) {
+            nInstVars = _intVal(_INST(instSize));
+            flags = _intVal(_INST(flags)) & ARRAYMASK;
+            switch (flags) {
+                case BYTEARRAY:
+                    instsize = OHDR_SIZE + nInstVars * sizeof(OBJ) + nindexedinstvars * sizeof(char);
+		    PROTECT(self);
+                    _qNew(newobj, instsize, SENDER);
+		    UNPROTECT(self);
+                    _InstPtr(newobj)->o_class = self;
+#if defined(FAST_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
+                    /*
+                     * knowing that nil is 0
+                     */
+                    memset(_InstPtr(newobj)->i_instvars, 0, instsize - OHDR_SIZE);
+#else
+                    op = _InstPtr(newobj)->i_instvars;
+                    while (nInstVars--)
+                        *op++ = nil;
+                    cp = (char *)op;
+                    while (nindexedinstvars >= sizeof(long)) {
+                        *(long *)cp = 0;
+			cp += sizeof(long);
+			nindexedinstvars -= sizeof(long);
+		    }
+                    while (nindexedinstvars--)
+                        *cp++ = '\0';
+#endif
+                    RETURN ( newobj );
+                    break;
+
+                case WORDARRAY:
+                    instsize = OHDR_SIZE + nInstVars * sizeof(OBJ) + nindexedinstvars * sizeof(short);
+		    PROTECT(self);
+                    _qNew(newobj, instsize, SENDER);
+		    UNPROTECT(self);
+                    _InstPtr(newobj)->o_class = self;
+#if defined(FAST_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
+                    /*
+                     * knowing that nil is 0
+                     */
+                    memset(_InstPtr(newobj)->i_instvars, 0, instsize - OHDR_SIZE);
+#else
+                    op = _InstPtr(newobj)->i_instvars;
+                    while (nInstVars--)
+                        *op++ = nil;
+                    sp = (short *)op;
+                    while (nindexedinstvars--)
+                        *sp++ = 0;
+#endif
+                    RETURN ( newobj );
+                    break;
+
+               case LONGARRAY:
+                    instsize = OHDR_SIZE + nInstVars * sizeof(OBJ) + nindexedinstvars * sizeof(long);
+		    PROTECT(self);
+                    _qNew(newobj, instsize, SENDER);
+		    UNPROTECT(self);
+                    _InstPtr(newobj)->o_class = self;
+#if defined(memset4) && ! defined(NEGATIVE_ADDRESSES)
+                    /*
+                     * knowing that nil is 0
+                     */
+                    memset4(_InstPtr(newobj)->i_instvars, 0, nInstVars + nindexedinstvars);
+#else
+# if defined(FAST_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
+                    /*
+                     * knowing that nil is 0
+                     */
+                    memset(_InstPtr(newobj)->i_instvars, 0, instsize - OHDR_SIZE);
+# else
+                    op = _InstPtr(newobj)->i_instvars;
+                    while (nInstVars--)
+                        *op++ = nil;
+                    lp = (long *)op;
+                    while (nindexedinstvars--)
+                        *lp++ = 0;
+# endif
+#endif
+                    RETURN ( newobj );
+                    break;
+
+               case FLOATARRAY:
+                    instsize = OHDR_SIZE + nInstVars * sizeof(OBJ) + nindexedinstvars * sizeof(float);
+		    PROTECT(self);
+                    _qNew(newobj, instsize, SENDER);
+		    UNPROTECT(self);
+                    _InstPtr(newobj)->o_class = self;
+                    op = _InstPtr(newobj)->i_instvars;
+                    while (nInstVars--)
+                        *op++ = nil;
+                    fp = (float *)op;
+                    while (nindexedinstvars--)
+                        *fp++ = 0.0;
+                    RETURN ( newobj );
+                    break;
+
+               case DOUBLEARRAY:
+                    instsize = OHDR_SIZE + nInstVars * sizeof(OBJ) + nindexedinstvars * sizeof(double);
+		    PROTECT(self);
+                    _qNew(newobj, instsize, SENDER);
+		    UNPROTECT(self);
+                    _InstPtr(newobj)->o_class = self;
+                    op = _InstPtr(newobj)->i_instvars;
+                    while (nInstVars--)
+                        *op++ = nil;
+                    dp = (double *)op;
+                    while (nindexedinstvars--)
+                        *dp++ = 0.0;
+                    RETURN ( newobj );
+                    break;
+
+                case WKPOINTERARRAY:
+                case POINTERARRAY:
+                    nInstVars += nindexedinstvars;
+                    instsize = OHDR_SIZE + nInstVars * sizeof(OBJ);
+		    PROTECT(self);
+                    _qAlignedNew(newobj, instsize, SENDER);
+		    UNPROTECT(self);
+                    _InstPtr(newobj)->o_class = self;
+#if defined(memset4)
+                    memset4(_InstPtr(newobj)->i_instvars, nil, nInstVars);
+#else
+# if defined(FAST_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
+                    /*
+                     * knowing that nil is 0
+                     */
+                    memset(_InstPtr(newobj)->i_instvars, 0, instsize - OHDR_SIZE);
+# else
+                    op = _InstPtr(newobj)->i_instvars;
+                    while (nInstVars--)
+                        *op++ = nil;
+# endif
+#endif
+                    RETURN ( newobj );
+                    break;
+
+                default:
+                    /*
+                     * new:n for non-variable classes only allowed if
+                     * n == 0
+                     */
+                    if (nindexedinstvars == 0) {
+                        instsize = OHDR_SIZE + nInstVars * sizeof(OBJ);
+		        PROTECT(self);
+                        _qAlignedNew(newobj, instsize, SENDER);
+		        UNPROTECT(self);
+                        _InstPtr(newobj)->o_class = self;
+                        if (nInstVars) {
+#if defined(memset4)
+                            memset4(_InstPtr(newobj)->i_instvars, nil, nInstVars);
+#else
+# if defined(FAST_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
+                            /*
+                             * knowing that nil is 0
+                             */
+                            memset(_InstPtr(newobj)->i_instvars, 0, instsize - OHDR_SIZE);
+# else
+                            op = _InstPtr(newobj)->i_instvars;
+                            do {
+                                *op++ = nil;
+                            } while (--nInstVars);
+# endif
+#endif
+                        }
+                        RETURN ( newobj );
+                    }
+                    break;
+            }
+        }
+    }
+%}
+.
+    (anInteger isMemberOf:SmallInteger) ifFalse:[
+        self error:'argument to new: must be Integer'
+    ] ifTrue:[
+        (anInteger >= 0) ifTrue:[
+            "sorry but this class has no indexed instvars - need 'new' "
+            self error:'not indexed - cannot create with new:'
+        ] ifFalse:[
+            self error:'bad argument'
+        ]
+    ]
+! !
+
+!Behavior methodsFor:'accessing'!
+
+superclass
+    "return the receivers superclass"
+
+    ^ superclass
+!
+
+selectors
+    "return the receivers selector array"
+
+    ^ selectors
+!
+
+methods
+    "return the receivers method array"
+
+    ^ methods
+!
+
+methodDictionary
+    "return the receivers method dictionary - since no dictionary is
+     used (for now) just return the method array"
+
+    ^ methods
+!
+
+instSize
+    "return the number of instance variables of the receiver"
+
+    ^ instSize
+!
+
+flags
+    "return the receivers flag bits"
+
+    ^ flags
+!
+
+isVariable
+    "return true, if instances have indexed instance variables"
+
+    "this used to be defined as:
+        ^ (flags bitAnd:16r0F) ~~ 0
+     but then, changes in stc.h would not affect us here. Therefore:"
+
+%{  /* NOCONTEXT */
+    /* this is defined as a primitive to get defines from stc.h */
+
+    RETURN ( (_intVal(_INST(flags)) & ARRAYMASK) ? true : false ); 
+%}
+!
+
+isFixed
+    "return true, if instances do not have indexed instance variables"
+
+%{  /* NOCONTEXT */
+    /* this is defined as a primitive to get defines from stc.h */
+
+    RETURN ( (_intVal(_INST(flags)) & ARRAYMASK) ? false : true ); 
+%}
+!
+
+isBits
+    "return true, if instances have indexed byte or short instance variables.
+     Ignore long, float and double arrays, since classes using isBits are probably
+     not prepared to handle them correctly."
+
+%{  /* NOCONTEXT */
+    /* this is defined as a primitive to get defines from stc.h */
+
+    REGISTER int flags;
+
+    RETURN ( (((flags = (_intVal(_INST(flags)) & ARRAYMASK)) == BYTEARRAY)
+	     || (flags == WORDARRAY)) ? true : false ); 
+%}
+!
+
+isBytes
+    "return true, if instances have indexed byte instance variables"
+
+%{  /* NOCONTEXT */
+    /* this is defined as a primitive to get defines from stc.h */
+
+    RETURN ( ((_intVal(_INST(flags)) & ARRAYMASK) == BYTEARRAY) ? true : false ); 
+%}
+!
+
+isWords
+    "return true, if instances have indexed short instance variables"
+
+%{  /* NOCONTEXT */
+    /* this is defined as a primitive to get defines from stc.h */
+
+    RETURN ( ((_intVal(_INST(flags)) & ARRAYMASK) == WORDARRAY) ? true : false ); 
+%}
+!
+
+isLongs
+    "return true, if instances have indexed long instance variables"
+
+%{  /* NOCONTEXT */
+    /* this is defined as a primitive to get defines from stc.h */
+
+    RETURN ( ((_intVal(_INST(flags)) & ARRAYMASK) == LONGARRAY) ? true : false ); 
+%}
+!
+
+isFloats
+    "return true, if instances have indexed float instance variables"
+
+%{  /* NOCONTEXT */
+    RETURN ( ((_intVal(_INST(flags)) & ARRAYMASK) == FLOATARRAY) ? true : false ); 
+%}
+!
+
+isDoubles
+    "return true, if instances have indexed double instance variables"
+
+%{  /* NOCONTEXT */
+    RETURN ( ((_intVal(_INST(flags)) & ARRAYMASK) == DOUBLEARRAY) ? true : false ); 
+%}
+!
+
+isPointers
+    "return true, if instances have pointer instance variables"
+
+    "question: should we ignore WeakPointers ?"
+%{  /* NOCONTEXT */
+    REGISTER int flags;
+
+    RETURN ( (((flags = (_intVal(_INST(flags)) & ARRAYMASK)) == POINTERARRAY)
+	     || (flags == WKPOINTERARRAY)) ? true : false ); 
+%}
+! !
+
+!Behavior methodsFor:'queries'!
+
+isBehavior
+    "return true, if the receiver describing another objecs behavior
+     i.e. is a class."
+
+    ^ true
+!
+
+hasMultipleSuperclasses
+    "NO multiple inheritance in this system"
+
+    ^ false
+!
+
+superclasses
+    "return a collection of the receivers immediate superclasses
+     - since we have NO multiple inheritance, there is only one"
+
+    ^ Array with:superclass
+!
+
+allSuperclasses
+    "return a collection of the receivers accumulated superclasses"
+
+    |aCollection theSuperClass|
+
+    theSuperClass := superclass.
+    theSuperClass notNil ifTrue:[
+        aCollection := OrderedCollection new.
+        [theSuperClass notNil] whileTrue:[
+            aCollection add:theSuperClass.
+            theSuperClass := theSuperClass superclass
+        ]
+    ].
+    ^ aCollection
+!
+
+withAllSuperclasses
+    "return a collection containing the receiver and all
+     of the receivers accumulated superclasses"
+
+    |aCollection theSuperClass|
+
+    aCollection := OrderedCollection with:self.
+    theSuperClass := superclass.
+    [theSuperClass notNil] whileTrue:[
+        aCollection add:theSuperClass.
+        theSuperClass := theSuperClass superclass
+    ].
+    ^ aCollection
+!
+
+subclasses
+    "return a collection of the direct subclasses of the receiver"
+
+    |newColl|
+
+    newColl := OrderedCollection new.
+    self subclassesDo:[:aClass |
+        newColl add:aClass
+    ].
+    ^ newColl
+!
+
+allSubclasses
+    "return a collection of all subclasses (direct AND indirect) of
+     the receiver"
+
+    |newColl|
+
+    newColl := OrderedCollection new.
+    self allSubclassesDo:[:aClass |
+        newColl add:aClass
+    ].
+    ^ newColl
+!
+
+withAllSubclasses
+    "return a collection containing the receiver and 
+     all subclasses (direct AND indirect) of the receiver"
+
+    |newColl|
+
+    newColl := OrderedCollection with:self.
+    self allSubclassesDo:[:aClass |
+        newColl add:aClass
+    ].
+    ^ newColl
+!
+
+isSubclassOf:aClass
+    "return true, if I am a subclass of the argument, aClass"
+
+    |theClass|
+    theClass := superclass.
+    [theClass notNil] whileTrue:[
+        (theClass == aClass) ifTrue:[^ true].
+        theClass := theClass superclass
+    ].
+    ^ false
+!
+
+allInstances
+    "return a collection of all my instances"
+
+    |coll|
+
+    coll := OrderedCollection new.
+    ObjectMemory allObjectsDo:[:anObject |
+        (anObject class == self) ifTrue:[
+            coll add:anObject
+        ]
+    ].
+    ^ coll asArray
+!
+
+instanceCount
+    "return the number of instances of myself"
+
+    |count|
+
+    count := 0.
+    ObjectMemory allObjectsDo:[:anObject |
+        (anObject class == self) ifTrue:[
+            count := count + 1
+        ]
+    ].
+    ^ count
+! !
+
+!Behavior methodsFor:'private accessing'!
+
+setSuperclass:sup selectors:sels methods:m instSize:i flags:f
+    "set some inst vars (private use only)"
+
+    superclass := sup.
+    selectors := sels.
+    methods := m.
+    instSize := i.
+    flags := f
+!
+
+setSuperclass:aClass
+    "set the superclass of the receiver"
+
+    superclass := aClass
+!
+
+instSize:aNumber
+    "set the instance size"
+
+    instSize := aNumber
+!
+
+flags:aNumber
+    "set the flags"
+
+    flags := aNumber
+!
+
+setSelectors:anArray
+    "set the selector array of the receiver"
+
+    selectors := anArray
+!
+
+setMethodDictionary:anArray
+    "set the method array of the receiver"
+
+    methods := anArray
+!
+
+superclass:aClass
+    "set the superclass"
+
+    "must flush caches since lookup chain changes"
+    ObjectMemory flushCaches.
+    superclass := aClass
+!
+
+selectors:selectorArray methods:methodArray
+    "set both selector array and method array of the receiver,
+     and flush caches"
+
+    ObjectMemory flushCaches.
+    selectors := selectorArray.
+    methods := methodArray
+! !
+
+!Behavior methodsFor:'enumeration'!
+
+allInstancesDo:aBlock
+    "evaluate a block for all of my instances"
+
+    ObjectMemory allObjectsDo:[:anObject |
+        (anObject class == self) ifTrue:[
+            aBlock value:anObject
+        ]
+    ]
+!
+
+subclassesDo:aBlock
+    "evaluate the argument, aBlock for all immediate subclasses"
+
+    Smalltalk allClassesDo:[:aClass |
+        (aClass superclass == self) ifTrue:[
+            aBlock value:aClass
+        ]
+    ]
+!
+
+allSubclassesDo:aBlock
+    "evaluate a block for all of my subclasses"
+
+    Smalltalk allClassesDo:[:aClass |
+        (aClass isSubclassOf:self) ifTrue:[
+            aBlock value:aClass
+        ]
+    ]
+!
+
+allSubclassesInOrderDo:aBlock
+    "evaluate a block for all of my subclasses where superclasses come
+     first"
+
+    self subclassesDo:[:aClass |
+        aBlock value:aClass.
+        aClass allSubclassesInOrderDo:aBlock
+    ]
+!
+
+allSuperclassesDo:aBlock
+    "evaluate a block for all of my superclasses"
+
+    |theClass|
+
+    theClass := superclass.
+    [theClass notNil] whileTrue:[
+        aBlock value:theClass.
+        theClass := theClass superclass
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Block.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,733 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Block
+       instanceVariableNames:'code flags byteCode home nargs
+                              sourcePos initialPC literals
+                              selfValue'
+       classVariableNames:'InvalidNewSignal'
+       poolDictionaries:''
+       category:'Kernel-Methods'
+!
+
+Block comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+%W% %E%
+
+written spring 89 by claus
+'!
+
+!Block class methodsFor:'documentation'!
+
+documentation
+"
+Blocks are pieces of executable code which can be evaluated by sending
+them a value-message (''value'', ''value:'', ''value:value:'' etc).
+
+Blocks with arguments need a message of type ''value:arg1 ... value:argn''
+for evaluation; the number of arguments passed when evaluating must match
+the number of arguments the block was declared with otherwise an error is
+raised. Blocks without args need a ''value'' message for evaluation.
+
+Blocks keep a reference to the method context where the block was declared -
+this allows blocks to access the methods arguments and/or variables.
+This is also true when the method has already returned - since the
+block keeps this reference, the methods context will NOT die in this case.
+
+A return (via ^-statement) out of a block will force a return from the
+blocks method context (if it is still living) - this make the implementation
+of long-jumps and control structures possible.
+(If the method is not alive (i.e. has already returned), a return out of the block 
+is ignored and a simple return from the block is performed).
+
+Long-jump is done by defining a catchBlock as ''[^ self]''
+somewhere up in the calling-tree. Then, to do the long-jump from out of some 
+deeply nested method, simply do: ''catchBlock value''.
+
+Instance variables:
+
+code        <not_an_object>   the function pointer if its a compiled block
+flags       <SmallInteger>    special flag bits coded in a number
+byteCode    <ByteArray>       bytecode of home method if its an interpreted block
+home        <Context>         the context where this block lives
+nargs       <SmallInteger>    the number of arguments the block expects
+sourcePos   <SmallInteger>    the character poistion of its source, in chars
+                              relative to methods source beginning
+initialPC   <SmallInteger>    the start position within the byteCode
+literals    <Array>           the blocks literal array
+selfValue   <Object>          value to use for self if its a copying block
+
+NOTICE: layout known by runtime system and compiler - do not change
+"
+! !
+
+!Block class methodsFor:'initialization' !
+
+initialize
+    "setup the signals"
+
+    InvalidNewSignal := (Signal new).
+    InvalidNewSignal mayProceed:false.
+    InvalidNewSignal notifierString:'blocks are only created by the system'.
+! !
+
+!Block class methodsFor:'instance creation'!
+
+code:codeAddress byteCode:bCode nargs:numArgs sourcePosition:sourcePos initialPC:initialPC literals:literals
+    "create a new cheap (homeless) block.
+     Not for public use - special hook for the compiler."
+
+    |newBlock|
+
+    newBlock := super basicNew.
+    newBlock code:codeAddress.
+    newBlock byteCode:bCode.
+    newBlock nargs:numArgs.
+    newBlock sourcePosition:sourcePos. 
+    newBlock initialPC:initialPC. 
+    newBlock literals:literals.
+    ^ newBlock
+!
+
+basicNew
+    "catch creation of blocks - only the system creates blocks"
+
+    InvalidNewSignal raise.
+    ^ nil
+!
+
+basicNew:size
+    "catch creation of blocks - only the system creates blocks"
+
+    InvalidNewSignal raise.
+    ^ nil
+! !
+
+!Block methodsFor:'testing'!
+
+isBlock
+    ^ true
+! !
+
+!Block methodsFor:'accessing'!
+
+instVarAt:index
+    "have to catch instVar access to code - since its no object"
+
+    (index == 1) ifTrue:[^ self code].
+    ^ super instVarAt:index
+!
+
+instVarAt:index put:value
+    "have to catch instVar access to code - since its no object"
+
+    (index == 1) ifTrue:[^ self code:value].
+    ^ super instVarAt:index put:value
+!
+
+code
+    "return the code field. This is not an object but the address of the machine instructions. 
+     Therefore an integer representing the code-address is returned"
+
+%{  /* NOCONTEXT */
+
+    if (_INST(code) != nil) {
+        RETURN ( _MKSMALLINT((int)(_INST(code))) )
+    }
+%}
+.
+    ^ nil
+!
+
+byteCode
+    "return the bytecode (a ByteArray) of the block"
+
+    ^ byteCode
+!
+
+nargs
+    "return the number of arguments I expect for evaluation"
+
+    ^ nargs
+!
+
+selfValue
+    "return the copied self"
+
+    ^ selfValue
+! !
+
+!Block methodsFor:'private accessing'!
+
+code:anAddress
+    "set the code field - danger alert. 
+     This is not an object but the address of the blocks machine instructions.
+     Therefore the argument must be an integer representing for this address.
+     You can crash Smalltalk very badly when playing around here ..."
+
+%{  /* NOCONTEXT */
+    if (_isSmallInteger(anAddress))
+        _INST(code) = (OBJ)(_intVal(anAddress));
+%}
+!
+
+byteCode:aByteArray
+    "set the bytecode field - danger alert"
+
+    byteCode := aByteArray
+!
+
+nargs:numArgs
+    "set the number of arguments I expect for evaluation - danger alert"
+
+    nargs := numArgs
+!
+
+sourcePosition:position 
+    "set the position of the source within my method"
+
+    sourcePos := position
+!
+
+initialPC:initial 
+    "set the initial pc for evaluation - danger alert"
+
+    initialPC := initial
+!
+
+literals:aLiteralArray 
+    "set the literal array for evaluation - danger alert"
+
+    literals := aLiteralArray
+! !
+
+!Block methodsFor:'error handling'!
+
+argumentCountError:numberGiven
+    "report that the number of arguments given does not match the number expected"
+
+    self error:('Block got ' , numberGiven printString ,
+                ' args while ' , nargs printString , ' where expected')
+!
+
+invalidMethod
+    "this is sent by the bytecode interpreter when the blocks definition is bad.
+     Can only happen when playing around with the blocks instvars
+     or the Compiler/runtime system is buggy"
+
+    self error:'invalid block - not executable'
+!
+
+invalidByteCode
+    "this is sent by the bytecode interpreter when trying to execute
+     an invalid bytecode.
+     Can only happen when playing around with the blocks instvars
+      or the Compiler/runtime system is buggy"
+
+    self error:'invalid byteCode in block - not executable'
+!
+
+receiverNotBoolean
+    "this error is triggered when the bytecode-interpreter tries to
+     execute ifTrue:/ifFalse or whileTrue: type of expressions where the
+     receiver is neither true nor false."
+
+    self error:'if/while on non-boolean receiver'
+! !
+
+!Block methodsFor:'evaluation'!
+
+value
+    "evaluate the receiver with no block args. The receiver must be a block without arguments."
+
+%{  /* NOCONTEXT */
+
+    REGISTER OBJFUNC thecode;
+    OBJ home;
+    extern OBJ interpret();
+
+    if (_INST(nargs) == _MKSMALLINT(0)) {
+#if defined(THIS_CONTEXT)
+        if (1 /* _intVal(__pilc->ilc_lineNo) > 0 */)
+            _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+        home = _BlockInstPtr(self)->b_home;
+        thecode = _BlockInstPtr(self)->b_code;
+        if (thecode != (OBJFUNC)nil) {
+            /* compiled machine code */
+            RETURN ( (*thecode)(home COMMA_SND) );
+        }
+        /* interpreted code */
+        RETURN ( interpret(self, 0, nil, home COMMA_SND, nil) );
+    }
+%}
+.
+    ^ self argumentCountError:0
+!
+
+value:arg
+    "evaluate the receiver with one argument. The receiver must be a 1-arg block."
+
+%{  /* NOCONTEXT */
+
+    REGISTER OBJFUNC thecode;
+    OBJ home;
+    extern OBJ interpret();
+
+    if (_INST(nargs) == _MKSMALLINT(1)) {
+#if defined(THIS_CONTEXT)
+        if (1 /* _intVal(__pilc->ilc_lineNo) > 0 */)
+            _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+        home = _BlockInstPtr(self)->b_home;
+        thecode = _BlockInstPtr(self)->b_code;
+        if (thecode != (OBJFUNC)nil) {
+#ifdef PASS_ARG_REF
+            RETURN ( (*thecode)(home COMMA_SND, &arg) );
+#else
+            RETURN ( (*thecode)(home COMMA_SND, arg) );
+#endif
+        }
+        /* interpreted code */
+#ifdef PASS_ARG_REF
+        RETURN ( interpret(self, 1, nil, home COMMA_SND, nil, &arg) );
+#else
+        RETURN ( interpret(self, 1, nil, home COMMA_SND, nil, arg) );
+#endif
+    }
+%}
+.
+    ^ self argumentCountError:1
+!
+
+value:arg1 value:arg2
+    "evaluate the receiver with two arguments. The receiver must be a 2-arg block."
+
+%{  /* NOCONTEXT */
+
+    REGISTER OBJFUNC thecode;
+    OBJ home;
+    extern OBJ interpret();
+
+    if (_INST(nargs) == _MKSMALLINT(2)) {
+#if defined(THIS_CONTEXT)
+        if (1 /* _intVal(__pilc->ilc_lineNo) > 0 */)
+            _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+        home = _BlockInstPtr(self)->b_home;
+        thecode = _BlockInstPtr(self)->b_code;
+        if (thecode != (OBJFUNC)nil) {
+#ifdef PASS_ARG_REF
+            RETURN ( (*thecode)(home COMMA_SND, &arg1) );
+#else
+            RETURN ( (*thecode)(home COMMA_SND, arg1, arg2) );
+#endif
+        }
+#ifdef PASS_ARG_REF
+        RETURN ( interpret(self, 2, nil, home COMMA_SND, nil, &arg1) );
+#else
+        RETURN ( interpret(self, 2, nil, home COMMA_SND, nil, arg1, arg2) );
+#endif
+    }
+%}
+.
+    ^ self argumentCountError:2
+!
+
+value:arg1 value:arg2 value:arg3
+    "evaluate the receiver with three arguments. The receiver must be a 3-arg block."
+
+%{  /* NOCONTEXT */
+
+    REGISTER OBJFUNC thecode;
+    OBJ home;
+    extern OBJ interpret();
+
+    if (_INST(nargs) == _MKSMALLINT(3)) {
+#if defined(THIS_CONTEXT)
+        if (1 /* _intVal(__pilc->ilc_lineNo) > 0 */)
+            _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+        home = _BlockInstPtr(self)->b_home;
+        thecode = _BlockInstPtr(self)->b_code;
+        if (thecode != (OBJFUNC)nil) {
+#ifdef PASS_ARG_REF
+            RETURN ( (*thecode)(home COMMA_SND, &arg1) );
+#else
+            RETURN ( (*thecode)(home COMMA_SND, arg1, arg2, arg3) );
+#endif
+        }
+#ifdef PASS_ARG_REF
+        RETURN ( interpret(self, 3, nil, home COMMA_SND, nil, &arg1) );
+#else
+        RETURN ( interpret(self, 3, nil, home COMMA_SND, nil, arg1, arg2, arg3) );
+#endif
+    }
+%}
+.
+    ^ self argumentCountError:3
+!
+
+value:arg1 value:arg2 value:arg3 value:arg4
+    "evaluate the receiver with four arguments. The receiver must be a 4-arg block."
+
+%{  /* NOCONTEXT */
+
+    REGISTER OBJFUNC thecode;
+    OBJ home;
+    extern OBJ interpret();
+
+    if (_INST(nargs) == _MKSMALLINT(4)) {
+#if defined(THIS_CONTEXT)
+        if (1 /* _intVal(__pilc->ilc_lineNo) > 0 */)
+            _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+        home = _BlockInstPtr(self)->b_home;
+        thecode = _BlockInstPtr(self)->b_code;
+        if (thecode != (OBJFUNC)nil) {
+#ifdef PASS_ARG_REF
+            RETURN ( (*thecode)(home COMMA_SND, &arg1) );
+#else
+            RETURN ( (*thecode)(home COMMA_SND, arg1, arg2, arg3, arg4) );
+#endif
+        }
+#ifdef PASS_ARG_REF
+        RETURN ( interpret(self, 4, nil, home COMMA_SND, nil, &arg1) );
+#else
+        RETURN ( interpret(self, 4, nil, home COMMA_SND, nil, arg1, arg2, arg3, arg4) );
+#endif
+    }
+%}
+.
+    ^ self argumentCountError:4
+!
+
+value:arg1 value:arg2 value:arg3 value:arg4 value:arg5
+    "evaluate the receiver with four arguments. The receiver must be a 5-arg block."
+
+%{  /* NOCONTEXT */
+
+    REGISTER OBJFUNC thecode;
+    OBJ home;
+    extern OBJ interpret();
+
+    if (_INST(nargs) == _MKSMALLINT(5)) {
+#if defined(THIS_CONTEXT)
+        if (1 /* _intVal(__pilc->ilc_lineNo) > 0 */)
+            _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+        home = _BlockInstPtr(self)->b_home;
+        thecode = _BlockInstPtr(self)->b_code;
+        if (thecode != (OBJFUNC)nil) {
+#ifdef PASS_ARG_REF
+            RETURN ( (*thecode)(home COMMA_SND, &arg1) );
+#else
+            RETURN ( (*thecode)(home COMMA_SND, arg1, arg2, arg3, arg4, arg5) );
+#endif
+        }
+#ifdef PASS_ARG_REF
+        RETURN ( interpret(self, 5, nil, home COMMA_SND, nil, &arg1) );
+#else
+        RETURN ( interpret(self, 5, nil, home COMMA_SND, nil, arg1, arg2, arg3, arg4, arg5) );
+#endif
+    }
+%}
+.
+    ^ self argumentCountError:5
+!
+
+value:arg1 value:arg2 value:arg3 value:arg4 value:arg5 value:arg6
+    "evaluate the receiver with four arguments. The receiver must be a 6-arg block."
+
+%{  /* NOCONTEXT */
+
+    REGISTER OBJFUNC thecode;
+    OBJ home;
+    extern OBJ interpret();
+
+    if (_INST(nargs) == _MKSMALLINT(6)) {
+#if defined(THIS_CONTEXT)
+        if (1 /* _intVal(__pilc->ilc_lineNo) > 0 */)
+            _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+        home = _BlockInstPtr(self)->b_home;
+        thecode = _BlockInstPtr(self)->b_code;
+        if (thecode != (OBJFUNC)nil) {
+#ifdef PASS_ARG_REF
+            RETURN ( (*thecode)(home COMMA_SND, &arg1) );
+#else
+            RETURN ( (*thecode)(home COMMA_SND, arg1, arg2, arg3, arg4, arg5, arg6) );
+#endif
+        }
+#ifdef PASS_ARG_REF
+        RETURN ( interpret(self, 6, nil, home COMMA_SND, nil, &arg1) );
+#else
+        RETURN ( interpret(self, 6, nil, home COMMA_SND, nil, arg1, arg2, arg3, arg4, arg5, arg6) );
+#endif
+    }
+%}
+.
+    ^ self argumentCountError:6
+!
+
+valueWithArguments:argArray
+    "evaluate the receiver with arguments taken from argArray.
+     The size of the argArray must match the number of arguments the receiver expects."
+
+    |a1 a2 a3 a4 a5 a6 a7|
+
+    (argArray class == Array) ifFalse:[
+        ^ self error:'argument must be an array'
+    ].
+    (argArray size == nargs) ifFalse:[
+        ^ self argumentCountError:(argArray size)
+    ].
+%{
+
+    REGISTER OBJFUNC thecode;
+    OBJ home;
+    extern OBJ interpret();
+
+#if defined(THIS_CONTEXT)
+    if (1 /* _intVal(__pilc->ilc_lineNo) > 0 */)
+        _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+    switch (_intVal(_INST(nargs))) {
+        case 7:
+            a7 = _ArrayInstPtr(argArray)->a_element[6];
+        case 6:
+            a6 = _ArrayInstPtr(argArray)->a_element[5];
+        case 5:
+            a5 = _ArrayInstPtr(argArray)->a_element[4];
+        case 4:
+            a4 = _ArrayInstPtr(argArray)->a_element[3];
+        case 3:
+            a3 = _ArrayInstPtr(argArray)->a_element[2];
+        case 2:
+            a2 = _ArrayInstPtr(argArray)->a_element[1];
+        case 1:
+            a1 = _ArrayInstPtr(argArray)->a_element[0];
+        case 0:
+            break;
+    }
+    home = _BlockInstPtr(self)->b_home;
+    thecode = _BlockInstPtr(self)->b_code;
+    if (thecode != (OBJFUNC)nil) {
+#ifdef PASS_ARG_REF
+        RETURN ( (*thecode)(home COMMA_SND, &a1) );
+#else
+        RETURN ( (*thecode)(home COMMA_SND, a1, a2, a3, a4, a5, a6, a7) );
+#endif
+    }
+#ifdef PASS_ARG_REF
+    RETURN ( interpret(self, _intVal(_INST(nargs)), nil,
+                                    home COMMA_SND, nil, &a1) );
+#else
+    RETURN ( interpret(self, _intVal(_INST(nargs)), nil,
+                                    home COMMA_SND, nil, a1, a2, a3, a4, a5, a6, a7) );
+#endif
+%}
+!
+
+valueNowOrOnUnwindDo:aBlock
+    "evaluate the receiver - after that, or when some method sent within unwinds (i.e. does
+     a long return), evaluate the argument, aBlock.
+     This is used to make certain that cleanup actions (for example closing files etc.) are
+     executed regardless of error actions"
+
+    |v|
+
+    v := self value.       "the real logic is in Context"
+    aBlock value.
+    ^ v
+!
+
+valueOnUnwindDo:aBlock
+    "evaluate the receiver - when some method sent within unwinds (i.e. does
+     a long return), evaluate the argument, aBlock.
+     This is used to make certain that cleanup actions (for example closing files etc.) are
+     executed regardless of error actions"
+
+    ^ self value        "the real logic is in Context"
+! !
+
+!Block methodsFor:'looping'!
+
+whileTrue:aBlock
+    "evaluate the argument, aBlock while the receiver evaluates to true.
+     - open coded by compiler but needed here for #perform and expression evaluation."
+%{
+    extern OBJ _value;
+    static struct inlineCache bval = _ILC0;
+    static struct inlineCache selfVal = _ILC0;
+
+    while ((*bval.ilc_func)(self, _value, CON_COMMA nil, &bval) == true) {
+        if (InterruptPending != nil) interrupt(CONARG);
+        (*selfVal.ilc_func)(aBlock, _value, CON_COMMA nil, &selfVal);
+    }
+%}
+.
+    ^ nil
+!
+
+whileTrue
+    "evaluate the receiver until it evaluates to false (ST80 compatibility)"
+
+    ^ self whileTrue:[]
+!
+
+whileFalse:aBlock
+    "evaluate the argument while the receiver evaluates to false.
+     - open coded by compiler but needed here for #perform and expression evaluation."
+%{
+    extern OBJ _value;
+    static struct inlineCache bval = _ILC0;
+    static struct inlineCache selfVal = _ILC0;
+
+    while ((*bval.ilc_func)(self, _value, CON_COMMA nil, &bval) == false) {
+        if (InterruptPending != nil) interrupt(CONARG);
+        (*selfVal.ilc_func)(aBlock, _value, CON_COMMA nil, &selfVal);
+    }
+%}
+.
+    ^ nil
+!
+
+whileFalse
+    "evaluate the receiver until it evaluates to true (ST80 compatibility)"
+
+    ^ self whileFalse:[]
+!
+
+doWhile:aBlock
+    "repeat the receiver block until aBlock evaluates to false.
+     The receiver is evaluated at least once."
+
+    self value.
+    [aBlock value] whileTrue:[
+        self value
+    ]
+!
+
+doUntil:aBlock
+    "repeat the receiver block until aBlock evaluates to true.
+     The receiver is evaluated at least once."
+
+    self value.
+    [aBlock value] whileFalse:[
+        self value
+    ]
+!
+
+loop
+    "repeat the receiver forever (should contain a return somewhere).
+     Inspired by a corresponding Self method."
+
+    [true] whileTrue:[self value]
+
+    "[Transcript showCr:'hello'] loop"  "must be stopped with interrupt"
+!
+
+valueWithExit
+    "the receiver must be a block of one argument.  It is evaluated, and is passed a block,
+     which, if sent a value:-message, will exit the receiver block, returning the parameter of the 
+     value:-message. Used for premature returns to the caller.
+     Taken from a manchester goody (also appears in Self)."
+
+    ^ self value: [:exitValue | ^exitValue]
+
+    "[:exit |
+        1 to:10 do:[:i |
+            i == 5 ifTrue:[exit value:'thats it']
+        ].
+        'regular block-value; never returned'
+     ] valueWithExit"
+!
+
+loopWithExit
+    "the receiver must be a block of one argument.  It is evaluated in a loop forever, and is passed a 
+     block, which, if sent a value:-message, will exit the receiver block, returning the parameter of 
+     the value:-message. Used for loops with exit in the middle.
+     Inspired by a corresponding Self method."
+
+    |exitBlock|
+
+    exitBlock := [:exitValue | ^ exitValue].
+    [true] whileTrue:[self value:exitBlock]
+
+    "|i|
+     i := 1.
+     [:exit |
+        i == 5 ifTrue:[exit value:'thats it'].
+        i := i + 1
+     ] loopWithExit"
+! !
+
+!Block methodsFor:'process creation'!
+
+newProcess
+    "create a new (unscheduled) process executing the receiver"
+
+    |p pBlock startUp|
+
+    startUp := self.
+    pBlock := [ startUp value. Processor terminate:p ].
+    p := Processor newProcessFor:pBlock.
+    ^ p
+!
+
+fork
+    "create a new process executing the receiver"
+
+    ^ self newProcess resume
+!
+
+forkWith:argumentArray
+    |b|
+
+    b := [self valueWithArguments:argumentArray].
+    b fork
+!
+
+forkAt:priority
+    "create a new process executing the receiver"
+
+    ^ (self newProcess priority:priority) resume
+! !
+
+!Block methodsFor:'printing'!
+
+printString
+    |homeClass|
+
+    home notNil ifTrue:[
+        ^ '[] in ', home printString
+    ].
+    ^ '[] in ???'
+!
+
+printOn:aStream
+    |homeClass|
+
+    aStream nextPutAll:'[] in '.
+    homeClass := home containingClass.
+    homeClass notNil ifTrue:[
+        homeClass name printOn:aStream.
+        aStream space.
+        (homeClass selectorForMethod:home) printOn:aStream
+    ] ifFalse:[
+        aStream nextPutAll:' ???'
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BlockContext.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,106 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Context subclass:#BlockContext
+       instanceVariableNames:''  "do not add instvars here"
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Kernel-Methods'
+!
+
+BlockContext comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+BlockContexts represent the stack context objects of blocks. The layout is the same
+as for other contexts - this class has been added to avoid a flag in an instance variable.
+(has become necessary with cheap blocks, which have no home).
+
+Warning: layout and size known by compiler and runtime system - do not change.
+
+%W% %E%
+'!
+
+!BlockContext methodsFor:'accessing'!
+
+isBlockContext
+    "return true, iff the receiver is a BlockContext, false otherwise"
+
+    ^ true
+!
+
+methodHome
+    "return the method-home for block contexts"
+
+    |con h|
+
+    home isNil ifTrue:[^ nil]. "XXX will change soon"
+    home isContext ifFalse:[^ nil]. "copying blocks have no method home"
+
+    con := self.
+    h := home.
+    [h notNil] whileTrue:[
+        con := h.
+        h := con home
+    ].
+    ^ con
+!
+
+home
+    "return the immediate home of the receiver.
+     normally this is the methodcontext, where the block was created,
+     for nested block contexts, this is the surrounding blocks context."
+
+    home isContext ifFalse:[^ nil]. "copying blocks have no home"
+    ^ home
+!
+
+selector
+    "return the selector of the context - which is one of the value
+     selectors"
+
+    |nargs|
+
+    nargs := self nargs.
+    (nargs == 0) ifTrue:[^ #value].
+    (nargs == 1) ifTrue:[^ #value:].
+    (nargs == 2) ifTrue:[^ #value:value:].
+    (nargs == 3) ifTrue:[^ #value:value:value:].
+    (nargs == 4) ifTrue:[^ #value:value:value:value:].
+    (nargs == 5) ifTrue:[^ #value:value:value:value:value:].
+    ^ nil
+! !
+
+!BlockContext methodsFor:'printing'!
+
+receiverPrintString
+    home isNil ifTrue:[
+	^ '[] optimized'
+    ].
+    home isContext ifFalse:[
+	"a copying block"
+
+	"receiverClassName := home selfValue class name."
+	^ '[] optimized'
+    ].
+
+    ^ '[] in ' , receiver class name , '-' , self methodHome selector
+!
+
+printReceiver
+    self receiverPrintString print
+!
+    
+printString
+    ^ self receiverPrintString , ' ' , self selector printString
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Boolean.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,74 @@
+"
+ COPYRIGHT (c) 1988/89/90 by Claus Gittinger
+	      All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Boolean
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Kernel-Objects'
+!
+
+Boolean comment:'
+
+COPYRIGHT (c) 1988/89/90 by Claus Gittinger
+	      All Rights Reserved
+
+Boolean is an abstract class defining the common protocol for logical
+values. The logical values are represented by its two subclasses True and False.
+
+There are no instances of Boolean in the system and there is only one
+instance of True (which is the global true) and one of False (false).
+
+Boolean catches some messages which deal with copying Booleans to make
+certain there is only one instance of each.
+The system will behave strange if you fiddle around here and create
+new instances of True or False.
+
+written 1988 by claus
+
+@(#)Boolean.st	2.4 92/06/06
+'!
+
+!Boolean class methodsFor:'instance creation'!
+
+basicNew
+    "catch instance creation
+     - there must be exactly on instance of each - no more"
+
+    self error:'new instances of True/False are not allowed'
+! !
+
+!Boolean methodsFor:'copying'!
+
+shallowCopy
+    "return a shallow copy of the receiver
+     - since both true and false are unique, return the receiver"
+
+    ^ self
+!
+
+deepCopy
+    "return a deep copy of the receiver
+     - since both true and false are unique, return the receiver"
+
+    ^ self
+! !
+
+!Boolean methodsFor:'printing & storing'!
+
+storeOn:aStream
+    "append a Character sequence to the argument, aStream from which the
+     receiver can be reconstructed.
+     return the receiver"
+
+    self printOn:aStream
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ByteArray.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,492 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+IntegerArray subclass:#ByteArray
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Indexed'
+!
+
+ByteArray comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+              All Rights Reserved
+
+ByteArrays store integers in the range 0..255
+unlike Smalltalk/80, my ByteArrays have fixed size - may change
+
+%W% %E%
+
+written spring 89 by claus
+'!
+
+!ByteArray class methodsFor:'instance creation'!
+
+uninitializedNew:anInteger
+    "return a new instance of the receiver with uninitialized
+     (i.e. undefined) contents. The indexed elements have any random
+     value. use, when contents will be set anyway shortly after."
+
+%{  /* NOCONTEXT */
+
+    OBJ newobj;
+    INT instsize, nInstVars, nindexedinstvars;
+    REGISTER OBJ *op;
+    extern OBJ new();
+
+    if (_isSmallInteger(anInteger)) {
+        nindexedinstvars = _intVal(anInteger);
+        if (nindexedinstvars >= 0) {
+            nInstVars = _intVal(_ClassInstPtr(self)->c_ninstvars);
+            if ((_intVal(_ClassInstPtr(self)->c_flags) & ARRAYMASK) == BYTEARRAY) {
+                instsize = OHDR_SIZE + nInstVars * sizeof(OBJ) + nindexedinstvars * sizeof(char);
+		PROTECT(self);
+                _qNew(newobj, instsize, SENDER);
+		UNPROTECT(self);
+                _InstPtr(newobj)->o_class = self;
+#if defined(FAST_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
+                /*
+                 * knowing that nil is 0
+                 */
+                memset(_InstPtr(newobj)->i_instvars, 0, instsize - OHDR_SIZE);
+#else
+                op = _InstPtr(newobj)->i_instvars;
+                while (nInstVars--)
+                    *op++ = nil;
+#endif
+                RETURN ( newobj );
+	    }
+	}
+    }
+%}
+.
+    ^ self basicNew:anInteger
+
+! !
+
+!ByteArray methodsFor:'accessing'!
+
+basicAt:index
+    "return the indexed instance variable with index, anInteger
+     - added here for speed i.e. to avoid double send at: - basicAt:"
+
+%{  /* NOCONTEXT */
+
+    REGISTER int indx;
+    int nIndex;
+
+    if ((_qClass(self) == ByteArray) && _isSmallInteger(index)) {
+        indx = _intVal(index);
+        nIndex = _qSize(self) - OHDR_SIZE;
+        if ((indx > 0) && (indx <= nIndex)) {
+            RETURN ( _MKSMALLINT(_ByteArrayInstPtr(self)->ba_element[indx - 1]) );
+        }
+    }
+%}
+.
+    ^ super basicAt:index
+!
+
+basicAt:index put:value
+    "set the indexed instance variable with index, anInteger to value
+     - added here for speed i.e. to avoid double send at:put: - basicAt:put:"
+
+%{  /* NOCONTEXT */
+
+    REGISTER int indx;
+    int nIndex;
+    int val;
+
+    if ((_qClass(self) == ByteArray)
+     && _isSmallInteger(index)
+     && _isSmallInteger(value)) {
+        val = _intVal(value);
+        if ((val >= 0) && (val <= 255)) {
+            indx = _intVal(index);
+            nIndex = _qSize(self) - OHDR_SIZE;
+            if ((indx > 0) && (indx <= nIndex)) {
+                _ByteArrayInstPtr(self)->ba_element[indx - 1] = val;
+                RETURN ( value );
+            }
+        }
+    }
+%}
+.
+    ^ super basicAt:index put:value
+! !
+
+!ByteArray methodsFor:'queries'!
+
+indexOf:aByte startingAt:start
+    "return the index of the first occurrence of the argument, aByte
+     in the receiver starting at start, anInteger; return 0 if not found.
+     - reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+    REGISTER unsigned char *cp;
+    REGISTER int index, byteValue;
+    REGISTER int len;
+
+    if (! _isSmallInteger(aByte)) {
+        RETURN ( _MKSMALLINT(0) );
+    }
+
+    byteValue = _intVal(aByte);
+
+    if ((byteValue < 0) || (byteValue > 255)) {
+        RETURN ( _MKSMALLINT(0) );
+    }
+
+    if (_isSmallInteger(start)
+     && (_qClass(self) == ByteArray)) {
+        index = _intVal(start);
+        len = _qSize(self) - OHDR_SIZE;
+        cp = &(_ByteArrayInstPtr(self)->ba_element[0]);
+        cp += index - 1;
+        while (index <= len) {
+            if (*cp == byteValue) {
+                RETURN ( _MKSMALLINT(index) );
+            }
+            index++;
+            cp++;
+        }
+        RETURN ( _MKSMALLINT(0) );
+    }
+%}
+.
+    ^ super indexOf:aByte startingAt:start
+!
+
+usedValues
+    "return a new ByteArray with all used values (actually a kind of Set);
+     needed specially for Image class."
+
+    |result l|
+%{
+    REGISTER unsigned char *cp;
+    REGISTER int len;
+    unsigned char flags[256];
+    static struct inlineCache nw = _ILC1;
+    extern OBJ ByteArray, _new_;
+
+    if (_qClass(self) == ByteArray) {
+        memset(flags, 0, sizeof(flags));
+        len = _qSize(self) - OHDR_SIZE;
+        cp = &(_ByteArrayInstPtr(self)->ba_element[0]);
+
+        /* for each used byte, set flag */
+        while (len > 0) {
+            flags[*cp] = 1;
+            cp++;
+            len--;
+        }
+        /* count 1's */
+        len = 0;
+        for (cp=&flags[255]; cp >= flags; cp--)
+            if (*cp) len++;
+
+        /* create ByteArray of used values */
+#ifdef PASS_ARG_REF
+        l = _MKSMALLINT(len);
+        result = (*nw.ilc_func)(ByteArray, _new_, CON_COMMA nil, &nw, &l);
+#else
+        result = (*nw.ilc_func)(ByteArray, _new_, CON_COMMA nil, &nw, _MKSMALLINT(len));
+#endif
+        if (_Class(result) == ByteArray) {
+            cp = &(_ByteArrayInstPtr(result)->ba_element[0]);
+            for (len=0; len < 256; len++) {
+                if (flags[len]) 
+                    *cp++ = len;
+            }
+        }
+        RETURN ( result );
+    }
+%}
+.
+    self primitiveFailed
+!
+
+usageCounts
+    "return an array filled with value-counts -
+     added for Image handling"
+
+    |counts|
+
+    counts := Array new:256.
+%{
+    REGISTER unsigned char *cp;
+    REGISTER int nByte;
+    REGISTER int index;
+    int icounts[256];
+
+    if ((_qClass(self) == ByteArray) && _isArray(counts)) {
+        /*
+         * zero counts
+         */
+        for (index=0; index<256; index++) {
+            icounts[index] = 0;
+        }
+
+        /*
+         * count
+         */
+        nByte = _qSize(self) - OHDR_SIZE;
+        cp = &(_ByteArrayInstPtr(self)->ba_element[0]);
+        while (nByte--) {
+            icounts[*cp++]++;
+        }
+
+        /*
+         * make it real counts
+         */
+        for (index=0; index<256; index++) {
+            _ArrayInstPtr(counts)->a_element[index] = _MKSMALLINT(icounts[index]);
+        }
+        RETURN ( counts );
+    }
+%}
+.
+    self primitiveFailed
+!
+
+maximumValue
+    "return the maximum value in the receiver -
+     added for sound-player (which needs a fast method for this)"
+
+%{  /* NOCONTEXT */
+
+    REGISTER unsigned char *cp;
+    REGISTER int index, max;
+    int len;
+
+    if (_qClass(self) == ByteArray) {
+        max = 0;
+        index = 0;
+        len = _qSize(self) - OHDR_SIZE;
+        cp = &(_ByteArrayInstPtr(self)->ba_element[0]);
+        while (++index <= len) {
+            if (*cp > max) max = *cp;
+            cp++;
+        }
+        RETURN ( _MKSMALLINT(max) );
+    }
+%}
+.
+    self primitiveFailed
+! !
+
+!ByteArray methodsFor:'filling and replacing'!
+
+replaceFrom:start to:stop with:aCollection startingAt:repStart
+    "reimplemented for speed"
+
+%{      /* NOCONTEXT */
+
+        int nIndex, repNIndex;
+        int startIndex, stopIndex;
+        REGISTER unsigned char *src;
+        REGISTER int repStartIndex;
+        int repStopIndex, count;
+        REGISTER unsigned char *dst;
+
+        if ((_qClass(self) == ByteArray)
+         && (_Class(aCollection) == ByteArray)
+         && _isSmallInteger(start)
+         && _isSmallInteger(stop)
+         && _isSmallInteger(repStart)) {
+            startIndex = _intVal(start) - 1;
+            if (startIndex >= 0) {
+              nIndex = _qSize(self) - OHDR_SIZE;
+              stopIndex = _intVal(stop) - 1;
+              count = stopIndex - startIndex + 1;
+              if ((count > 0) && (stopIndex < nIndex)) {
+                repStartIndex = _intVal(repStart) - 1;
+                if (repStartIndex >= 0) {
+                  repNIndex = _qSize(aCollection) - OHDR_SIZE;
+                  repStopIndex = repStartIndex + (stopIndex - startIndex);
+                  if (repStopIndex < repNIndex) {
+                    src = &(_ByteArrayInstPtr(aCollection)->ba_element[repStartIndex]);
+                    dst = &(_ByteArrayInstPtr(self)->ba_element[startIndex]);
+
+                    if (aCollection == self) {
+                        /* take care of overlapping copy */
+                        if (src < dst) {
+                            /* must do a reverse copy */
+                            src += count;
+                            dst += count;
+                            while (count-- > 0) {
+                                *--dst = *--src;
+                            }
+                            RETURN ( self );
+                        }
+                    }
+#ifdef FAST_MEMCPY
+                    bcopy(src, dst, count);
+#else
+                    while (count-- > 0) {
+                        *dst++ = *src++;
+                    }
+#endif
+                    RETURN ( self );
+                  }
+                }
+              }
+            }
+        }
+%}
+.
+    ^ super replaceFrom:start to:stop with:aCollection startingAt:repStart
+! !
+
+!ByteArray methodsFor:'image manipulation'!
+
+invert
+    "invert all bytes - used with image manipulations
+     written as a primitive for speed"
+
+%{  /* NOCONTEXT */
+
+    REGISTER unsigned char *dst;
+    REGISTER unsigned long *ldst;
+    REGISTER int cnt;
+
+    if (_qClass(self) == ByteArray) {
+        cnt = _qSize(self) - OHDR_SIZE;
+        dst = _ByteArrayInstPtr(self)->ba_element;
+        if (! ((int)dst & (sizeof(long)-1))) {
+            ldst = (unsigned long *)dst;
+            while (cnt >= sizeof(long)) {
+                *ldst = ~(*ldst);
+                ldst++;
+                cnt -= sizeof(long);
+            }
+            dst = (unsigned char *)ldst;
+        }
+        while (cnt--) {
+            *dst = ~(*dst);
+            dst++;
+        }
+        RETURN ( self );
+    }
+%}
+.
+    self primitiveFailed
+!
+
+expandPixels:nBitsPerPixel width:width height:height into:aByteArray
+                         mapping:aMapByteArray
+    "given the receiver with nBitsPerPixel-depth pixels, expand them into
+     aByteArray with 8-bit pixels. The width/height-arguments are needed
+     to skip any padded src-bits. On the fly, the destination pixels
+     are translated using aMapByteArray if nonnil.
+     - used to display mono, 2-bit and 4-bit bitmaps on grey-scale/color
+       machines"
+
+%{  /* NOCONTEXT */
+
+    {
+        REGISTER unsigned char *src, *dst;
+        REGISTER int wrun;
+        unsigned char *srcNext;
+        int bytesPerRow, mask, shift0, shift;
+        int w, h, hrun;
+        int srcBytes, dstBytes;
+        int bitsPerPixel;
+        int bits;
+        int ncells;
+        unsigned char *map;
+
+        if ((_qClass(self) == ByteArray) 
+         && (_qClass(aByteArray) == ByteArray)
+         && _isSmallInteger(nBitsPerPixel)
+         && _isSmallInteger(height)
+         && _isSmallInteger(width)) {
+            if ((aMapByteArray != nil)
+             && (_Class(aMapByteArray) == ByteArray)) {
+                map = _ByteArrayInstPtr(aMapByteArray)->ba_element;
+            } else {
+                map = (unsigned char *)0;
+            }
+
+            bitsPerPixel = _intVal(nBitsPerPixel);
+            w = _intVal(width);
+            h = _intVal(height);
+            src = _ByteArrayInstPtr(self)->ba_element;
+            dst = _ByteArrayInstPtr(aByteArray)->ba_element;
+            switch (bitsPerPixel) {
+                case 1:
+                    mask = 0x01;
+                    break;
+                case 2:
+                    mask = 0x03;
+                    break;
+                case 4:
+                    mask = 0x0F;
+                    break;
+                case 8:
+                    mask = 0xFF;
+                    break;
+                default:
+                    goto fail;
+            }
+            ncells = mask + 1;
+            if (map) {
+                if ((_qSize(aMapByteArray) - OHDR_SIZE) < ncells)
+                    goto fail;
+            }
+
+            bytesPerRow = (w * bitsPerPixel + 7) / 8;
+            shift0 = 8 - bitsPerPixel;
+            srcBytes = bytesPerRow * h;
+            dstBytes = w * h;
+
+            if (((_qSize(self) - OHDR_SIZE) >= srcBytes)
+             && ((_qSize(aByteArray) - OHDR_SIZE) >= dstBytes)) {
+                for (hrun=h; hrun; hrun--) {
+                    srcNext = src + bytesPerRow;
+                    shift = shift0;
+                    if (map) {
+                        for (wrun=w; wrun; wrun--) {
+                            if (shift == shift0) {
+                                bits = *src++;
+                            }
+                            *dst++ = map[(bits >> shift) & mask];
+                            shift -= bitsPerPixel;
+                            if (shift < 0) {
+                                shift = shift0;
+                            }
+                        }
+                    } else {
+                        for (wrun=w; wrun; wrun--) {
+                            if (shift == shift0) {
+                                bits = *src++;
+                            }
+                            *dst++ = (bits >> shift) & mask;
+                            shift -= bitsPerPixel;
+                            if (shift < 0) {
+                                shift = shift0;
+                            }
+                        }
+                    }
+                    src = srcNext;
+                }
+                RETURN ( self );
+            }
+        }
+    }
+fail: ;
+%}
+.
+    self primitiveFailed
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CCReader.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,81 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#ClassCategoryReader
+       instanceVariableNames:'myClass myCategory'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Kernel-Support'
+!
+
+ClassCategoryReader comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+             All Rights Reserved
+
+a helper class for fileIn - keeps track of class and category to filein for.
+
+%W% %E%
+written 89 by claus
+'!
+
+!ClassCategoryReader class methodsFor:'instance creation'!
+
+class:aClass category:aCategory
+    "return a new ClassCategoryReader to read methods for aClass with
+     methodCategory aCategory"
+
+    ^ self new class:aClass category:aCategory
+! !
+
+!ClassCategoryReader methodsFor:'private'!
+
+class:aClass category:aCategory
+    "set the instance variables"
+
+    myClass := aClass.
+    myCategory := aCategory
+! !
+
+!ClassCategoryReader methodsFor:'fileIn'!
+
+fileInFrom:aStream notifying:requestor
+    "read method-chunks from the input stream, aStream; compile them
+     and add the methods to the class defined by the class-instance var;
+     errors notifications are passed to requestor"
+
+    |aString done method|
+
+    done := false.
+    [done] whileFalse:[
+        done := aStream atEnd.
+        done ifFalse:[
+            aString := aStream nextChunk.
+            done := aString isNil or:[aString isEmpty].
+            done ifFalse:[
+                method := Compiler compile:aString
+                                  forClass:myClass
+                                inCategory:myCategory
+                                 notifying:requestor
+                                   install:true
+                                skipIfSame:true
+            ]
+        ]
+    ]
+!
+
+fileInFrom:aStream
+    "read method-chunks from the input stream, aStream; compile them
+     and add the methods to the class defined by the class-instance var"
+
+    self fileInFrom:aStream notifying:nil
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Character.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,573 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Magnitude subclass:#Character
+       instanceVariableNames:'asciivalue'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Magnitude-General'
+!
+
+Character comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+Characters are unique; this means that for every asciiValue (0..255) there
+is exactly one instance of Character, which is shared.
+
+Methods marked as (JS) come from the manchester Character goody
+(CharacterComparing) by Jan Steinman, which allow Characters to be used as
+Interval elements (i.e. ($a to:$z) do:[...] ).
+
+WARNING: characters are known by compiler and runtime system -
+do not change the instance layout. (also, its not easy to define
+subclasses of Character since the Compiler always creates Character
+instances for $x and, since equality check on the Character class is
+wired into the system in many places.)
+
+%W% %E%
+
+'!
+
+!Character class methodsFor:'instance creation'!
+
+basicNew
+    "catch new - Characters cannot be created with new"
+
+    ^ self error:'Characters cannot be created with new'
+!
+
+value:anInteger
+    "return a character with asciivalue anInteger"
+
+%{  /* NOCONTEXT */
+
+    int ascii;
+
+    if (_isSmallInteger(anInteger)) {
+        ascii = _intVal(anInteger);
+        if ((ascii >= 0) && (ascii <= 255))
+            RETURN ( _MKCHARACTER(_intVal(anInteger)) );
+    }
+%}
+.
+    (anInteger between:0 and:16rFF) ifTrue:[
+        ^ CharacterTable at:(anInteger + 1)
+    ].
+    (anInteger between:16r100 and:16rFFFF) ifTrue:[
+        ^ super basicNew setAsciiValue:anInteger
+    ].
+    self error:'invalid ascii code for character'
+!
+
+digitValue:anInteger
+    "return a character that corresponds to anInteger.
+     0-9 map to $0-$9, 10-35 map to $A-$Z"
+
+    (anInteger between:0 and:9) ifTrue:[
+        ^ Character value:(anInteger + ($0 asciiValue))
+    ].
+    (anInteger between:10 and:35) ifTrue:[
+        ^ Character value:(anInteger - 10 + ($A asciiValue))
+    ].
+    ^self error:'value not in range 0 to 35'
+! !
+
+!Character class methodsFor:'primitive input'!
+
+fromUser
+    "return a character from the keyboard
+     - this should only be used for emergency evaluators and the like."
+
+%{  /* NOCONTEXT */
+    int c;
+
+    c = getchar();
+    if (c < 0) {
+        RETURN (nil);
+    }
+    RETURN ( _MKCHARACTER(c & 0xFF) );
+%}
+! !
+
+!Character class methodsFor:'constants'!
+
+bell
+    "return the bell character"
+
+    ^ Character value:7
+!
+
+backspace
+    "return the backspace character"
+
+    ^ Character value:8
+!
+
+nl
+    "return the newline character"
+
+    ^ Character value:10
+!
+
+lf
+    "return the newline/linefeed character"
+
+    ^ Character value:10
+!
+
+cr
+    "return the carriage-return character 
+     - actually (in unix) this is also a newline"
+
+    ^ Character value:10
+!
+
+tab
+    "return the tabulator character"
+
+    ^ Character value:9
+!
+
+newPage
+    "return the form-feed character"
+
+    ^ Character value:12
+!
+
+ff
+    "return the form-feed character"
+
+    ^ Character value:12
+!
+
+space
+    "return the blank character"
+
+    ^ Character value:32
+!
+
+esc
+    "return the escape character"
+
+    ^ Character value:27
+!
+
+quote
+    "return the single-quote character"
+
+    ^ Character value:39
+!
+
+doubleQuote
+    "return the double-quote character"
+
+    ^ Character value:34
+!
+
+excla
+    "return the exclamation-mark character"
+    ^ $!!
+! !
+
+!Character methodsFor:'copying'!
+
+shallowCopy
+    "return a shallow copy of myself
+     reimplemented since characters are unique"
+
+     ^ self
+!
+
+deepCopy
+    "return a depp copy of myself
+     reimplemented since characters are unique"
+
+     ^ self
+! !
+
+!Character methodsFor:'private accessing'!
+
+setAsciiValue:anInteger
+    "very private - set the ascii value - only used for
+     characters with codes > 16rFF"
+
+    asciivalue := anInteger
+! !
+	
+!Character methodsFor:'accessing'!
+
+asciiValue
+    "return the asciivalue of myself"
+
+    ^asciivalue
+!
+
+instVarAt:index put:anObject
+    "catch instvar access - asciivalue cannot be changed"
+
+    self error:'Characters may not be modified'
+! !
+
+!Character methodsFor:'converting'!
+
+digitValue
+    "return my digitValue for any base"
+
+    (asciivalue between:($0 asciiValue) and:($9 asciiValue)) ifTrue:[
+        ^ asciivalue - $0 asciiValue
+    ].
+    (asciivalue between:($a asciiValue) and:($z asciiValue)) ifTrue:[
+        ^ asciivalue - $a asciiValue + 10
+    ]. 
+    (asciivalue between:($A asciiValue) and:($Z asciiValue)) ifTrue:[
+        ^ asciivalue - $A asciiValue + 10
+    ]. 
+    self error:'bad character'
+! !
+
+!Character methodsFor:'comparing'!
+
+= aCharacter
+    "return true, if the argument, aCharacter is the same character
+     redefined to avoid the overhead of [Object =] -> [Object ==] 
+     (although the compiler creates a shortcut code for this)"
+
+    ^ (self == aCharacter)
+!
+
+~= aCharacter
+    "return true, if the argument, aCharacter is not the same character
+     redefined to avoid the overhead of [Object ~=] -> [Object not] -> [Object =] -> [Object ==]
+     (although the compiler creates a shortcut code for this)"
+
+    ^ (self ~~ aCharacter)
+!
+
+> aCharacter
+    "return true, if the arguments asciiValue is less than mine"
+
+    ^ (asciivalue > aCharacter asciiValue)
+!
+
+< aCharacter
+    "return true, if the arguments asciiValue is greater than mine"
+
+    ^ (asciivalue < aCharacter asciiValue)
+!
+
+<= aCharacter
+    "return true, if the arguments asciiValue is greater or equal to mine"
+
+    ^ (asciivalue <= aCharacter asciiValue)
+!
+
+>= aCharacter
+    "return true, if the arguments asciiValue is less or equal to mine"
+
+    ^ (asciivalue >= aCharacter asciiValue)
+!
+
+identityHash
+    "return an integer useful for hashing on identity"
+
+    ^ 4096 + asciivalue
+! !
+
+!Character methodsFor: 'arithmetic'!
+
++ aMagnitude
+    "Return the Character that is <aMagnitude> higher than the receiver. 
+     Wrap if the resulting value is not a legal Character value. (JS)"
+
+    ^ Character value:(self asInteger + aMagnitude asInteger \\ 256)
+!
+
+- aMagnitude
+    "Return the Character that is <aMagnitude> lower than the receiver.  
+     Wrap if the resulting value is not a legal Character value. (JS)"
+
+    ^ Character value:(self asInteger - aMagnitude asInteger \\ 256)
+!
+
+// aMagnitude
+    "Return the Character who's value is the receiver divided by <aMagnitude>. 
+     Wrap if the resulting value is not a legal Character value. (JS)"
+
+    ^ Character value:(self asInteger // aMagnitude asInteger \\ 256)
+!
+
+\\ aMagnitude
+    "Return the Character who's value is the receiver modulo <aMagnitude>.  
+     Wrap if the resulting value is not a legal Character value. (JS)"
+
+    ^ Character value:(self asInteger \\ aMagnitude asInteger \\ 256)
+! !
+
+!Character methodsFor:'testing'!
+
+isDigit
+    "return true, if I am a digit (i.e. $0 .. $9)"
+
+    ^ asciivalue between:($0 asciiValue) and:($9 asciiValue)
+!
+
+isDigitRadix:r
+    "return true, if I am a digit of a base r number"
+
+    (asciivalue < $0 asciiValue) ifTrue:[^ false]. 
+    (r > 10) ifTrue:[
+        (asciivalue between:($0 asciiValue) and:($9 asciiValue)) ifTrue:[
+            ^ true
+        ].
+        ((asciivalue - $a asciiValue) between:0 and:(r - 10)) ifTrue:[
+            ^ true
+        ].
+        ^ (asciivalue - $A asciiValue) between:0 and:(r - 10)
+    ].
+    (asciivalue - $0 asciiValue) < r ifTrue:[^ true].
+    ^ false
+!
+
+isLowercase
+    "return true, if I am a lower-case letter"
+
+%{  /* NOCONTEXT */
+
+    REGISTER int val;
+
+    val = _intVal(_INST(asciivalue));
+    RETURN ( ((val >= 'a') && (val <= 'z')) ? true : false );
+%}
+!
+
+isUppercase
+    "return true, if I am an upper-case letter"
+
+%{  /* NOCONTEXT */
+
+    REGISTER int val;
+
+    val = _intVal(_INST(asciivalue));
+    RETURN ( ((val >= 'A') && (val <= 'Z')) ? true : false );
+%}
+!
+
+isLetter
+    "return true, if I am a letter"
+
+%{  /*NOCONTEXT */
+
+    REGISTER int val;
+
+    val = _intVal(_INST(asciivalue));
+    RETURN ( (((val >= 'a') && (val <= 'z')) ||
+              ((val >= 'A') && (val <= 'Z'))) ? true : false );
+%}
+!
+
+isAlphaNumeric
+    "return true, if I am a letter or a digit"
+
+%{  /* NOCONTEXT */
+
+    REGISTER int val;
+
+    val = _intVal(_INST(asciivalue));
+    if ((val >= 'a') && (val <= 'z')) {
+        RETURN ( true );
+    }
+    if ((val >= 'A') && (val <= 'Z')) {
+        RETURN ( true );
+    }
+    if ((val >= '0') && (val <= '9')) {
+        RETURN ( true );
+    }
+    RETURN ( false );
+%}
+!
+
+isVowel
+    "return true, if I am a vowel (lower- or uppercase)"
+
+    (self == $a) ifTrue:[^ true].
+    (self == $e) ifTrue:[^ true].
+    (self == $i) ifTrue:[^ true].
+    (self == $o) ifTrue:[^ true].
+    (self == $u) ifTrue:[^ true].
+    (self == $A) ifTrue:[^ true].
+    (self == $E) ifTrue:[^ true].
+    (self == $I) ifTrue:[^ true].
+    (self == $O) ifTrue:[^ true].
+    (self == $U) ifTrue:[^ true].
+    ^ false
+!
+
+isSeparator
+    "return true if I am a space, cr, tab, nl, or newPage"
+
+%{  /* NOCONTEXT */
+
+    REGISTER int val;
+
+    val = _intVal(_INST(asciivalue));
+    if (val <= ' ') {
+        if ((val == ' ')
+         || (val == '\n') 
+         || (val == '\t')
+         || (val == '\r')
+         || (val == '\f')) {
+            RETURN ( true );
+        }
+    }
+%}
+.
+    ^ false
+!
+
+isEndOfLineCharacter
+    "return true if I am a line delimitting character"
+
+%{  /* NOCONTEXT */
+
+    REGISTER int val;
+
+    val = _intVal(_INST(asciivalue));
+    if (val <= ' ') {
+        if ((val == '\n')
+         || (val == '\r')
+         || (val == '\f')) {
+            RETURN ( true );
+        }
+    }
+%}
+.
+    ^ false
+! !
+
+!Character methodsFor:'converting'!
+
+asLowercase
+    "return a character with same letter as myself but lowercase
+     (myself if I am lowercase)"
+
+    self isUppercase ifFalse:[^ self].
+    ^ Character value:(asciivalue + 32)
+!
+
+asUppercase
+    "return a character with same letter as myself but uppercase
+     (myself if I am lowercase)"
+
+    self isLowercase ifFalse:[^ self].
+    ^ Character value:(asciivalue - 32)
+!
+
+asInteger
+    "return an Integer with my ascii-value"
+
+    ^ asciivalue
+!
+
+asSymbol
+    "return a unique symbol which prints like I print"
+
+    ^ Symbol internCharacter:self
+!
+
+asString
+    "return a string of len 1 with myself as contents"
+
+%{  /* NOCONTEXT */
+
+    char buffer[2];
+
+    buffer[0] = (char) _intVal(_characterVal(self));
+    buffer[1] = '\0';
+    RETURN ( _MKSTRING(buffer COMMA_SND) );
+%}
+
+"
+    |newString|
+
+    newString := String new:1.
+    newString at:1 put:self.
+    ^ newString
+"
+!
+
+to:aMagnitude
+    "Return an Interval over the characters from the receiver to <aMagnitude>. 
+     Wrap <aMagnitude> if it is not a legal Character value. (JS)"
+
+    ^ Interval from:self to:(aMagnitude \\ 256)
+
+! !
+
+!Character methodsFor:'printing & storing'!
+
+printString
+    "return a string to print me"
+
+    ^ self asString
+!
+
+printOn:aStream
+    "print myself on aStream"
+
+    aStream nextPut:self
+!
+
+print
+    "print myself on stdout"
+
+%{  /* NOCONTEXT */
+
+    putchar(_intVal(_INST(asciivalue)));
+%}
+!
+
+displayString
+    "return a string used when the receiver is to be displayed
+     in an inspector kind-of-thing"
+
+    ^ self storeString
+!
+
+storeString
+    "return a string for storing"
+
+    (asciivalue between:33 and:127) ifFalse:[
+	(self == Character space) ifTrue:[
+	    ^ '(Character space)'
+	].
+	(self == Character cr) ifTrue:[
+	    ^ '(Character cr)'
+	].
+        ^ '(Character value:' , asciivalue printString , ')'
+    ].
+    ^ '$' , self asString
+!
+
+storeOn:aStream
+    "store myself on aStream"
+
+    (asciivalue between:33 and:127) ifFalse:[
+        aStream nextPutAll:'(Character value:'.
+        aStream nextPutAll:(asciivalue printString).
+        aStream nextPutAll:')'
+    ] ifTrue:[
+        aStream nextPut:$$.
+        aStream nextPut:self
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Class.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,1651 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+               All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+ClassDescription subclass:#Class
+       instanceVariableNames:'classvars comment subclasses'
+       classVariableNames:'updatingChanges'
+       poolDictionaries:''
+       category:'Kernel-Classes'
+!
+
+Class comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+This class adds more functionality to classes; minimum stuff has already
+been defined in Behavior; this one adds naming, categories etc.
+also changes management and recompilation is defined here.
+
+For a minimum system, the compiler generates classes as instances of
+Behavior - this excludes all name, source info etc., however, the more 
+usual case is to create instances of Class.
+
+Instance variables:
+
+classvars       <String>        the names of the class variables
+comment         <String>        the classes comment
+subclasses      <Collection>    cached collection of subclasses
+                                (currently unused - but will be soon)
+
+Class variables:
+
+updatingChanges <Boolean>       true if the changes-file shall be updated
+
+WARNING: layout known by compiler and runtime system
+
+%W% %E%
+written Spring 89 by claus
+'!
+
+!Class class methodsFor:'initialization'!
+
+initialize
+    "the classvariable 'updatingChanges' controls if changes are put
+     into the changes-file; normally this variable is set to true, but
+     for example during fileIn or when changes are applied, it is set to false
+     to prevent changes file from getting too much junk."
+     
+    updatingChanges := true
+! !
+
+!Class class methodsFor:'creating new classes'!
+
+new
+    "creates and returs a new class"
+
+    |newClass|
+
+    newClass := super new.
+    newClass setComment:(self comment)
+               category:(self category).
+    ^ newClass
+! !
+
+!Class methodsFor:'autoload check'!
+
+autoload
+    "force autoloading - do nothing here; redefined in Autoload;
+     see comment there"
+
+    ^ self
+! !
+
+!Class methodsFor:'subclass creation'!
+
+subclass:t instanceVariableNames:f
+              classVariableNames:d
+                poolDictionaries:s
+                        category:cat
+
+    "create a new class as a subclass of an existing class (the receiver).
+     The subclass will have indexed variables if the receiving-class has."
+
+    self isVariable ifFalse:[
+        ^ self class
+            name:t
+            inEnvironment:Smalltalk
+            subclassOf:self
+            instanceVariableNames:f
+            variable:false
+            words:true
+            pointers:true
+            classVariableNames:d
+            poolDictionaries:s
+            category:cat
+            comment:nil
+            changed:false
+    ].
+    self isPointers ifTrue:[
+        ^ self
+            variableSubclass:t
+            instanceVariableNames:f
+            classVariableNames:d
+            poolDictionaries:s
+            category:cat
+    ].
+    self isBytes ifTrue:[
+        ^ self
+            variableByteSubclass:t
+            instanceVariableNames:f
+            classVariableNames:d
+            poolDictionaries:s
+            category:cat
+    ].
+    self isLongs ifTrue:[
+        ^ self
+            variableLongSubclass:t
+            instanceVariableNames:f
+            classVariableNames:d
+            poolDictionaries:s
+            category:cat
+    ].
+    self isFloats ifTrue:[
+        ^ self
+            variableFloatSubclass:t
+            instanceVariableNames:f
+            classVariableNames:d
+            poolDictionaries:s
+            category:cat
+    ].
+    self isDoubles ifTrue:[
+        ^ self
+            variableDoubleSubclass:t
+            instanceVariableNames:f
+            classVariableNames:d
+            poolDictionaries:s
+            category:cat
+    ].
+    "only word is left over"
+    ^ self
+        variableWordSubclass:t
+        instanceVariableNames:f
+        classVariableNames:d
+        poolDictionaries:s
+        category:cat
+!
+
+variableSubclass:t
+        instanceVariableNames:f
+        classVariableNames:d
+        poolDictionaries:s
+        category:cat
+
+    "create a new class as a subclass of an existing class (the receiver) 
+     in which the subclass has indexable pointer variables"
+
+    self isVariable ifTrue:[
+        self isPointers ifFalse:[
+            ^ self error:
+                'cannot make a variable pointer subclass of a variable non-pointer class'
+        ]
+    ].
+
+    ^ self class
+        name:t
+        inEnvironment:Smalltalk
+        subclassOf:self
+        instanceVariableNames:f
+        variable:true
+        words:false
+        pointers:true
+        classVariableNames:d
+        poolDictionaries:s
+        category:cat
+        comment:nil
+        changed:false
+!
+
+variableByteSubclass:t
+        instanceVariableNames:f
+        classVariableNames:d
+        poolDictionaries:s
+        category:cat
+
+    "create a new class as a subclass of an existing class (the receiver) 
+     in which the subclass has indexable byte-sized nonpointer variables"
+
+    self isVariable ifTrue:[
+        self isBytes ifFalse:[
+            ^ self error:
+                'cannot make a variable byte subclass of a variable non-byte class'
+        ].
+    ].
+
+    ^ self class
+        name:t
+        inEnvironment:Smalltalk
+        subclassOf:self
+        instanceVariableNames:f
+        variable:true
+        words:false
+        pointers:false
+        classVariableNames:d
+        poolDictionaries:s
+        category:cat
+        comment:nil
+        changed:false
+!
+
+variableWordSubclass:t
+        instanceVariableNames:f
+        classVariableNames:d
+        poolDictionaries:s
+        category:cat
+
+    "create a new class as a subclass of an existing class (the receiver) 
+     in which the subclass has indexable word-sized nonpointer variables"
+
+    self isVariable ifTrue:[
+        self isWords ifFalse:[
+            ^ self error:
+                'cannot make a variable word subclass of a variable non-word class'
+        ].
+    ].
+
+    ^ self class
+        name:t
+        inEnvironment:Smalltalk
+        subclassOf:self
+        instanceVariableNames:f
+        variable:true
+        words:true
+        pointers:false
+        classVariableNames:d
+        poolDictionaries:s
+        category:cat
+        comment:nil
+        changed:false
+!
+
+variableLongSubclass:t
+        instanceVariableNames:f
+        classVariableNames:d
+        poolDictionaries:s
+        category:cat
+
+    "create a new class as a subclass of an existing class (the receiver) 
+     in which the subclass has indexable long-sized nonpointer variables"
+
+    self isVariable ifTrue:[
+        self isLongs ifFalse:[
+            ^ self error:
+                'cannot make a variable long subclass of a variable non-long class'
+        ].
+    ].
+
+    ^ self class
+        name:t
+        inEnvironment:Smalltalk
+        subclassOf:self
+        instanceVariableNames:f
+        variable:#long 
+        words:false
+        pointers:false
+        classVariableNames:d
+        poolDictionaries:s
+        category:cat
+        comment:nil
+        changed:false
+!
+
+variableFloatSubclass:t
+        instanceVariableNames:f
+        classVariableNames:d
+        poolDictionaries:s
+        category:cat
+
+    "create a new class as a subclass of an existing class (the receiver) 
+     in which the subclass has indexable float-sized nonpointer variables"
+
+    self isVariable ifTrue:[
+        self isFloats ifFalse:[
+            ^ self error:
+                'cannot make a variable float subclass of a variable non-float class'
+        ].
+    ].
+
+    ^ self class
+        name:t
+        inEnvironment:Smalltalk
+        subclassOf:self
+        instanceVariableNames:f
+        variable:#float 
+        words:false
+        pointers:false
+        classVariableNames:d
+        poolDictionaries:s
+        category:cat
+        comment:nil
+        changed:false
+!
+
+variableDoubleSubclass:t
+        instanceVariableNames:f
+        classVariableNames:d
+        poolDictionaries:s
+        category:cat
+
+    "create a new class as a subclass of an existing class (the receiver) 
+     in which the subclass has indexable double-sized nonpointer variables"
+
+    self isVariable ifTrue:[
+        self isDoubles ifFalse:[
+            ^ self error:
+                'cannot make a variable double subclass of a variable non-double class'
+        ].
+    ].
+
+    ^ self class
+        name:t
+        inEnvironment:Smalltalk
+        subclassOf:self
+        instanceVariableNames:f
+        variable:#double 
+        words:false
+        pointers:false
+        classVariableNames:d
+        poolDictionaries:s
+        category:cat
+        comment:nil
+        changed:false
+! !
+
+!Class methodsFor:'ST/V subclass creation'!
+
+subclass:t
+        instanceVariableNames:f
+        classVariableNames:d
+        poolDictionaries:s
+
+    "this methods allows fileIn of ST/V classes 
+     (which seem to have no category)"
+
+    ^ self subclass:t 
+           instanceVariableNames:f
+           classVariableNames:d
+           poolDictionaries:s
+           category:'ST/V classes'
+!
+
+variableByteSubclass:t
+        classVariableNames:d
+        poolDictionaries:s
+
+    "this methods allows fileIn of ST/V variable byte classes 
+     (which seem to have no category and no instvars)"
+
+    ^ self variableByteSubclass:t 
+           instanceVariableNames:''
+           classVariableNames:d
+           poolDictionaries:s
+           category:'ST/V classes'
+!
+
+variableSubclass:t
+        instanceVariableNames:f
+        classVariableNames:d
+        poolDictionaries:s
+
+    "this methods allows fileIn of ST/V variable pointer classes 
+     (which seem to have no category)"
+
+    ^ self variableSubclass:t 
+           instanceVariableNames:f
+           classVariableNames:d
+           poolDictionaries:s
+           category:'ST/V classes'
+! !
+
+!Class methodsFor:'accessing'!
+
+classVariableString
+    "return a string of the class variables names "
+
+    classvars isNil ifTrue:[^ ''].
+    ^ classvars
+!
+
+classVarNames
+    "return a collection of the class variable name-strings"
+
+    ^ self addClassVarNamesTo:(OrderedCollection new)
+!
+
+allClassVarNames
+    "return a collection of all the class variable name-strings
+     this includes all superclass-class variables"
+
+    ^ self addAllClassVarNamesTo:(OrderedCollection new)
+!
+
+instVarNames
+    "return a collection of the instance variable name-strings"
+
+    ^ self addInstVarNamesTo:(OrderedCollection new)
+!
+
+allInstVarNames
+    "return a collection of all the instance variable name-strings
+     this includes all superclass-instance variables"
+
+    ^ self addAllInstVarNamesTo:(OrderedCollection new)
+!
+
+comment
+    "return the comment (aString) of the class"
+
+    ^ comment
+!
+
+setComment:aString
+    "set the comment of the class to be the argument, aString;
+     do NOT create a change record"
+
+    comment := aString
+!
+
+comment:aString
+    "set the comment of the class to be the argument, aString;
+     create a change record"
+
+    comment := aString.
+    self addChangeRecordForClassComment:self
+!
+
+definition
+    "return an expression-string to define myself"
+
+    |s|
+
+    s := WriteStream on:(String new).
+    self fileOutDefinitionOn:s.
+    ^ s contents
+
+    "Object definition"
+    "Point definition"
+!
+
+setComment:com category:categoryStringOrSymbol
+    "set the comment and category of the class;
+     do NOT create a change record"
+
+    comment := com.
+    category := categoryStringOrSymbol asSymbol
+!
+
+setName:aString
+    "set the classes name"
+
+    name := aString
+!
+
+setClassVariableString:aString
+    "set the classes classvarnames string"
+
+    classvars := aString
+!
+
+classVariableString:aString
+    "set the classes classvarnames string;
+     initialize new class variables with nil, clear and remove
+     old ones"
+
+    |prevVarNames varNames|
+
+    "ignore for metaclasses except the one"
+    (self isMeta "isKindOf:Metaclass") ifTrue:[
+        (self == Metaclass) ifFalse:[
+            ^ self
+        ]
+    ].
+    (classvars = aString) ifFalse:[
+        prevVarNames := self classVarNames.
+        classvars := aString.
+        varNames := self classVarNames.
+
+        "new ones get initialized to nil;
+         - old ones are nilled and removed from Smalltalk"
+
+        varNames do:[:aName |
+            (prevVarNames includes:aName) ifFalse:[
+                "a new one"
+                Smalltalk at:(self name , ':' , aName) asSymbol put:nil.
+            ] ifTrue:[
+                prevVarNames remove:aName
+            ]
+        ].
+        "left overs are gone"
+        prevVarNames do:[:aName |
+            Smalltalk at:(self name , ':' , aName) asSymbol put:nil.
+            Smalltalk removeKey:(self name , ':' , aName) asSymbol
+        ].
+        Smalltalk changed
+    ]
+!
+
+addClassVarName:aString
+    "add a class variable"
+
+    (self classVarNames includes:aString) ifFalse:[
+        self classVariableString:(self classVariableString , ' ' , aString)
+    ]
+! !
+
+!Class methodsFor:'adding/removing'!
+
+addSelector:newSelector withMethod:newMethod
+    "add the method given by 2nd argument under the selector given by
+     1st argument to the methodDictionary"
+
+    |index oldSelectorArray oldMethodArray
+     newSelectorArray newMethodArray nargs|
+
+    (newSelector isMemberOf:Symbol) ifFalse:[^ self error:'invalid selector'].
+    newMethod isNil ifTrue:[^ self error:'invalid method'].
+
+    index := selectors identityIndexOf:newSelector startingAt:1.
+    (index == 0) ifTrue:[
+        newSelectorArray := selectors copyWith:newSelector.
+        newMethodArray := methods copyWith:newMethod.
+        "keep a reference so they wont go away ..."
+        oldSelectorArray := selectors.
+        oldMethodArray := methods.
+        selectors := newSelectorArray.
+        methods := newMethodArray
+    ] ifFalse:[
+        methods at:index put:newMethod
+    ].
+
+    nargs := newSelector nArgsIfSelector.
+
+    "actually, we would do better with less flushing ..."
+    ObjectMemory flushMethodCache.
+    ObjectMemory flushInlineCachesWithArgs:nargs.
+
+    self addChangeRecordForMethod:newMethod
+!
+
+removeSelector:aSelector
+    "remove the selector, aSelector and its associated method 
+     from the methodDictionary"
+
+    |index oldSelectorArray oldMethodArray 
+     newSelectorArray newMethodArray nargs|
+
+    index := selectors identityIndexOf:aSelector startingAt:1.
+    (index ~~ 0) ifTrue:[
+        newSelectorArray := selectors copyWithoutIndex:index.
+        newMethodArray := methods copyWithoutIndex:index.
+        oldSelectorArray := selectors.
+        oldMethodArray := methods.
+        selectors := newSelectorArray.
+        methods := newMethodArray.
+"
+        nargs := aSelector nArgsIfSelector.
+        ObjectMemory flushMethodCacheFor:self.
+        ObjectMemory flushInlineCachesWithArgs:nargs.
+"
+        "actually, we would do better with less flushing ..."
+        ObjectMemory flushCaches.
+
+        self addChangeRecordForRemoveSelector:aSelector
+    ]
+! !
+
+!Class methodsFor:'changes management'!
+
+updateChanges:aBoolean
+    "turn on/off changes management"
+
+    |prev|
+
+    prev := updatingChanges.
+    updatingChanges := aBoolean.
+    ^ prev
+!
+
+changesStream
+    "return a Stream for the changes file"
+
+    |aStream|
+
+    updatingChanges ifTrue:[
+        aStream := FileStream oldFileNamed:'changes'.
+        aStream isNil ifTrue:[
+            aStream := FileStream newFileNamed:'changes'.
+            aStream isNil ifTrue:[
+                self error:'cannot update changes file'
+            ]
+        ] ifFalse:[
+            aStream setToEnd
+        ]
+    ].
+    ^ aStream
+!
+
+addChangeRecordForMethod:aMethod
+    "add a method-change-record to the changes file"
+
+    |aStream p|
+
+    aStream := self changesStream.
+    aStream notNil ifTrue:[
+        p := aStream position.
+        self fileOutMethod:aMethod on:aStream.
+        aStream cr.
+        aStream close.
+        Project current notNil ifTrue:[
+            Project current changeSet addMethodChange:aMethod in:self
+        ]
+    ]
+!
+
+addChangeRecordForRemoveSelector:aSelector
+    "add a method-remove-record to the changes file"
+
+    |aStream|
+
+    aStream := self changesStream.
+    aStream notNil ifTrue:[
+        self printClassNameOn:aStream.
+        aStream nextPutAll:(' removeSelector:#' , aSelector).
+        aStream nextPut:(aStream class chunkSeparator).
+        aStream cr.
+        aStream close
+    ]
+!
+
+addChangeRecordForClass:aClass
+    "add a class-definition-record to the changes file"
+
+    |aStream|
+
+    aStream := self changesStream.
+    aStream notNil ifTrue:[
+        aClass fileOutDefinitionOn:aStream.
+        aStream nextPut:(aStream class chunkSeparator).
+        aStream cr.
+        aStream close
+    ]
+!
+
+addChangeRecordForClassComment:aClass
+    "add a class-comment-record to the changes file"
+
+    |aStream|
+
+    aStream := self changesStream.
+    aStream notNil ifTrue:[
+        aClass fileOutCommentOn:aStream.
+        aStream nextPut:$!!.
+        aStream cr.
+        aStream close
+    ]
+!
+
+addChangeRecordForSnapshot
+    "add a snapshot-record to the changes file"
+
+    |aStream|
+
+    aStream := self changesStream.
+    aStream notNil ifTrue:[
+        aStream nextPutAll:('''---- snapshot ' ,
+                            Date today printString , ' ' ,
+                            Time now printString ,
+                            ' ----''!').
+        aStream cr.
+        aStream close
+    ]
+! !
+
+!Class methodsFor:'compiling'!
+
+compile:code
+    "compile code, aString for this class; if sucessful update method
+     dictionary."
+
+    (Smalltalk at:#Compiler) compile:code forClass:self
+!
+
+compile:code notifying:requestor
+    "compile code, aString for this class; on any error, notify
+     requestor, anObject with the error reason"
+
+    (Smalltalk at:#Compiler) compile:code forClass:self notifying:requestor
+!
+
+recompile:aSelector
+    "recompile the method associated with the argument, aSelector;
+     used when a superclass changes instances and we have to recompile
+     subclasses"
+
+    |cat code|
+
+    cat := (self compiledMethodAt:aSelector) category.
+    code := self sourceCodeAt:aSelector.
+    (Smalltalk at:#Compiler) compile:code forClass:self inCategory:cat
+!
+
+recompile
+    "recompile all methods
+     used when a class changes instances and therefore all methods
+     have to be recompiled"
+
+    self selectors do:[:aSelector |
+        self recompile:aSelector
+    ]
+!
+
+recompileAll
+    "recompile this class and all subclasses"
+
+    |subclasses|
+
+    subclasses := self subclasses.
+    self recompile.
+    subclasses do:[:aClass |
+        aClass recompileAll
+    ]
+! !
+
+!Class methodsFor:'queries'!
+
+selectorIndex:aSelector
+    "return the index in the arrays for given selector aSelector"
+
+    ^ selectors identityIndexOf:aSelector startingAt:1
+!
+
+compiledMethodAt:aSelector
+    "return the method for given selector aSelector"
+
+    |index|
+
+    index := selectors identityIndexOf:aSelector startingAt:1.
+    (index == 0) ifTrue:[^ nil].
+    ^ methods at:index
+!
+
+sourceCodeAt:aSelector
+    "return the methods source for given selector aSelector"
+
+    |index|
+
+    index := selectors identityIndexOf:aSelector startingAt:1.
+    (index == 0) ifTrue:[^ nil].
+    ^ (methods at:index) source
+!
+
+hasMethods
+    "return true, if there are any (local) methods in this class"
+
+    methods isNil ifTrue:[^ false].
+    ^ (methods size ~~ 0)
+!
+
+implements:aSelector
+    "Return true, if I implement selector"
+
+    ^ (selectors identityIndexOf:aSelector startingAt:1) ~~ 0
+!
+
+canUnderstand:aSelector
+    "Return true, if I or one of my superclasses implements selector"
+
+    |classToLookAt|
+
+    classToLookAt := self.
+    [classToLookAt notNil] whileTrue:[
+        (classToLookAt implements:aSelector) ifTrue:[^ true].
+        classToLookAt := classToLookAt superclass
+    ].
+    ^ false
+!
+
+whichClassImplements:aSelector
+    "Return the class (the receiver or a class in the superclass-chain) 
+     which implements given selector aSelector, if none, return nil"
+
+    |classToLookAt|
+
+    classToLookAt := self.
+    [classToLookAt notNil] whileTrue:[
+        (classToLookAt implements:aSelector) ifTrue:[^ classToLookAt].
+        classToLookAt := classToLookAt superclass
+    ].
+    ^ nil
+!
+
+selectorForMethod:aMethod
+    "Return the selector for given method aMethod"
+
+    |index|
+
+    index := methods identityIndexOf:aMethod startingAt:1.
+    (index == 0) ifTrue:[^ nil].
+    ^ selectors at:index
+!
+
+containsMethod:aMethod
+    "Return true, if aMethod is a method of myself"
+
+    ^ (methods identityIndexOf:aMethod startingAt:1) ~~ 0
+!
+
+categories
+    "Return a Collection of all method-category strings known in class"
+
+    |newList cat|
+
+    newList := OrderedCollection new.
+    methods do:[:aMethod |
+        cat := aMethod category.
+        newList indexOf:cat ifAbsent:[newList add:cat]
+    ].
+    ^ newList
+!
+
+allCategories
+    "Return a Collection of all method-category strings known in class
+     and all superclasses"
+
+    ^ self addAllCategoriesTo:(OrderedCollection new)
+! !
+
+!Class methodsFor:'private'!
+
+addFromString:aString to:aCollection
+    "helper - take individual words from the first argument, aString
+     and add them as strings to the 2nd argument, aCollection.
+     return aCollection"
+
+    |start stop strLen|
+
+    aString isNil ifFalse:[
+        start := 1.
+        strLen := aString size.
+        [start <= strLen] whileTrue:[
+            (aString at:start) isSeparator ifTrue:[
+                start := start + 1
+            ] ifFalse:[
+                stop := aString indexOfSeparatorStartingAt:start.
+                stop == 0 ifTrue:[
+                    stop := strLen + 1
+                ].
+                aCollection add:(aString copyFrom:start to:(stop - 1)).
+                start := stop
+            ]
+        ]
+    ].
+    ^ aCollection
+!
+
+addInstVarNamesTo:aCollection
+    "add the name-strings of the instance variables
+     to the argument, aCollection. Return aCollection"
+
+    ^ self addFromString:instvars to:aCollection
+!
+
+addClassVarNamesTo:aCollection
+    "add the name-strings of the class varvariables
+     to the argument, aCollection. Return aCollection"
+
+    ^ self addFromString:classvars to:aCollection
+!
+
+addAllInstVarNamesTo:aCollection
+    "add the name-strings of the instance variables and of the inst-vars
+     of all superclasses to the argument, aCollection. Return aCollection"
+
+    (superclass notNil) ifTrue:[
+        superclass addAllInstVarNamesTo:aCollection
+    ].
+    ^ self addInstVarNamesTo:aCollection
+!
+
+addAllClassVarNamesTo:aCollection
+    "add the name-strings of the class variables and of the class-vars
+     of all superclasses to the argument, aCollection. Return aCollection"
+
+    (superclass notNil) ifTrue:[
+        superclass addAllClassVarNamesTo:aCollection
+    ].
+    ^ self addClassVarNamesTo:aCollection
+!
+
+addCategoriesTo:aCollection
+    "helper - add categories to the argument, aCollection"
+
+    |cat|
+
+    methods do:[:aMethod |
+        cat := aMethod category.
+        (aCollection detect:[:element | cat = element]
+                     ifNone:[nil])
+            isNil ifTrue:[
+                aCollection add:cat
+        ]
+    ].
+    ^ aCollection
+!
+
+addAllCategoriesTo:aCollection
+    "helper - add categories and all superclasses categories
+     to the argument, aCollection"
+
+    (superclass notNil) ifTrue:[
+        superclass addAllCategoriesTo:aCollection
+    ].
+    ^ self addCategoriesTo:aCollection
+! !
+
+!Class methodsFor:'fileIn interface'!
+
+methodsFor:aCategory
+    "return a ClassCategoryReader to read in and compile methods for me"
+
+    ^ ClassCategoryReader class:self category:aCategory
+!
+
+publicMethodsFor:aCategory
+    "this method allows fileIn of ENVY methods - currently we do not support method visibility.
+     return a ClassCategoryReader to read in and compile methods for me."
+
+    ^ self methodsFor:aCategory
+!
+
+privateMethodsFor:aCategory
+    "this method allows fileIn of ENVY methods - currently we do not support method visibility.
+     return a ClassCategoryReader to read in and compile methods for me."
+
+    ^ self methodsFor:aCategory
+!
+
+binaryMethods
+    "return a ClassCategoryReader to read in binary methods for me"
+
+    ^ BinaryClassCategoryReader class:self category:'binary'
+!
+
+methods
+    "this method allows fileIn of ST/V methods -
+     return a ClassCategoryReader to read in and compile methods for me."
+
+    ^ ClassCategoryReader class:self category:'ST/V methods'
+! !
+
+!Class methodsFor:'fileOut'!
+
+printClassNameOn:aStream
+    "helper for fileOut - print my name if I am not a Metaclass;
+     otherwise my name without -class followed by space-class"
+
+    (self isMeta "isMemberOf:Metaclass") ifTrue:[
+        aStream nextPutAll:(name copyFrom:1 to:(name size - 5)).
+        aStream nextPutAll:' class'
+    ] ifFalse:[
+        name printOn:aStream
+    ]
+!
+
+printNameArray:anArray on:aStream indent:indent
+    "print an array of strings separated by spaces; when the stream
+     defines a lineLength, break when this limit is reached; indent
+     every line; used to printOut instanve variable names"
+
+    |thisName nextName arraySize lenMax pos mustBreak line spaces|
+
+    arraySize := 0.
+    anArray notNil ifTrue:[
+        arraySize := anArray size
+    ].
+    arraySize ~~ 0 ifTrue:[
+        pos := indent.
+        lenMax := aStream lineLength.
+        thisName := anArray at:1.
+        line := ''.
+        1 to:arraySize do:[:index |
+            line := line , thisName.
+            pos := pos + thisName size.
+            (index == arraySize) ifFalse:[
+                nextName := anArray at:(index + 1).
+                mustBreak := false.
+                (lenMax > 0) ifTrue:[
+                    ((pos + nextName size) > lenMax) ifTrue:[
+                        mustBreak := true
+                    ]
+                ].
+                mustBreak ifTrue:[
+                    aStream nextPutAll:line.
+                    aStream cr.
+                    spaces isNil ifTrue:[
+                        spaces := String new:indent
+                    ].
+                    line := spaces.
+                    pos := indent
+                ] ifFalse:[
+                    line := line , ' '.
+                    pos := pos + 1
+                ].
+                thisName := nextName
+            ]
+        ].
+        aStream nextPutAll:line
+    ]
+!
+
+printClassVarNamesOn:aStream indent:indent
+    "print the class variable names indented and breaking at line end"
+
+    self printNameArray:(self classVarNames) on:aStream indent:indent
+!
+
+printInstVarNamesOn:aStream indent:indent
+    "print the instance variable names indented and breaking at line end"
+
+    self printNameArray:(self instVarNames) on:aStream indent:indent
+!
+
+printHierarchyOn:aStream
+    "print my class hierarchy on aStream"
+
+    self printHierarchyAnswerIndentOn:aStream
+!
+
+printHierarchyAnswerIndentOn:aStream
+    "print my class hierarchy on aStream - return indent
+     recursively calls itself to print superclass and use returned indent
+     for my description - used in the browser"
+
+    |indent|
+
+    indent := 0.
+    (superclass notNil) ifTrue:[
+        indent := (superclass printHierarchyAnswerIndentOn:aStream) + 2
+    ].
+    aStream nextPutAll:(String new:indent).
+    aStream nextPutAll:name.
+    aStream nextPutAll:' ('.
+    self printInstVarNamesOn:aStream indent:(indent + name size + 2).
+    aStream nextPutAll:')'.
+    aStream cr.
+    ^ indent
+!
+    
+printFullHierarchyOn:aStream indent:indent
+    "print myself and all subclasses on aStream.
+     recursively calls itself to print subclasses. 
+     Can be used to print hierarchy on the printer."
+
+    aStream nextPutAll:(String new:indent).
+    aStream bold.
+    aStream nextPutAll:name.
+    aStream normal.
+    aStream nextPutAll:' ('.
+    self printInstVarNamesOn:aStream indent:(indent + name size + 2).
+    aStream nextPutAll:')'.
+    aStream cr.
+
+    (self subclasses sort:[:a :b | a name < b name]) do:[:aSubclass |
+        aSubclass printFullHierarchyOn:aStream indent:(indent + 2)
+    ]
+
+    "|printStream|
+     printStream := Printer new.
+     Object printFullHierarchyOn:printStream indent:0.
+     printStream close"
+!
+
+fileOutCommentOn:aStream
+    "print an expression on aStream to define my comment"
+
+    aStream nextPutAll:name.
+    aStream nextPutAll:' comment:'.
+    comment isNil ifTrue:[
+        aStream nextPutAll:''''''
+    ] ifFalse:[
+        aStream nextPutAll:(comment storeString)
+    ].
+    aStream cr
+!
+
+fileOutDefinitionOn:aStream
+    "print an expression to define myself on aStream"
+
+    |isVar line|
+
+    superclass isNil ifTrue:[
+        line := 'Object'
+    ] ifFalse:[
+        line := (superclass name)
+    ].
+    superclass isNil ifTrue:[
+        isVar := self isVariable
+    ] ifFalse:[
+        "I cant remember what this is for ?"
+        isVar := (self isVariable and:[superclass isVariable not])
+    ].
+    isVar ifTrue:[
+        self isPointers ifTrue:[
+            line := line , ' variableSubclass:#'
+        ] ifFalse:[
+            self isBytes ifTrue:[
+                line := line , ' variableByteSubclass:#'
+            ] ifFalse:[
+                self isWords ifTrue:[
+                    line := line , ' variableWordSubclass:#'
+                ] ifFalse:[
+                    self isLongs ifTrue:[
+                        line := line , ' variableLongSubclass:#'
+                    ] ifFalse:[
+                        self isFloats ifTrue:[
+                            line := line , ' variableFloatSubclass:#'
+                        ] ifFalse:[
+                            line := line , ' variableDoubleSubclass:#'
+                        ]
+                    ]
+                ]
+            ]
+        ]
+    ] ifFalse:[
+        line := line , ' subclass:#'
+    ].
+    line := line , name.
+    aStream nextPutAll:line.
+
+    aStream crTab. 
+    aStream nextPutAll:' instanceVariableNames:'''.
+    self printInstVarNamesOn:aStream indent:16.
+    aStream nextPutAll:''''.
+
+    aStream crTab.
+    aStream nextPutAll:' classVariableNames:'''.
+    self printClassVarNamesOn:aStream indent:16.
+    aStream nextPutAll:''''.
+
+    aStream crTab.
+    aStream nextPutAll:' poolDictionaries:'''''.
+
+    aStream crTab.
+    aStream nextPutAll:' category:'.
+    category isNil ifTrue:[
+        aStream nextPutAll:''''''
+    ] ifFalse:[
+        aStream nextPutAll:(category asString storeString)
+    ].
+    aStream cr
+!
+
+fileOutClassInstVarDefinitionOn:aStream
+    aStream nextPutAll:(name , ' class instanceVariableNames:''').
+    self class printInstVarNamesOn:aStream indent:8.
+    aStream nextPutAll:''''
+!
+
+fileOutCategory:aCategory on:aStream
+    "file out all methods belonging to aCategory, aString onto aStream"
+
+    |nMethods count|
+
+    methods notNil ifTrue:[
+        nMethods := 0.
+        methods do:[:aMethod |
+            (aCategory = aMethod category) ifTrue:[
+                nMethods := nMethods + 1
+            ]
+        ].
+        (nMethods ~~ 0) ifTrue:[
+            aStream nextPut:$!!.
+            self printClassNameOn:aStream.
+            aStream nextPutAll:' methodsFor:'''.
+            aCategory notNil ifTrue:[
+                aStream nextPutAll:aCategory
+            ].
+            aStream nextPut:$'. aStream nextPut:$!!. aStream cr.
+            aStream cr.
+            count := 1.
+            methods do:[:aMethod |
+                (aCategory = aMethod category) ifTrue:[
+                    aStream nextChunkPut:(aMethod source).
+                    (count ~~ nMethods) ifTrue:[
+                        aStream cr.
+                        aStream cr
+                    ].
+                    count := count + 1
+                ]
+            ].
+            aStream space.
+            aStream nextPut:$!!.
+            aStream cr
+        ]
+    ]
+!
+
+fileOutMethod:aMethod on:aStream
+    "file out the method, aMethod onto aStream"
+
+    |cat|
+
+    methods notNil ifTrue:[
+        aStream nextPut:$!!.
+        self printClassNameOn:aStream.
+        aStream nextPutAll:' methodsFor:'''.
+        cat := aMethod category.
+        cat notNil ifTrue:[
+            aStream nextPutAll:cat
+        ].
+        aStream nextPut:$'.
+        aStream nextPut:$!!.
+        aStream cr.
+        aStream cr.
+        aStream nextChunkPut:(aMethod source).
+        aStream space.
+        aStream nextPut:$!!.
+        aStream cr
+    ]
+!
+
+fileOutOn:aStream
+    "file out all methods onto aStream"
+
+    |collectionOfCategories|
+
+    aStream nextPutAll:(Smalltalk timeStamp).
+    aStream nextPut:$!. 
+    aStream cr.
+    aStream cr.
+    self fileOutDefinitionOn:aStream.
+    aStream nextPut:$!!. 
+    aStream cr.
+    aStream cr.
+    self class instanceVariableString isBlank ifFalse:[
+        self fileOutClassInstVarDefinitionOn:aStream.
+        aStream nextPut:$!!. 
+        aStream cr.
+        aStream cr
+    ].
+
+    comment notNil ifTrue:[
+        aStream nextPutAll:name.
+        aStream nextPutAll:' comment:'.
+        aStream nextPutAll:(comment storeString).
+        aStream nextPut:$!!.
+        aStream cr.
+        aStream cr
+    ].
+    collectionOfCategories := self class categories.
+    collectionOfCategories notNil ifTrue:[
+        collectionOfCategories do:[:aCategory |
+            self class fileOutCategory:aCategory on:aStream.
+            aStream cr
+        ]
+    ].
+    collectionOfCategories := self categories.
+    collectionOfCategories notNil ifTrue:[
+        collectionOfCategories do:[:aCategory |
+            self fileOutCategory:aCategory on:aStream.
+            aStream cr
+        ]
+    ].
+    (self class implements:#initialize) ifTrue:[
+        aStream nextPutAll:(name , ' initialize').
+        aStream nextPut:$!!. 
+        aStream cr
+    ]
+!
+
+fileOutCategory:aCategory
+    "create a file 'class-category.st' consisting of all methods in aCategory"
+
+    |aStream fileName|
+
+    fileName := name , '-' , aCategory , '.st'.
+    aStream := FileStream newFileNamed:fileName.
+    self fileOutCategory:aCategory on:aStream.
+    aStream close
+!
+
+fileOutMethod:aMethod
+    "create a file 'class-method.st' consisting of the method, aMethod"
+
+    |aStream fileName selector|
+
+    selector := self selectorForMethod:aMethod.
+    selector notNil ifTrue:[
+        fileName := name , '-' , selector, '.st'.
+        aStream := FileStream newFileNamed:fileName.
+        self fileOutMethod:aMethod on:aStream.
+        aStream close
+    ]
+!
+
+fileOut
+    "create a file 'class.st' consisting of all methods in myself"
+
+    |aStream fileName|
+
+    fileName := (Smalltalk fileNameForClass:self name) , '.st'.
+    aStream := FileStream newFileNamed:fileName.
+    aStream isNil ifTrue:[
+        ^ self error:('cannot create source file:', fileName)
+    ].
+    self fileOutOn:aStream.
+    aStream close
+!
+
+fileOutIn:aFileDirectory
+    "create a file 'class.st' consisting of all methods in self in
+     directory aFileDirectory"
+
+    |aStream fileName|
+
+    fileName := (Smalltalk fileNameForClass:self) , '.st'.
+    aStream := FileStream newFileNamed:fileName
+                                    in:aFileDirectory.
+    aStream isNil ifTrue:[
+        ^ self error:('cannot create source file:', fileName)
+    ].
+    self fileOutOn:aStream.
+    aStream close
+!
+
+binaryFileOutMethodsOn:aStream
+    "binary file out all methods onto aStream"
+
+    |temporaryMethod index|
+
+    methods notNil ifTrue:[
+        aStream nextPut:$!!.
+        self printClassNameOn:aStream.
+        aStream nextPutAll:' binaryMethods'.
+        aStream nextPut:$!!.
+        aStream cr.
+        index := 1.
+        methods do:[:aMethod |
+            (selectors at:index) storeOn:aStream.
+            aStream nextPut:$!!.
+
+            aMethod byteCode isNil ifTrue:[
+                temporaryMethod := Compiler compile:(aMethod source)
+                                           forClass:self
+                                         inCategory:(aMethod category)
+                                          notifying:nil
+                                            install:false.
+                temporaryMethod binaryFileOutOn:aStream
+            ] ifFalse:[
+                aMethod binaryFileOutOn:aStream
+            ].
+            aStream cr.
+            index := index + 1
+        ].
+        aStream nextPut:$!!.
+        aStream cr
+    ]
+!
+
+binaryFileOutOn:aStream
+    "file out all methods onto aStream"
+
+    aStream nextPut:$'.
+    aStream nextPutAll:('From Smalltalk/X, Version:'
+                        , (Smalltalk version)
+                        , ' on ').
+    aStream nextPutAll:(Date today printString , ' at ' , Time now printString).
+    aStream nextPut:$'.
+    aStream nextPut:$!!.
+    aStream cr.
+    self fileOutDefinitionOn:aStream.
+    aStream nextPut:$!!. 
+    aStream cr.
+    comment notNil ifTrue:[
+        aStream nextPutAll:name.
+        aStream nextPutAll:' comment:'.
+        aStream nextPutAll:(comment storeString).
+        aStream nextPut:$!!.
+        aStream cr
+    ].
+    self class binaryFileOutMethodsOn:aStream.
+    self binaryFileOutMethodsOn:aStream.
+    (self class implements:#initialize) ifTrue:[
+        aStream nextPutAll:(name , ' initialize').
+        aStream nextPut:$!!. 
+        aStream cr
+    ]
+!
+
+binaryFileOut
+    "create a file 'class.sb' consisting of all methods in myself"
+
+    |aStream fileName|
+
+    fileName := (Smalltalk fileNameForClass:self name) , '.sb'.
+    aStream := FileStream newFileNamed:fileName.
+    aStream isNil ifTrue:[
+        ^ self error:('cannot create class file:', fileName)
+    ].
+    self binaryFileOutOn:aStream.
+    aStream close
+! !
+
+!Class methodsFor:'printOut'!
+
+printOutDefinitionOn:aPrintStream
+    "print out my definition"
+
+    aPrintStream nextPutAll:'class                '.
+    aPrintStream bold.
+    aPrintStream nextPutAll:name.
+    aPrintStream normal.
+    aPrintStream cr. 
+
+    aPrintStream nextPutAll:'superclass           '.
+    superclass isNil ifTrue:[
+        aPrintStream nextPutAll:'Object'
+    ] ifFalse:[
+        aPrintStream nextPutAll:(superclass name)
+    ].
+    aPrintStream cr. 
+
+    aPrintStream nextPutAll:'instance Variables   '.
+    self printInstVarNamesOn:aPrintStream indent:21.
+    aPrintStream cr. 
+
+    aPrintStream nextPutAll:'class Variables      '.
+    self printClassVarNamesOn:aPrintStream indent:21.
+    aPrintStream cr.
+
+    category notNil ifTrue:[
+        aPrintStream nextPutAll:'category             '.
+        aPrintStream nextPutAll:(category printString).
+        aPrintStream cr
+    ].
+
+    comment notNil ifTrue:[
+        aPrintStream cr.
+        aPrintStream nextPutAll:'comment:'.
+        aPrintStream cr.
+        aPrintStream italic.
+        aPrintStream nextPutAll:comment.
+        aPrintStream normal.
+        aPrintStream cr
+    ]
+!
+
+printOutSourceProtocol:aString on:aPrintStream
+    "given the source in aString, print the methods message specification
+     and any method comments - without source; used to generate documentation
+     pages"
+
+    |text line nQuote index|
+
+    text := aString asText.
+    (text size < 1) ifTrue:[^self].
+    aPrintStream bold.
+    aPrintStream nextPutAll:(text at:1).
+    aPrintStream cr.
+    (text size >= 2) ifTrue:[
+        aPrintStream italic.
+        line := (text at:2).
+        nQuote := line occurrencesOf:(Character doubleQuote).
+        (nQuote == 2) ifTrue:[
+            aPrintStream nextPutAll:line.
+            aPrintStream cr
+        ] ifFalse:[
+            (nQuote == 1) ifTrue:[
+                aPrintStream nextPutAll:line.
+                aPrintStream cr.
+                index := 3.
+                line := text at:index.
+                nQuote := line occurrencesOf:(Character doubleQuote).
+                [nQuote ~~ 1] whileTrue:[
+                    aPrintStream nextPutAll:line.
+                    aPrintStream cr.
+                    index := index + 1.
+                    line := text at:index.
+                    nQuote := line occurrencesOf:(Character doubleQuote)
+                ].
+                aPrintStream nextPutAll:(text at:index).
+                aPrintStream cr
+             ]
+         ]
+    ].
+    aPrintStream normal
+!
+
+printOutSource:aString on:aPrintStream
+    "print out a source-string; the message-specification is printed bold,
+     comments are printed italic"
+
+    |text textIndex textSize line lineIndex lineSize inComment aCharacter|
+    text := aString asText.
+    aPrintStream bold.
+    aPrintStream nextPutAll:(text at:1).
+    aPrintStream normal.
+    aPrintStream cr.
+    inComment := false.
+    textSize := text size.
+    textIndex := 2.
+    [textIndex <= textSize] whileTrue:[
+        line := text at:textIndex.
+        ((line occurrencesOf:Character doubleQuote) == 0) ifTrue:[
+            aPrintStream nextPutAll:line
+        ] ifFalse:[
+            lineSize := line size.
+            lineIndex := 1.
+            [lineIndex <= lineSize] whileTrue:[
+                aCharacter := line at:lineIndex.
+                (aCharacter == Character doubleQuote) ifTrue:[
+                    inComment ifTrue:[
+                        aPrintStream normal.
+                        aPrintStream nextPut:aCharacter.
+                        inComment := false
+                    ] ifFalse:[
+                        aPrintStream nextPut:aCharacter.
+                        aPrintStream italic.
+                        inComment := true
+                    ]
+                ] ifFalse:[
+                    aPrintStream nextPut:aCharacter
+                ].
+                lineIndex := lineIndex + 1
+            ]
+        ].
+        aPrintStream cr.
+        textIndex := textIndex + 1
+    ]
+!
+    
+printOutCategory:aCategory on:aPrintStream
+    "print out all methods in aCategory on aPrintStream should be a PrintStream"
+
+    |any|
+    methods notNil ifTrue:[
+        any := false.
+        methods do:[:aMethod |
+            (aCategory = aMethod category) ifTrue:[
+                any := true
+            ]
+        ].
+        any ifTrue:[
+             aPrintStream italic.
+             aPrintStream nextPutAll:aCategory.
+             aPrintStream normal.
+             aPrintStream cr.
+             aPrintStream cr.
+             methods do:[:aMethod |
+                 (aCategory = aMethod category) ifTrue:[
+                     self printOutSource:(aMethod source) on:aPrintStream.
+                     aPrintStream cr.
+                     aPrintStream cr
+                 ]
+             ].
+             aPrintStream cr
+         ]
+    ]
+!
+
+printOutOn:aPrintStream
+    "print out all methods on aPrintStream which should be a printStream"
+
+    |collectionOfCategories|
+    self printOutDefinitionOn:aPrintStream.
+    aPrintStream cr.
+    collectionOfCategories := self class categories.
+    collectionOfCategories notNil ifTrue:[
+        aPrintStream nextPutAll:'class protocol'.
+        aPrintStream cr. aPrintStream cr.
+        collectionOfCategories do:[:aCategory |
+            self class printOutCategory:aCategory on:aPrintStream
+        ]
+    ].
+    collectionOfCategories := self categories.
+    collectionOfCategories notNil ifTrue:[
+        aPrintStream nextPutAll:'instance protocol'.
+        aPrintStream cr. aPrintStream cr.
+        collectionOfCategories do:[:aCategory |
+            self printOutCategory:aCategory on:aPrintStream
+        ]
+    ]
+!
+
+printOutCategoryProtocol:aCategory on:aPrintStream
+    |any|
+    methods notNil ifTrue:[
+        any := false.
+        methods do:[:aMethod |
+            (aCategory = aMethod category) ifTrue:[
+                any := true
+            ]
+        ].
+        any ifTrue:[
+            aPrintStream italic.
+            aPrintStream nextPutAll:aCategory.
+            aPrintStream normal.
+            aPrintStream cr.
+            aPrintStream cr.
+            methods do:[:aMethod |
+                (aCategory = aMethod category) ifTrue:[
+                    self printOutSourceProtocol:(aMethod source) 
+                                             on:aPrintStream.
+                    aPrintStream cr.
+                    aPrintStream cr
+                ]
+            ].
+            aPrintStream cr
+        ]
+    ]
+!
+
+printOutProtocolOn:aPrintStream
+    |collectionOfCategories|
+    self printOutDefinitionOn:aPrintStream.
+    aPrintStream cr.
+    collectionOfCategories := self class categories.
+    collectionOfCategories notNil ifTrue:[
+        aPrintStream nextPutAll:'class protocol'.
+        aPrintStream cr. aPrintStream cr.
+        collectionOfCategories do:[:aCategory |
+            self class printOutCategoryProtocol:aCategory on:aPrintStream
+        ]
+    ].
+    collectionOfCategories := self categories.
+    collectionOfCategories notNil ifTrue:[
+        aPrintStream nextPutAll:'instance protocol'.
+        aPrintStream cr. aPrintStream cr.
+        collectionOfCategories do:[:aCategory |
+            self printOutCategoryProtocol:aCategory on:aPrintStream
+        ]
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ClassCategoryReader.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,81 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#ClassCategoryReader
+       instanceVariableNames:'myClass myCategory'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Kernel-Support'
+!
+
+ClassCategoryReader comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+             All Rights Reserved
+
+a helper class for fileIn - keeps track of class and category to filein for.
+
+%W% %E%
+written 89 by claus
+'!
+
+!ClassCategoryReader class methodsFor:'instance creation'!
+
+class:aClass category:aCategory
+    "return a new ClassCategoryReader to read methods for aClass with
+     methodCategory aCategory"
+
+    ^ self new class:aClass category:aCategory
+! !
+
+!ClassCategoryReader methodsFor:'private'!
+
+class:aClass category:aCategory
+    "set the instance variables"
+
+    myClass := aClass.
+    myCategory := aCategory
+! !
+
+!ClassCategoryReader methodsFor:'fileIn'!
+
+fileInFrom:aStream notifying:requestor
+    "read method-chunks from the input stream, aStream; compile them
+     and add the methods to the class defined by the class-instance var;
+     errors notifications are passed to requestor"
+
+    |aString done method|
+
+    done := false.
+    [done] whileFalse:[
+        done := aStream atEnd.
+        done ifFalse:[
+            aString := aStream nextChunk.
+            done := aString isNil or:[aString isEmpty].
+            done ifFalse:[
+                method := Compiler compile:aString
+                                  forClass:myClass
+                                inCategory:myCategory
+                                 notifying:requestor
+                                   install:true
+                                skipIfSame:true
+            ]
+        ]
+    ]
+!
+
+fileInFrom:aStream
+    "read method-chunks from the input stream, aStream; compile them
+     and add the methods to the class defined by the class-instance var"
+
+    self fileInFrom:aStream notifying:nil
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ClassDescr.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,80 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Behavior subclass:#ClassDescription
+       instanceVariableNames:'name category instvars'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Kernel-Classes'
+!
+
+ClassDescription comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+this class has been added for ST-80 compatibility only.
+All class stuff used to be in Behavior and Class - but, to be
+able to file in some PD code, it became nescessary to add it.
+
+Instance variables:
+
+name            <String>        the classes name
+category        <String>        the classes category
+instvars        <String>        the names of the instance variables
+
+%W% %E%
+'!
+
+!ClassDescription methodsFor:'accessing'!
+
+instanceVariableString
+    "return a string of the instance variable names"
+
+    instvars isNil ifTrue:[^ ''].
+    ^ instvars
+!
+
+instanceVariableString:aString
+    "set the classes instvarnames string - notice, that this
+     should be used only during class creation; the number of
+     instance variables is determined by another instance 
+     (see Behavior)."
+
+    instvars := aString.
+    self changed
+!
+
+setName:aString
+    "set the classes name - be careful, it will be still
+     in the Smalltalk dictionary - under another key"
+
+    name := aString
+!
+
+name
+    "return the name (aString) of the class"
+
+    ^ name
+!
+
+category
+    "return the category (aString) of the class"
+
+    ^ category
+!
+
+category:aStringOrSymbol
+    "set the category of the class to be the argument, aString"
+
+    category := aStringOrSymbol asSymbol
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ClassDescription.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,80 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Behavior subclass:#ClassDescription
+       instanceVariableNames:'name category instvars'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Kernel-Classes'
+!
+
+ClassDescription comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+this class has been added for ST-80 compatibility only.
+All class stuff used to be in Behavior and Class - but, to be
+able to file in some PD code, it became nescessary to add it.
+
+Instance variables:
+
+name            <String>        the classes name
+category        <String>        the classes category
+instvars        <String>        the names of the instance variables
+
+%W% %E%
+'!
+
+!ClassDescription methodsFor:'accessing'!
+
+instanceVariableString
+    "return a string of the instance variable names"
+
+    instvars isNil ifTrue:[^ ''].
+    ^ instvars
+!
+
+instanceVariableString:aString
+    "set the classes instvarnames string - notice, that this
+     should be used only during class creation; the number of
+     instance variables is determined by another instance 
+     (see Behavior)."
+
+    instvars := aString.
+    self changed
+!
+
+setName:aString
+    "set the classes name - be careful, it will be still
+     in the Smalltalk dictionary - under another key"
+
+    name := aString
+!
+
+name
+    "return the name (aString) of the class"
+
+    ^ name
+!
+
+category
+    "return the category (aString) of the class"
+
+    ^ category
+!
+
+category:aStringOrSymbol
+    "set the category of the class to be the argument, aString"
+
+    category := aStringOrSymbol asSymbol
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Coll.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,546 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Collection
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Abstract'
+!
+
+Collection comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+Abstract superclass for all collections
+
+%W% %E%
+'!
+
+!Collection class methodsFor:'instance creation'!
+
+with:anObject
+    "return a new Collection with one element:anObject"
+
+    |newCollection|
+
+    newCollection := self new.
+    newCollection add:anObject.
+    ^ newCollection
+!
+
+with:firstObject with:secondObject
+    "return a new Collection with two elements:firstObject and secondObject"
+
+    |newCollection|
+
+    newCollection := self new.
+    newCollection add:firstObject.
+    newCollection add:secondObject.
+    ^ newCollection
+!
+
+with:firstObject with:secondObject with:thirdObject
+    "return a new Collection with three elements"
+
+    |newCollection|
+
+    newCollection := self new.
+    newCollection add:firstObject.
+    newCollection add:secondObject.
+    newCollection add:thirdObject.
+    ^ newCollection
+!
+
+with:firstObject with:secondObject with:thirdObject with:fourthObject
+    "return a new Collection with four elements"
+
+    |newCollection|
+
+    newCollection := self new.
+    newCollection add:firstObject.
+    newCollection add:secondObject.
+    newCollection add:thirdObject.
+    newCollection add:fourthObject.
+    ^ newCollection
+!
+
+new:size withAll:element
+    "return a new COllection of size, where all elements are
+     initialized to element"
+
+    |newCollection|
+
+    newCollection := self new:size.
+    size timesRepeat:[newCollection add:element]
+!
+
+withAll:aCollection
+    "return a new Collection with all elements taken from the argument,
+     aCollection"
+
+    |newCollection|
+
+    newCollection := self new.
+    newCollection addAll:aCollection.
+    ^newCollection
+! !
+
+!Collection methodsFor:'error handling'!
+
+errorNotFound
+    "report an error that Object was not in the collection"
+
+    self error:'Object is not in the Collection'
+!
+
+errorNotKeyed
+    "report an error that keyed access methods are not allowed"
+
+    self error:(self class name, 's do not respond to keyed accessing messages')
+! !
+
+!Collection methodsFor:'accessing'!
+
+anElement
+    "return any element from the collection"
+
+    self do: [:each | ^ each].
+    ^ nil
+! !
+
+!Collection methodsFor:'adding & removing'!
+
+add:anObject
+    "add the argument, anObject to the receiver"
+
+    ^ self subclassResponsibility
+!
+
+addLast:anObject
+    "add the argument, anObject to the receiver"
+
+    ^ self add:anObject
+!
+
+addAll:aCollection
+    "add all elements of the argument, aCollection to the receiver"
+
+    aCollection do:[:element |
+        self add:element
+    ].
+    ^ aCollection
+!
+
+remove:anObject ifAbsent:exceptionBlock
+    "remove the argument, anObject from the receiver - if it was not
+     in the collection returns the the value of the exceptionBlock"
+
+    ^ self subclassResponsibility
+!
+
+remove:anObject
+    "remove the argument, anObject from the receiver"
+
+    self remove:anObject ifAbsent:[self errorNotFound]
+!
+
+removeAll:aCollection
+    "remove all elements of the argument, aCollection from the receiver"
+
+    aCollection do:[:element | self remove:element].
+    ^ aCollection
+! !
+
+!Collection methodsFor:'growing'!
+
+growSize
+    "return a suitable size increment for growing"
+
+    ^ self size max:2
+!
+
+grow
+    "make the receiver larger"
+
+    self grow:(self size + self growSize)
+!
+
+grow:howBig
+    "change the receivers size"
+
+    ^ self subclassResponsibility
+! !
+
+!Collection methodsFor:'testing'!
+
+isEmpty
+    "return true, if the receiver is empty"
+
+    ^ self size == 0
+!
+
+includes:anElement
+    "return true, if the argument, anObject is in the list"
+
+    self do:[:element |
+        (anElement = element) ifTrue:[^ true].
+    ].
+    ^ false
+!
+
+includesAll:aCollection
+    "return true, if the the receiver includes all elements of
+     the argument, aCollection; false if any is missing"
+
+    aCollection do:[:element |
+        (self includes:element) ifFalse:[^ false].
+    ].
+    ^ true
+!
+
+occurrencesOf:anElement
+    "return the number of occurrences of the argument, anElement in
+     the receiver"
+
+    |count "<SmallInteger>" |
+
+    count := 0.
+    self do:[:element |
+        (anElement = element) ifTrue:[
+            count := count + 1
+        ].
+    ].
+    ^ count
+!
+
+size
+    "return the number of elements in the receiver"
+
+    |count "<SmallInteger>" |
+
+    count := 0.
+    self do:[:element |
+        count := count + 1
+    ].
+    ^ count
+! !
+
+!Collection methodsFor:'enumerating'!
+
+do:aBlock
+    "evaluate the argument, aBlock for each element"
+
+    ^ self subclassResponsibility
+!
+
+collect:aBlock
+    "for each element in the receiver, evaluate the argument, aBlock
+     and return a new collection with the results"
+
+    |newCollection|
+
+    newCollection := self species new.
+    self do:[:each | newCollection add:(aBlock value:each)].
+    ^ newCollection
+!
+
+detect:aBlock
+    "evaluate the argument, aBlock for each element in the receiver until
+     the block returns true; in this case return the element which caused
+     the true evaluation.
+     If none of the evaluations return true, report an error"
+
+    ^ self detect:aBlock ifNone:[self errorNotFound]
+!
+
+detect:aBlock ifNone:exceptionBlock
+    "evaluate the argument, aBlock for each element in the receiver until
+     the block returns true; in this case return the element which caused
+     the true evaluation.
+     If none of the evaluations returns true, return the result of the
+     evaluation of the exceptionBlock"
+
+    self do:[:each | 
+        (aBlock value:each) ifTrue:[^ each].
+    ].
+    ^ exceptionBlock value
+!
+
+inject:thisValue into:binaryBlock
+    |nextValue|
+
+    nextValue := thisValue.
+    self do: [:each | nextValue := binaryBlock value:nextValue value:each].
+    ^ nextValue
+!
+
+reject:aBlock
+    "return a new collection with all elements from the receiver, for which
+     the argument aBlock evaluates to false"
+
+    ^ self select:[:element | (aBlock value:element) == false]
+!
+
+select:aBlock
+    "return a new collection with all elements from the receiver, for which
+     the argument aBlock evaluates to true"
+
+    |newCollection|
+
+    newCollection := self species new.
+    self do:[:each |
+        (aBlock value:each) ifTrue:[newCollection add:each].
+    ].
+    ^ newCollection
+! !
+
+!Collection methodsFor:'converting'!
+
+asArray
+    "return a new Array with the collections elements"
+
+    |anArray 
+     index "<SmallInteger>" |
+
+    anArray := Array new:(self size).
+    index := 1.
+    self do:[:each |
+        anArray at:index put:each.
+        index := index + 1
+    ].
+    ^ anArray
+!
+
+asByteArray
+    "return a new ByteArray with the collections elements"
+
+    |aByteArray 
+     index "<SmallInteger>" |
+
+    aByteArray := ByteArray new:(self size).
+    index := 1.
+    self do:[:each |
+        aByteArray at:index put:each asInteger.
+        index := index + 1
+    ].
+    ^ aByteArray
+!
+
+asString
+    "return a String with the collections elements 
+     (which must convertable to characters)"
+
+    |aString 
+     index "<SmallInteger>" |
+
+    aString := String new:(self size).
+    index := 1.
+    self do:[:each |
+        aString at:index put:each asCharacter.
+        index := index + 1
+    ].
+    ^ aString
+!
+
+asText
+    "return a new Text-object with the elements printstings"
+
+    |aText
+     index "<SmallInteger>" |
+
+    aText := Text new:(self size).
+    index := 1.
+    self do:[:each |
+        aText at:index put:(each printString).
+        index := index + 1
+    ].
+    ^ aText
+!
+
+asBag
+    "return a new Bag with the receiver collections elements"
+
+    |aBag|
+
+    aBag := Bag new.
+    self do:[:each | aBag add:each].
+    ^ aBag
+!
+
+asOrderedCollection
+    "return a new OrderedCollection with the receiver collections elements"
+
+    |anOrderedCollection|
+
+    anOrderedCollection := OrderedCollection new:self size.
+    self do:[:each | anOrderedCollection addLast:each].
+    ^ anOrderedCollection
+!
+
+asSet
+    "return a new Set with the receiver collections elements"
+
+    |aSet|
+
+    aSet := Set new: self size.
+    self do:[:each | aSet add:each].
+    ^ aSet
+!
+
+asSortedCollection
+    "return a new SortedCollection with the receiver collections elements"
+
+    |aSortedCollection|
+
+    aSortedCollection := SortedCollection new:self size.
+    aSortedCollection addAll:self.
+    ^ aSortedCollection
+!
+
+asSortedCollection:sortBlock
+    "return a new SortedCollection with the receiver collections elements,
+     using sortBlock for comparing"
+
+    |aSortedCollection|
+
+    aSortedCollection := SortedCollection sortBlock:sortBlock.
+    aSortedCollection addAll:self.
+    ^ aSortedCollection
+! !
+
+!Collection methodsFor:'printing & storing'!
+
+maxPrint
+    ^ 5000
+!
+
+printString
+    "return the printString of a big collection can take a long time
+     due to long temporary strings - I use a buffer here collecting some
+     elements to reduce the GC overhead ...
+    "
+
+    |thisString buffer count string noneYet total|
+
+    string := (self class name) , '('.
+    noneYet := true.
+    buffer := ''.
+    count := 0.
+    total := 0.
+    self do: [:element |
+        thisString := element printString.
+        noneYet ifTrue:[
+            noneYet := false.
+            buffer := buffer , thisString
+        ] ifFalse:[
+            buffer := buffer , (' ' , thisString)
+        ].
+        count := count + 1.
+        (count == 20) ifTrue:[
+            string := string , buffer.
+            buffer := ''.
+            count := 0
+        ].
+        total := total + 1.
+        (total > 5000) ifTrue:[
+            string := string , buffer , '... )'.
+            ^string
+        ]
+    ].
+    string := string , buffer , ')'.
+    ^string
+!
+
+displayString
+    "return the printString of a big collection can take a long time
+     due to long temporary strings - I use a buffer here collecting some
+     elements to reduce the GC overhead ...
+    "
+
+    |thisString buffer count string noneYet total|
+
+    string := (self class name) , '('.
+    noneYet := true.
+    buffer := ''.
+    count := 0.
+    total := 0.
+    self do: [:element |
+        thisString := element displayString.
+        noneYet ifTrue:[
+            noneYet := false.
+            buffer := buffer , thisString
+        ] ifFalse:[
+            buffer := buffer , (' ' , thisString)
+        ].
+        count := count + 1.
+        (count == 20) ifTrue:[
+            string := string , buffer.
+            buffer := ''.
+            count := 0
+        ].
+        total := total + 1.
+        (total > 5000) ifTrue:[
+            string := string , buffer , '... )'.
+            ^string
+        ]
+    ].
+    string := string , buffer , ')'.
+    ^string
+!
+
+printOn:aStream
+    |tooMany firstOne noMore|
+
+    tooMany := aStream position + self maxPrint.
+    aStream nextPutAll:self class name.
+    aStream nextPut:$(.
+    firstOne := true.
+    noMore := false.
+    self do:[:element |
+        noMore ifFalse:[
+            firstOne ifFalse:[
+                aStream nextPut:(Character space)
+            ] ifTrue:[
+                firstOne := false
+            ].
+            (aStream position > tooMany) ifTrue:[
+                aStream nextPutAll:'...etc...)'.
+                noMore := true
+            ] ifFalse:[
+                element printOn:aStream
+            ]
+        ].
+    ].
+    aStream nextPut:$)
+!
+
+storeOn:aStream
+    "output a printed representation (which can be re-read)
+     onto the argument aStream"
+
+    |isEmpty|
+
+    aStream nextPutAll:'(('.
+    aStream nextPutAll:(self class name).
+    aStream nextPutAll:' new)'.
+    isEmpty := true.
+    self do:[:element |
+        aStream nextPutAll:' add: '.
+        element storeOn:aStream.
+        aStream nextPut:$;.
+        isEmpty := false
+    ].
+    isEmpty ifFalse:[aStream nextPutAll:' yourself'].
+    aStream nextPut:$)
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Collection.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,546 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Collection
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Abstract'
+!
+
+Collection comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+Abstract superclass for all collections
+
+%W% %E%
+'!
+
+!Collection class methodsFor:'instance creation'!
+
+with:anObject
+    "return a new Collection with one element:anObject"
+
+    |newCollection|
+
+    newCollection := self new.
+    newCollection add:anObject.
+    ^ newCollection
+!
+
+with:firstObject with:secondObject
+    "return a new Collection with two elements:firstObject and secondObject"
+
+    |newCollection|
+
+    newCollection := self new.
+    newCollection add:firstObject.
+    newCollection add:secondObject.
+    ^ newCollection
+!
+
+with:firstObject with:secondObject with:thirdObject
+    "return a new Collection with three elements"
+
+    |newCollection|
+
+    newCollection := self new.
+    newCollection add:firstObject.
+    newCollection add:secondObject.
+    newCollection add:thirdObject.
+    ^ newCollection
+!
+
+with:firstObject with:secondObject with:thirdObject with:fourthObject
+    "return a new Collection with four elements"
+
+    |newCollection|
+
+    newCollection := self new.
+    newCollection add:firstObject.
+    newCollection add:secondObject.
+    newCollection add:thirdObject.
+    newCollection add:fourthObject.
+    ^ newCollection
+!
+
+new:size withAll:element
+    "return a new COllection of size, where all elements are
+     initialized to element"
+
+    |newCollection|
+
+    newCollection := self new:size.
+    size timesRepeat:[newCollection add:element]
+!
+
+withAll:aCollection
+    "return a new Collection with all elements taken from the argument,
+     aCollection"
+
+    |newCollection|
+
+    newCollection := self new.
+    newCollection addAll:aCollection.
+    ^newCollection
+! !
+
+!Collection methodsFor:'error handling'!
+
+errorNotFound
+    "report an error that Object was not in the collection"
+
+    self error:'Object is not in the Collection'
+!
+
+errorNotKeyed
+    "report an error that keyed access methods are not allowed"
+
+    self error:(self class name, 's do not respond to keyed accessing messages')
+! !
+
+!Collection methodsFor:'accessing'!
+
+anElement
+    "return any element from the collection"
+
+    self do: [:each | ^ each].
+    ^ nil
+! !
+
+!Collection methodsFor:'adding & removing'!
+
+add:anObject
+    "add the argument, anObject to the receiver"
+
+    ^ self subclassResponsibility
+!
+
+addLast:anObject
+    "add the argument, anObject to the receiver"
+
+    ^ self add:anObject
+!
+
+addAll:aCollection
+    "add all elements of the argument, aCollection to the receiver"
+
+    aCollection do:[:element |
+        self add:element
+    ].
+    ^ aCollection
+!
+
+remove:anObject ifAbsent:exceptionBlock
+    "remove the argument, anObject from the receiver - if it was not
+     in the collection returns the the value of the exceptionBlock"
+
+    ^ self subclassResponsibility
+!
+
+remove:anObject
+    "remove the argument, anObject from the receiver"
+
+    self remove:anObject ifAbsent:[self errorNotFound]
+!
+
+removeAll:aCollection
+    "remove all elements of the argument, aCollection from the receiver"
+
+    aCollection do:[:element | self remove:element].
+    ^ aCollection
+! !
+
+!Collection methodsFor:'growing'!
+
+growSize
+    "return a suitable size increment for growing"
+
+    ^ self size max:2
+!
+
+grow
+    "make the receiver larger"
+
+    self grow:(self size + self growSize)
+!
+
+grow:howBig
+    "change the receivers size"
+
+    ^ self subclassResponsibility
+! !
+
+!Collection methodsFor:'testing'!
+
+isEmpty
+    "return true, if the receiver is empty"
+
+    ^ self size == 0
+!
+
+includes:anElement
+    "return true, if the argument, anObject is in the list"
+
+    self do:[:element |
+        (anElement = element) ifTrue:[^ true].
+    ].
+    ^ false
+!
+
+includesAll:aCollection
+    "return true, if the the receiver includes all elements of
+     the argument, aCollection; false if any is missing"
+
+    aCollection do:[:element |
+        (self includes:element) ifFalse:[^ false].
+    ].
+    ^ true
+!
+
+occurrencesOf:anElement
+    "return the number of occurrences of the argument, anElement in
+     the receiver"
+
+    |count "<SmallInteger>" |
+
+    count := 0.
+    self do:[:element |
+        (anElement = element) ifTrue:[
+            count := count + 1
+        ].
+    ].
+    ^ count
+!
+
+size
+    "return the number of elements in the receiver"
+
+    |count "<SmallInteger>" |
+
+    count := 0.
+    self do:[:element |
+        count := count + 1
+    ].
+    ^ count
+! !
+
+!Collection methodsFor:'enumerating'!
+
+do:aBlock
+    "evaluate the argument, aBlock for each element"
+
+    ^ self subclassResponsibility
+!
+
+collect:aBlock
+    "for each element in the receiver, evaluate the argument, aBlock
+     and return a new collection with the results"
+
+    |newCollection|
+
+    newCollection := self species new.
+    self do:[:each | newCollection add:(aBlock value:each)].
+    ^ newCollection
+!
+
+detect:aBlock
+    "evaluate the argument, aBlock for each element in the receiver until
+     the block returns true; in this case return the element which caused
+     the true evaluation.
+     If none of the evaluations return true, report an error"
+
+    ^ self detect:aBlock ifNone:[self errorNotFound]
+!
+
+detect:aBlock ifNone:exceptionBlock
+    "evaluate the argument, aBlock for each element in the receiver until
+     the block returns true; in this case return the element which caused
+     the true evaluation.
+     If none of the evaluations returns true, return the result of the
+     evaluation of the exceptionBlock"
+
+    self do:[:each | 
+        (aBlock value:each) ifTrue:[^ each].
+    ].
+    ^ exceptionBlock value
+!
+
+inject:thisValue into:binaryBlock
+    |nextValue|
+
+    nextValue := thisValue.
+    self do: [:each | nextValue := binaryBlock value:nextValue value:each].
+    ^ nextValue
+!
+
+reject:aBlock
+    "return a new collection with all elements from the receiver, for which
+     the argument aBlock evaluates to false"
+
+    ^ self select:[:element | (aBlock value:element) == false]
+!
+
+select:aBlock
+    "return a new collection with all elements from the receiver, for which
+     the argument aBlock evaluates to true"
+
+    |newCollection|
+
+    newCollection := self species new.
+    self do:[:each |
+        (aBlock value:each) ifTrue:[newCollection add:each].
+    ].
+    ^ newCollection
+! !
+
+!Collection methodsFor:'converting'!
+
+asArray
+    "return a new Array with the collections elements"
+
+    |anArray 
+     index "<SmallInteger>" |
+
+    anArray := Array new:(self size).
+    index := 1.
+    self do:[:each |
+        anArray at:index put:each.
+        index := index + 1
+    ].
+    ^ anArray
+!
+
+asByteArray
+    "return a new ByteArray with the collections elements"
+
+    |aByteArray 
+     index "<SmallInteger>" |
+
+    aByteArray := ByteArray new:(self size).
+    index := 1.
+    self do:[:each |
+        aByteArray at:index put:each asInteger.
+        index := index + 1
+    ].
+    ^ aByteArray
+!
+
+asString
+    "return a String with the collections elements 
+     (which must convertable to characters)"
+
+    |aString 
+     index "<SmallInteger>" |
+
+    aString := String new:(self size).
+    index := 1.
+    self do:[:each |
+        aString at:index put:each asCharacter.
+        index := index + 1
+    ].
+    ^ aString
+!
+
+asText
+    "return a new Text-object with the elements printstings"
+
+    |aText
+     index "<SmallInteger>" |
+
+    aText := Text new:(self size).
+    index := 1.
+    self do:[:each |
+        aText at:index put:(each printString).
+        index := index + 1
+    ].
+    ^ aText
+!
+
+asBag
+    "return a new Bag with the receiver collections elements"
+
+    |aBag|
+
+    aBag := Bag new.
+    self do:[:each | aBag add:each].
+    ^ aBag
+!
+
+asOrderedCollection
+    "return a new OrderedCollection with the receiver collections elements"
+
+    |anOrderedCollection|
+
+    anOrderedCollection := OrderedCollection new:self size.
+    self do:[:each | anOrderedCollection addLast:each].
+    ^ anOrderedCollection
+!
+
+asSet
+    "return a new Set with the receiver collections elements"
+
+    |aSet|
+
+    aSet := Set new: self size.
+    self do:[:each | aSet add:each].
+    ^ aSet
+!
+
+asSortedCollection
+    "return a new SortedCollection with the receiver collections elements"
+
+    |aSortedCollection|
+
+    aSortedCollection := SortedCollection new:self size.
+    aSortedCollection addAll:self.
+    ^ aSortedCollection
+!
+
+asSortedCollection:sortBlock
+    "return a new SortedCollection with the receiver collections elements,
+     using sortBlock for comparing"
+
+    |aSortedCollection|
+
+    aSortedCollection := SortedCollection sortBlock:sortBlock.
+    aSortedCollection addAll:self.
+    ^ aSortedCollection
+! !
+
+!Collection methodsFor:'printing & storing'!
+
+maxPrint
+    ^ 5000
+!
+
+printString
+    "return the printString of a big collection can take a long time
+     due to long temporary strings - I use a buffer here collecting some
+     elements to reduce the GC overhead ...
+    "
+
+    |thisString buffer count string noneYet total|
+
+    string := (self class name) , '('.
+    noneYet := true.
+    buffer := ''.
+    count := 0.
+    total := 0.
+    self do: [:element |
+        thisString := element printString.
+        noneYet ifTrue:[
+            noneYet := false.
+            buffer := buffer , thisString
+        ] ifFalse:[
+            buffer := buffer , (' ' , thisString)
+        ].
+        count := count + 1.
+        (count == 20) ifTrue:[
+            string := string , buffer.
+            buffer := ''.
+            count := 0
+        ].
+        total := total + 1.
+        (total > 5000) ifTrue:[
+            string := string , buffer , '... )'.
+            ^string
+        ]
+    ].
+    string := string , buffer , ')'.
+    ^string
+!
+
+displayString
+    "return the printString of a big collection can take a long time
+     due to long temporary strings - I use a buffer here collecting some
+     elements to reduce the GC overhead ...
+    "
+
+    |thisString buffer count string noneYet total|
+
+    string := (self class name) , '('.
+    noneYet := true.
+    buffer := ''.
+    count := 0.
+    total := 0.
+    self do: [:element |
+        thisString := element displayString.
+        noneYet ifTrue:[
+            noneYet := false.
+            buffer := buffer , thisString
+        ] ifFalse:[
+            buffer := buffer , (' ' , thisString)
+        ].
+        count := count + 1.
+        (count == 20) ifTrue:[
+            string := string , buffer.
+            buffer := ''.
+            count := 0
+        ].
+        total := total + 1.
+        (total > 5000) ifTrue:[
+            string := string , buffer , '... )'.
+            ^string
+        ]
+    ].
+    string := string , buffer , ')'.
+    ^string
+!
+
+printOn:aStream
+    |tooMany firstOne noMore|
+
+    tooMany := aStream position + self maxPrint.
+    aStream nextPutAll:self class name.
+    aStream nextPut:$(.
+    firstOne := true.
+    noMore := false.
+    self do:[:element |
+        noMore ifFalse:[
+            firstOne ifFalse:[
+                aStream nextPut:(Character space)
+            ] ifTrue:[
+                firstOne := false
+            ].
+            (aStream position > tooMany) ifTrue:[
+                aStream nextPutAll:'...etc...)'.
+                noMore := true
+            ] ifFalse:[
+                element printOn:aStream
+            ]
+        ].
+    ].
+    aStream nextPut:$)
+!
+
+storeOn:aStream
+    "output a printed representation (which can be re-read)
+     onto the argument aStream"
+
+    |isEmpty|
+
+    aStream nextPutAll:'(('.
+    aStream nextPutAll:(self class name).
+    aStream nextPutAll:' new)'.
+    isEmpty := true.
+    self do:[:element |
+        aStream nextPutAll:' add: '.
+        element storeOn:aStream.
+        aStream nextPut:$;.
+        isEmpty := false
+    ].
+    isEmpty ifFalse:[aStream nextPutAll:' yourself'].
+    aStream nextPut:$)
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Context.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,393 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object variableSubclass:#Context
+       instanceVariableNames:'flags sender home receiver selector searchClass
+                              lineNr retvalTemp handle'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Kernel-Methods'
+!
+
+Context comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+Context represents the stack context objects; each message send adds a context
+to a chain, which can be traced back via the sender field. (The actual implementation
+uses the machines stack for this, building real contexts when needed only).
+For both method- and block-contexts, the layout is the same. For method contexts, the 
+home-field is nil, while for blockcontexts the home-field is either the context of its
+surrounding block (if its a nested block) or of its method. Contexts of cheap blocks do not
+have a home context - their home field is also nil.
+
+Warning: layout and size known by the compiler and runtime system - do not change.
+
+%W% %E%
+'!
+
+!Context methodsFor:'testing'!
+
+isContext
+    "return true, iff the receiver is a Context, false otherwise"
+
+    ^ true
+!
+
+isBlockContext
+    "return true, iff the receiver is a BlockContext, false otherwise"
+
+    ^ false
+! !
+
+!Context methodsFor:'accessing'!
+
+instVarAt:index
+    "have to catch instVar access to retVal and handle - they are invalid"
+
+    (index == 8) ifTrue:[^ nil].
+    (index == 9) ifTrue:[^ nil].
+    ^ super instVarAt:index
+!
+
+instVarAt:index put:value
+    "have to catch instVar access to retVal and handle - they are invalid"
+
+    (index == 8) ifTrue:[^ nil].
+    (index == 9) ifTrue:[^ nil].
+    ^ super instVarAt:index put:value
+!
+
+methodHome
+    "return the method-home - for method contexts this is the receiver"
+
+    ^ self
+!
+
+home
+    "return the immediate home of the receiver.
+     for block contexts, this is the methodcontext, where the block was created,
+     for nested block contexts, its the surrounding blocks context.
+     for method-contexts this is nil."
+
+    ^ nil "home"
+!
+
+method
+    "return the method which corresponds to the receiver"
+
+    |c|
+
+    c := self searchClass whichClassImplements:selector.
+    c notNil ifTrue:[
+        ^ c compiledMethodAt:selector
+    ].
+    ^ nil
+!
+
+sender
+    "return the sender of the context"
+
+    "this special test is for the very first context (startup-context)"
+    (sender isNil or:[sender selector isNil]) ifTrue:[^ nil].
+
+    ^ sender
+!
+
+receiver
+    "return the receiver of the context"
+
+    ^ receiver
+!
+
+searchClass
+    "this is the class where the method-lookup started"
+
+    searchClass notNil ifTrue:[^ searchClass].
+    ^ receiver class
+!
+
+selector
+    "return the selector of the context"
+
+    ^ selector
+!
+
+nargs
+    "return the number of arguments to the Block/Method"
+
+%{  /* NOCONTEXT */
+
+    RETURN ( _MKSMALLINT( (_intVal(_INST(flags)) >> __NARG_SHIFT) & __NARG_MASK) );
+%}
+!
+
+nvars
+    "return the number of variables to the Block/Method"
+
+%{  /* NOCONTEXT */
+
+    RETURN ( _MKSMALLINT( (_intVal(_INST(flags)) >> __NVAR_SHIFT) & __NVAR_MASK) );
+%}
+!
+
+args
+    "return an array filled with the arguments of this context"
+
+    |newArray n "{ Class: SmallInteger }" |
+
+    n := self nargs.
+    newArray := Array new:n.
+    1 to:n do:[:index |
+        newArray at:index put:(self at:index)
+    ].
+    ^ newArray
+!
+
+argsAndVars
+    "return an array filled with the arguments and variables of this context"
+
+    |newArray mySize "{ Class: SmallInteger }" |
+
+    mySize := self nargs + self nvars.
+    newArray := Array new:mySize.
+    1 to:mySize do:[:index |
+        newArray at:index put:(self at:index)
+    ].
+    ^ newArray
+!
+
+lineNumber
+    "this returns the lineNumber within the methods source, where the context was
+     interrupted or called another method. (currently, sometimes this information
+     is not available - in this case 0 is returned)"
+
+    ^ lineNr
+! !
+
+!Context methodsFor:'printing'!
+
+argsPrintString
+    |fullString n "{ Class: SmallInteger }" |
+
+    fullString := ''.
+    n := self nargs.
+    1 to:n do:[:index |
+        fullString := fullString , (' ' , (self at:index) printString)
+    ].
+    ^ fullString
+!
+
+printReceiver
+    |implementorClass|
+
+    (receiver class == SmallInteger "isKindOf:Number") ifTrue:[
+        '(' print. receiver print. ') ' print
+    ].
+    receiver class name print.
+
+    selector notNil ifTrue:[
+        implementorClass := self searchClass whichClassImplements:selector.
+        implementorClass notNil ifTrue: [
+            (implementorClass ~= receiver class) ifTrue: [
+                '>>>' print.
+                implementorClass name print
+            ]
+        ] ifFalse:[
+            '>>>**NONE**' print
+        ]
+    ]
+!
+    
+receiverPrintString
+    |newString receiverClassName implementorClass|
+
+    receiverClassName := receiver class name.
+    (receiver class == SmallInteger) ifTrue:[
+        newString := '(' , receiver printString , ') ' , receiverClassName
+    ] ifFalse:[
+        newString := receiverClassName
+    ].
+
+    selector notNil ifTrue:[
+        implementorClass := self searchClass whichClassImplements:selector.
+        implementorClass notNil ifTrue: [
+            (implementorClass ~~ receiver class) ifTrue: [
+                newString := newString , '>>>',
+                             implementorClass name printString
+            ]
+        ] ifFalse:[
+            newString := newString , '>>>**NONE**'
+        ]
+    ].
+
+    ^ newString
+!
+    
+printString
+    ^ self receiverPrintString , ' ' , self selector printString
+!
+
+printOn:aStream
+    aStream nextPutAll:(self receiverPrintString).
+    aStream space.
+    self selector printOn:aStream
+!
+
+fullPrint
+    self printReceiver.
+    ' ' print.
+    selector print.
+    self size ~~ 0 ifTrue: [
+        ' ' print.
+        self argsPrintString print
+    ].
+    ' [' print. lineNr print. ']' printNewline
+!
+
+fullPrintOn:aStream
+    aStream nextPutAll:self receiverPrintString.
+    aStream space.
+    aStream nextPutAll:selector printString.
+    self size ~~ 0 ifTrue: [
+        aStream space.
+        aStream nextPutAll:self argsPrintString
+    ]
+!
+
+debugPrint
+    | n "{ Class: SmallInteger }" |
+
+    'context ' print. self address printNewline.
+    'receiver: ' print. receiver address printNewline.
+    'selector: ' print. selector address printNewline.
+    n := self size.
+    n ~~ 0 ifTrue:[
+        1 to:n do:[:index |
+            'arg ' print. index print. '   : ' print.
+             (self at:index) address printNewline
+        ]
+    ].
+    '' printNewline
+!
+
+fullPrintString
+    |aString|
+
+    aString := self receiverPrintString , ' ' , selector printString.
+    self size ~~ 0 ifTrue: [
+        aString := aString , ' ' , (self argsPrintString)
+    ].
+    ^ aString
+!
+
+printAll
+    |context|
+    context := self.
+    [context notNil] whileTrue: [
+        context print.
+        context := context sender
+    ]
+!
+
+fullPrintAll
+    |context|
+    context := self.
+    [context notNil] whileTrue: [
+        context fullPrint.
+        context := context sender
+    ]
+!
+
+debugPrintAll
+    |context|
+    context := self.
+    [context notNil] whileTrue:[
+        context debugPrint.
+        context := context sender
+    ]
+! !
+
+!Context methodsFor:'non local control flow'!
+
+restart
+    "restart the receiver - i.e. the method is evaluated again.
+     if the context to restart already died - do nothing"
+
+    sender isNil ifTrue:[^ nil].
+%{
+    __RESUMECONTEXT(SND_COMMA self, (OBJ)1);
+
+    /* when we reach here, something went wrong */
+    printf("restart failed\n");
+%}
+.
+    ^ nil
+!
+
+resume
+    "resume the receiver with nil - i.e. return nil from the receiver.
+     if the context to resume already died - do nothing"
+
+    self resume:nil
+!
+
+resume:value
+    "resume the receiver - i.e. return value from the receiver.
+     if the context to resume already died - do nothing. No unwind
+     blocks are evaluated (see unwind: in this class)."
+
+    sender isNil ifTrue:[^ nil].
+%{
+    __RESUMECONTEXT(SND_COMMA self, value);
+
+    /* when we reach here, something went wrong */
+    printf("resume failed\n");
+%}
+.
+    ^ nil
+!
+
+unwind
+    "resume the receiver - i.e. return nil from the receiver.
+     if the context to resume already died - do nothing.
+     Evaluate all unwind-blocks as specified in Block>>valueNowOrOnUnwind:
+     and Block>>valueOnUnwindDo: on the way."
+
+    self unwind:nil
+!
+
+unwind:value
+    "resume the receiver - i.e. return value from the receiver.
+     if the context to resume already died - do nothing.
+     Evaluate all unwind-blocks as specified in Block>>valueNowOrOnUnwind:
+     and Block>>valueOnUnwindDo: on the way."
+
+    |con|
+
+    sender isNil ifTrue:[^ nil].
+
+    "start with this context, moving up"
+    con := thisContext.
+    [con ~~ self] whileTrue:[
+        con isBlockContext ifFalse:[
+            "the way we find those unwind contexts seems kludgy"
+            ((con selector == #valueNowOrOnUnwindDo:) or:[con selector == #valueOnUnwindDo:]) ifTrue:[
+                "the way we evaluate the unwind blocks too"
+                (con args at:1) value
+            ]
+        ].
+        con := con sender
+    ].
+    self resume:value
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Date.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,476 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+
+
+ ----------------------------------------------------------------
+ For code marked as (GNU) the following applies:
+
+ Copyright (C) 1988, 1989 Free Software Foundation, Inc.
+ Written by Steve Byrne.
+
+ This file is part of GNU Smalltalk.
+
+ GNU Smalltalk is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 1, or (at your option) any later version.
+
+ GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Smalltalk; see the file LICENSE.  If not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ ----------------------------------------------------------------
+"
+
+Magnitude subclass:#Date
+       instanceVariableNames:'unixTimeLow unixTimeHi'
+       classVariableNames:'dayNames monthNames dayAbbrevs monthAbbrevs
+                           environmentChange'
+       poolDictionaries:''
+       category:'Magnitude-General'
+!
+
+Date comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+              All Rights Reserved
+
+date represents a particular second in a day; since we depend on
+unix, the second is counted from 1. Jan 1970 NOT as in Smalltalk-80
+from 1. Jan 1901 !
+
+%W% %E%
+
+written Spring 89 by claus
+'!
+
+!Date class methodsFor:'private'!
+
+initNames
+    "read the language specific names"
+
+    dayNames := Resource array:#('DAY_MONDAY'
+                                 'DAY_TUESDAY'
+                                 'DAY_WEDNESDAY'
+                                 'DAY_THURSDAY'
+                                 'DAY_FRIDAY'
+                                 'DAY_SATURDAY'
+                                 'DAY_SUNDAY')
+                      defaults:#('monday'
+                                 'tuesday'
+                                 'wednesday'
+                                 'thursday'
+                                 'friday'
+                                 'saturday'
+                                 'sunday')
+                      fromFile:'Smalltalk.rs'.
+
+    dayAbbrevs := Resource array:#('DAY_MON'
+                                   'DAY_TUE'
+                                   'DAY_WED'
+                                   'DAY_THU'
+                                   'DAY_FRI'
+                                   'DAY_SAT'
+                                   'DAY_SUN')
+                        defaults:#('mon' 
+                                   'tue' 
+                                   'wed'
+                                   'thu' 
+                                   'fri' 
+                                   'sat' 
+                                   'sun')
+                      fromFile:'Smalltalk.rs'.
+
+    monthNames := Resource array:#('MON_JANUARY' 
+                                   'MON_FEBRUARY' 
+                                   'MON_MARCH'
+                                   'MON_APRIL' 
+                                   'MON_MAY' 
+                                   'MON_JUNE' 
+                                   'MON_JULY'
+                                   'MON_AUGUST' 
+                                   'MON_SEPTEMBER' 
+                                   'MON_OCTOBER'
+                                   'MON_NOVEMBER' 
+                                   'MON_DECEMBER')
+                        defaults:#('january'
+                                   'february'
+                                   'march'
+                                   'april'
+                                   'may'
+                                   'june'
+                                   'july'
+                                   'august'
+                                   'september'
+                                   'october'
+                                   'november'
+                                   'december')
+                       fromFile:'Smalltalk.rs'.
+
+    monthAbbrevs := Resource array:#('MON_JAN' 
+                                     'MON_FEB' 
+                                     'MON_MAR'
+                                     'MON_APR' 
+                                     'MON_MAY_ABBREV' 
+                                     'MON_JUN' 
+                                     'MON_JUL'
+                                     'MON_AUG' 
+                                     'MON_SEP' 
+                                     'MON_OCT'
+                                     'MON_NOV' 
+                                     'MON_DEC')
+                          defaults:#('jan'
+                                     'feb'
+                                     'mar'
+                                     'apr'
+                                     'may'
+                                     'jun'
+                                     'jul'
+                                     'aug'
+                                     'sep'
+                                     'oct'
+                                     'nov'
+                                     'dec')
+                         fromFile:'Smalltalk.rs'.
+
+    environmentChange := false
+! !
+
+!Date class methodsFor:'handling language changes'!
+
+initialize
+    super initialize.
+    Smalltalk addDependent:self.
+    environmentChange := true
+!
+
+update:something
+    ((something == #Language) or:[something == #LanguageTerritory]) ifTrue:[
+        "just remember change for next access"
+        environmentChange := true
+    ]
+! !
+
+!Date class methodsFor:'general queries'!
+
+dayOfWeek:dayName
+    "given the name of a day (either string or symbol),
+     return the day-index (1 for monday; 7 for sunday).
+     Return 0 for invalid day name"
+
+    environmentChange ifTrue:[
+        self initNames
+    ].
+    ^ dayNames indexOf:dayName
+
+    "Date dayOfWeek:'wednesday'"
+!
+
+indexOfMonth:aMonthString
+    "given the name of a month (either string or symbol),
+     return the month-index (1 for jan; 12 for december).
+     Return 0 for invalid month name"
+
+    |idx name|
+
+    environmentChange ifTrue:[
+        self initNames
+    ].
+    name := aMonthString asLowercase.
+    idx := monthAbbrevs indexOf:name.
+    idx ~~ 0 ifTrue:[^ idx].
+    idx := monthNames indexOf:name.
+    idx ~~ 0 ifTrue:[^ idx].
+
+    name at:1 put:(name at:1) asUppercase.
+    idx := monthAbbrevs indexOf:name.
+    idx ~~ 0 ifTrue:[^ idx].
+    idx := monthNames indexOf:name.
+    idx ~~ 0 ifTrue:[^ idx].
+
+    ^ idx
+
+    "Date indexOfMonth:'jan'"
+    "Date indexOfMonth:'Jan'"
+    "Date indexOfMonth:'December'"
+!
+
+nameOfDay:dayIndex
+    "given a day index (1..7), return the name of the day 
+     as a symbol"
+
+    environmentChange ifTrue:[
+        self initNames
+    ].
+    ^ (dayNames at:dayIndex) asSymbol
+
+    "Date nameOfDay:4"
+!
+
+nameOfMonth:monthIndex
+    "given a month index (1..12), return the name of the month 
+     as a symbol"
+
+    environmentChange ifTrue:[
+        self initNames
+    ].
+    ^ (monthNames at:monthIndex) asSymbol
+
+    "Date nameOfMonth:11"
+    "Date nameOfMonth:12"
+    "Date nameOfMonth:4"
+!
+
+abbreviatedNameOfDay:dayIndex
+    "given a day index (1..7), return the abbreviated name
+     of the day as a symbol"
+
+    environmentChange ifTrue:[
+        self initNames
+    ].
+    ^ (dayAbbrevs at:dayIndex) asSymbol
+!
+
+abbreviatedNameOfMonth:monthIndex
+    "given a month index (1..12), return the abbreviated name
+     of the month as a symbol"
+
+    environmentChange ifTrue:[
+        self initNames
+    ].
+    ^ (monthAbbrevs at:monthIndex) asSymbol
+!
+
+daysInMonth:monthName forYear:yearInteger
+    "given the name of a minth and a year, return the number 
+     of days this month has (modified GNU).
+     return 0 if the month name was invalid."
+
+    |monthIndex|
+
+    monthIndex := self indexOfMonth:monthName.
+    (monthIndex == 0) ifTrue:[
+        ^ 0
+    ].
+    ^ self daysInMonthIndex:monthIndex forYear:yearInteger
+
+    "Date daysInMonth:2 forYear:1980"
+    "Date daysInMonth:2 forYear:1981"
+!
+
+daysUntilMonth:monthName forYear:yearInteger
+    "given the name of a month and a year, return the number 
+     of days from 1st of january to last of prev month.
+     return 0 if the month name was invalid."
+
+    |monthIndex sumDays|
+
+    monthIndex := self indexOfMonth:monthName.
+    (monthIndex == 0) ifTrue:[
+        ^ 0
+    ].
+    sumDays := 0.
+    1 to:monthIndex-1 do:[:m |
+        sumDays := sumDays + (self daysInMonthIndex:m forYear:yearInteger)
+    ].
+    ^ sumDays
+
+    "Date daysUntilMonth:'feb' forYear:1993"
+    "Date daysUntilMonth:'jan' forYear:1993"
+!
+
+daysInYear:yearInteger
+    "return the number of days in a year"
+
+    (self isLeapYear:yearInteger) ifTrue:[^ 366].
+    ^ 365
+
+    "Date daysInYear:1980"
+    "Date daysInYear:1981"
+!
+
+yearAsDays: yearInteger
+    "Returns the number of days since Jan 1, 1901. (GNU)"
+
+    |y|
+
+    y := yearInteger - 1900.
+    ^ (y - 1) * 365
+        + (y // 4)
+        - (y // 100)
+        + (y // 400)
+!
+
+isLeapYear:yearInteger
+    "return true, if a year is a leap year"
+
+    (yearInteger \\ 4 == 0) ifTrue:[
+        (yearInteger \\ 100 ~~ 0) ifTrue:[^ true].
+        (yearInteger \\ 400 == 0) ifTrue:[^ true]
+    ].
+    ^ false
+!
+
+daysInMonthIndex: monthIndex forYear: yearInteger
+    "return the number of days in month monthIndex of
+     year yearInteger (GNU)"
+
+    |days|
+
+    days := #(31 28 31           "Jan Feb Mar"
+              30 31 30           "Apr May Jun"
+              31 31 30           "Jul Aug Sep"
+              31 30 31           "Oct Nov Dec"
+             ) at: monthIndex.
+
+    (monthIndex == 2) ifTrue:[
+        (self isLeapYear:yearInteger) ifTrue:[
+            ^ days + 1
+        ]
+    ].
+    ^ days
+! !
+
+!Date class methodsFor:'instance creation'!
+
+dateAndTimeNow
+    "return an array with the current date and time"
+
+    ^ Array with:(Date today) with:(Time now)
+!
+
+today
+    "return a date, representing today"
+
+    ^ self basicNew setTimeLow:(OperatingSystem getTimeLow)
+                           and:(OperatingSystem getTimeHi)
+!
+
+fromDays:dayCount
+    ^ self new setDays:dayCount
+!
+
+newDay:dayCount year:yearInteger
+    ^ self new setDays:(dayCount + self yearAsDays:yearInteger)
+!
+
+newDay:day month:monthName year:yearInteger
+    ^self new setDays:
+        (day + (self daysUntilMonth: monthName forYear: yearInteger)
+             + (self yearAsDays: yearInteger))
+
+    "Date newDay:8 month:'may' year:1993"
+! !
+
+!Date methodsFor:'comparing'!
+
+
+> aDate
+    "return true, if the date represented by the receiver
+     is after the argument, aDate"
+
+    |other|
+
+    OperatingSystem computeDatePartsOf:unixTimeLow and:unixTimeHi
+                                   for:[:year :month :day |
+        other := aDate year.
+        (year > other) ifTrue:[^ true].
+        (year < other) ifTrue:[^ false].
+        other := aDate month.
+        (month > other) ifTrue:[^ true].
+        (month < other) ifTrue:[^ false].
+        ^ day > aDate day
+    ]
+!
+
+= aDate
+    "return true, if the date represented by the receiver
+     is the same as the one represented by argument, aDate"
+
+    OperatingSystem computeDatePartsOf:unixTimeLow and:unixTimeHi
+                                   for:[:year :month :day |
+        (year ~~ aDate year) ifTrue:[^ false].
+        (month ~~ aDate month) ifTrue:[^ false].
+        ^ (day == aDate day)
+    ]
+! !
+
+!Date methodsFor:'accessing'!
+
+day
+    "return the day-in-month of the receiver (1..31)"
+
+    OperatingSystem computeDatePartsOf:unixTimeLow and:unixTimeHi
+                                   for:[:year :month :day |
+        ^ day
+    ]
+!
+
+month
+    "return the month of the receiver (1..12)"
+
+    OperatingSystem computeDatePartsOf:unixTimeLow and:unixTimeHi
+                                   for:[:year :month :day |
+        ^ month
+    ]
+!
+
+year
+    "return the year of the receiver i.e. 1992"
+
+    OperatingSystem computeDatePartsOf:unixTimeLow and:unixTimeHi
+                                   for:[:year :month :day |
+        ^ year
+    ]
+! !
+
+!Date methodsFor:'printing'!
+
+printString
+    |string|
+
+    OperatingSystem computeDatePartsOf:unixTimeLow and:unixTimeHi
+                                   for:[:year :month :day |
+        string :=   day printString
+                  , '-' 
+                  , (Date abbreviatedNameOfMonth:month)
+                  , '-' 
+                  , year printString
+    ].
+    ^ string
+! !
+
+!Date methodsFor:'storing'!
+
+storeString
+    |string|
+    string := '(' , self class name , ' new settimeLow:'.
+    string := string , unixTimeLow storeString.
+    string := string , ' and:' , unixTimeHi storeString.
+    string := string , ')'.
+    ^ string
+! !
+
+!Date methodsFor:'private'!
+
+setTimeLow:timeLow and:timeHi
+    unixTimeLow := timeLow.
+    unixTimeHi := timeHi
+! !
+
+!Date methodsFor:'converting'!
+
+asSeconds
+    ^ (unixTimeHi * 16r10000) + unixTimeLow
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Dict.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,503 @@
+"
+ COPYRIGHT (c) 1991-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Collection subclass:#Dictionary
+         instanceVariableNames:'valueArray keyArray tally'
+         classVariableNames:''
+         poolDictionaries:''
+         category:'Collections-Unordered'
+!
+
+Dictionary comment:'
+
+COPYRIGHT (c) 1991-93 by Claus Gittinger
+              All Rights Reserved
+
+a Dictionary is (conceptionally) a collection of Associations storing key-value pairs.
+(The implementation uses two array to store the keys and values separately.)
+Searching for an element is done using a hash into the key arrlay.
+
+%W% %E%
+
+written jun 91 by claus
+rewritten 92 to use hash scheme
+'!
+
+!Dictionary class methodsFor:'instance creation'!
+
+new
+    "return a new empty Dictionary"
+
+    ^ self new:5
+!
+
+new:anInteger
+    "return a new empty Dictionary with space for anInteger elements"
+
+    ^ self basicNew setTally:anInteger
+! !
+
+!Dictionary methodsFor:'testing'!
+
+size
+    "return the number of elements in the receiver"
+
+    ^ tally
+!
+
+includesKey:aKey
+    "return true, if the argument, aKey is a key in the receiver"
+
+    ^ (self findKey:aKey ifAbsent:[0]) ~~ 0
+!
+
+isFixedSize
+    "return true if the receiver cannot grow - this will vanish once
+     Arrays and Strings learn how to grow ..."
+
+    ^ false
+! !
+
+!Dictionary methodsFor:'accessing'!
+
+at:aKey
+    "return the element indexed by aKey - report an error if none found"
+
+    |index|
+
+    aKey isNil ifTrue:[
+        self error:'nil is not allowed as key'
+    ] ifFalse:[
+        index := self findKey:aKey ifAbsent:[0].
+        index == 0 ifTrue:[^ self errorKeyNotFound].
+        ^ valueArray basicAt:index
+    ]
+!
+
+at:aKey ifAbsent:exceptionBlock
+    |index|
+
+    aKey isNil ifTrue:[
+        self error:'nil is not allowed as key'
+    ] ifFalse:[
+        index := self findKey:aKey ifAbsent:[0].
+        index == 0 ifTrue:[^ exceptionBlock value].
+        ^ valueArray basicAt:index
+    ]
+!
+
+at:aKey put:anObject
+    "add the argument anObject under key, aKey to the receiver"
+
+    |index element|
+
+    aKey isNil ifTrue:[
+        self error:'nil is not allowed as key'
+    ] ifFalse:[
+        index := self findKeyOrNil:aKey.
+        element := valueArray basicAt:index.
+        element notNil ifTrue:[
+            valueArray basicAt:index put:anObject.
+            ^ anObject
+        ].
+        keyArray basicAt:index put:aKey.
+        valueArray basicAt:index put:anObject.
+        tally := tally + 1.
+
+        "grow if filled more than 70% "
+        tally > (keyArray basicSize * 7 // 10) ifTrue:[
+            self grow
+        ].
+
+        ^ anObject
+    ]
+!
+
+keys
+    "return a collection containing all keys of the receiver"
+
+    ^ keyArray select:[:key | key notNil]
+! !
+
+!Dictionary methodsFor:'adding & removing'!
+
+add:anAssociation
+    "add the argument, anAssociation to the receiver"
+
+    self at:(anAssociation key) put:(anAssociation value).
+    ^ anAssociation
+!
+
+remove:oldObject ifAbsent:aBlock
+    "remove oldObject from the collection and return it.
+     If it was not in the collection return the value of aBlock."
+
+    self shouldNotImplement
+!
+
+removeAssociation:assoc
+    "remove the association from the collection.
+     If it was not in the collection report an error"
+
+    self removeKey:assoc key
+!
+
+removeKey:aKey
+    "remove the association under aKey from the collection.
+     If it was not in the collection report an error"
+
+    |index 
+     next  "{ Class:SmallInteger }" |
+
+    aKey isNil ifTrue:[
+        self error:'nil is not allowed as key'
+    ] ifFalse:[
+        index := self findKey:aKey ifAbsent:[0].
+        (index == 0) ifTrue:[^ self errorNotFound].
+        valueArray basicAt:index put:nil.
+        keyArray basicAt:index put:nil.
+        tally := tally - 1.
+        tally == 0 ifTrue:[
+            self setTally:0
+        ] ifFalse:[
+            index == keyArray basicSize ifTrue:[
+                next := 1
+            ] ifFalse:[
+                next := index + 1.
+            ].
+            "redundant check to save a send sometimes"
+            (keyArray basicAt:next) notNil ifTrue:[
+                self rehashFrom:next.
+            ]
+        ]
+    ]
+!
+
+removeKey:aKey ifAbsent:aBlock
+    "remove the association under aKey from the collection.
+     If it was not in the collection return result from evaluating aBlock"
+
+    |index 
+     next  "{ Class:SmallInteger }" |
+
+    aKey isNil ifTrue:[
+        self error:'nil is not allowed as key'
+    ] ifFalse:[
+        index := self findKey:aKey ifAbsent:[0].
+        index == 0 ifTrue:[^ aBlock value].
+        valueArray basicAt:index put:nil.
+        keyArray basicAt:index put:nil.
+        tally := tally - 1.
+        tally == 0 ifTrue:[
+            self setTally:0
+        ] ifFalse:[
+            index == keyArray basicSize ifTrue:[
+                next := 1
+            ] ifFalse:[
+                next := index + 1.
+            ].
+            "redundant check to save a send sometimes"
+            (keyArray basicAt:next) notNil ifTrue:[
+                self rehashFrom:next.
+            ]
+        ]
+    ]
+! !
+
+!Dictionary methodsFor:'enumeration'!
+
+allKeysDo:aBlock
+    "perform the block for all keys in the collection."
+
+    keyArray nonNilElementsDo:aBlock
+!
+
+associationsDo:aBlock
+    "perform the block for all associations in the collection."
+
+    |key|
+
+    tally == 0 ifTrue:[^ self].
+    1 to:(keyArray basicSize) do:[:index |
+        key := keyArray basicAt:index.
+        key notNil ifTrue:[
+            aBlock value:(Association key:key value:(valueArray basicAt:index))
+        ]
+    ]
+!
+
+do:aBlock
+    "perform the block for all values in the collection."
+
+    |index "{ Class:SmallInteger }" |
+
+    tally == 0 ifTrue:[^ self].
+    index := 1.
+    keyArray do:[:key |
+        key notNil ifTrue:[
+            aBlock value:(valueArray basicAt:index)
+        ].
+        index := index + 1
+    ]
+!
+
+collect:aBlock
+    "for each element in the receiver, evaluate the argument, aBlock
+     and return a Bag with the results"
+
+    |newCollection|
+
+    newCollection := Bag new.
+    self do:[:each |
+        newCollection add:each
+    ].
+    ^ newCollection
+!
+
+select:aBlock
+    "return a new collection with all elements from the receiver, for which
+     the argument aBlock evaluates to true"
+
+    |newCollection|
+
+    newCollection := self species new.
+    self associationsDo:[:assoc |
+        (aBlock value:(assoc value)) ifTrue:[
+            newCollection add:assoc
+        ]
+    ].
+    ^ newCollection
+! !
+
+!Dictionary methodsFor:'private'!
+
+goodSizeFor:arg
+    "return a good array size for the given argument.
+     Returns the next prime after arg"
+
+    arg <= 7 ifTrue:[^ 7].
+    arg <= 16384 ifTrue:[
+           "2 4 8  16 32 64 128 256 512 1024 2048 4096 8192 16384"
+        ^ #(7 7 11 17 37 67 131 257 521 1031 2053 4099 8209 16411) at:(arg highBit)
+    ].
+    ^ arg bitOr:1
+!
+
+setTally:count
+    "initialize the contents array (for at least count slots)
+     and set tally to zero.
+     The size is increased to the next prime for better hashing behavior."
+
+    |n|
+
+    n := self goodSizeFor:count.
+    valueArray := Array new:n.
+    keyArray := Array new:n.
+    tally := 0
+!
+
+findKeyOrNil:key  
+    "Look for the key in the receiver.  If it is found, return
+     the index of the association containing the key, otherwise
+     return the index of the first unused slot. Grow the receiver,
+     if key was not found, and no unused slots where present"
+
+    |index  "{ Class:SmallInteger }"
+     length "{ Class:SmallInteger }"
+     startIndex
+     probe|
+
+    length := keyArray basicSize.
+    startIndex := key hash \\ length + 1.
+
+    index := startIndex.
+    [true] whileTrue:[
+        probe := keyArray basicAt:index.
+        (probe isNil or: [key = probe]) ifTrue:[^ index].
+
+        index == length ifTrue:[
+            index := 1
+        ] ifFalse:[
+            index := index + 1
+        ].
+        index == startIndex ifTrue:[^ self grow findKeyOrNil:key]
+    ]
+!
+
+findKey:key ifAbsent:aBlock 
+    "Look for the key in the receiver.  If it is found, return
+     the index of the association containing the key, otherwise
+     return the value of evaluating aBlock."
+
+    |index  "{ Class:SmallInteger }"
+     length "{ Class:SmallInteger }"
+     startIndex
+     probe|
+
+    length := keyArray basicSize.
+    length < 10 ifTrue:[
+        "assuming, that for small dictionaries the overhead of hashing
+         is large ... maybe that proves wrong (if overhead of comparing
+         is high)"
+        index := keyArray indexOf:key.
+        index == 0 ifTrue:[
+            ^ aBlock value
+        ].
+        ^ index
+    ].
+
+    startIndex := key hash \\ length + 1.
+
+    index := startIndex.
+    [true] whileTrue:[
+        probe := (keyArray basicAt:index).
+        key = probe ifTrue:[^ index].
+
+        index == length ifTrue:[
+            index := 1
+        ] ifFalse:[
+            index := index + 1
+        ].
+        ((probe isNil) or:[index == startIndex]) ifTrue:[
+            ^ aBlock value
+        ]
+    ]
+!
+
+grow
+    "change the number of element slots of the collection to a useful
+     new size"
+
+    self grow:(keyArray basicSize * 2)
+!
+
+grow:newSize
+    "grow the receiver to make space for at least newSize elements.
+     To do this, we have to rehash into the new arrays.
+     (which is done by re-adding all elements to a new, empty key/value array pair)."
+
+    |oldKeys oldValues
+     index "{ Class:SmallInteger }" |
+
+    oldKeys := keyArray.
+    oldValues := valueArray.
+
+    self setTally:newSize.
+
+    index := 1.
+    oldKeys do:[:aKey |
+        aKey notNil ifTrue:[
+            self at:aKey put:(oldValues basicAt:index)
+        ].
+        index := index + 1
+    ]
+!
+
+rehash
+    "rehash contents - is done by re-adding all elements to a new, empty key/value array pair)."
+
+    | oldKeyArray oldValueArray n key 
+      newIndex index |
+
+    oldKeyArray := keyArray.
+    oldValueArray := valueArray.
+
+    n := keyArray size.
+    valueArray := Array new:n.
+    keyArray := Array new:n.
+
+    index := 1.
+    oldKeyArray do:[:key |
+        key notNil ifTrue:[
+            newIndex := self findKeyOrNil:key.
+            keyArray basicAt:newIndex put:key.
+            valueArray basicAt:newIndex put:(oldValueArray basicAt:index).
+        ].
+	index := index + 1
+    ]
+!
+
+rehashFrom:startIndex
+    "rehash elements starting at index - after a remove"
+
+    |key i length
+     index "{ Class:SmallInteger }" |
+
+    length := keyArray basicSize.
+    index := startIndex.
+    key := keyArray basicAt:index.
+    [key notNil] whileTrue:[
+        i := self findKeyOrNil:key.
+        i == index ifTrue:[
+            ^ self
+        ].
+        keyArray basicAt:i put:key.
+        valueArray basicAt:i put:(valueArray basicAt:index).
+        keyArray basicAt:index put:nil.
+        valueArray basicAt:index put:nil.
+
+        index == length ifTrue:[
+            index := 1
+        ] ifFalse:[
+            index := index + 1.
+        ].
+        key := keyArray basicAt:index.
+    ]
+! !
+
+!Dictionary methodsFor:'printing & storing'!
+
+stringWith:aSelector
+    "common code for printString & displayString"
+
+    |thisString string noneYet|
+
+    string := (self class name) , '('.
+    noneYet := true.
+    self associationsDo:[:element |
+        thisString := element perform:aSelector.
+        noneYet ifTrue:[noneYet := false]
+               ifFalse:[thisString := ' ' , thisString].
+        string := string , thisString
+    ].
+    string := string , ')'.
+    ^string
+!
+
+printString
+    ^ self stringWith:#printString
+!
+
+displayString
+    ^ self stringWith:#displayString
+! !
+
+!Dictionary methodsFor:'inspecting'!
+
+inspect
+    "redefined to launch a DictionaryInspector on the receiver
+     (instead of the default InspectorView)."
+
+    DictionaryInspectorView isNil ifTrue:[
+        super inspect
+    ] ifFalse:[
+        DictionaryInspectorView openOn:self
+    ]
+! !
+
+!Dictionary methodsFor:'error handling'!
+
+errorKeyNotFound
+    "report an error that an element was not found in the collection"
+
+    self error:'the key is not in the collection'
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Dictionary.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,503 @@
+"
+ COPYRIGHT (c) 1991-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Collection subclass:#Dictionary
+         instanceVariableNames:'valueArray keyArray tally'
+         classVariableNames:''
+         poolDictionaries:''
+         category:'Collections-Unordered'
+!
+
+Dictionary comment:'
+
+COPYRIGHT (c) 1991-93 by Claus Gittinger
+              All Rights Reserved
+
+a Dictionary is (conceptionally) a collection of Associations storing key-value pairs.
+(The implementation uses two array to store the keys and values separately.)
+Searching for an element is done using a hash into the key arrlay.
+
+%W% %E%
+
+written jun 91 by claus
+rewritten 92 to use hash scheme
+'!
+
+!Dictionary class methodsFor:'instance creation'!
+
+new
+    "return a new empty Dictionary"
+
+    ^ self new:5
+!
+
+new:anInteger
+    "return a new empty Dictionary with space for anInteger elements"
+
+    ^ self basicNew setTally:anInteger
+! !
+
+!Dictionary methodsFor:'testing'!
+
+size
+    "return the number of elements in the receiver"
+
+    ^ tally
+!
+
+includesKey:aKey
+    "return true, if the argument, aKey is a key in the receiver"
+
+    ^ (self findKey:aKey ifAbsent:[0]) ~~ 0
+!
+
+isFixedSize
+    "return true if the receiver cannot grow - this will vanish once
+     Arrays and Strings learn how to grow ..."
+
+    ^ false
+! !
+
+!Dictionary methodsFor:'accessing'!
+
+at:aKey
+    "return the element indexed by aKey - report an error if none found"
+
+    |index|
+
+    aKey isNil ifTrue:[
+        self error:'nil is not allowed as key'
+    ] ifFalse:[
+        index := self findKey:aKey ifAbsent:[0].
+        index == 0 ifTrue:[^ self errorKeyNotFound].
+        ^ valueArray basicAt:index
+    ]
+!
+
+at:aKey ifAbsent:exceptionBlock
+    |index|
+
+    aKey isNil ifTrue:[
+        self error:'nil is not allowed as key'
+    ] ifFalse:[
+        index := self findKey:aKey ifAbsent:[0].
+        index == 0 ifTrue:[^ exceptionBlock value].
+        ^ valueArray basicAt:index
+    ]
+!
+
+at:aKey put:anObject
+    "add the argument anObject under key, aKey to the receiver"
+
+    |index element|
+
+    aKey isNil ifTrue:[
+        self error:'nil is not allowed as key'
+    ] ifFalse:[
+        index := self findKeyOrNil:aKey.
+        element := valueArray basicAt:index.
+        element notNil ifTrue:[
+            valueArray basicAt:index put:anObject.
+            ^ anObject
+        ].
+        keyArray basicAt:index put:aKey.
+        valueArray basicAt:index put:anObject.
+        tally := tally + 1.
+
+        "grow if filled more than 70% "
+        tally > (keyArray basicSize * 7 // 10) ifTrue:[
+            self grow
+        ].
+
+        ^ anObject
+    ]
+!
+
+keys
+    "return a collection containing all keys of the receiver"
+
+    ^ keyArray select:[:key | key notNil]
+! !
+
+!Dictionary methodsFor:'adding & removing'!
+
+add:anAssociation
+    "add the argument, anAssociation to the receiver"
+
+    self at:(anAssociation key) put:(anAssociation value).
+    ^ anAssociation
+!
+
+remove:oldObject ifAbsent:aBlock
+    "remove oldObject from the collection and return it.
+     If it was not in the collection return the value of aBlock."
+
+    self shouldNotImplement
+!
+
+removeAssociation:assoc
+    "remove the association from the collection.
+     If it was not in the collection report an error"
+
+    self removeKey:assoc key
+!
+
+removeKey:aKey
+    "remove the association under aKey from the collection.
+     If it was not in the collection report an error"
+
+    |index 
+     next  "{ Class:SmallInteger }" |
+
+    aKey isNil ifTrue:[
+        self error:'nil is not allowed as key'
+    ] ifFalse:[
+        index := self findKey:aKey ifAbsent:[0].
+        (index == 0) ifTrue:[^ self errorNotFound].
+        valueArray basicAt:index put:nil.
+        keyArray basicAt:index put:nil.
+        tally := tally - 1.
+        tally == 0 ifTrue:[
+            self setTally:0
+        ] ifFalse:[
+            index == keyArray basicSize ifTrue:[
+                next := 1
+            ] ifFalse:[
+                next := index + 1.
+            ].
+            "redundant check to save a send sometimes"
+            (keyArray basicAt:next) notNil ifTrue:[
+                self rehashFrom:next.
+            ]
+        ]
+    ]
+!
+
+removeKey:aKey ifAbsent:aBlock
+    "remove the association under aKey from the collection.
+     If it was not in the collection return result from evaluating aBlock"
+
+    |index 
+     next  "{ Class:SmallInteger }" |
+
+    aKey isNil ifTrue:[
+        self error:'nil is not allowed as key'
+    ] ifFalse:[
+        index := self findKey:aKey ifAbsent:[0].
+        index == 0 ifTrue:[^ aBlock value].
+        valueArray basicAt:index put:nil.
+        keyArray basicAt:index put:nil.
+        tally := tally - 1.
+        tally == 0 ifTrue:[
+            self setTally:0
+        ] ifFalse:[
+            index == keyArray basicSize ifTrue:[
+                next := 1
+            ] ifFalse:[
+                next := index + 1.
+            ].
+            "redundant check to save a send sometimes"
+            (keyArray basicAt:next) notNil ifTrue:[
+                self rehashFrom:next.
+            ]
+        ]
+    ]
+! !
+
+!Dictionary methodsFor:'enumeration'!
+
+allKeysDo:aBlock
+    "perform the block for all keys in the collection."
+
+    keyArray nonNilElementsDo:aBlock
+!
+
+associationsDo:aBlock
+    "perform the block for all associations in the collection."
+
+    |key|
+
+    tally == 0 ifTrue:[^ self].
+    1 to:(keyArray basicSize) do:[:index |
+        key := keyArray basicAt:index.
+        key notNil ifTrue:[
+            aBlock value:(Association key:key value:(valueArray basicAt:index))
+        ]
+    ]
+!
+
+do:aBlock
+    "perform the block for all values in the collection."
+
+    |index "{ Class:SmallInteger }" |
+
+    tally == 0 ifTrue:[^ self].
+    index := 1.
+    keyArray do:[:key |
+        key notNil ifTrue:[
+            aBlock value:(valueArray basicAt:index)
+        ].
+        index := index + 1
+    ]
+!
+
+collect:aBlock
+    "for each element in the receiver, evaluate the argument, aBlock
+     and return a Bag with the results"
+
+    |newCollection|
+
+    newCollection := Bag new.
+    self do:[:each |
+        newCollection add:each
+    ].
+    ^ newCollection
+!
+
+select:aBlock
+    "return a new collection with all elements from the receiver, for which
+     the argument aBlock evaluates to true"
+
+    |newCollection|
+
+    newCollection := self species new.
+    self associationsDo:[:assoc |
+        (aBlock value:(assoc value)) ifTrue:[
+            newCollection add:assoc
+        ]
+    ].
+    ^ newCollection
+! !
+
+!Dictionary methodsFor:'private'!
+
+goodSizeFor:arg
+    "return a good array size for the given argument.
+     Returns the next prime after arg"
+
+    arg <= 7 ifTrue:[^ 7].
+    arg <= 16384 ifTrue:[
+           "2 4 8  16 32 64 128 256 512 1024 2048 4096 8192 16384"
+        ^ #(7 7 11 17 37 67 131 257 521 1031 2053 4099 8209 16411) at:(arg highBit)
+    ].
+    ^ arg bitOr:1
+!
+
+setTally:count
+    "initialize the contents array (for at least count slots)
+     and set tally to zero.
+     The size is increased to the next prime for better hashing behavior."
+
+    |n|
+
+    n := self goodSizeFor:count.
+    valueArray := Array new:n.
+    keyArray := Array new:n.
+    tally := 0
+!
+
+findKeyOrNil:key  
+    "Look for the key in the receiver.  If it is found, return
+     the index of the association containing the key, otherwise
+     return the index of the first unused slot. Grow the receiver,
+     if key was not found, and no unused slots where present"
+
+    |index  "{ Class:SmallInteger }"
+     length "{ Class:SmallInteger }"
+     startIndex
+     probe|
+
+    length := keyArray basicSize.
+    startIndex := key hash \\ length + 1.
+
+    index := startIndex.
+    [true] whileTrue:[
+        probe := keyArray basicAt:index.
+        (probe isNil or: [key = probe]) ifTrue:[^ index].
+
+        index == length ifTrue:[
+            index := 1
+        ] ifFalse:[
+            index := index + 1
+        ].
+        index == startIndex ifTrue:[^ self grow findKeyOrNil:key]
+    ]
+!
+
+findKey:key ifAbsent:aBlock 
+    "Look for the key in the receiver.  If it is found, return
+     the index of the association containing the key, otherwise
+     return the value of evaluating aBlock."
+
+    |index  "{ Class:SmallInteger }"
+     length "{ Class:SmallInteger }"
+     startIndex
+     probe|
+
+    length := keyArray basicSize.
+    length < 10 ifTrue:[
+        "assuming, that for small dictionaries the overhead of hashing
+         is large ... maybe that proves wrong (if overhead of comparing
+         is high)"
+        index := keyArray indexOf:key.
+        index == 0 ifTrue:[
+            ^ aBlock value
+        ].
+        ^ index
+    ].
+
+    startIndex := key hash \\ length + 1.
+
+    index := startIndex.
+    [true] whileTrue:[
+        probe := (keyArray basicAt:index).
+        key = probe ifTrue:[^ index].
+
+        index == length ifTrue:[
+            index := 1
+        ] ifFalse:[
+            index := index + 1
+        ].
+        ((probe isNil) or:[index == startIndex]) ifTrue:[
+            ^ aBlock value
+        ]
+    ]
+!
+
+grow
+    "change the number of element slots of the collection to a useful
+     new size"
+
+    self grow:(keyArray basicSize * 2)
+!
+
+grow:newSize
+    "grow the receiver to make space for at least newSize elements.
+     To do this, we have to rehash into the new arrays.
+     (which is done by re-adding all elements to a new, empty key/value array pair)."
+
+    |oldKeys oldValues
+     index "{ Class:SmallInteger }" |
+
+    oldKeys := keyArray.
+    oldValues := valueArray.
+
+    self setTally:newSize.
+
+    index := 1.
+    oldKeys do:[:aKey |
+        aKey notNil ifTrue:[
+            self at:aKey put:(oldValues basicAt:index)
+        ].
+        index := index + 1
+    ]
+!
+
+rehash
+    "rehash contents - is done by re-adding all elements to a new, empty key/value array pair)."
+
+    | oldKeyArray oldValueArray n key 
+      newIndex index |
+
+    oldKeyArray := keyArray.
+    oldValueArray := valueArray.
+
+    n := keyArray size.
+    valueArray := Array new:n.
+    keyArray := Array new:n.
+
+    index := 1.
+    oldKeyArray do:[:key |
+        key notNil ifTrue:[
+            newIndex := self findKeyOrNil:key.
+            keyArray basicAt:newIndex put:key.
+            valueArray basicAt:newIndex put:(oldValueArray basicAt:index).
+        ].
+	index := index + 1
+    ]
+!
+
+rehashFrom:startIndex
+    "rehash elements starting at index - after a remove"
+
+    |key i length
+     index "{ Class:SmallInteger }" |
+
+    length := keyArray basicSize.
+    index := startIndex.
+    key := keyArray basicAt:index.
+    [key notNil] whileTrue:[
+        i := self findKeyOrNil:key.
+        i == index ifTrue:[
+            ^ self
+        ].
+        keyArray basicAt:i put:key.
+        valueArray basicAt:i put:(valueArray basicAt:index).
+        keyArray basicAt:index put:nil.
+        valueArray basicAt:index put:nil.
+
+        index == length ifTrue:[
+            index := 1
+        ] ifFalse:[
+            index := index + 1.
+        ].
+        key := keyArray basicAt:index.
+    ]
+! !
+
+!Dictionary methodsFor:'printing & storing'!
+
+stringWith:aSelector
+    "common code for printString & displayString"
+
+    |thisString string noneYet|
+
+    string := (self class name) , '('.
+    noneYet := true.
+    self associationsDo:[:element |
+        thisString := element perform:aSelector.
+        noneYet ifTrue:[noneYet := false]
+               ifFalse:[thisString := ' ' , thisString].
+        string := string , thisString
+    ].
+    string := string , ')'.
+    ^string
+!
+
+printString
+    ^ self stringWith:#printString
+!
+
+displayString
+    ^ self stringWith:#displayString
+! !
+
+!Dictionary methodsFor:'inspecting'!
+
+inspect
+    "redefined to launch a DictionaryInspector on the receiver
+     (instead of the default InspectorView)."
+
+    DictionaryInspectorView isNil ifTrue:[
+        super inspect
+    ] ifFalse:[
+        DictionaryInspectorView openOn:self
+    ]
+! !
+
+!Dictionary methodsFor:'error handling'!
+
+errorKeyNotFound
+    "report an error that an element was not found in the collection"
+
+    self error:'the key is not in the collection'
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DirStr.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,169 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+FileStream subclass:#DirectoryStream
+       instanceVariableNames:'dirPointer readAhead'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Streams-External'
+!
+
+DirectoryStream comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+%W% %E%
+'!
+
+%{
+#include <stdio.h>
+#ifndef transputer
+# include <sys/types.h>
+# include <sys/stat.h>
+# ifdef OPENDIR
+#  include <sys/types.h>
+#  ifdef NEXT
+#   include <sys/dir.h>
+#  else
+#   include <dirent.h>
+#  endif
+# endif
+#endif
+%}
+
+!DirectoryStream methodsFor:'instance release'!
+
+closeFile
+    "a directoryStream has been collected - close it"
+%{
+#ifdef OPENDIR
+    closedir((DIR *)MKFD(_INST(dirPointer)));
+#endif
+%}
+! !
+
+!DirectoryStream class methodsFor:'instance creation'!
+
+directoryNamed:dirName
+    "return a DirectoryStream for directory named dirName, aString"
+
+    |newStream|
+
+    newStream := (self basicNew) pathName:dirName.
+    newStream openForReading isNil ifTrue:[^nil].
+    ^ newStream
+! !
+
+!DirectoryStream methodsFor:'access reading'!
+
+nextLine
+    "return the next filename as a string"
+
+    |prevEntry nextEntry|
+%{
+#ifdef OPENDIR
+    DIR *d;
+#ifdef NEXT
+    struct direct *dp;
+#else
+    struct dirent *dp;
+#endif
+
+    if (_INST(dirPointer) != nil) {
+        d = (DIR *)MKFD(_INST(dirPointer));
+        dp = readdir(d);
+        if (dp != NULL) {
+            nextEntry = _MKSTRING((char *)(dp->d_name) COMMA_CON);
+        }
+    }
+#endif
+%}
+.
+    prevEntry := readAhead.
+    readAhead := nextEntry.
+    ^ prevEntry
+! !
+
+!DirectoryStream methodsFor:'private'!
+
+openForReading
+    "open the file for readonly"
+
+    |retVal|
+
+    mode := #readonly.
+%{
+#ifdef OPENDIR
+    DIR *d;
+    OBJ path;
+    extern OBJ ErrorNumber, ErrorString;
+    extern errno;
+
+    retVal = false;
+    if (_INST(dirPointer) == nil) {
+        path = _INST(pathName);
+        if (_isString(path)) {
+            d = opendir((char *) _stringVal(path));
+            if (d == NULL) {
+                /* ErrorString = _MKSTRING(perror("popen:") COMMA_CON); */
+                ErrorNumber = _MKSMALLINT(errno);
+            } else {
+                _INST(dirPointer) = MKOBJ(d);
+                retVal = true;
+            }
+        }
+    }
+#endif
+%}
+.
+    retVal isNil ifTrue:[
+        "opendir not avalable - use slow pipe"
+        ^ PipeStream readingFrom:('cd ' , pathName , '; ls -a')
+    ].
+    (retVal == true) ifTrue:[
+        lobby register:self.
+        self nextLine. "read 1st entry into readAhead buffer"
+        ^ self
+    ].
+    ^ nil
+!
+
+reOpen
+    "sent after snapin to reopen streams"
+
+    filePointer notNil ifTrue:[
+        "it was open, when snapped-out"
+        filePointer := nil.
+        'cannot reopen directorystream' printNewline
+    ]
+! !
+
+!DirectoryStream methodsFor:'testing'!
+
+atEnd
+    "return true, if position is at end"
+
+    ^ readAhead == nil
+! !
+
+!DirectoryStream methodsFor:'closing'!
+
+close
+    "close the stream - tell operating system"
+
+    dirPointer notNil ifTrue:[
+        lobby unregister:self.
+        self closeFile.
+        dirPointer := nil
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DirectoryStream.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,169 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+FileStream subclass:#DirectoryStream
+       instanceVariableNames:'dirPointer readAhead'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Streams-External'
+!
+
+DirectoryStream comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+%W% %E%
+'!
+
+%{
+#include <stdio.h>
+#ifndef transputer
+# include <sys/types.h>
+# include <sys/stat.h>
+# ifdef OPENDIR
+#  include <sys/types.h>
+#  ifdef NEXT
+#   include <sys/dir.h>
+#  else
+#   include <dirent.h>
+#  endif
+# endif
+#endif
+%}
+
+!DirectoryStream methodsFor:'instance release'!
+
+closeFile
+    "a directoryStream has been collected - close it"
+%{
+#ifdef OPENDIR
+    closedir((DIR *)MKFD(_INST(dirPointer)));
+#endif
+%}
+! !
+
+!DirectoryStream class methodsFor:'instance creation'!
+
+directoryNamed:dirName
+    "return a DirectoryStream for directory named dirName, aString"
+
+    |newStream|
+
+    newStream := (self basicNew) pathName:dirName.
+    newStream openForReading isNil ifTrue:[^nil].
+    ^ newStream
+! !
+
+!DirectoryStream methodsFor:'access reading'!
+
+nextLine
+    "return the next filename as a string"
+
+    |prevEntry nextEntry|
+%{
+#ifdef OPENDIR
+    DIR *d;
+#ifdef NEXT
+    struct direct *dp;
+#else
+    struct dirent *dp;
+#endif
+
+    if (_INST(dirPointer) != nil) {
+        d = (DIR *)MKFD(_INST(dirPointer));
+        dp = readdir(d);
+        if (dp != NULL) {
+            nextEntry = _MKSTRING((char *)(dp->d_name) COMMA_CON);
+        }
+    }
+#endif
+%}
+.
+    prevEntry := readAhead.
+    readAhead := nextEntry.
+    ^ prevEntry
+! !
+
+!DirectoryStream methodsFor:'private'!
+
+openForReading
+    "open the file for readonly"
+
+    |retVal|
+
+    mode := #readonly.
+%{
+#ifdef OPENDIR
+    DIR *d;
+    OBJ path;
+    extern OBJ ErrorNumber, ErrorString;
+    extern errno;
+
+    retVal = false;
+    if (_INST(dirPointer) == nil) {
+        path = _INST(pathName);
+        if (_isString(path)) {
+            d = opendir((char *) _stringVal(path));
+            if (d == NULL) {
+                /* ErrorString = _MKSTRING(perror("popen:") COMMA_CON); */
+                ErrorNumber = _MKSMALLINT(errno);
+            } else {
+                _INST(dirPointer) = MKOBJ(d);
+                retVal = true;
+            }
+        }
+    }
+#endif
+%}
+.
+    retVal isNil ifTrue:[
+        "opendir not avalable - use slow pipe"
+        ^ PipeStream readingFrom:('cd ' , pathName , '; ls -a')
+    ].
+    (retVal == true) ifTrue:[
+        lobby register:self.
+        self nextLine. "read 1st entry into readAhead buffer"
+        ^ self
+    ].
+    ^ nil
+!
+
+reOpen
+    "sent after snapin to reopen streams"
+
+    filePointer notNil ifTrue:[
+        "it was open, when snapped-out"
+        filePointer := nil.
+        'cannot reopen directorystream' printNewline
+    ]
+! !
+
+!DirectoryStream methodsFor:'testing'!
+
+atEnd
+    "return true, if position is at end"
+
+    ^ readAhead == nil
+! !
+
+!DirectoryStream methodsFor:'closing'!
+
+close
+    "close the stream - tell operating system"
+
+    dirPointer notNil ifTrue:[
+        lobby unregister:self.
+        self closeFile.
+        dirPointer := nil
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DoubleArray.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,49 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+	      All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+ArrayedCollection variableDoubleSubclass:#DoubleArray
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Indexed'
+!
+
+DoubleArray comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+	      All Rights Reserved
+
+DoubleArrays store double floats (and nothing else).
+They have been added to support heavy duty number crunching somewhat
+more than other smalltalks do. Storing Floats & Doubles in these
+objects many benefits:
+
+1) since the values are stored directly (instead of pointers to them)
+   both access overhead and garbage collect overhead is minimized.
+
+2) they can be much faster passed to c functions (such as graphics 
+libraries or heavy duty math packages)
+
+3) they could (in theory) be much more easily be processed by things like
+vector and array processors
+
+Be aware however, that Float- and DoubleArrays are not supported in other
+smalltalks - your program will thus become somewhat less portable.
+(since their protocol is the same as normal arrays filled with floats,
+ they can of course be easily simulated - a bit slower)
+
+See an example use in the GLX interface.
+
+%W% %E%
+
+written june 93 by claus
+'!
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Exception.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,141 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Exception
+         instanceVariableNames:'signal parameter suspendedContext handlerContext
+                                resumeBlock rejectBlock'
+         classVariableNames:''
+         poolDictionaries:''
+         category:'Kernel-Exceptions'
+!
+
+Exception comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+Instances of Exception are passed to a Signal handling block as argument.
+The handler block may perform various actions by sending corresponding messages
+to the exception handler. The following actions are possible:
+
+        reject          - dont handle this signal;
+                          another handler will be searched for, 
+                          upper in the calling hierarchy
+
+        proceed         - continue after the Signal>>raise, returning nil as value
+        proceedWith:val - same, but return val from Signal>>raise
+
+        return          - return from the Signal>>handle:do:, returning nil as value
+        return:val      - same, but return val from Signal>>handle:do:
+
+        restart         - restart the Signal>>handle:do:, after repairing
+
+Via the Exception object, the handler can also query the state of execution,
+where the Signal was raised.
+'!
+
+!Exception methodsFor:'accessing'!
+
+signal
+    "return the signal, that caused the exception"
+
+    ^ signal
+!
+
+parameter:aParameter
+    parameter := aParameter
+!
+
+parameter
+    ^ parameter
+!
+
+suspendedContext
+! !
+
+!Exception methodsFor:'setup'!
+
+signal:aSignal
+    "this is meant to be sent by Signal only"
+
+    signal := aSignal
+!
+
+handlerContext:aContext
+    "this is meant to be sent by Signal only"
+
+    handlerContext := aContext
+!
+
+rejectBlock:aBlock
+    "this is meant to be sent by Signal only"
+
+    rejectBlock := aBlock
+!
+
+resumeBlock:aBlock
+    "this is meant to be sent by Signal only"
+
+    resumeBlock := aBlock
+! !
+
+!Exception methodsFor:'handler actions'!
+
+reject
+    "handler decided not to handle this signal -
+     system will look for another handler"
+
+    rejectBlock value
+!
+
+resume
+    "Continue after the raise - the raise returns nil"
+
+    resumeBlock value:nil
+!
+
+resumeWith:value
+    "Continue after the raise - the raise returns value"
+
+    resumeBlock value:value
+!
+
+proceed
+    "Continue after the raise - the raise returns nil"
+
+    resumeBlock value:nil
+!
+
+proceedWith:value
+    "Continue after the raise - the raise returns value"
+
+    resumeBlock value:value
+!
+
+return
+    "Continue after the handle:do: - the handle:do: returns nil"
+
+    handlerContext resume
+!
+
+returnWith:value
+    "Continue after the handle:do: - the handle:do: returns value"
+
+    handlerContext resume:value
+!
+
+restart
+    "restart the handle:do: - usually after some repair work is done
+     in handler"
+
+    handlerContext restart
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ExtStream.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,1437 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+ReadWriteStream subclass:#ExternalStream
+       instanceVariableNames:'filePointer mode unBuffered binary'
+       classVariableNames:'lobby'
+       poolDictionaries:''
+       category:'Streams-External'
+!
+
+ExternalStream comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ExternalStream defines protocol common to Streams which have a file-descriptor and 
+represent some File or CommunicationChannel of the underlying OperatingSystem.
+ExternalStream is abstract; concrete classes are FileStream, PipeStream etc.
+ExternalStreams can be in two modes: text (the default) and binary.
+In text-mode, the elements read/written are characters; while in binary-mode the basic
+elements are bytes which read/write as SmallIntegers in the range 0..255.
+
+%W% %E%
+
+written 88 by claus
+'!
+
+%{
+#include <stdio.h>
+%}
+
+!ExternalStream class methodsFor:'initialization'!
+
+initialize
+    lobby isNil ifTrue:[
+        lobby := Registry new.
+
+        "want to get informed when returning from snapshot"
+        ObjectMemory addDependent:self
+    ]
+!
+
+reOpenFiles
+    "reopen all files (if possible) after a snapShot load"
+
+    lobby contentsDo:[:aFileStream |
+        aFileStream reOpen
+    ]
+!
+
+update:something
+    "have to reopen files when returning from snapshot"
+
+    something == #returnFromSnapshot ifTrue:[
+        self reOpenFiles
+    ]
+! !
+
+!ExternalStream methodsFor:'instance release'!
+
+disposed
+    "some Stream has been collected - close the file if not already done"
+
+    self closeFile
+!
+
+closeFile
+    "low level close - may be redefined in subclasses"
+
+%{  /* NOCONTEXT */
+
+    fclose(MKFD(_INST(filePointer)));
+%}
+! !
+
+!ExternalStream methodsFor:'private'!
+
+reOpen
+    "sent after snapin to reopen streams.
+     cannot reopen here since I am abstract and have no device knowledge"
+
+    self class name print. ': cannot reOpen stream - stream closed' printNewline.
+    filePointer := nil.
+    lobby unregister:self.
+! !
+
+!ExternalStream methodsFor:'error handling'!
+
+errorNotOpen
+    "report an error, that the stream has not been opened"
+
+    ^ self error:(self class name , ' not open')
+!
+
+errorReadOnly
+    "report an error, that the stream is a readOnly stream"
+
+    ^ self error:(self class name , ' is readonly')
+!
+
+errorWriteOnly
+    "report an error, that the stream is a writeOnly stream"
+
+    ^ self error:(self class name , ' is writeonly')
+!
+
+errorNotBinary
+    "report an error, that the stream is not in binary mode"
+
+    ^ self error:(self class name , ' is not in binary mode')
+!
+
+argumentMustBeInteger
+    "report an error, that the argument must be an integer"
+
+    ^ self error:'argument must be an integer'
+!
+
+argumentMustBeCharacter
+    "report an error, that the argument must be a character"
+
+    ^ self error:'argument must be a character'
+!
+
+argumentMustBeString
+    "report an error, that the argument must be a string"
+
+    ^ self error:'argument must be a string'
+! !
+
+!ExternalStream methodsFor:'accessing'!
+
+readonly
+    "set access mode to readonly"
+
+    mode := #readonly
+!
+
+writeonly
+    "set access mode to writeonly"
+
+    mode := #writeonly
+!
+
+readwrite
+    "set access mode to readwrite"
+
+    mode := #readwrite
+!
+
+filePointer
+    "return the filePointer of the receiver -
+     notice: for portability stdio is used; this means you will get
+     a FILE * - not a fileDescriptor"
+
+    ^ filePointer
+!
+
+fileDescriptor
+    "return the fileDescriptor of the receiver -
+     notice: this one returns the underlying OSs fileDescriptor -
+     this may not be available on all platforms (i.e. non unix systems)."
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+
+    if (_INST(filePointer) != nil) {
+        f = MKFD(_INST(filePointer));
+        RETURN ( MKOBJ(fileno(f)) );
+    }
+%}
+.
+    ^ self errorNotOpen
+!
+
+buffered:aBoolean
+    "turn buffering on or off"
+
+    unBuffered := aBoolean not
+!
+
+binary
+    "switch to binary mode"
+
+    binary := true
+!
+
+text
+    "switch to text mode"
+
+    binary := false
+!
+
+contents
+    "return the contents of the file as a Text-object"
+
+    |text|
+
+    text := Text new.
+    [self atEnd] whileFalse:[
+        text add:(self nextLine)
+    ].
+    ^ text
+! !
+
+!ExternalStream methodsFor:'basic'!
+
+open
+    "open the stream
+     - this must be redefined in subclass"
+
+    ^ self subclassResponsibility
+!
+
+create
+    "create the stream
+     - this must be redefined in subclass"
+
+    ^ self subclassResponsibility
+!
+
+position
+    "return the position
+     - this must be redefined in subclass"
+
+    ^ self subclassResponsibility
+!
+
+position:anInteger
+    "set the position
+     - this must be redefined in subclass"
+
+    ^ self subclassResponsibility
+! !
+
+!ExternalStream methodsFor:'low level I/O'!
+
+ioctl:ioctlNumber with:arg
+    "to provide a simple ioctl facility"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    int ret, ioNum, ioArg;
+    extern OBJ ErrorNumber;
+    extern errno;
+
+    if (_INST(filePointer) != nil) {
+        if (_isSmallInteger(ioctlNumber) && _isSmallInteger(arg)) {
+            ioNum = _intVal(ioctlNumber);
+            ioArg = _intVal(arg);
+            f = MKFD(_INST(filePointer));
+            ret = ioctl(fileno(f), ioNum, ioArg);
+            if (ret >= 0) {
+                RETURN ( _MKSMALLINT(ret) );
+            }
+            ErrorNumber = _MKSMALLINT(errno);
+            RETURN ( nil );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    self primitiveFailed
+!
+
+nextByte
+    "read the next byte return it as an Integer
+     nil on error.  Use with care - non object oriented i/o"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    unsigned char byte;
+    int cnt;
+    extern OBJ ErrorNumber;
+    extern errno;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _writeonly) {
+            f = MKFD(_INST(filePointer));
+            _immediateInterrupt = 1;
+            if (_INST(unBuffered) == true) {
+                cnt = read(fileno(f), &byte, 1);
+            } else {
+		if (_INST(mode) == _readwrite)
+		    fseek(f, 0L, 1); /* needed in stdio */
+                cnt = fread(&byte, 1, 1, f);
+            }
+            _immediateInterrupt = 0;
+            if (cnt == 1) {
+                if (_INST(position) != nil)
+                    _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 1);
+                RETURN ( _MKSMALLINT(byte) );
+            }
+            if (cnt < 0) {
+                ErrorNumber = _MKSMALLINT(errno);
+            }
+            RETURN ( nil );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    self errorWriteOnly
+!
+
+nextBytes:count into:anObject
+    "read the next count bytes into an object and return the number of
+     bytes read or nil on error.
+     Use with care - non object oriented i/o"
+
+    ^ self nextBytes:count into:anObject startingAt:1
+!
+
+nextBytes:count into:anObject startingAt:start
+    "read the next count bytes into an object and return the number of
+     bytes read or nil on error.
+     Use with care - non object oriented i/o"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    int cnt, offs;
+    int objSize;
+    char *cp;
+    extern OBJ ErrorNumber;
+    extern errno;
+    OBJ pos;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _writeonly) {
+            if (_isSmallInteger(count) && _isSmallInteger(start)) {
+                cnt = _intVal(count);
+                offs = _intVal(start) - 1;
+                f = MKFD(_INST(filePointer));
+                objSize = _Size(anObject) - OHDR_SIZE;
+                if ((offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs))) {
+                    cp = (char *)_InstPtr(anObject) + OHDR_SIZE + offs;
+                    _immediateInterrupt = 1;
+                    if (_INST(unBuffered) == true) {
+                        cnt = read(fileno(f), cp, cnt);
+                    } else {
+		        if (_INST(mode) == _readwrite)
+		            fseek(f, 0L, 1); /* needed in stdio */
+                        cnt = fread(cp, 1, cnt, f);
+                    }
+                    _immediateInterrupt = 0;
+                    if (cnt >= 0) {
+                        pos = _INST(position);
+                        if (pos != nil)
+                            _INST(position) = _MKSMALLINT(_intVal(pos) + cnt);
+                        RETURN ( _MKSMALLINT(cnt) );
+                    }
+                    ErrorNumber = _MKSMALLINT(errno);
+                    RETURN ( nil );
+                }
+            }
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+    self primitiveFailed
+!
+
+nextPutByte:aByteValue
+    "write a byte"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    char c;
+    extern OBJ ErrorNumber;
+    extern errno;
+    OBJ pos;
+    int cnt;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _readonly) {
+            if (_isSmallInteger(aByteValue)) {
+                c = _intVal(aByteValue);
+                f = MKFD(_INST(filePointer));
+                _immediateInterrupt = 1;
+		if (_INST(mode) == _readwrite)
+		    fseek(f, 0L, 1); /* needed in stdio */
+                cnt = fwrite(&c, 1, 1, f);
+                _immediateInterrupt = 0;
+                if (cnt == 1) {
+                    if (_INST(unBuffered) == true) {
+                        fflush(f);
+                    }
+                    pos = _INST(position);
+                    if (pos != nil)
+                        _INST(position) = _MKSMALLINT(_intVal(pos) + 1);
+                    RETURN ( self );
+                }
+                ErrorNumber = _MKSMALLINT(errno);
+            }
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    (mode == #readonly) ifTrue:[^ self errorReadOnly].
+    self primitiveFailed
+!
+
+nextPutBytes:count from:anObject
+    "write count bytes from an object starting at index start.
+     return the number of bytes written or nil on error.
+     Use with care - non object oriented i/o"
+
+    ^ self nextPutBytes:count from:anObject startingAt:1
+!
+
+nextPutBytes:count from:anObject startingAt:start
+    "write count bytes from an object starting at index start.
+     return the number of bytes written or nil on error.
+     Use with care - non object oriented i/o"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    int cnt, offs;
+    int objSize;
+    char *cp;
+    extern OBJ ErrorNumber;
+    extern errno;
+    OBJ pos;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _readonly) {
+            if (_isSmallInteger(count) && _isSmallInteger(start)) {
+                cnt = _intVal(count);
+                offs = _intVal(start) - 1;
+                f = MKFD(_INST(filePointer));
+
+                objSize = _Size(anObject) - OHDR_SIZE;
+                if ( (offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs)) ) {
+                    cp = (char *)_InstPtr(anObject) + OHDR_SIZE + offs;
+                    _immediateInterrupt = 1;
+                    if (_INST(unBuffered) == true) {
+                        cnt = write(fileno(f), cp, cnt);
+                    } else {
+		        if (_INST(mode) == _readwrite)
+		            fseek(f, 0L, 1); /* needed in stdio */
+                        cnt = fwrite(cp, 1, cnt, f);
+                    }
+                    _immediateInterrupt = 0;
+                    if (cnt >= 0) {
+                        pos = _INST(position);
+                        if (pos != nil)
+                            _INST(position) = _MKSMALLINT(_intVal(pos) + cnt);
+                        RETURN ( _MKSMALLINT(cnt) );
+                    }
+                    ErrorNumber = _MKSMALLINT(errno);
+                    RETURN ( nil );
+                }
+            }
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    (mode == #readonly) ifTrue:[^ self errorReadOnly].
+    self primitiveFailed
+! !
+
+!ExternalStream methodsFor:'character I/O'!
+
+peek
+    "return the character to be read next without advancing read position"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    REGISTER int c;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _writeonly) {
+            f = MKFD(_INST(filePointer));
+            c = getc(f);
+            if (c != EOF) {
+                ungetc(c, f);
+                if (_INST(binary) == true) {
+                    RETURN ( _MKSMALLINT(c & 0xFF) );
+                }
+                RETURN ( _MKCHARACTER(c & 0xFF) );
+            }
+            RETURN ( nil );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    self errorWriteOnly
+!
+
+next
+    "return the character; advance read position"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    int c;
+    OBJ pos;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _writeonly) {
+            f = MKFD(_INST(filePointer));
+            _immediateInterrupt = 1;
+            if (_INST(unBuffered) == true) {
+                if (read(fileno(f), &c, 1) != 1)
+                    c = EOF;
+            } else {
+		if (_INST(mode) == _readwrite)
+		    fseek(f, 0L, 1); /* needed in stdio */
+                c = getc(f);
+            }
+            _immediateInterrupt = 0;
+            if (c != EOF) {
+                pos = _INST(position);
+                if (pos != nil) {
+                    _INST(position) = _MKSMALLINT(_intVal(pos) + 1);
+                }
+                if (_INST(binary) == true) {
+                    RETURN ( _MKSMALLINT(c & 0xFF) );
+                }
+                RETURN ( _MKCHARACTER(c & 0xFF) );
+            }
+            RETURN ( nil );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    self errorWriteOnly
+!
+
+nextPut:aCharacter
+    "write the argument, aCharacter - return nil if failed, self if ok"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    char c;
+    extern OBJ ErrorNumber;
+    extern errno;
+    int cnt;
+    OBJ pos;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _readonly) {
+            if (_isCharacter(aCharacter)) {
+                c = _intVal(_CharacterInstPtr(aCharacter)->c_asciivalue);
+    doWrite:
+                f = MKFD(_INST(filePointer));
+
+                if (_INST(unBuffered) == true) {
+		    cnt = write(fileno(f), &c, 1);
+		} else { 
+		    if (_INST(mode) == _readwrite)
+		        fseek(f, 0L, 1); /* needed in stdio */
+                    cnt = fwrite(&c, 1, 1, f);
+		}
+                if (cnt == 1) {
+                    if (_INST(unBuffered) == true) {
+                        fflush(f);
+                    }
+                    pos = _INST(position);
+                    if (pos != nil) {
+                        _INST(position) = _MKSMALLINT(_intVal(pos) + 1);
+                    }
+                    RETURN ( self );
+                }
+                ErrorNumber = _MKSMALLINT(errno);
+                RETURN ( nil );
+            } else {
+                if (_INST(binary) == true) {
+                    if (_isSmallInteger(aCharacter)) {
+                        c = _intVal(aCharacter);
+                        goto doWrite;
+                    }
+                }
+            }
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    (mode == #readonly) ifTrue:[^ self errorReadOnly].
+    self argumentMustBeCharacter
+!
+
+nextPutAll:aCollection
+    "write all elements of the argument, aCollection"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    unsigned char *cp;
+    int len, cnt;
+    extern OBJ ErrorNumber;
+    extern errno;
+    OBJ pos;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _readonly) {
+            if (_isString(aCollection) || _isSymbol(aCollection)) {
+                cp = _stringVal(aCollection);
+                len = _stringSize(aCollection);
+                f = MKFD(_INST(filePointer));
+
+                if (_INST(unBuffered) == true) {
+		    cnt = write(fileno(f), cp, len);
+		} else { 
+		    if (_INST(mode) == _readwrite)
+		        fseek(f, 0L, 1); /* needed in stdio */
+                    cnt = fwrite(cp, 1, len, f);
+		}
+                if (cnt == len) {
+                    if (_INST(unBuffered) == true) {
+                        fflush(f);
+                    }
+                    pos = _INST(position);
+                    if (pos != nil) {
+                        _INST(position) = _MKSMALLINT(_intVal(pos) + len);
+                    }
+                    RETURN ( self );
+                }
+                ErrorNumber = _MKSMALLINT(errno);
+                RETURN ( nil );
+            }
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    (mode == #readonly) ifTrue:[^ self errorReadOnly].
+
+    aCollection do:[:element |
+        self nextPut:element
+    ]
+!
+
+nextPut:aCollection from:start to:stop
+    "write a range of elements of the argument, aCollection"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    unsigned char *cp;
+    int len, cnt, index1, index2;
+    extern OBJ ErrorNumber;
+    extern errno;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_isString(aCollection)
+         && _isSmallInteger(start)
+         && _isSmallInteger(stop)) {
+            f = MKFD(_INST(filePointer));
+            cp = _stringVal(aCollection);
+            len = _stringSize(aCollection);
+            index1 = _intVal(start);
+            index2 = _intVal(stop);
+            if ((index1 < 1) || (index2 > len) || (index2 < index1)) {
+                RETURN ( self );
+            }
+            if (index2 > len)
+                index2 = len;
+
+            len = index2 - index1 + 1;
+            if (_INST(unBuffered) == true) {
+	        cnt = write(fileno(f), cp + index1 - 1, len);
+	    } else { 
+		if (_INST(mode) == _readwrite)
+		    fseek(f, 0L, 1); /* needed in stdio */
+                cnt = fwrite(cp + index1 - 1, 1, len, f);
+	    }
+            if (cnt == len) {
+                if (_INST(unBuffered) == true) {
+                    fflush(f);
+                }
+                if (_INST(position) != nil) {
+                    _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + len);
+                }
+                RETURN ( self );
+            }
+            ErrorNumber = _MKSMALLINT(errno);
+            RETURN ( nil );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    (mode == #readonly) ifTrue:[^ self errorReadOnly].
+
+    start to:stop do:[:index |
+        self nextPut:(aCollection at:index)
+    ]
+!
+
+cr
+    "reimplemented for speed"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    extern OBJ ErrorNumber;
+    extern errno;
+    extern int _immediateInterrupt;
+    int cnt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _readonly) {
+            f = MKFD(_INST(filePointer));
+
+            if (_INST(unBuffered) == true) {
+	        cnt = write(fileno(f), "\n", 1);
+	    } else { 
+		if (_INST(mode) == _readwrite)
+		    fseek(f, 0L, 1); /* needed in stdio */
+                cnt = fwrite("\n", 1, 1, f);
+	    }
+            if (cnt == 1) {
+                if (_INST(unBuffered) == true) {
+                    fflush(f);
+                }
+                if (_INST(position) != nil) {
+                    _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 1);
+                }
+                RETURN ( self );
+            }
+            ErrorNumber = _MKSMALLINT(errno);
+            return ( nil );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    self errorReadOnly
+! !
+
+!ExternalStream methodsFor:'more character I/O'!
+
+nextWord
+    "in text-mode:
+         read the next word (i.e. up to non letter-or-digit).
+         return a string containing those characters.
+     in binary-mode:
+         read two bytes (msb-first) and return the value as a 16-bit unsigned Integer
+         (msb-first for compatibility with other smalltalks)"
+
+%{  /* NOCONTEXT */
+    extern int _immediateInterrupt;
+
+    if (_INST(binary) == true) {
+        if (_INST(filePointer) != nil) {
+            if (_INST(mode) != _writeonly) {
+                FILE *f;
+                int hi, low;
+
+                f = MKFD(_INST(filePointer));
+                hi = getc(f);
+                if (hi == EOF) {
+                    RETURN ( nil );
+                }
+                low = getc(f);
+                if (low == EOF) {
+                    if (_INST(position) != nil) {
+                        _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 1);
+                    }
+                    RETURN ( _MKSMALLINT(hi & 0xFF) );
+                }
+                if (_INST(position) != nil) {
+                    _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 2);
+                }
+                RETURN ( _MKSMALLINT(((hi & 0xFF)<<8) | (low & 0xFF)) );
+            }
+        }
+    }
+%}
+.
+%{
+    FILE *f;
+    int len;
+    char buffer[1024];
+    int ch;
+    int cnt = 0;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _writeonly) {
+            f = MKFD(_INST(filePointer));
+            /* text-mode */
+            for (;;) {
+                ch = getc(f);
+                cnt++;
+
+                if (ch >= ' ') break;
+                if ((ch != ' ') && (ch != '\t') && (ch != '\r')
+                 && (ch != '\n') && (ch != 0x0b)) break;
+            }
+            ungetc(ch, f);
+            cnt--;
+
+            len = 0;
+            for (;;) {
+                ch = getc(f);
+                if (ch == EOF)
+                    break;
+                ch &= 0xFF;
+                if (! (((ch >= 'a') && (ch <= 'z')) ||
+                       ((ch >= 'A') && (ch <= 'Z')) ||
+                       ((ch >= '0') && (ch <= '9')))) {
+                    ungetc(ch, f);
+                    break;
+                }
+                cnt++;
+                buffer[len++] = ch;
+                if (len >= sizeof(buffer)-1) {
+                    /* emergency */
+                    break;
+                }
+            }
+            if (_INST(position) != nil) {
+                _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + cnt);
+            }
+            buffer[len] = '\0';
+            if (len != 0) {
+                RETURN ( _MKSTRING(buffer COMMA_CON) );
+            }
+            RETURN ( nil );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    self errorWriteOnly
+!
+
+nextWordPut:aNumber
+    "only in binary-mode:
+         write the argument, aNumber as two bytes (msb-first)
+         (msb-first for compatibility with other smalltalks)"
+
+%{  /* NOCONTEXT */
+
+    int num;
+    char bytes[2];
+    FILE *f;
+    extern OBJ ErrorNumber;
+    extern errno;
+    extern int _immediateInterrupt;
+
+    if (_INST(binary) == true) {
+        if (_INST(filePointer) != nil) {
+            if (_INST(mode) != _readonly) {
+                if (_isSmallInteger(aNumber)) {
+                    num = _intVal(aNumber);
+                    bytes[0] = (num >> 8) & 0xFF;
+                    bytes[1] = num & 0xFF;
+
+                    f = MKFD(_INST(filePointer));
+                    if (fwrite(bytes, 1, 2, f) == 2) {
+                        if (_INST(unBuffered) == true) {
+                            fflush(f);
+                        }
+                        if (_INST(position) != nil) {
+                            _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 2);
+                        }
+                        RETURN ( self );
+                    }
+                    ErrorNumber = _MKSMALLINT(errno);
+                    return ( nil );
+                }
+            }
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    (mode == #readonly) ifTrue:[^ self errorReadOnly].
+    binary ifFalse:[^ self errorNotBinary].
+    self argumentMustBeInteger
+!
+
+nextLong
+    "in binary-mode:
+         read two bytes (msb-first) and return the value as a 16-bit unsigned Integer
+         (msb-first for compatibility with other smalltalks)"
+
+    |lo hi|
+
+    binary ifFalse:[
+        ^ self error:'method only valid in binary mode'
+    ].
+    hi := self nextWord.
+    lo := self nextWord.
+    ^ hi * 16r10000 + lo
+!
+
+nextLine
+    "read the next line (characters up to newline).
+     Return a string containing those characters excluding the newline.
+     If the previous-to-last character is a cr, this is also removed,
+     so its possible to read alien (i.e. ms-dos) text as well."
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    int len;
+    char buffer[1024*16];
+    extern int _immediateInterrupt;
+    char *rslt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(filePointer) != _writeonly) {
+            f = MKFD(_INST(filePointer));
+            _immediateInterrupt = 1;
+            buffer[0] = 0;
+            rslt = fgets(buffer, sizeof(buffer), f);
+            _immediateInterrupt = 0;
+            if (rslt != NULL) {
+                len = strlen(buffer);
+                if (_INST(position) != nil) {
+                    _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + len);
+                }
+                /* remove EOL character */
+                if ((len != 0) && (buffer[len-1] == '\n')) {
+                    buffer[--len] = '\0';
+                }
+                if ((len != 0) && (buffer[len-1] == '\r')) {
+                    buffer[--len] = '\0';
+                }
+                RETURN ( _MKSTRING(buffer COMMA_CON) );
+            }
+        }
+    }
+%}
+.
+    (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    ^ nil
+!
+
+nextPutLine:aString
+    "write the characters in aString and append a newline"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    int len, cnt;
+    OBJ pos;
+    char *s;
+    extern OBJ ErrorNumber;
+    extern errno;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _readonly) {
+            if (_isString(aString)) {
+                f = MKFD(_INST(filePointer));
+                s = (char *) _stringVal(aString);
+                len = _stringSize(aString);
+
+                if (_INST(unBuffered) == true) {
+		    cnt = write(fileno(f), s, len);
+		} else { 
+		    if (_INST(mode) == _readwrite)
+		        fseek(f, 0L, 1); /* needed in stdio */
+                    cnt = fwrite(s, 1, len, f);
+		}
+                if (cnt == len) {
+                    if (_INST(unBuffered) == true) {
+		        cnt = write(fileno(f), "\n", 1);
+		    } else { 
+                        cnt = fwrite("\n", 1, 1, f);
+		    }
+                    if (cnt == 1) {
+                        pos = _INST(position);
+                        if (pos != nil) {
+                            _INST(position) = _MKSMALLINT(_intVal(pos)+len+1);
+                        }
+                        RETURN ( self );
+                    }
+                }
+                ErrorNumber = _MKSMALLINT(errno);
+                RETURN ( nil );
+            }
+        }
+    }
+%}
+.
+    (mode == #readonly) ifTrue:[^ self errorReadOnly].
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    self argumentMustBeString
+!
+
+nextPutLinesFrom:aStream upToLineStartingWith:aStringOrNil
+    "used to copy large files
+     - read from aStream up to and including a line starting with aStringOrNil
+     and append it to self. If aStringOrNil is nil or not matched,
+     copy preceeds to the end"
+
+    |srcFilePointer|
+
+    (mode == #readonly) ifTrue:[^ self errorReadOnly].
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    srcFilePointer := aStream filePointer.
+    srcFilePointer isNil ifTrue:[^ aStream errorNotOpen].
+%{
+    FILE *dst, *src;
+    char *matchString;
+    int matchLen = 0;
+    char buffer[1024*16];
+    extern int _immediateInterrupt;
+
+    if (_isSmallInteger(srcFilePointer)) {
+        if ((aStringOrNil == nil)
+         || _isString(aStringOrNil)) {
+            if (aStringOrNil != nil) {
+                matchString = (char *) _stringVal(aStringOrNil);
+                matchLen = _stringSize(aStringOrNil);
+            }
+            dst = MKFD(_INST(filePointer));
+            src = (FILE *)_intVal(srcFilePointer);
+            for (;;) {
+                if (fgets(buffer, sizeof(buffer), src) == NULL) break;
+                if (fputs(buffer, dst) == EOF) break;
+                if (matchLen) {
+                    if (strncmp(matchString, buffer, matchLen) == 0) 
+                        break;
+                }
+            }
+            if (_INST(unBuffered) == true) {
+                fflush(dst);
+            }
+            _INST(position) = nil;
+            RETURN ( self );
+        }
+    }
+%}
+.
+    ^ self primitiveFailed
+!
+
+peekForLineStartingWith:aString
+    "read ahead for next line starting with aString;
+     return the line-string if found, nil otherwise..
+     do not advance position i.e. nextLine will reread this line"
+
+    (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+%{
+    FILE *f;
+    int l;
+    char buffer[1024*10];
+    char *cp;
+    char *matchString;
+    int  firstpos, lastpos;
+    extern int _immediateInterrupt;
+
+    if (_isString(aString)) {
+        matchString = (char *) _stringVal(aString);
+        l = _stringSize(aString);
+
+        f = MKFD(_INST(filePointer));
+        firstpos = ftell(f);
+        for (;;) {
+            lastpos = ftell(f);
+            _immediateInterrupt = 1;
+            cp = fgets(buffer, sizeof(buffer), f);
+            _immediateInterrupt = 0;
+            if (cp == NULL) {
+                fseek(f, firstpos, 0);
+                RETURN ( nil );
+            }
+            if (strncmp(cp, matchString, l) == 0) {
+                fseek(f, lastpos, 0);
+                break;
+            }
+        }
+        /* remove EOL character */
+        cp = buffer;
+        while (*cp && (*cp != '\n')) cp++;
+        *cp = '\0';
+        RETURN ( _MKSTRING(buffer COMMA_CON) );
+    }
+%}
+.
+    self argumentMustBeString
+!
+
+peekForLineStartingWithAny:aCollectionOfStrings
+    "read ahead for next line starting with any of aCollectionOfStrings;
+     return the index in aCollection if found, nil otherwise..
+     If no match, do not change position; otherwise advance right before the
+     matched line so that nextLine will return this line."
+
+    |line startPos linePos index|
+
+    (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    startPos := self position.
+    [self atEnd] whileFalse:[
+        linePos := self position.
+        line := self nextLine.
+        index := 1.
+        aCollectionOfStrings do:[:prefix |
+            (line startsWith:prefix) ifTrue:[
+                self position:linePos.
+                ^ index
+            ].
+            index := index + 1
+        ]
+    ].
+    self position:startPos.
+    ^ nil
+! !
+
+!ExternalStream methodsFor:'testing'!
+
+atEnd
+    "return true, if position is at end"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+
+    if (_INST(filePointer) != nil) {
+        f = MKFD(_INST(filePointer));
+        RETURN ( feof(f) ? true : false );
+    }
+%}
+.
+    self errorNotOpen
+! !
+
+!ExternalStream methodsFor:'closing'!
+
+close
+    "close the stream - tell operating system"
+
+    filePointer isNil ifTrue:[^ self].
+    lobby unregister:self.
+    self closeFile.
+    filePointer := nil
+! !
+
+!ExternalStream methodsFor:'reimplemented for speed'!
+
+peekFor:aCharacter
+    "return true and move past if next == something.
+     - reimplemented for speed"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    int c;
+    int peekvalue;
+    extern int _immediateInterrupt;
+
+    if (_isCharacter(aCharacter)) {
+        if (_INST(filePointer) != nil) {
+            peekvalue = _intVal(_CharacterInstPtr(aCharacter)->c_asciivalue);
+            f = MKFD(_INST(filePointer));
+            c = getc(f);
+            if (c == peekvalue) {
+                RETURN ( true );
+            }
+            ungetc(c, f);
+            RETURN ( false );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    ^ super peekFor:aCharacter
+!
+
+skipLine
+    "read the next line (characters up to newline) skip only;
+     return nil if EOF reached"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    char buffer[1024*10];
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _writeonly) {
+            f = MKFD(_INST(filePointer));
+            if (fgets(buffer, sizeof(buffer), f) != NULL) {
+                RETURN ( self );
+            }
+            RETURN ( nil );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    self errorWriteOnly
+!
+
+skipToAll:aString
+    "skip for the sequence given by the argument, aCollection;
+     return nil if not found, self otherwise. On a successful match, next read
+     will return characters of aString."
+
+    |oldPos buffer l first idx|
+
+    (aString isKindOf:String) ifTrue:[
+        oldPos := self position.
+        l := aString size.
+        first := aString at:1.
+        buffer := String new:l.
+        [true] whileTrue:[
+            (self nextBytes:l into:buffer) == l ifFalse:[
+                self position:oldPos.
+                ^ nil
+            ].
+            buffer = aString ifTrue:[
+                self position:(self position - l).
+                ^ self
+            ].
+            idx := buffer indexOf:first startingAt:2.
+            idx == 0 ifFalse:[
+                self position:(self position - l + idx - 1)
+            ]
+        ]
+    ].
+    ^ super skipFor:aString
+!
+
+skipSeparators
+    "skip all whitespace; next will return next non-white-space character
+     or nil if endOfFile reached.
+     - reimplemented for speed"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    REGISTER int c;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _writeonly) {
+            f = MKFD(_INST(filePointer));
+            while (1) {
+                if (feof(f)) {
+                    RETURN ( nil );
+                }
+                c = getc(f);
+                if (c < 0) {
+                    RETURN ( nil );
+                }
+                switch (c) {
+                    case ' ':
+                    case '\t':
+                    case '\n':
+                    case '\r':
+                    case '\b':
+                    case '\014':
+                        break;
+                    default:
+                        ungetc(c, f);
+                        RETURN ( _MKCHARACTER(c & 0xFF) );
+                }
+            }
+        }
+    }
+%}
+.
+    (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+    self errorNotOpen
+!
+
+skipSeparatorsExceptCR
+    "skip all whitespace but no newlines;
+     next will return next non-white-space character
+     or nil if endOfFile reached.
+     - reimplemented for speed"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    int c;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _writeonly) {
+            f = MKFD(_INST(filePointer));
+            while (1) {
+                if (feof(f)) {
+                    RETURN ( nil );
+                }
+                c = getc(f);
+                if (c < 0) {
+                    RETURN ( nil );
+                }
+                switch (c) {
+                    case ' ':
+                    case '\t':
+                    case '\b':
+                        break;
+                    default:
+                        ungetc(c, f);
+                        RETURN ( _MKCHARACTER(c & 0xFF) );
+                }
+            }
+        }
+    }
+%}
+.
+    (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+    self errorNotOpen
+!
+
+nextChunk
+    "return the next chunk, i.e. all characters up to the next
+     non-doubled exclamation mark; undouble doubled exclamation marks.
+     - reimplemented for speed"
+    |retVal|
+
+    filePointer isNil ifTrue:[
+        ^ self errorNotOpen
+    ].
+%{
+    FILE *f;
+    int done = 0;
+    REGISTER int c;
+    unsigned char peekC;
+    char *buffer, *newBuffer;
+    REGISTER int index;
+    int currSize;
+    int inComment, inString, inPrimitive = 0;
+    extern int _immediateInterrupt;
+
+    f = MKFD(_INST(filePointer));
+    /*
+     * skip spaces
+     */
+    while (! done) {
+        if (feof(f)) {
+            RETURN ( nil );
+        }
+        c = getc(f);
+        switch (c) {
+            case ' ':
+            case '\t':
+            case '\n':
+            case '\r':
+            case '\b':
+            case '\014':
+                break;
+
+            case EOF:
+                RETURN ( nil );
+
+            default:
+                ungetc(c, f);
+                done = 1;
+                break;
+        }
+    }
+
+    /*
+     * read chunk into a buffer
+     */
+    buffer = (char *)malloc(3000);
+    currSize = 3000;
+    index = 0;
+    while (! feof(f)) {
+        /* do we have to resize the buffer ? */
+        if ((index+2) >= currSize) {
+            newBuffer = (char *)malloc(currSize * 2);
+            bcopy(buffer, newBuffer, index);
+            free(buffer);
+            buffer = newBuffer;
+            currSize = currSize * 2;
+        }
+        c = getc(f);
+        if (c == '%') {
+            peekC = getc(f);
+            ungetc(peekC, f);
+            if (peekC == '{') {
+                inPrimitive++;
+            } else if (peekC == '}') {
+                inPrimitive--;
+            }
+        } else {
+            if (! inPrimitive) {
+                if (c == '!') {
+                    c = getc(f);
+                    if (c != '!') {
+                        ungetc(c, f);
+                        break;
+                    }
+                }
+            }
+        }
+        if (c == EOF) break;
+        buffer[index++] = c;
+    }
+    buffer[index] = '\0';
+    /*
+     * make it a string
+     */
+    retVal = _MKSTRING(buffer COMMA_CON);
+    free(buffer);
+%}
+.
+    ^ retVal
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ExternalStream.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,1437 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+ReadWriteStream subclass:#ExternalStream
+       instanceVariableNames:'filePointer mode unBuffered binary'
+       classVariableNames:'lobby'
+       poolDictionaries:''
+       category:'Streams-External'
+!
+
+ExternalStream comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ExternalStream defines protocol common to Streams which have a file-descriptor and 
+represent some File or CommunicationChannel of the underlying OperatingSystem.
+ExternalStream is abstract; concrete classes are FileStream, PipeStream etc.
+ExternalStreams can be in two modes: text (the default) and binary.
+In text-mode, the elements read/written are characters; while in binary-mode the basic
+elements are bytes which read/write as SmallIntegers in the range 0..255.
+
+%W% %E%
+
+written 88 by claus
+'!
+
+%{
+#include <stdio.h>
+%}
+
+!ExternalStream class methodsFor:'initialization'!
+
+initialize
+    lobby isNil ifTrue:[
+        lobby := Registry new.
+
+        "want to get informed when returning from snapshot"
+        ObjectMemory addDependent:self
+    ]
+!
+
+reOpenFiles
+    "reopen all files (if possible) after a snapShot load"
+
+    lobby contentsDo:[:aFileStream |
+        aFileStream reOpen
+    ]
+!
+
+update:something
+    "have to reopen files when returning from snapshot"
+
+    something == #returnFromSnapshot ifTrue:[
+        self reOpenFiles
+    ]
+! !
+
+!ExternalStream methodsFor:'instance release'!
+
+disposed
+    "some Stream has been collected - close the file if not already done"
+
+    self closeFile
+!
+
+closeFile
+    "low level close - may be redefined in subclasses"
+
+%{  /* NOCONTEXT */
+
+    fclose(MKFD(_INST(filePointer)));
+%}
+! !
+
+!ExternalStream methodsFor:'private'!
+
+reOpen
+    "sent after snapin to reopen streams.
+     cannot reopen here since I am abstract and have no device knowledge"
+
+    self class name print. ': cannot reOpen stream - stream closed' printNewline.
+    filePointer := nil.
+    lobby unregister:self.
+! !
+
+!ExternalStream methodsFor:'error handling'!
+
+errorNotOpen
+    "report an error, that the stream has not been opened"
+
+    ^ self error:(self class name , ' not open')
+!
+
+errorReadOnly
+    "report an error, that the stream is a readOnly stream"
+
+    ^ self error:(self class name , ' is readonly')
+!
+
+errorWriteOnly
+    "report an error, that the stream is a writeOnly stream"
+
+    ^ self error:(self class name , ' is writeonly')
+!
+
+errorNotBinary
+    "report an error, that the stream is not in binary mode"
+
+    ^ self error:(self class name , ' is not in binary mode')
+!
+
+argumentMustBeInteger
+    "report an error, that the argument must be an integer"
+
+    ^ self error:'argument must be an integer'
+!
+
+argumentMustBeCharacter
+    "report an error, that the argument must be a character"
+
+    ^ self error:'argument must be a character'
+!
+
+argumentMustBeString
+    "report an error, that the argument must be a string"
+
+    ^ self error:'argument must be a string'
+! !
+
+!ExternalStream methodsFor:'accessing'!
+
+readonly
+    "set access mode to readonly"
+
+    mode := #readonly
+!
+
+writeonly
+    "set access mode to writeonly"
+
+    mode := #writeonly
+!
+
+readwrite
+    "set access mode to readwrite"
+
+    mode := #readwrite
+!
+
+filePointer
+    "return the filePointer of the receiver -
+     notice: for portability stdio is used; this means you will get
+     a FILE * - not a fileDescriptor"
+
+    ^ filePointer
+!
+
+fileDescriptor
+    "return the fileDescriptor of the receiver -
+     notice: this one returns the underlying OSs fileDescriptor -
+     this may not be available on all platforms (i.e. non unix systems)."
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+
+    if (_INST(filePointer) != nil) {
+        f = MKFD(_INST(filePointer));
+        RETURN ( MKOBJ(fileno(f)) );
+    }
+%}
+.
+    ^ self errorNotOpen
+!
+
+buffered:aBoolean
+    "turn buffering on or off"
+
+    unBuffered := aBoolean not
+!
+
+binary
+    "switch to binary mode"
+
+    binary := true
+!
+
+text
+    "switch to text mode"
+
+    binary := false
+!
+
+contents
+    "return the contents of the file as a Text-object"
+
+    |text|
+
+    text := Text new.
+    [self atEnd] whileFalse:[
+        text add:(self nextLine)
+    ].
+    ^ text
+! !
+
+!ExternalStream methodsFor:'basic'!
+
+open
+    "open the stream
+     - this must be redefined in subclass"
+
+    ^ self subclassResponsibility
+!
+
+create
+    "create the stream
+     - this must be redefined in subclass"
+
+    ^ self subclassResponsibility
+!
+
+position
+    "return the position
+     - this must be redefined in subclass"
+
+    ^ self subclassResponsibility
+!
+
+position:anInteger
+    "set the position
+     - this must be redefined in subclass"
+
+    ^ self subclassResponsibility
+! !
+
+!ExternalStream methodsFor:'low level I/O'!
+
+ioctl:ioctlNumber with:arg
+    "to provide a simple ioctl facility"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    int ret, ioNum, ioArg;
+    extern OBJ ErrorNumber;
+    extern errno;
+
+    if (_INST(filePointer) != nil) {
+        if (_isSmallInteger(ioctlNumber) && _isSmallInteger(arg)) {
+            ioNum = _intVal(ioctlNumber);
+            ioArg = _intVal(arg);
+            f = MKFD(_INST(filePointer));
+            ret = ioctl(fileno(f), ioNum, ioArg);
+            if (ret >= 0) {
+                RETURN ( _MKSMALLINT(ret) );
+            }
+            ErrorNumber = _MKSMALLINT(errno);
+            RETURN ( nil );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    self primitiveFailed
+!
+
+nextByte
+    "read the next byte return it as an Integer
+     nil on error.  Use with care - non object oriented i/o"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    unsigned char byte;
+    int cnt;
+    extern OBJ ErrorNumber;
+    extern errno;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _writeonly) {
+            f = MKFD(_INST(filePointer));
+            _immediateInterrupt = 1;
+            if (_INST(unBuffered) == true) {
+                cnt = read(fileno(f), &byte, 1);
+            } else {
+		if (_INST(mode) == _readwrite)
+		    fseek(f, 0L, 1); /* needed in stdio */
+                cnt = fread(&byte, 1, 1, f);
+            }
+            _immediateInterrupt = 0;
+            if (cnt == 1) {
+                if (_INST(position) != nil)
+                    _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 1);
+                RETURN ( _MKSMALLINT(byte) );
+            }
+            if (cnt < 0) {
+                ErrorNumber = _MKSMALLINT(errno);
+            }
+            RETURN ( nil );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    self errorWriteOnly
+!
+
+nextBytes:count into:anObject
+    "read the next count bytes into an object and return the number of
+     bytes read or nil on error.
+     Use with care - non object oriented i/o"
+
+    ^ self nextBytes:count into:anObject startingAt:1
+!
+
+nextBytes:count into:anObject startingAt:start
+    "read the next count bytes into an object and return the number of
+     bytes read or nil on error.
+     Use with care - non object oriented i/o"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    int cnt, offs;
+    int objSize;
+    char *cp;
+    extern OBJ ErrorNumber;
+    extern errno;
+    OBJ pos;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _writeonly) {
+            if (_isSmallInteger(count) && _isSmallInteger(start)) {
+                cnt = _intVal(count);
+                offs = _intVal(start) - 1;
+                f = MKFD(_INST(filePointer));
+                objSize = _Size(anObject) - OHDR_SIZE;
+                if ((offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs))) {
+                    cp = (char *)_InstPtr(anObject) + OHDR_SIZE + offs;
+                    _immediateInterrupt = 1;
+                    if (_INST(unBuffered) == true) {
+                        cnt = read(fileno(f), cp, cnt);
+                    } else {
+		        if (_INST(mode) == _readwrite)
+		            fseek(f, 0L, 1); /* needed in stdio */
+                        cnt = fread(cp, 1, cnt, f);
+                    }
+                    _immediateInterrupt = 0;
+                    if (cnt >= 0) {
+                        pos = _INST(position);
+                        if (pos != nil)
+                            _INST(position) = _MKSMALLINT(_intVal(pos) + cnt);
+                        RETURN ( _MKSMALLINT(cnt) );
+                    }
+                    ErrorNumber = _MKSMALLINT(errno);
+                    RETURN ( nil );
+                }
+            }
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+    self primitiveFailed
+!
+
+nextPutByte:aByteValue
+    "write a byte"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    char c;
+    extern OBJ ErrorNumber;
+    extern errno;
+    OBJ pos;
+    int cnt;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _readonly) {
+            if (_isSmallInteger(aByteValue)) {
+                c = _intVal(aByteValue);
+                f = MKFD(_INST(filePointer));
+                _immediateInterrupt = 1;
+		if (_INST(mode) == _readwrite)
+		    fseek(f, 0L, 1); /* needed in stdio */
+                cnt = fwrite(&c, 1, 1, f);
+                _immediateInterrupt = 0;
+                if (cnt == 1) {
+                    if (_INST(unBuffered) == true) {
+                        fflush(f);
+                    }
+                    pos = _INST(position);
+                    if (pos != nil)
+                        _INST(position) = _MKSMALLINT(_intVal(pos) + 1);
+                    RETURN ( self );
+                }
+                ErrorNumber = _MKSMALLINT(errno);
+            }
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    (mode == #readonly) ifTrue:[^ self errorReadOnly].
+    self primitiveFailed
+!
+
+nextPutBytes:count from:anObject
+    "write count bytes from an object starting at index start.
+     return the number of bytes written or nil on error.
+     Use with care - non object oriented i/o"
+
+    ^ self nextPutBytes:count from:anObject startingAt:1
+!
+
+nextPutBytes:count from:anObject startingAt:start
+    "write count bytes from an object starting at index start.
+     return the number of bytes written or nil on error.
+     Use with care - non object oriented i/o"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    int cnt, offs;
+    int objSize;
+    char *cp;
+    extern OBJ ErrorNumber;
+    extern errno;
+    OBJ pos;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _readonly) {
+            if (_isSmallInteger(count) && _isSmallInteger(start)) {
+                cnt = _intVal(count);
+                offs = _intVal(start) - 1;
+                f = MKFD(_INST(filePointer));
+
+                objSize = _Size(anObject) - OHDR_SIZE;
+                if ( (offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs)) ) {
+                    cp = (char *)_InstPtr(anObject) + OHDR_SIZE + offs;
+                    _immediateInterrupt = 1;
+                    if (_INST(unBuffered) == true) {
+                        cnt = write(fileno(f), cp, cnt);
+                    } else {
+		        if (_INST(mode) == _readwrite)
+		            fseek(f, 0L, 1); /* needed in stdio */
+                        cnt = fwrite(cp, 1, cnt, f);
+                    }
+                    _immediateInterrupt = 0;
+                    if (cnt >= 0) {
+                        pos = _INST(position);
+                        if (pos != nil)
+                            _INST(position) = _MKSMALLINT(_intVal(pos) + cnt);
+                        RETURN ( _MKSMALLINT(cnt) );
+                    }
+                    ErrorNumber = _MKSMALLINT(errno);
+                    RETURN ( nil );
+                }
+            }
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    (mode == #readonly) ifTrue:[^ self errorReadOnly].
+    self primitiveFailed
+! !
+
+!ExternalStream methodsFor:'character I/O'!
+
+peek
+    "return the character to be read next without advancing read position"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    REGISTER int c;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _writeonly) {
+            f = MKFD(_INST(filePointer));
+            c = getc(f);
+            if (c != EOF) {
+                ungetc(c, f);
+                if (_INST(binary) == true) {
+                    RETURN ( _MKSMALLINT(c & 0xFF) );
+                }
+                RETURN ( _MKCHARACTER(c & 0xFF) );
+            }
+            RETURN ( nil );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    self errorWriteOnly
+!
+
+next
+    "return the character; advance read position"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    int c;
+    OBJ pos;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _writeonly) {
+            f = MKFD(_INST(filePointer));
+            _immediateInterrupt = 1;
+            if (_INST(unBuffered) == true) {
+                if (read(fileno(f), &c, 1) != 1)
+                    c = EOF;
+            } else {
+		if (_INST(mode) == _readwrite)
+		    fseek(f, 0L, 1); /* needed in stdio */
+                c = getc(f);
+            }
+            _immediateInterrupt = 0;
+            if (c != EOF) {
+                pos = _INST(position);
+                if (pos != nil) {
+                    _INST(position) = _MKSMALLINT(_intVal(pos) + 1);
+                }
+                if (_INST(binary) == true) {
+                    RETURN ( _MKSMALLINT(c & 0xFF) );
+                }
+                RETURN ( _MKCHARACTER(c & 0xFF) );
+            }
+            RETURN ( nil );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    self errorWriteOnly
+!
+
+nextPut:aCharacter
+    "write the argument, aCharacter - return nil if failed, self if ok"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    char c;
+    extern OBJ ErrorNumber;
+    extern errno;
+    int cnt;
+    OBJ pos;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _readonly) {
+            if (_isCharacter(aCharacter)) {
+                c = _intVal(_CharacterInstPtr(aCharacter)->c_asciivalue);
+    doWrite:
+                f = MKFD(_INST(filePointer));
+
+                if (_INST(unBuffered) == true) {
+		    cnt = write(fileno(f), &c, 1);
+		} else { 
+		    if (_INST(mode) == _readwrite)
+		        fseek(f, 0L, 1); /* needed in stdio */
+                    cnt = fwrite(&c, 1, 1, f);
+		}
+                if (cnt == 1) {
+                    if (_INST(unBuffered) == true) {
+                        fflush(f);
+                    }
+                    pos = _INST(position);
+                    if (pos != nil) {
+                        _INST(position) = _MKSMALLINT(_intVal(pos) + 1);
+                    }
+                    RETURN ( self );
+                }
+                ErrorNumber = _MKSMALLINT(errno);
+                RETURN ( nil );
+            } else {
+                if (_INST(binary) == true) {
+                    if (_isSmallInteger(aCharacter)) {
+                        c = _intVal(aCharacter);
+                        goto doWrite;
+                    }
+                }
+            }
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    (mode == #readonly) ifTrue:[^ self errorReadOnly].
+    self argumentMustBeCharacter
+!
+
+nextPutAll:aCollection
+    "write all elements of the argument, aCollection"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    unsigned char *cp;
+    int len, cnt;
+    extern OBJ ErrorNumber;
+    extern errno;
+    OBJ pos;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _readonly) {
+            if (_isString(aCollection) || _isSymbol(aCollection)) {
+                cp = _stringVal(aCollection);
+                len = _stringSize(aCollection);
+                f = MKFD(_INST(filePointer));
+
+                if (_INST(unBuffered) == true) {
+		    cnt = write(fileno(f), cp, len);
+		} else { 
+		    if (_INST(mode) == _readwrite)
+		        fseek(f, 0L, 1); /* needed in stdio */
+                    cnt = fwrite(cp, 1, len, f);
+		}
+                if (cnt == len) {
+                    if (_INST(unBuffered) == true) {
+                        fflush(f);
+                    }
+                    pos = _INST(position);
+                    if (pos != nil) {
+                        _INST(position) = _MKSMALLINT(_intVal(pos) + len);
+                    }
+                    RETURN ( self );
+                }
+                ErrorNumber = _MKSMALLINT(errno);
+                RETURN ( nil );
+            }
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    (mode == #readonly) ifTrue:[^ self errorReadOnly].
+
+    aCollection do:[:element |
+        self nextPut:element
+    ]
+!
+
+nextPut:aCollection from:start to:stop
+    "write a range of elements of the argument, aCollection"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    unsigned char *cp;
+    int len, cnt, index1, index2;
+    extern OBJ ErrorNumber;
+    extern errno;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_isString(aCollection)
+         && _isSmallInteger(start)
+         && _isSmallInteger(stop)) {
+            f = MKFD(_INST(filePointer));
+            cp = _stringVal(aCollection);
+            len = _stringSize(aCollection);
+            index1 = _intVal(start);
+            index2 = _intVal(stop);
+            if ((index1 < 1) || (index2 > len) || (index2 < index1)) {
+                RETURN ( self );
+            }
+            if (index2 > len)
+                index2 = len;
+
+            len = index2 - index1 + 1;
+            if (_INST(unBuffered) == true) {
+	        cnt = write(fileno(f), cp + index1 - 1, len);
+	    } else { 
+		if (_INST(mode) == _readwrite)
+		    fseek(f, 0L, 1); /* needed in stdio */
+                cnt = fwrite(cp + index1 - 1, 1, len, f);
+	    }
+            if (cnt == len) {
+                if (_INST(unBuffered) == true) {
+                    fflush(f);
+                }
+                if (_INST(position) != nil) {
+                    _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + len);
+                }
+                RETURN ( self );
+            }
+            ErrorNumber = _MKSMALLINT(errno);
+            RETURN ( nil );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    (mode == #readonly) ifTrue:[^ self errorReadOnly].
+
+    start to:stop do:[:index |
+        self nextPut:(aCollection at:index)
+    ]
+!
+
+cr
+    "reimplemented for speed"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    extern OBJ ErrorNumber;
+    extern errno;
+    extern int _immediateInterrupt;
+    int cnt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _readonly) {
+            f = MKFD(_INST(filePointer));
+
+            if (_INST(unBuffered) == true) {
+	        cnt = write(fileno(f), "\n", 1);
+	    } else { 
+		if (_INST(mode) == _readwrite)
+		    fseek(f, 0L, 1); /* needed in stdio */
+                cnt = fwrite("\n", 1, 1, f);
+	    }
+            if (cnt == 1) {
+                if (_INST(unBuffered) == true) {
+                    fflush(f);
+                }
+                if (_INST(position) != nil) {
+                    _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 1);
+                }
+                RETURN ( self );
+            }
+            ErrorNumber = _MKSMALLINT(errno);
+            return ( nil );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    self errorReadOnly
+! !
+
+!ExternalStream methodsFor:'more character I/O'!
+
+nextWord
+    "in text-mode:
+         read the next word (i.e. up to non letter-or-digit).
+         return a string containing those characters.
+     in binary-mode:
+         read two bytes (msb-first) and return the value as a 16-bit unsigned Integer
+         (msb-first for compatibility with other smalltalks)"
+
+%{  /* NOCONTEXT */
+    extern int _immediateInterrupt;
+
+    if (_INST(binary) == true) {
+        if (_INST(filePointer) != nil) {
+            if (_INST(mode) != _writeonly) {
+                FILE *f;
+                int hi, low;
+
+                f = MKFD(_INST(filePointer));
+                hi = getc(f);
+                if (hi == EOF) {
+                    RETURN ( nil );
+                }
+                low = getc(f);
+                if (low == EOF) {
+                    if (_INST(position) != nil) {
+                        _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 1);
+                    }
+                    RETURN ( _MKSMALLINT(hi & 0xFF) );
+                }
+                if (_INST(position) != nil) {
+                    _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 2);
+                }
+                RETURN ( _MKSMALLINT(((hi & 0xFF)<<8) | (low & 0xFF)) );
+            }
+        }
+    }
+%}
+.
+%{
+    FILE *f;
+    int len;
+    char buffer[1024];
+    int ch;
+    int cnt = 0;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _writeonly) {
+            f = MKFD(_INST(filePointer));
+            /* text-mode */
+            for (;;) {
+                ch = getc(f);
+                cnt++;
+
+                if (ch >= ' ') break;
+                if ((ch != ' ') && (ch != '\t') && (ch != '\r')
+                 && (ch != '\n') && (ch != 0x0b)) break;
+            }
+            ungetc(ch, f);
+            cnt--;
+
+            len = 0;
+            for (;;) {
+                ch = getc(f);
+                if (ch == EOF)
+                    break;
+                ch &= 0xFF;
+                if (! (((ch >= 'a') && (ch <= 'z')) ||
+                       ((ch >= 'A') && (ch <= 'Z')) ||
+                       ((ch >= '0') && (ch <= '9')))) {
+                    ungetc(ch, f);
+                    break;
+                }
+                cnt++;
+                buffer[len++] = ch;
+                if (len >= sizeof(buffer)-1) {
+                    /* emergency */
+                    break;
+                }
+            }
+            if (_INST(position) != nil) {
+                _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + cnt);
+            }
+            buffer[len] = '\0';
+            if (len != 0) {
+                RETURN ( _MKSTRING(buffer COMMA_CON) );
+            }
+            RETURN ( nil );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    self errorWriteOnly
+!
+
+nextWordPut:aNumber
+    "only in binary-mode:
+         write the argument, aNumber as two bytes (msb-first)
+         (msb-first for compatibility with other smalltalks)"
+
+%{  /* NOCONTEXT */
+
+    int num;
+    char bytes[2];
+    FILE *f;
+    extern OBJ ErrorNumber;
+    extern errno;
+    extern int _immediateInterrupt;
+
+    if (_INST(binary) == true) {
+        if (_INST(filePointer) != nil) {
+            if (_INST(mode) != _readonly) {
+                if (_isSmallInteger(aNumber)) {
+                    num = _intVal(aNumber);
+                    bytes[0] = (num >> 8) & 0xFF;
+                    bytes[1] = num & 0xFF;
+
+                    f = MKFD(_INST(filePointer));
+                    if (fwrite(bytes, 1, 2, f) == 2) {
+                        if (_INST(unBuffered) == true) {
+                            fflush(f);
+                        }
+                        if (_INST(position) != nil) {
+                            _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 2);
+                        }
+                        RETURN ( self );
+                    }
+                    ErrorNumber = _MKSMALLINT(errno);
+                    return ( nil );
+                }
+            }
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    (mode == #readonly) ifTrue:[^ self errorReadOnly].
+    binary ifFalse:[^ self errorNotBinary].
+    self argumentMustBeInteger
+!
+
+nextLong
+    "in binary-mode:
+         read two bytes (msb-first) and return the value as a 16-bit unsigned Integer
+         (msb-first for compatibility with other smalltalks)"
+
+    |lo hi|
+
+    binary ifFalse:[
+        ^ self error:'method only valid in binary mode'
+    ].
+    hi := self nextWord.
+    lo := self nextWord.
+    ^ hi * 16r10000 + lo
+!
+
+nextLine
+    "read the next line (characters up to newline).
+     Return a string containing those characters excluding the newline.
+     If the previous-to-last character is a cr, this is also removed,
+     so its possible to read alien (i.e. ms-dos) text as well."
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    int len;
+    char buffer[1024*16];
+    extern int _immediateInterrupt;
+    char *rslt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(filePointer) != _writeonly) {
+            f = MKFD(_INST(filePointer));
+            _immediateInterrupt = 1;
+            buffer[0] = 0;
+            rslt = fgets(buffer, sizeof(buffer), f);
+            _immediateInterrupt = 0;
+            if (rslt != NULL) {
+                len = strlen(buffer);
+                if (_INST(position) != nil) {
+                    _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + len);
+                }
+                /* remove EOL character */
+                if ((len != 0) && (buffer[len-1] == '\n')) {
+                    buffer[--len] = '\0';
+                }
+                if ((len != 0) && (buffer[len-1] == '\r')) {
+                    buffer[--len] = '\0';
+                }
+                RETURN ( _MKSTRING(buffer COMMA_CON) );
+            }
+        }
+    }
+%}
+.
+    (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    ^ nil
+!
+
+nextPutLine:aString
+    "write the characters in aString and append a newline"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    int len, cnt;
+    OBJ pos;
+    char *s;
+    extern OBJ ErrorNumber;
+    extern errno;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _readonly) {
+            if (_isString(aString)) {
+                f = MKFD(_INST(filePointer));
+                s = (char *) _stringVal(aString);
+                len = _stringSize(aString);
+
+                if (_INST(unBuffered) == true) {
+		    cnt = write(fileno(f), s, len);
+		} else { 
+		    if (_INST(mode) == _readwrite)
+		        fseek(f, 0L, 1); /* needed in stdio */
+                    cnt = fwrite(s, 1, len, f);
+		}
+                if (cnt == len) {
+                    if (_INST(unBuffered) == true) {
+		        cnt = write(fileno(f), "\n", 1);
+		    } else { 
+                        cnt = fwrite("\n", 1, 1, f);
+		    }
+                    if (cnt == 1) {
+                        pos = _INST(position);
+                        if (pos != nil) {
+                            _INST(position) = _MKSMALLINT(_intVal(pos)+len+1);
+                        }
+                        RETURN ( self );
+                    }
+                }
+                ErrorNumber = _MKSMALLINT(errno);
+                RETURN ( nil );
+            }
+        }
+    }
+%}
+.
+    (mode == #readonly) ifTrue:[^ self errorReadOnly].
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    self argumentMustBeString
+!
+
+nextPutLinesFrom:aStream upToLineStartingWith:aStringOrNil
+    "used to copy large files
+     - read from aStream up to and including a line starting with aStringOrNil
+     and append it to self. If aStringOrNil is nil or not matched,
+     copy preceeds to the end"
+
+    |srcFilePointer|
+
+    (mode == #readonly) ifTrue:[^ self errorReadOnly].
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    srcFilePointer := aStream filePointer.
+    srcFilePointer isNil ifTrue:[^ aStream errorNotOpen].
+%{
+    FILE *dst, *src;
+    char *matchString;
+    int matchLen = 0;
+    char buffer[1024*16];
+    extern int _immediateInterrupt;
+
+    if (_isSmallInteger(srcFilePointer)) {
+        if ((aStringOrNil == nil)
+         || _isString(aStringOrNil)) {
+            if (aStringOrNil != nil) {
+                matchString = (char *) _stringVal(aStringOrNil);
+                matchLen = _stringSize(aStringOrNil);
+            }
+            dst = MKFD(_INST(filePointer));
+            src = (FILE *)_intVal(srcFilePointer);
+            for (;;) {
+                if (fgets(buffer, sizeof(buffer), src) == NULL) break;
+                if (fputs(buffer, dst) == EOF) break;
+                if (matchLen) {
+                    if (strncmp(matchString, buffer, matchLen) == 0) 
+                        break;
+                }
+            }
+            if (_INST(unBuffered) == true) {
+                fflush(dst);
+            }
+            _INST(position) = nil;
+            RETURN ( self );
+        }
+    }
+%}
+.
+    ^ self primitiveFailed
+!
+
+peekForLineStartingWith:aString
+    "read ahead for next line starting with aString;
+     return the line-string if found, nil otherwise..
+     do not advance position i.e. nextLine will reread this line"
+
+    (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+%{
+    FILE *f;
+    int l;
+    char buffer[1024*10];
+    char *cp;
+    char *matchString;
+    int  firstpos, lastpos;
+    extern int _immediateInterrupt;
+
+    if (_isString(aString)) {
+        matchString = (char *) _stringVal(aString);
+        l = _stringSize(aString);
+
+        f = MKFD(_INST(filePointer));
+        firstpos = ftell(f);
+        for (;;) {
+            lastpos = ftell(f);
+            _immediateInterrupt = 1;
+            cp = fgets(buffer, sizeof(buffer), f);
+            _immediateInterrupt = 0;
+            if (cp == NULL) {
+                fseek(f, firstpos, 0);
+                RETURN ( nil );
+            }
+            if (strncmp(cp, matchString, l) == 0) {
+                fseek(f, lastpos, 0);
+                break;
+            }
+        }
+        /* remove EOL character */
+        cp = buffer;
+        while (*cp && (*cp != '\n')) cp++;
+        *cp = '\0';
+        RETURN ( _MKSTRING(buffer COMMA_CON) );
+    }
+%}
+.
+    self argumentMustBeString
+!
+
+peekForLineStartingWithAny:aCollectionOfStrings
+    "read ahead for next line starting with any of aCollectionOfStrings;
+     return the index in aCollection if found, nil otherwise..
+     If no match, do not change position; otherwise advance right before the
+     matched line so that nextLine will return this line."
+
+    |line startPos linePos index|
+
+    (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    startPos := self position.
+    [self atEnd] whileFalse:[
+        linePos := self position.
+        line := self nextLine.
+        index := 1.
+        aCollectionOfStrings do:[:prefix |
+            (line startsWith:prefix) ifTrue:[
+                self position:linePos.
+                ^ index
+            ].
+            index := index + 1
+        ]
+    ].
+    self position:startPos.
+    ^ nil
+! !
+
+!ExternalStream methodsFor:'testing'!
+
+atEnd
+    "return true, if position is at end"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+
+    if (_INST(filePointer) != nil) {
+        f = MKFD(_INST(filePointer));
+        RETURN ( feof(f) ? true : false );
+    }
+%}
+.
+    self errorNotOpen
+! !
+
+!ExternalStream methodsFor:'closing'!
+
+close
+    "close the stream - tell operating system"
+
+    filePointer isNil ifTrue:[^ self].
+    lobby unregister:self.
+    self closeFile.
+    filePointer := nil
+! !
+
+!ExternalStream methodsFor:'reimplemented for speed'!
+
+peekFor:aCharacter
+    "return true and move past if next == something.
+     - reimplemented for speed"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    int c;
+    int peekvalue;
+    extern int _immediateInterrupt;
+
+    if (_isCharacter(aCharacter)) {
+        if (_INST(filePointer) != nil) {
+            peekvalue = _intVal(_CharacterInstPtr(aCharacter)->c_asciivalue);
+            f = MKFD(_INST(filePointer));
+            c = getc(f);
+            if (c == peekvalue) {
+                RETURN ( true );
+            }
+            ungetc(c, f);
+            RETURN ( false );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    ^ super peekFor:aCharacter
+!
+
+skipLine
+    "read the next line (characters up to newline) skip only;
+     return nil if EOF reached"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    char buffer[1024*10];
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _writeonly) {
+            f = MKFD(_INST(filePointer));
+            if (fgets(buffer, sizeof(buffer), f) != NULL) {
+                RETURN ( self );
+            }
+            RETURN ( nil );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    self errorWriteOnly
+!
+
+skipToAll:aString
+    "skip for the sequence given by the argument, aCollection;
+     return nil if not found, self otherwise. On a successful match, next read
+     will return characters of aString."
+
+    |oldPos buffer l first idx|
+
+    (aString isKindOf:String) ifTrue:[
+        oldPos := self position.
+        l := aString size.
+        first := aString at:1.
+        buffer := String new:l.
+        [true] whileTrue:[
+            (self nextBytes:l into:buffer) == l ifFalse:[
+                self position:oldPos.
+                ^ nil
+            ].
+            buffer = aString ifTrue:[
+                self position:(self position - l).
+                ^ self
+            ].
+            idx := buffer indexOf:first startingAt:2.
+            idx == 0 ifFalse:[
+                self position:(self position - l + idx - 1)
+            ]
+        ]
+    ].
+    ^ super skipFor:aString
+!
+
+skipSeparators
+    "skip all whitespace; next will return next non-white-space character
+     or nil if endOfFile reached.
+     - reimplemented for speed"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    REGISTER int c;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _writeonly) {
+            f = MKFD(_INST(filePointer));
+            while (1) {
+                if (feof(f)) {
+                    RETURN ( nil );
+                }
+                c = getc(f);
+                if (c < 0) {
+                    RETURN ( nil );
+                }
+                switch (c) {
+                    case ' ':
+                    case '\t':
+                    case '\n':
+                    case '\r':
+                    case '\b':
+                    case '\014':
+                        break;
+                    default:
+                        ungetc(c, f);
+                        RETURN ( _MKCHARACTER(c & 0xFF) );
+                }
+            }
+        }
+    }
+%}
+.
+    (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+    self errorNotOpen
+!
+
+skipSeparatorsExceptCR
+    "skip all whitespace but no newlines;
+     next will return next non-white-space character
+     or nil if endOfFile reached.
+     - reimplemented for speed"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    int c;
+    extern int _immediateInterrupt;
+
+    if (_INST(filePointer) != nil) {
+        if (_INST(mode) != _writeonly) {
+            f = MKFD(_INST(filePointer));
+            while (1) {
+                if (feof(f)) {
+                    RETURN ( nil );
+                }
+                c = getc(f);
+                if (c < 0) {
+                    RETURN ( nil );
+                }
+                switch (c) {
+                    case ' ':
+                    case '\t':
+                    case '\b':
+                        break;
+                    default:
+                        ungetc(c, f);
+                        RETURN ( _MKCHARACTER(c & 0xFF) );
+                }
+            }
+        }
+    }
+%}
+.
+    (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+    self errorNotOpen
+!
+
+nextChunk
+    "return the next chunk, i.e. all characters up to the next
+     non-doubled exclamation mark; undouble doubled exclamation marks.
+     - reimplemented for speed"
+    |retVal|
+
+    filePointer isNil ifTrue:[
+        ^ self errorNotOpen
+    ].
+%{
+    FILE *f;
+    int done = 0;
+    REGISTER int c;
+    unsigned char peekC;
+    char *buffer, *newBuffer;
+    REGISTER int index;
+    int currSize;
+    int inComment, inString, inPrimitive = 0;
+    extern int _immediateInterrupt;
+
+    f = MKFD(_INST(filePointer));
+    /*
+     * skip spaces
+     */
+    while (! done) {
+        if (feof(f)) {
+            RETURN ( nil );
+        }
+        c = getc(f);
+        switch (c) {
+            case ' ':
+            case '\t':
+            case '\n':
+            case '\r':
+            case '\b':
+            case '\014':
+                break;
+
+            case EOF:
+                RETURN ( nil );
+
+            default:
+                ungetc(c, f);
+                done = 1;
+                break;
+        }
+    }
+
+    /*
+     * read chunk into a buffer
+     */
+    buffer = (char *)malloc(3000);
+    currSize = 3000;
+    index = 0;
+    while (! feof(f)) {
+        /* do we have to resize the buffer ? */
+        if ((index+2) >= currSize) {
+            newBuffer = (char *)malloc(currSize * 2);
+            bcopy(buffer, newBuffer, index);
+            free(buffer);
+            buffer = newBuffer;
+            currSize = currSize * 2;
+        }
+        c = getc(f);
+        if (c == '%') {
+            peekC = getc(f);
+            ungetc(peekC, f);
+            if (peekC == '{') {
+                inPrimitive++;
+            } else if (peekC == '}') {
+                inPrimitive--;
+            }
+        } else {
+            if (! inPrimitive) {
+                if (c == '!') {
+                    c = getc(f);
+                    if (c != '!') {
+                        ungetc(c, f);
+                        break;
+                    }
+                }
+            }
+        }
+        if (c == EOF) break;
+        buffer[index++] = c;
+    }
+    buffer[index] = '\0';
+    /*
+     * make it a string
+     */
+    retVal = _MKSTRING(buffer COMMA_CON);
+    free(buffer);
+%}
+.
+    ^ retVal
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/False.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,120 @@
+"
+ COPYRIGHT (c) 1988-92 by Claus Gittinger
+	      All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Boolean subclass:#False
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Kernel-Objects'
+!
+
+False comment:'
+
+COPYRIGHT (c) 1988-92 by Claus Gittinger
+	      All Rights Reserved
+
+Class False has only one instance, false, representing logical falsehood.
+
+%W% %E%
+'!
+
+!False methodsFor:'logical operations'!
+
+& aBoolean
+    "return true, if both the receiver and the argument are true
+     (since the receiver is false, return false)"
+
+    ^ false
+!
+
+| aBoolean
+    "return true, if either the receiver or the argument is true
+     (since the receiver is false, return the argument)"
+
+    ^ aBoolean
+!
+
+not
+    "return true, if the receiver is false, false otherwise
+     (since the receiver is false, return true)"
+
+    ^ true
+!
+
+eqv:aBoolean
+    "return true, if the receiver and the argument are the same truth value
+     (since the receiver is false, return true if the argument is also false)"
+
+    ^ aBoolean not
+!
+
+xor:aBoolean
+    "return true, if the receiver and the argument are different truth values
+     (since the receiver is false, return true, if the armument is not false)"
+
+    ^ aBoolean
+! !
+
+!False methodsFor:'conditional evaluation'!
+
+and:aBlock
+    "evaluate aBlock if the receiver is true.
+     (since the receiver is false return false).
+     - open coded by compiler"
+
+    ^ self
+!
+
+or:aBlock
+    "evaluate aBlock if the receiver is false.
+     (since the receiver is false return the value of evaluating aBlock).
+     - open coded by compiler"
+
+    ^ aBlock value
+!
+
+ifFalse:aBlock
+    "return the value of evaluating aBlock if the receiver is false.
+     (since the receiver is known to be false always evaluate)
+     - open coded by compiler"
+
+    ^ aBlock value
+!
+
+ifTrue:aBlock
+    "return the false alternative, nil (since the receiver is false)
+     - open coded by compiler"
+
+    ^ nil
+!
+
+ifTrue:trueBlock ifFalse:falseBlock
+    "return the value of evaluating falseBlock (since the receiver is false)
+     - open coded by compiler"
+
+    ^ falseBlock value
+!
+
+ifFalse:falseBlock ifTrue:trueBlock
+    "return the value of evaluating falseBlock (since the receiver is false)
+     - open coded by compiler"
+
+    ^ falseBlock value
+! !
+
+!False methodsFor: 'printing'!
+
+printString
+    "return a Character sequence representing the receiver"
+
+    ^ 'false'
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FileDir.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,553 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Collection subclass:#FileDirectory
+       instanceVariableNames:'pathName lazy'
+       classVariableNames:'pathOfCurrentDirectory'
+       poolDictionaries:''
+       category:'Collections-Files'
+!
+
+FileDirectory comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+             All Rights Reserved
+
+FileDirectories represent directories in the underlying host system.
+They provide various methods to create/delete and query for files and/or
+directories.
+
+%W% %E%
+
+written winter 89 by claus
+'!
+
+!FileDirectory class methodsFor:'instance creation'!
+
+rootDirectory
+    "create and return a new FileDirectory for the root directory"
+
+    ^ (self basicNew) pathName:'/'
+!
+
+currentDirectory
+    "create and return a new FileDirectory for the current directory"
+
+    ^ (self basicNew) pathName:'.'
+!
+
+directoryNamed:name
+    "create and return a new FileDirectory for the directory
+     with given pathname"
+
+    ^ (self basicNew) pathName:name
+!
+
+directoryNamed:name in:aFileDirectory
+    "create and return a new FileDirectory for the directory with given name
+     in another FileDirectory"
+
+    |baseName|
+
+    ((name at:1) == $/) ifTrue:[
+        ^ self directoryNamed:name
+    ].
+    (aFileDirectory isKindOf:FileDirectory) ifTrue:[
+        baseName := aFileDirectory pathName
+    ] ifFalse:[
+        baseName := aFileDirectory
+    ].
+"
+    (name = '..') ifTrue:[
+        ^ (self basicNew) pathName:(OperatingSystem directoryNameOf:baseName)
+    ].
+"
+    (name = '.') ifTrue:[^ aFileDirectory].
+
+    (baseName = '/') ifFalse:[
+        (baseName endsWith:'/') ifFalse:[
+            baseName := baseName , '/'
+        ]
+    ].
+    ^ (self basicNew) pathName:(baseName , name)
+! !
+
+!FileDirectory methodsFor:'accessing'!
+
+baseName
+    "return my baseName
+     - thats the directory name without leading parent-dirs"
+
+    lazy ifTrue:[self getFullPathName].
+    ^ OperatingSystem baseNameOf:pathName
+!
+
+directoryName
+    "return my directoryName
+     - thats the directory name where I'm in"
+
+    lazy ifTrue:[self getFullPathName].
+    ^ OperatingSystem directoryNameOf:pathName
+!
+
+pathName
+    "return my full pathname"
+
+    lazy ifTrue:[self getFullPathName].
+    ^ pathName
+!
+
+pathName:dirName
+    "set my pathname; return nil if not a valid path; self otherwise"
+
+    pathName := dirName.
+    (dirName startsWith:'/') ifFalse:[
+        lazy := true
+    ] ifTrue:[
+        (dirName includes:$.) ifTrue:[
+            lazy := true
+        ]
+    ].
+    ^ self
+"
+    (OperatingSystem isDirectory:pathName) ifFalse:[^ nil]
+"
+!
+
+contents 
+    "return a collection with all files and subdirectories in the receiver"
+
+    |coll|
+
+    coll := OrderedCollection new.
+    self do:[:name |
+        coll add:name
+    ].
+    (coll size ~~ 0) ifTrue:[
+        coll sort
+    ].
+    ^ coll
+!
+
+directories
+    "return a collection with all subdirectories in the receiver directory"
+
+    |coll|
+
+    coll := OrderedCollection new.
+    self directoriesDo:[:name |
+        coll add:name
+    ].
+    (coll size ~~ 0) ifTrue:[
+        coll sort
+    ].
+    ^ coll
+!
+
+files
+    "return a collection with all plain files in the receiver directory"
+
+    |coll|
+
+    coll := OrderedCollection new.
+    self filesDo:[:name |
+        coll add:name
+    ].
+    ^ coll sort
+! !
+
+!FileDirectory methodsFor:'private'!
+
+getFullPathName
+    "make my pathname be a full pathname - i.e. starting at root"
+
+    |aStream command shortPathName fullPathName|
+
+    (pathName = '/') ifTrue:[
+        lazy := false.
+        ^ self
+    ].
+
+    "since currentDirectory is used very often, cache its path here"
+
+    (pathName = '.') ifTrue:[
+        pathOfCurrentDirectory notNil ifTrue:[
+            pathName := pathOfCurrentDirectory.
+            lazy := false.
+            ^ self
+        ]
+    ].
+
+    shortPathName := pathName.
+
+    "sys5.4 and sunos have a convenient function for this ..."
+%{
+#if defined(SYSV4) || defined(sunos)
+#   include <stdlib.h>
+#   include <sys/param.h>
+
+    char nameBuffer[MAXPATHLEN + 1];
+
+    if (realpath(_stringVal(_INST(pathName)), nameBuffer)) {
+	fullPathName = _MKSTRING(nameBuffer COMMA_CON);
+    }
+#endif
+%}
+.
+    fullPathName notNil ifTrue:[
+	pathName := fullPathName.
+	lazy := false
+    ] ifFalse:[
+        "since there might be symbolic links and other stuff involved,
+         better trust pwd than removing '..' by ourself
+         - although this is very slow"
+
+        command := 'cd ' , pathName , '; pwd'.
+        aStream := PipeStream readingFrom:command.
+        aStream isNil ifFalse:[
+            (aStream atEnd) ifFalse:[
+                fullPathName := aStream nextLine
+            ].
+            aStream close.
+            fullPathName notNil ifTrue:[
+                pathName := fullPathName.
+                lazy := false
+            ]
+        ] ifTrue:[
+            self error:('PipeStream for <' , command , '> failed').
+            "by clearing lazy, we avoid triggering the error again"
+            lazy := false
+        ]
+    ].
+
+    "if it was the current dir, keep name for next query"
+    (shortPathName = '.') ifTrue:[
+        pathOfCurrentDirectory := fullPathName
+    ]
+! !
+
+!FileDirectory methodsFor:'basic'!
+
+createDirectory:newName
+    "create a new filedirectory as a subdirectory of myself;
+     return true if successful"
+
+    |realName|
+
+    (newName = '.') ifFalse:[
+        (newName = '..') ifFalse:[
+            ((newName at:1) == $/) ifTrue:[
+                realName := newName copyFrom:2
+            ] ifFalse:[
+                realName := newName
+            ].
+            (realName startsWith:'/') ifTrue:[
+                ^ OperatingSystem createDirectory:realName
+            ] ifFalse:[
+                ^ OperatingSystem createDirectory:(pathName , '/' , realName)
+            ]
+        ]
+    ].
+    ^ false
+!
+
+removeFile:fileName
+    "remove the file 'fileName' from myself; return true if successful"
+
+    (fileName startsWith:'/') ifTrue:[
+        ^ OperatingSystem removeFile:fileName
+    ].
+    ^ OperatingSystem removeFile:(pathName , '/' , fileName)
+!
+
+removeDirectory:dirName
+    "remove the directory 'dirName' from myself; return true if successful"
+
+    (dirName startsWith:'/') ifTrue:[
+        ^ OperatingSystem removeDirectory:dirName
+    ].
+    ^ OperatingSystem removeDirectory:(pathName , '/' , dirName)
+!
+    
+remove:aFileOrDirectoryName
+    "remove the file or directory from myself; return true if successful"
+
+    |path|
+
+    (aFileOrDirectoryName startsWith:'/') ifTrue:[
+        path := aFileOrDirectoryName
+    ] ifFalse:[
+        path := (pathName , '/' , aFileOrDirectoryName)
+    ].
+    (OperatingSystem isDirectory:path) ifTrue:[
+        ^ OperatingSystem removeDirectory:path
+    ].
+    ^ OperatingSystem removeFile:path
+!
+
+link:oldFileName to:newFileName
+    "link oldFileName to newFileName in myself, return true if successful"
+
+    |path1 path2|
+
+    (oldFileName startsWith:'/') ifTrue:[
+        path1 := oldFileName
+    ] ifFalse:[
+        path1 := (pathName , '/' , oldFileName)
+    ].
+    (newFileName startsWith:'/') ifTrue:[
+        path2 := newFileName
+    ] ifFalse:[
+        path2 := (pathName , '/' , newFileName)
+    ].
+    ^ OperatingSystem link:path1 to:path2
+!
+
+renameFile:oldFileName newName:newFileName
+    "rename the file; return true if successful"
+
+    |path1 path2|
+
+    (oldFileName startsWith:'/') ifTrue:[
+        path1 := oldFileName
+    ] ifFalse:[
+        path1 := (pathName , '/' , oldFileName)
+    ].
+    (newFileName startsWith:'/') ifTrue:[
+        path2 := newFileName
+    ] ifFalse:[
+        path2 := (pathName , '/' , newFileName)
+    ].
+    ^ OperatingSystem rename:path1 to:path2
+! !
+
+!FileDirectory methodsFor:'queries'!
+
+id
+    "return the directories file-id (inode number)"
+
+    ^ OperatingSystem idOf:pathName
+!
+
+exists
+    "return true if this directory exists"
+
+    ^ OperatingSystem isDirectory:pathName
+    "(FileDirectory directoryNamed:'fooBar') exists"
+!
+
+infoOf:name
+    "return an array filled with file info for the file 'aFileName';
+     return nil if such a file does not exist"
+
+    (name startsWith:'/') ifTrue:[
+        ^ OperatingSystem infoOf:name
+    ].
+    ^ OperatingSystem infoOf:(pathName , '/' , name)
+!
+
+timeOfLastChange:name
+    "return the timeStamp of a file in myself"
+
+    (name startsWith:'/') ifTrue:[
+        ^ OperatingSystem timeOfLastChange:name
+    ].
+    ^ OperatingSystem timeOfLastChange:(pathName , '/' , name)
+!
+
+timeOfLastChange
+    "return the timeStamp of myself"
+
+    ^ OperatingSystem timeOfLastChange:pathName
+!
+
+accessModeOf:aFileName
+    "return the access-mode bits (rwxrwxrwx) of a file in myself"
+
+    (aFileName startsWith:'/') ifTrue:[
+        ^ OperatingSystem accessModeOf:aFileName
+    ].
+    ^ OperatingSystem accessModeOf:(pathName , '/' , aFileName)
+!
+
+changeAccessModeOf:aFileName to:modeBits
+    "set the access-mode bits (rwxrwxrwx) of a file in myself"
+
+    (aFileName startsWith:'/') ifTrue:[
+        ^ OperatingSystem changeAccessModeOf:aFileName
+                                          to:modeBits
+    ].
+    ^ OperatingSystem changeAccessModeOf:(pathName , '/' , aFileName)
+                                      to:modeBits
+!
+
+typeOf:aFileName
+    "return the symbolic type of a file in myself"
+
+    (aFileName startsWith:'/') ifTrue:[
+        ^ OperatingSystem typeOf:aFileName
+    ].
+    ^ OperatingSystem typeOf:(pathName , '/' , aFileName)
+!
+
+isDirectory:name
+    "return true, if the given name is that of a directory in myself"
+
+    (name startsWith:'/') ifTrue:[
+        ^ OperatingSystem isDirectory:name
+    ].
+    ^ OperatingSystem isDirectory:(pathName , '/' , name)
+!
+
+isReadable:name
+    "return true, if the given file is readable"
+
+    (name startsWith:'/') ifTrue:[
+        ^ OperatingSystem isReadable:name
+    ].
+    ^ OperatingSystem isReadable:(pathName , '/' , name)
+!
+
+isWritable:name
+    "return true, if the given file is readable"
+
+    (name startsWith:'/') ifTrue:[
+        ^ OperatingSystem isWritable:name
+    ].
+    ^ OperatingSystem isWritable:(pathName , '/' , name)
+!
+
+isExecutable:name
+    "return true, if the given file is executable"
+
+    (name startsWith:'/') ifTrue:[
+        ^ OperatingSystem isExecutable:name
+    ].
+    ^ OperatingSystem isExecutable:(pathName , '/' , name)
+! !
+
+!FileDirectory methodsFor:'printing & storing'!
+
+printString
+    lazy ifTrue:[self getFullPathName].
+    ^ '(a FileDirectory pathName:' , pathName, ')'
+!
+
+storeOn:aStream
+    lazy ifTrue:[self getFullPathName].
+    aStream nextPutAll:'(FileDirectory directoryNamed:'.
+    aStream nextPutAll:pathName.
+    aStream nextPut:$)
+! !
+
+!FileDirectory methodsFor:'more instance creation'!
+
+directoryNamed:aName
+    ^ self class directoryNamed:aName in:self pathName
+! !
+
+!FileDirectory methodsFor:'enumerating'!
+
+where:testBlock do:aBlock
+    "evaluate the argument, aBlock for every object in the directory
+     for which testBlock evaluates to true."
+
+    |aStream name|
+
+    aStream := DirectoryStream directoryNamed:pathName.
+    aStream isNil ifTrue:[^ nil].
+    [aStream atEnd] whileFalse:[
+        name := aStream nextLine.
+        name notNil ifTrue:[
+            (testBlock value:name) ifTrue:[
+                aBlock value:name
+            ]
+        ]
+    ].
+    aStream close
+!
+
+do:aBlock
+    "evaluate the argument, aBlock for every name in the directory"
+
+    self where:[:name | true] do:aBlock
+!
+
+namesDo:aBlock
+    "evaluate the argument, aBlock for every name in the directory.
+     for ST-80 compatibility"
+
+    self do:aBlock
+!
+
+filesDo:aBlock
+    "evaluate the argument, aBlock for every plain file name in the directory"
+
+    self where:[:name | (self isDirectory:name) not] do:aBlock
+!
+
+directoriesDo:aBlock
+    "evaluate the argument, aBlock for every subdirectory name in the directory"
+
+    self where:[:name | (self isDirectory:name) ifTrue:[
+                            ((name ~= '.') and:[name ~= '..'])
+                        ] ifFalse:[
+                            false
+                        ]
+               ] do:aBlock
+!
+
+allFilesDo:aBlock
+    "evaluate the argument, aBlock for every file name in the directory and in all
+     subdirectories"
+
+    |aStream command line|
+
+    lazy ifTrue:[self getFullPathName].
+    command := 'cd ' , pathName , '; find . -print'.
+    aStream := PipeStream readingFrom:command.
+    aStream isNil ifTrue:[^ nil].
+    [aStream atEnd] whileFalse:[
+        line := aStream nextLine.
+        line notNil ifTrue:[
+            (line = '.') ifFalse:[
+                "cut off initial ./"
+                line := line copyFrom:3 to:(line size)
+            ].
+            aBlock value:line
+        ]
+    ].
+    aStream close
+!
+
+allDirectoriesDo:aBlock
+    "evaluate the argument, aBlock for every directory name
+     in the directory and in all subdirectories"
+
+    |aStream command line|
+
+    lazy ifTrue:[self getFullPathName].
+    command := 'cd ' , pathName , '; find . -type d -print'.
+    aStream := PipeStream readingFrom:command.
+    aStream isNil ifTrue:[^ nil].
+    [aStream atEnd] whileFalse:[
+        line := aStream nextLine.
+        line notNil ifTrue:[
+            (line = '.') ifFalse:[
+                "cut off initial ./"
+                line := line copyFrom:3 to:(line size)
+            ].
+            aBlock value:line
+        ]
+    ].
+    aStream close
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FileDirectory.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,553 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Collection subclass:#FileDirectory
+       instanceVariableNames:'pathName lazy'
+       classVariableNames:'pathOfCurrentDirectory'
+       poolDictionaries:''
+       category:'Collections-Files'
+!
+
+FileDirectory comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+             All Rights Reserved
+
+FileDirectories represent directories in the underlying host system.
+They provide various methods to create/delete and query for files and/or
+directories.
+
+%W% %E%
+
+written winter 89 by claus
+'!
+
+!FileDirectory class methodsFor:'instance creation'!
+
+rootDirectory
+    "create and return a new FileDirectory for the root directory"
+
+    ^ (self basicNew) pathName:'/'
+!
+
+currentDirectory
+    "create and return a new FileDirectory for the current directory"
+
+    ^ (self basicNew) pathName:'.'
+!
+
+directoryNamed:name
+    "create and return a new FileDirectory for the directory
+     with given pathname"
+
+    ^ (self basicNew) pathName:name
+!
+
+directoryNamed:name in:aFileDirectory
+    "create and return a new FileDirectory for the directory with given name
+     in another FileDirectory"
+
+    |baseName|
+
+    ((name at:1) == $/) ifTrue:[
+        ^ self directoryNamed:name
+    ].
+    (aFileDirectory isKindOf:FileDirectory) ifTrue:[
+        baseName := aFileDirectory pathName
+    ] ifFalse:[
+        baseName := aFileDirectory
+    ].
+"
+    (name = '..') ifTrue:[
+        ^ (self basicNew) pathName:(OperatingSystem directoryNameOf:baseName)
+    ].
+"
+    (name = '.') ifTrue:[^ aFileDirectory].
+
+    (baseName = '/') ifFalse:[
+        (baseName endsWith:'/') ifFalse:[
+            baseName := baseName , '/'
+        ]
+    ].
+    ^ (self basicNew) pathName:(baseName , name)
+! !
+
+!FileDirectory methodsFor:'accessing'!
+
+baseName
+    "return my baseName
+     - thats the directory name without leading parent-dirs"
+
+    lazy ifTrue:[self getFullPathName].
+    ^ OperatingSystem baseNameOf:pathName
+!
+
+directoryName
+    "return my directoryName
+     - thats the directory name where I'm in"
+
+    lazy ifTrue:[self getFullPathName].
+    ^ OperatingSystem directoryNameOf:pathName
+!
+
+pathName
+    "return my full pathname"
+
+    lazy ifTrue:[self getFullPathName].
+    ^ pathName
+!
+
+pathName:dirName
+    "set my pathname; return nil if not a valid path; self otherwise"
+
+    pathName := dirName.
+    (dirName startsWith:'/') ifFalse:[
+        lazy := true
+    ] ifTrue:[
+        (dirName includes:$.) ifTrue:[
+            lazy := true
+        ]
+    ].
+    ^ self
+"
+    (OperatingSystem isDirectory:pathName) ifFalse:[^ nil]
+"
+!
+
+contents 
+    "return a collection with all files and subdirectories in the receiver"
+
+    |coll|
+
+    coll := OrderedCollection new.
+    self do:[:name |
+        coll add:name
+    ].
+    (coll size ~~ 0) ifTrue:[
+        coll sort
+    ].
+    ^ coll
+!
+
+directories
+    "return a collection with all subdirectories in the receiver directory"
+
+    |coll|
+
+    coll := OrderedCollection new.
+    self directoriesDo:[:name |
+        coll add:name
+    ].
+    (coll size ~~ 0) ifTrue:[
+        coll sort
+    ].
+    ^ coll
+!
+
+files
+    "return a collection with all plain files in the receiver directory"
+
+    |coll|
+
+    coll := OrderedCollection new.
+    self filesDo:[:name |
+        coll add:name
+    ].
+    ^ coll sort
+! !
+
+!FileDirectory methodsFor:'private'!
+
+getFullPathName
+    "make my pathname be a full pathname - i.e. starting at root"
+
+    |aStream command shortPathName fullPathName|
+
+    (pathName = '/') ifTrue:[
+        lazy := false.
+        ^ self
+    ].
+
+    "since currentDirectory is used very often, cache its path here"
+
+    (pathName = '.') ifTrue:[
+        pathOfCurrentDirectory notNil ifTrue:[
+            pathName := pathOfCurrentDirectory.
+            lazy := false.
+            ^ self
+        ]
+    ].
+
+    shortPathName := pathName.
+
+    "sys5.4 and sunos have a convenient function for this ..."
+%{
+#if defined(SYSV4) || defined(sunos)
+#   include <stdlib.h>
+#   include <sys/param.h>
+
+    char nameBuffer[MAXPATHLEN + 1];
+
+    if (realpath(_stringVal(_INST(pathName)), nameBuffer)) {
+	fullPathName = _MKSTRING(nameBuffer COMMA_CON);
+    }
+#endif
+%}
+.
+    fullPathName notNil ifTrue:[
+	pathName := fullPathName.
+	lazy := false
+    ] ifFalse:[
+        "since there might be symbolic links and other stuff involved,
+         better trust pwd than removing '..' by ourself
+         - although this is very slow"
+
+        command := 'cd ' , pathName , '; pwd'.
+        aStream := PipeStream readingFrom:command.
+        aStream isNil ifFalse:[
+            (aStream atEnd) ifFalse:[
+                fullPathName := aStream nextLine
+            ].
+            aStream close.
+            fullPathName notNil ifTrue:[
+                pathName := fullPathName.
+                lazy := false
+            ]
+        ] ifTrue:[
+            self error:('PipeStream for <' , command , '> failed').
+            "by clearing lazy, we avoid triggering the error again"
+            lazy := false
+        ]
+    ].
+
+    "if it was the current dir, keep name for next query"
+    (shortPathName = '.') ifTrue:[
+        pathOfCurrentDirectory := fullPathName
+    ]
+! !
+
+!FileDirectory methodsFor:'basic'!
+
+createDirectory:newName
+    "create a new filedirectory as a subdirectory of myself;
+     return true if successful"
+
+    |realName|
+
+    (newName = '.') ifFalse:[
+        (newName = '..') ifFalse:[
+            ((newName at:1) == $/) ifTrue:[
+                realName := newName copyFrom:2
+            ] ifFalse:[
+                realName := newName
+            ].
+            (realName startsWith:'/') ifTrue:[
+                ^ OperatingSystem createDirectory:realName
+            ] ifFalse:[
+                ^ OperatingSystem createDirectory:(pathName , '/' , realName)
+            ]
+        ]
+    ].
+    ^ false
+!
+
+removeFile:fileName
+    "remove the file 'fileName' from myself; return true if successful"
+
+    (fileName startsWith:'/') ifTrue:[
+        ^ OperatingSystem removeFile:fileName
+    ].
+    ^ OperatingSystem removeFile:(pathName , '/' , fileName)
+!
+
+removeDirectory:dirName
+    "remove the directory 'dirName' from myself; return true if successful"
+
+    (dirName startsWith:'/') ifTrue:[
+        ^ OperatingSystem removeDirectory:dirName
+    ].
+    ^ OperatingSystem removeDirectory:(pathName , '/' , dirName)
+!
+    
+remove:aFileOrDirectoryName
+    "remove the file or directory from myself; return true if successful"
+
+    |path|
+
+    (aFileOrDirectoryName startsWith:'/') ifTrue:[
+        path := aFileOrDirectoryName
+    ] ifFalse:[
+        path := (pathName , '/' , aFileOrDirectoryName)
+    ].
+    (OperatingSystem isDirectory:path) ifTrue:[
+        ^ OperatingSystem removeDirectory:path
+    ].
+    ^ OperatingSystem removeFile:path
+!
+
+link:oldFileName to:newFileName
+    "link oldFileName to newFileName in myself, return true if successful"
+
+    |path1 path2|
+
+    (oldFileName startsWith:'/') ifTrue:[
+        path1 := oldFileName
+    ] ifFalse:[
+        path1 := (pathName , '/' , oldFileName)
+    ].
+    (newFileName startsWith:'/') ifTrue:[
+        path2 := newFileName
+    ] ifFalse:[
+        path2 := (pathName , '/' , newFileName)
+    ].
+    ^ OperatingSystem link:path1 to:path2
+!
+
+renameFile:oldFileName newName:newFileName
+    "rename the file; return true if successful"
+
+    |path1 path2|
+
+    (oldFileName startsWith:'/') ifTrue:[
+        path1 := oldFileName
+    ] ifFalse:[
+        path1 := (pathName , '/' , oldFileName)
+    ].
+    (newFileName startsWith:'/') ifTrue:[
+        path2 := newFileName
+    ] ifFalse:[
+        path2 := (pathName , '/' , newFileName)
+    ].
+    ^ OperatingSystem rename:path1 to:path2
+! !
+
+!FileDirectory methodsFor:'queries'!
+
+id
+    "return the directories file-id (inode number)"
+
+    ^ OperatingSystem idOf:pathName
+!
+
+exists
+    "return true if this directory exists"
+
+    ^ OperatingSystem isDirectory:pathName
+    "(FileDirectory directoryNamed:'fooBar') exists"
+!
+
+infoOf:name
+    "return an array filled with file info for the file 'aFileName';
+     return nil if such a file does not exist"
+
+    (name startsWith:'/') ifTrue:[
+        ^ OperatingSystem infoOf:name
+    ].
+    ^ OperatingSystem infoOf:(pathName , '/' , name)
+!
+
+timeOfLastChange:name
+    "return the timeStamp of a file in myself"
+
+    (name startsWith:'/') ifTrue:[
+        ^ OperatingSystem timeOfLastChange:name
+    ].
+    ^ OperatingSystem timeOfLastChange:(pathName , '/' , name)
+!
+
+timeOfLastChange
+    "return the timeStamp of myself"
+
+    ^ OperatingSystem timeOfLastChange:pathName
+!
+
+accessModeOf:aFileName
+    "return the access-mode bits (rwxrwxrwx) of a file in myself"
+
+    (aFileName startsWith:'/') ifTrue:[
+        ^ OperatingSystem accessModeOf:aFileName
+    ].
+    ^ OperatingSystem accessModeOf:(pathName , '/' , aFileName)
+!
+
+changeAccessModeOf:aFileName to:modeBits
+    "set the access-mode bits (rwxrwxrwx) of a file in myself"
+
+    (aFileName startsWith:'/') ifTrue:[
+        ^ OperatingSystem changeAccessModeOf:aFileName
+                                          to:modeBits
+    ].
+    ^ OperatingSystem changeAccessModeOf:(pathName , '/' , aFileName)
+                                      to:modeBits
+!
+
+typeOf:aFileName
+    "return the symbolic type of a file in myself"
+
+    (aFileName startsWith:'/') ifTrue:[
+        ^ OperatingSystem typeOf:aFileName
+    ].
+    ^ OperatingSystem typeOf:(pathName , '/' , aFileName)
+!
+
+isDirectory:name
+    "return true, if the given name is that of a directory in myself"
+
+    (name startsWith:'/') ifTrue:[
+        ^ OperatingSystem isDirectory:name
+    ].
+    ^ OperatingSystem isDirectory:(pathName , '/' , name)
+!
+
+isReadable:name
+    "return true, if the given file is readable"
+
+    (name startsWith:'/') ifTrue:[
+        ^ OperatingSystem isReadable:name
+    ].
+    ^ OperatingSystem isReadable:(pathName , '/' , name)
+!
+
+isWritable:name
+    "return true, if the given file is readable"
+
+    (name startsWith:'/') ifTrue:[
+        ^ OperatingSystem isWritable:name
+    ].
+    ^ OperatingSystem isWritable:(pathName , '/' , name)
+!
+
+isExecutable:name
+    "return true, if the given file is executable"
+
+    (name startsWith:'/') ifTrue:[
+        ^ OperatingSystem isExecutable:name
+    ].
+    ^ OperatingSystem isExecutable:(pathName , '/' , name)
+! !
+
+!FileDirectory methodsFor:'printing & storing'!
+
+printString
+    lazy ifTrue:[self getFullPathName].
+    ^ '(a FileDirectory pathName:' , pathName, ')'
+!
+
+storeOn:aStream
+    lazy ifTrue:[self getFullPathName].
+    aStream nextPutAll:'(FileDirectory directoryNamed:'.
+    aStream nextPutAll:pathName.
+    aStream nextPut:$)
+! !
+
+!FileDirectory methodsFor:'more instance creation'!
+
+directoryNamed:aName
+    ^ self class directoryNamed:aName in:self pathName
+! !
+
+!FileDirectory methodsFor:'enumerating'!
+
+where:testBlock do:aBlock
+    "evaluate the argument, aBlock for every object in the directory
+     for which testBlock evaluates to true."
+
+    |aStream name|
+
+    aStream := DirectoryStream directoryNamed:pathName.
+    aStream isNil ifTrue:[^ nil].
+    [aStream atEnd] whileFalse:[
+        name := aStream nextLine.
+        name notNil ifTrue:[
+            (testBlock value:name) ifTrue:[
+                aBlock value:name
+            ]
+        ]
+    ].
+    aStream close
+!
+
+do:aBlock
+    "evaluate the argument, aBlock for every name in the directory"
+
+    self where:[:name | true] do:aBlock
+!
+
+namesDo:aBlock
+    "evaluate the argument, aBlock for every name in the directory.
+     for ST-80 compatibility"
+
+    self do:aBlock
+!
+
+filesDo:aBlock
+    "evaluate the argument, aBlock for every plain file name in the directory"
+
+    self where:[:name | (self isDirectory:name) not] do:aBlock
+!
+
+directoriesDo:aBlock
+    "evaluate the argument, aBlock for every subdirectory name in the directory"
+
+    self where:[:name | (self isDirectory:name) ifTrue:[
+                            ((name ~= '.') and:[name ~= '..'])
+                        ] ifFalse:[
+                            false
+                        ]
+               ] do:aBlock
+!
+
+allFilesDo:aBlock
+    "evaluate the argument, aBlock for every file name in the directory and in all
+     subdirectories"
+
+    |aStream command line|
+
+    lazy ifTrue:[self getFullPathName].
+    command := 'cd ' , pathName , '; find . -print'.
+    aStream := PipeStream readingFrom:command.
+    aStream isNil ifTrue:[^ nil].
+    [aStream atEnd] whileFalse:[
+        line := aStream nextLine.
+        line notNil ifTrue:[
+            (line = '.') ifFalse:[
+                "cut off initial ./"
+                line := line copyFrom:3 to:(line size)
+            ].
+            aBlock value:line
+        ]
+    ].
+    aStream close
+!
+
+allDirectoriesDo:aBlock
+    "evaluate the argument, aBlock for every directory name
+     in the directory and in all subdirectories"
+
+    |aStream command line|
+
+    lazy ifTrue:[self getFullPathName].
+    command := 'cd ' , pathName , '; find . -type d -print'.
+    aStream := PipeStream readingFrom:command.
+    aStream isNil ifTrue:[^ nil].
+    [aStream atEnd] whileFalse:[
+        line := aStream nextLine.
+        line notNil ifTrue:[
+            (line = '.') ifFalse:[
+                "cut off initial ./"
+                line := line copyFrom:3 to:(line size)
+            ].
+            aBlock value:line
+        ]
+    ].
+    aStream close
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FileStr.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,467 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+ExternalStream subclass:#FileStream
+       instanceVariableNames:'pathName'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Streams-External'
+!
+
+FileStream comment:'
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+This class provides access to the operating systems underlying file
+system (i.e. its an interface to the stdio library).
+
+%W% %E%
+'!
+
+%{
+#include <stdio.h>
+
+#ifdef transputer
+# include <iocntrl.h>
+# ifndef fileno
+   /* kludge: inmos forgot fileno */
+#  define fileno(f)     ((f)->__file)
+# endif
+#else
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif
+%}
+
+!FileStream class methodsFor:'instance creation'!
+
+newFileNamed:filename
+    "return a FileStream for new file named filename, aString.
+     If the file exists, it is truncated, otherwise created.
+     The file is opened for write access only."
+
+    |newStream|
+    newStream := (self basicNew) pathName:filename.
+    newStream createForWriting isNil ifTrue:[^nil].
+    ^ newStream
+!
+
+newFileNamed:filename in:aDirectory
+    "return a FileStream for new file named filename, aString
+     in aDirectory, a FileDirectory.
+     If the file exists, it is truncated, otherwise created.
+     The file is opened for write access only."
+
+    |newStream|
+    newStream := (self basicNew) pathName:filename in:aDirectory.
+    newStream createForWriting isNil ifTrue:[^nil].
+    ^ newStream
+!
+
+oldFileNamed:filename
+    "return a FileStream for existing file named filename, aString.
+     The file is opened for read/write access."
+
+    |newStream|
+
+    (OperatingSystem isReadable:filename) ifFalse:[^nil].
+    newStream := (self basicNew) pathName:filename.
+    newStream readwrite.
+    newStream openForReadWrite isNil ifTrue:[^nil].
+    newStream readLimit:(newStream size).
+    ^ newStream
+!
+
+oldFileNamed:filename in:aDirectory
+    "return a FileStream for existing file named filename, aString
+     in aDirectory, a FileDirectory.
+     The file is opened for read/write access."
+
+    |newStream|
+    newStream := (self basicNew) pathName:filename in:aDirectory.
+    newStream openForReadWrite isNil ifTrue:[^nil].
+    newStream readLimit:(newStream size).
+    ^ newStream
+!
+
+fileNamed:filename
+    "return a stream on file filename - if the file does not
+     already exist, create it."
+
+    |stream|
+
+    stream := self oldFileNamed:filename.
+    stream isNil ifTrue:[
+        stream := self newFileNamed:filename
+    ].
+    ^ stream
+!
+
+fileNamed:filename in:aDirectory
+    "return a stream on file filename - if the file does not
+     already exist, create it."
+
+    |stream|
+
+    stream := self oldFileNamed:filename in:aDirectory.
+    stream isNil ifTrue:[
+        stream := self newFileNamed:filename in:aDirectory
+    ].
+    ^ stream
+!
+
+readonlyFileNamed:filename
+    "return a readonly FileStream for existing file named filename, aString"
+
+    |newStream|
+
+    (OperatingSystem isReadable:filename) ifFalse:[^nil].
+
+    newStream := (self basicNew) pathName:filename.
+    newStream openForReading isNil ifTrue:[^nil].
+    newStream readLimit:(newStream size).
+    ^ newStream
+!
+
+readonlyFileNamed:filename in:aDirectory
+    "return a readonly FileStream for existing file named filename, aString
+     in aDirectory, a FileDirectory"
+
+    |newStream|
+    newStream := (self basicNew) pathName:filename in:aDirectory.
+    newStream openForReading isNil ifTrue:[^nil].
+    newStream readLimit:(newStream size).
+    ^ newStream
+!
+
+appendingOldFileNamed:filename
+    "return a FileStream for existing file named filename, aString"
+
+    |newStream|
+    newStream := (self basicNew) pathName:filename.
+    newStream openForAppending isNil ifTrue:[^nil].
+    newStream readLimit:(newStream size).
+    ^ newStream
+!
+
+appendingOldFileNamed:filename in:aDirectory
+    "return a FileStream for existing file named filename, aString
+     in aDirectory, a FileDirectory"
+
+    |newStream|
+    newStream := (self basicNew) pathName:filename in:aDirectory.
+    newStream openForAppending isNil ifTrue:[^nil].
+    newStream readLimit:(newStream size).
+    ^ newStream
+! !
+
+!FileStream methodsFor:'accessing'!
+
+store:something
+    "what really should this do"
+
+    self nextPutAll:something
+!
+
+directoryName
+    "return the name of the directory I'm in"
+
+    |path lastIndex index|
+
+    path := pathName.
+    lastIndex := 0.
+    index := path indexOf:$/.
+    [index ~~ 0] whileTrue:[
+        lastIndex := index.
+        index := path indexOf:$/ startingAt:(index + 1)
+    ].
+    (lastIndex == 0) ifTrue:[^ '.'].
+    (lastIndex == 1) ifTrue:[^ '/'].
+    ^ path copyFrom:1 to:(lastIndex - 1)
+!
+
+name
+    "return my name without leading direcory-path"
+
+    |lastIndex index|
+
+    lastIndex := 1.
+    [true] whileTrue:[
+        index := pathName indexOf:$/ startingAt:lastIndex.
+        (index == 0) ifTrue:[
+            ^ pathName copyFrom:lastIndex
+        ].
+        lastIndex := index + 1
+    ]
+!
+
+pathName
+    "return the pathname"
+
+    ^ pathName
+! !
+
+!FileStream methodsFor:'private'!
+
+pathName:filename
+    "set the pathname"
+
+    pathName := filename
+!
+
+pathName:filename in:aDirectory
+    "set the pathname starting at aDirectory, a FileDirectory"
+
+    ((filename at:1) == $/) ifTrue:[
+        "filename may not start with a '/'"
+        pathName := nil
+    ] ifFalse:[
+        pathName := aDirectory pathName.
+        (pathName endsWith:'/') ifFalse:[
+            pathName := pathName , '/'
+        ].
+        pathName := pathName , filename
+    ]
+!
+
+open
+    "open the file"
+
+    pathName isNil ifTrue:[^nil].
+    (mode == #readonly) ifTrue: [
+        ^ self openForReading
+    ].
+    (mode == #writeonly) ifTrue: [
+        ^ self openForWriting
+    ].
+    ^ self openForReadWrite
+!
+
+openWithMode:openmode
+    "open the file; openmode is the string defining the way to open"
+
+    |retVal|
+%{
+    FILE *f;
+    OBJ path;
+    extern OBJ ErrorNumber, Filename;
+    extern errno;
+
+    if (_INST(filePointer) == nil) {
+        path = _INST(pathName);
+        if (_isString(path) || (_Class(path) == Filename)) {
+            f = (FILE *)fopen((char *) _stringVal(path), (char *) _stringVal(openmode));
+            if (f == NULL) {
+                ErrorNumber = _MKSMALLINT(errno);
+                _INST(position) = nil;
+            } else {
+                _INST(filePointer) = _MKSMALLINT((int)f);
+                _INST(position) = _MKSMALLINT(1);
+                retVal = self;
+            }
+        }
+    }
+%}
+.
+    retVal notNil ifTrue:[
+        lobby register:self
+    ].
+    ^ retVal
+!
+
+openForReading
+    "open the file for readonly"
+
+    mode := #readonly.
+    ^ self openWithMode:'r'
+!
+
+openForWriting
+    "open the file for writeonly"
+
+    mode := #writeonly.
+    ^ self openWithMode:'w'
+!
+
+openForAppending
+    "open the file for writeonly appending to the end"
+
+    mode := #writeonly.
+    ^ self openWithMode:'a+'
+!
+
+createForWriting
+    "create/truncate the file for writeonly"
+
+    mode := #writeonly.
+    ^ self openWithMode:'w+'
+!
+
+openForReadWrite
+    "open the file for read/write"
+
+    mode := #readwrite.
+    ^ self openWithMode:'r+w'
+!
+
+createForReadWrite
+    "create/truncate the file for read/write"
+
+    mode := #readwrite.
+    ^ self openWithMode:'rw+'
+!
+
+reOpen
+    "sent after snapin to reopen streams"
+
+    filePointer notNil ifTrue:[
+        "it was open, when snapped-out"
+        filePointer := nil.
+        self open.
+        filePointer isNil ifTrue:[
+            Transcript showCr:('could not reopen file: ', pathName)
+        ] ifFalse:[
+            self position:position
+        ]
+    ]
+!
+
+size
+    "return the size in bytes of the file"
+
+%{  /* NOCONTEXT */
+
+#ifdef transputer
+    FILE *f;
+    int size;
+
+    if (_INST(filePointer) != nil) {
+        f = (FILE *)_intVal(_INST(filePointer));
+        if ((size = filesize(fileno(f))) >= 0) {
+            RETURN ( _MKSMALLINT(size) );
+        }
+    }
+#else
+    FILE *f;
+    struct stat buf;
+
+    if (_INST(filePointer) != nil) {
+        f = (FILE *)_intVal(_INST(filePointer));
+        if (fstat(fileno(f), &buf) >= 0) {
+            RETURN ( _MKSMALLINT(buf.st_size) );
+        }
+    }
+#endif
+%}
+.
+    "could add a fall-back here:
+
+        oldPosition := self position.
+        self setToEnd.
+        sz := self position.
+        self position:oldPosition.
+        ^ sz
+    "
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    ^ self primitiveFailed
+!
+
+position
+    "return the read/write position in the file -
+     notice, in smalltalk indices start at 1 so begin of file is 1""
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    long currentPosition;
+
+    if (_INST(filePointer) != nil) {
+        f = (FILE *)_intVal(_INST(filePointer));
+        currentPosition = ftell(f);
+        if (currentPosition >= 0) {
+            /*
+             * notice: Smalltalk index starts at 1
+             */
+            RETURN ( _MKSMALLINT(currentPosition + 1) );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    ^ self primitiveFailed
+!
+
+position:newPos
+    "set the read/write position in the file"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    extern OBJ ErrorNumber;
+    extern errno;
+
+    if (_INST(filePointer) != nil) {
+        if (_isSmallInteger(newPos)) {
+            f = (FILE *)_intVal(_INST(filePointer));
+            /*
+             * notice: Smalltalk index starts at 1
+             */
+            if (fseek(f, _intVal(newPos) - 1, 0) >= 0) {
+                _INST(position) = newPos;
+                RETURN ( self );
+            }
+            ErrorNumber = _MKSMALLINT(errno);
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    ^ self primitiveFailed
+!
+
+setToEnd
+    "set the read/write position in the file to be at the end of the file"
+
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+%{
+    FILE *f;
+    extern OBJ ErrorNumber;
+    extern errno;
+
+    f = (FILE *)_intVal(_INST(filePointer));
+    _INST(position) = nil;
+    if (fseek(f, 0, 2) >= 0) {
+        RETURN ( self );
+    }
+    ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+    ^ self primitiveFailed
+! !
+
+!FileStream methodsFor:'printing & storing'!
+
+printOn:aStream
+    aStream nextPutAll:'(a FileStream for:'.
+    aStream nextPutAll:pathName.
+    aStream nextPut:$)
+!
+
+storeOn:aStream
+    aStream nextPutAll:'(FileStream oldFileNamed:'.
+    aStream nextPutAll:pathName.
+    (self position ~~ 1) ifTrue:[
+        aStream nextPutAll:'; position:'.
+        self position storeOn:aStream
+    ].
+    aStream nextPut:$)
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FileStream.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,467 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+ExternalStream subclass:#FileStream
+       instanceVariableNames:'pathName'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Streams-External'
+!
+
+FileStream comment:'
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+This class provides access to the operating systems underlying file
+system (i.e. its an interface to the stdio library).
+
+%W% %E%
+'!
+
+%{
+#include <stdio.h>
+
+#ifdef transputer
+# include <iocntrl.h>
+# ifndef fileno
+   /* kludge: inmos forgot fileno */
+#  define fileno(f)     ((f)->__file)
+# endif
+#else
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif
+%}
+
+!FileStream class methodsFor:'instance creation'!
+
+newFileNamed:filename
+    "return a FileStream for new file named filename, aString.
+     If the file exists, it is truncated, otherwise created.
+     The file is opened for write access only."
+
+    |newStream|
+    newStream := (self basicNew) pathName:filename.
+    newStream createForWriting isNil ifTrue:[^nil].
+    ^ newStream
+!
+
+newFileNamed:filename in:aDirectory
+    "return a FileStream for new file named filename, aString
+     in aDirectory, a FileDirectory.
+     If the file exists, it is truncated, otherwise created.
+     The file is opened for write access only."
+
+    |newStream|
+    newStream := (self basicNew) pathName:filename in:aDirectory.
+    newStream createForWriting isNil ifTrue:[^nil].
+    ^ newStream
+!
+
+oldFileNamed:filename
+    "return a FileStream for existing file named filename, aString.
+     The file is opened for read/write access."
+
+    |newStream|
+
+    (OperatingSystem isReadable:filename) ifFalse:[^nil].
+    newStream := (self basicNew) pathName:filename.
+    newStream readwrite.
+    newStream openForReadWrite isNil ifTrue:[^nil].
+    newStream readLimit:(newStream size).
+    ^ newStream
+!
+
+oldFileNamed:filename in:aDirectory
+    "return a FileStream for existing file named filename, aString
+     in aDirectory, a FileDirectory.
+     The file is opened for read/write access."
+
+    |newStream|
+    newStream := (self basicNew) pathName:filename in:aDirectory.
+    newStream openForReadWrite isNil ifTrue:[^nil].
+    newStream readLimit:(newStream size).
+    ^ newStream
+!
+
+fileNamed:filename
+    "return a stream on file filename - if the file does not
+     already exist, create it."
+
+    |stream|
+
+    stream := self oldFileNamed:filename.
+    stream isNil ifTrue:[
+        stream := self newFileNamed:filename
+    ].
+    ^ stream
+!
+
+fileNamed:filename in:aDirectory
+    "return a stream on file filename - if the file does not
+     already exist, create it."
+
+    |stream|
+
+    stream := self oldFileNamed:filename in:aDirectory.
+    stream isNil ifTrue:[
+        stream := self newFileNamed:filename in:aDirectory
+    ].
+    ^ stream
+!
+
+readonlyFileNamed:filename
+    "return a readonly FileStream for existing file named filename, aString"
+
+    |newStream|
+
+    (OperatingSystem isReadable:filename) ifFalse:[^nil].
+
+    newStream := (self basicNew) pathName:filename.
+    newStream openForReading isNil ifTrue:[^nil].
+    newStream readLimit:(newStream size).
+    ^ newStream
+!
+
+readonlyFileNamed:filename in:aDirectory
+    "return a readonly FileStream for existing file named filename, aString
+     in aDirectory, a FileDirectory"
+
+    |newStream|
+    newStream := (self basicNew) pathName:filename in:aDirectory.
+    newStream openForReading isNil ifTrue:[^nil].
+    newStream readLimit:(newStream size).
+    ^ newStream
+!
+
+appendingOldFileNamed:filename
+    "return a FileStream for existing file named filename, aString"
+
+    |newStream|
+    newStream := (self basicNew) pathName:filename.
+    newStream openForAppending isNil ifTrue:[^nil].
+    newStream readLimit:(newStream size).
+    ^ newStream
+!
+
+appendingOldFileNamed:filename in:aDirectory
+    "return a FileStream for existing file named filename, aString
+     in aDirectory, a FileDirectory"
+
+    |newStream|
+    newStream := (self basicNew) pathName:filename in:aDirectory.
+    newStream openForAppending isNil ifTrue:[^nil].
+    newStream readLimit:(newStream size).
+    ^ newStream
+! !
+
+!FileStream methodsFor:'accessing'!
+
+store:something
+    "what really should this do"
+
+    self nextPutAll:something
+!
+
+directoryName
+    "return the name of the directory I'm in"
+
+    |path lastIndex index|
+
+    path := pathName.
+    lastIndex := 0.
+    index := path indexOf:$/.
+    [index ~~ 0] whileTrue:[
+        lastIndex := index.
+        index := path indexOf:$/ startingAt:(index + 1)
+    ].
+    (lastIndex == 0) ifTrue:[^ '.'].
+    (lastIndex == 1) ifTrue:[^ '/'].
+    ^ path copyFrom:1 to:(lastIndex - 1)
+!
+
+name
+    "return my name without leading direcory-path"
+
+    |lastIndex index|
+
+    lastIndex := 1.
+    [true] whileTrue:[
+        index := pathName indexOf:$/ startingAt:lastIndex.
+        (index == 0) ifTrue:[
+            ^ pathName copyFrom:lastIndex
+        ].
+        lastIndex := index + 1
+    ]
+!
+
+pathName
+    "return the pathname"
+
+    ^ pathName
+! !
+
+!FileStream methodsFor:'private'!
+
+pathName:filename
+    "set the pathname"
+
+    pathName := filename
+!
+
+pathName:filename in:aDirectory
+    "set the pathname starting at aDirectory, a FileDirectory"
+
+    ((filename at:1) == $/) ifTrue:[
+        "filename may not start with a '/'"
+        pathName := nil
+    ] ifFalse:[
+        pathName := aDirectory pathName.
+        (pathName endsWith:'/') ifFalse:[
+            pathName := pathName , '/'
+        ].
+        pathName := pathName , filename
+    ]
+!
+
+open
+    "open the file"
+
+    pathName isNil ifTrue:[^nil].
+    (mode == #readonly) ifTrue: [
+        ^ self openForReading
+    ].
+    (mode == #writeonly) ifTrue: [
+        ^ self openForWriting
+    ].
+    ^ self openForReadWrite
+!
+
+openWithMode:openmode
+    "open the file; openmode is the string defining the way to open"
+
+    |retVal|
+%{
+    FILE *f;
+    OBJ path;
+    extern OBJ ErrorNumber, Filename;
+    extern errno;
+
+    if (_INST(filePointer) == nil) {
+        path = _INST(pathName);
+        if (_isString(path) || (_Class(path) == Filename)) {
+            f = (FILE *)fopen((char *) _stringVal(path), (char *) _stringVal(openmode));
+            if (f == NULL) {
+                ErrorNumber = _MKSMALLINT(errno);
+                _INST(position) = nil;
+            } else {
+                _INST(filePointer) = _MKSMALLINT((int)f);
+                _INST(position) = _MKSMALLINT(1);
+                retVal = self;
+            }
+        }
+    }
+%}
+.
+    retVal notNil ifTrue:[
+        lobby register:self
+    ].
+    ^ retVal
+!
+
+openForReading
+    "open the file for readonly"
+
+    mode := #readonly.
+    ^ self openWithMode:'r'
+!
+
+openForWriting
+    "open the file for writeonly"
+
+    mode := #writeonly.
+    ^ self openWithMode:'w'
+!
+
+openForAppending
+    "open the file for writeonly appending to the end"
+
+    mode := #writeonly.
+    ^ self openWithMode:'a+'
+!
+
+createForWriting
+    "create/truncate the file for writeonly"
+
+    mode := #writeonly.
+    ^ self openWithMode:'w+'
+!
+
+openForReadWrite
+    "open the file for read/write"
+
+    mode := #readwrite.
+    ^ self openWithMode:'r+w'
+!
+
+createForReadWrite
+    "create/truncate the file for read/write"
+
+    mode := #readwrite.
+    ^ self openWithMode:'rw+'
+!
+
+reOpen
+    "sent after snapin to reopen streams"
+
+    filePointer notNil ifTrue:[
+        "it was open, when snapped-out"
+        filePointer := nil.
+        self open.
+        filePointer isNil ifTrue:[
+            Transcript showCr:('could not reopen file: ', pathName)
+        ] ifFalse:[
+            self position:position
+        ]
+    ]
+!
+
+size
+    "return the size in bytes of the file"
+
+%{  /* NOCONTEXT */
+
+#ifdef transputer
+    FILE *f;
+    int size;
+
+    if (_INST(filePointer) != nil) {
+        f = (FILE *)_intVal(_INST(filePointer));
+        if ((size = filesize(fileno(f))) >= 0) {
+            RETURN ( _MKSMALLINT(size) );
+        }
+    }
+#else
+    FILE *f;
+    struct stat buf;
+
+    if (_INST(filePointer) != nil) {
+        f = (FILE *)_intVal(_INST(filePointer));
+        if (fstat(fileno(f), &buf) >= 0) {
+            RETURN ( _MKSMALLINT(buf.st_size) );
+        }
+    }
+#endif
+%}
+.
+    "could add a fall-back here:
+
+        oldPosition := self position.
+        self setToEnd.
+        sz := self position.
+        self position:oldPosition.
+        ^ sz
+    "
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    ^ self primitiveFailed
+!
+
+position
+    "return the read/write position in the file -
+     notice, in smalltalk indices start at 1 so begin of file is 1""
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    long currentPosition;
+
+    if (_INST(filePointer) != nil) {
+        f = (FILE *)_intVal(_INST(filePointer));
+        currentPosition = ftell(f);
+        if (currentPosition >= 0) {
+            /*
+             * notice: Smalltalk index starts at 1
+             */
+            RETURN ( _MKSMALLINT(currentPosition + 1) );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    ^ self primitiveFailed
+!
+
+position:newPos
+    "set the read/write position in the file"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    extern OBJ ErrorNumber;
+    extern errno;
+
+    if (_INST(filePointer) != nil) {
+        if (_isSmallInteger(newPos)) {
+            f = (FILE *)_intVal(_INST(filePointer));
+            /*
+             * notice: Smalltalk index starts at 1
+             */
+            if (fseek(f, _intVal(newPos) - 1, 0) >= 0) {
+                _INST(position) = newPos;
+                RETURN ( self );
+            }
+            ErrorNumber = _MKSMALLINT(errno);
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    ^ self primitiveFailed
+!
+
+setToEnd
+    "set the read/write position in the file to be at the end of the file"
+
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+%{
+    FILE *f;
+    extern OBJ ErrorNumber;
+    extern errno;
+
+    f = (FILE *)_intVal(_INST(filePointer));
+    _INST(position) = nil;
+    if (fseek(f, 0, 2) >= 0) {
+        RETURN ( self );
+    }
+    ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+    ^ self primitiveFailed
+! !
+
+!FileStream methodsFor:'printing & storing'!
+
+printOn:aStream
+    aStream nextPutAll:'(a FileStream for:'.
+    aStream nextPutAll:pathName.
+    aStream nextPut:$)
+!
+
+storeOn:aStream
+    aStream nextPutAll:'(FileStream oldFileNamed:'.
+    aStream nextPutAll:pathName.
+    (self position ~~ 1) ifTrue:[
+        aStream nextPutAll:'; position:'.
+        self position storeOn:aStream
+    ].
+    aStream nextPut:$)
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Filename.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,71 @@
+"
+ COPYRIGHT (c) 1992-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Filename
+         instanceVariableNames:'name'
+         classVariableNames:''
+         poolDictionaries:''
+         category:'ST-80 compatibility'!
+
+Filename comment:'
+
+COPYRIGHT (c) 1992-93 by Claus Gittinger
+             All Rights Reserved
+
+Filenames for ST-80 compatibility. Only the minimum is implemented
+here to make some PD programs happy - I dont know what else there
+is in ST-80.
+
+%W% %E%
+'!
+
+!Filename class methodsFor:'instance creation'!
+
+named:aString
+    ^ (self basicNew) name:aString
+! !
+
+!Filename methodsFor:'converting'!
+
+asString
+    ^ name
+!
+
+asFilename
+    ^ self
+! !
+
+!Filename methodsFor:'private accessing'!
+
+name:aString
+    name := aString
+! !
+
+!Filename methodsFor:'file access'!
+
+exists
+    "return true, if such a file exists"
+
+    ^ OperatingSystem isValidPath:name
+!
+
+fileIn
+    ^ (FileStream readonlyFileNamed:name) fileIn
+!
+
+readStream
+    ^ FileStream readonlyFileNamed:name
+!
+
+writeStream
+    ^ FileStream newFileNamed:name
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Float.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,818 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Number variableByteSubclass:#Float
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Magnitude-Numbers'
+!
+
+Float comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+%W% %E%
+
+notice, that Floats are defined as Byte-array to prevent garbage collector
+from going into the value ... otherwise I needed a special case in many places.
+'!
+
+!Float class methodsFor:'instance creation'!
+
+new:aNumber
+    "catch this message - not allowed for floats"
+
+    self error:'Floats cannot be created with new:'
+!
+
+basicNew
+    "return a new float - here we return 0.0
+     - floats are usually NOT created this way ...
+     Its implemented here to allow things like binary store & load
+     of floats. (but even this support will go away eventually, its not
+     a good idea to store the bits of a float - the reader might have a
+     totally different representation."
+
+%{  /* NOCONTEXT */
+    RETURN ( _MKFLOAT((0.0) COMMA_SND) );
+%}
+! !
+
+!Float class methodsFor:'constants'!
+
+zero
+    "return the neutral element for addition"
+
+    ^ 0.0
+!
+
+unity
+    "return the neutral element for multiplication"
+
+    ^ 1.0
+!
+
+pi
+    "return the constant pi"
+
+    ^ 3.1415926535897932384626434
+! !
+
+!Float methodsFor:'copying'!
+
+shallowCopy
+    "return a shallow copy of myself
+     - reimplemented here since Floats are kind of kludgy"
+
+    ^ self
+!
+
+deepCopy
+    "return a deep copy of myself
+     - reimplemented here since Floats are kind of kludgy"
+
+    ^ self
+! !
+    
+!Float methodsFor:'accessing'!
+
+size
+   "redefined since floats are kludgy (ByteArry)"
+
+   ^ 0
+!
+
+at:index
+    "redefined to prevent access to individual bytes in a float"
+
+    self error:'not allowed for floats'
+!
+
+at:index put:aValue
+    "redefined to prevent access to individual bytes in a float"
+
+    self error:'not allowed for floats'
+! !
+
+!Float methodsFor:'arithmetic'!
+
++ aNumber
+    "return the sum of the receiver and the argument, aNumber"
+
+%{  /* NOCONTEXT */
+    OBJ newFloat;
+    double result;
+
+    if (_isSmallInteger(aNumber)) {
+        result = _floatVal(self) + (double)(_intVal(aNumber));
+retResult:
+        _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+        _InstPtr(newFloat)->o_class = Float;
+        _FloatInstPtr(newFloat)->f_floatvalue = result;
+        RETURN ( newFloat );
+    }
+    if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+        result = _floatVal(self) + _floatVal(aNumber);
+        goto retResult;
+    }
+%}
+.
+    ^ aNumber sumFromFloat:self
+!
+
+- aNumber
+    "return the difference of the receiver and the argument, aNumber"
+
+%{  /* NOCONTEXT */
+    OBJ newFloat;
+    double result;
+
+    if (_isSmallInteger(aNumber)) {
+        result = _floatVal(self) - (double)(_intVal(aNumber));
+retResult:
+        _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+        _InstPtr(newFloat)->o_class = Float;
+        _FloatInstPtr(newFloat)->f_floatvalue = result;
+        RETURN ( newFloat );
+    }
+    if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+        result = _floatVal(self) - _floatVal(aNumber);
+        goto retResult;
+    }
+%}
+.
+    ^ aNumber differenceFromFloat:self
+!
+
+* aNumber
+    "return the product of the receiver and the argument, aNumber"
+
+%{  /* NOCONTEXT */
+
+    OBJ newFloat;
+    double result;
+
+    if (_isSmallInteger(aNumber)) {
+        result = _floatVal(self) * (double)(_intVal(aNumber));
+retResult:
+        _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+        _InstPtr(newFloat)->o_class = Float;
+        _FloatInstPtr(newFloat)->f_floatvalue = result; 
+        RETURN ( newFloat );
+    }
+    if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+        result = _floatVal(self) * _floatVal(aNumber);
+        goto retResult;
+    }
+%}
+.
+    ^ aNumber productFromFloat:self
+!
+
+/ aNumber
+    "return the quotient of the receiver and the argument, aNumber"
+
+%{  /* NOCONTEXT */
+
+    OBJ newFloat;
+    double result, val;
+
+    if (_isSmallInteger(aNumber)) {
+        if (aNumber != _MKSMALLINT(0)) {
+            result = _floatVal(self) / ( (double)_intVal(aNumber)) ;
+retResult:
+            _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+            _InstPtr(newFloat)->o_class = Float;
+            _FloatInstPtr(newFloat)->f_floatvalue = result;
+            RETURN ( newFloat );
+        }
+    } else {
+        if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+            val = _floatVal(aNumber);
+            if (val != 0.0) {
+                result = _floatVal(self) / val;
+                goto retResult;
+            }
+        }
+    }
+%}
+.
+    ((aNumber == 0) or:[aNumber = 0.0]) ifTrue:[
+        DivisionByZeroSignal raise.
+        ^ self
+    ].
+    ^ aNumber quotientFromFloat:self
+!
+
+// aNumber
+    "return the integer quotient of dividing the receiver by aNumber with
+    truncation towards negative infinity."
+
+    ^ (self / aNumber) floor asInteger
+!
+
+\\ aNumber
+    "return the integer remainder of dividing the receiver by aNumber with
+    truncation towards negative infinity."
+
+    ^ (self - ((self / aNumber) floor * aNumber)) floor asInteger
+!
+
+negated
+    "return myself negated"
+
+%{  /* NOCONTEXT */
+    RETURN ( _MKFLOAT(- _floatVal(self) COMMA_SND) );
+%}
+! !
+
+!Float methodsFor:'testing'!
+
+positive
+    "return true if the receiver is greater or equal to zero"
+
+%{  /* NOCONTEXT */
+    RETURN ( (_floatVal(self) >= 0.0) ? true : false );
+%}
+!
+
+negative
+    "return true if the receiver is less than zero"
+
+%{  /* NOCONTEXT */
+    RETURN ( (_floatVal(self) < 0.0) ? true : false );
+%}
+! !
+
+!Float methodsFor:'comparing'!
+
+< aNumber
+    "return true, if the argument is greater"
+
+%{  /* NOCONTEXT */
+    if (_isSmallInteger(aNumber)) {
+        RETURN ( (_floatVal(self) < (double)(_intVal(aNumber))) ? true : false );
+    }
+    if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+        RETURN ( (_floatVal(self) < _floatVal(aNumber)) ? true : false );
+    }
+%}
+.
+    ^ aNumber lessFromFloat:self
+!
+
+> aNumber
+    "return true, if the argument is less"
+
+%{  /* NOCONTEXT */
+    if (_isSmallInteger(aNumber)) {
+        RETURN ( (_floatVal(self) > (double)(_intVal(aNumber))) ? true : false );
+    }
+    if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+        RETURN ( (_floatVal(self) > _floatVal(aNumber)) ? true : false );
+    }
+%}
+.
+    ^ self retry:#> coercing:aNumber
+!
+
+<= aNumber
+    "return true, if the argument is greater or equal"
+
+%{  /* NOCONTEXT */
+    if (_isSmallInteger(aNumber)) {
+        RETURN ( (_floatVal(self) <= (double)(_intVal(aNumber))) ? true : false );
+    }
+    if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+        RETURN ( (_floatVal(self) <= _floatVal(aNumber)) ? true : false );
+    }
+%}
+.
+    ^ self retry:#<= coercing:aNumber
+!
+
+>= aNumber
+    "return true, if the argument is less or equal"
+
+%{  /* NOCONTEXT */
+    if (_isSmallInteger(aNumber)) {
+        RETURN ( (_floatVal(self) >= (double)(_intVal(aNumber))) ? true : false );
+    }
+    if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+        RETURN ( (_floatVal(self) >= _floatVal(aNumber)) ? true : false );
+    }
+%}
+.
+    ^ self retry:#>= coercing:aNumber
+!
+
+= aNumber
+    "return true, if the arguments value are equal"
+
+%{  /* NOCONTEXT */
+    if (_isSmallInteger(aNumber)) {
+        RETURN ( (_floatVal(self) == (double)(_intVal(aNumber))) ? true : false );
+    }
+    if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+        RETURN ( (_floatVal(self) == _floatVal(aNumber)) ? true : false );
+    }
+%}
+.
+    ^ self retry:#= coercing:aNumber
+!
+
+~= aNumber
+    "return true, if the arguments value are not equal"
+
+%{  /* NOCONTEXT */
+    if (_isSmallInteger(aNumber)) {
+        RETURN ( (_floatVal(self) != (double)(_intVal(aNumber))) ? true : false );
+    }
+    if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+        RETURN ( (_floatVal(self) != _floatVal(aNumber)) ? true : false );
+    }
+%}
+.
+    ^ self retry:#~= coercing:aNumber
+! !
+
+!Float methodsFor:'coercion and converting'!
+
+coerce:aNumber
+    "return aNumber converted into receivers type"
+
+    ^ aNumber asFloat
+!
+
+generality
+    "return the generality value - see ArithmeticValue>>retry:coercing:"
+    ^ 80
+!
+
+asFloat
+    "return a float with same value - thats me"
+
+    ^ self
+!
+
+asFraction
+    "return a corresponding fraction
+     - notice, that fract asFloat asFract does not always return
+     a good fraction ..."
+
+    |fract digits power num denom|
+
+    "we (indirectly) use printf which knows the precision of floats"
+
+    fract := self fractionPart.
+    digits := fract printString copyFrom:3.
+    power := digits size.
+    num := (self - fract) asInteger.
+    denom := (10 raisedToInteger:power).
+    num := num * denom.
+    num := num + (Integer readFromString:digits).
+    ^ (Fraction numerator:num denominator:denom) reduced
+
+    "0.3 asFraction"
+    "0.5 asFraction"
+    "(1/5) asFloat asFraction"
+    "(1/8) asFloat asFraction"
+    "(1/12) asFloat asFraction -> inexact result due to float errors"
+    "(1/13) asFloat asFraction -> inexact result due to float errors"
+!
+
+asInteger
+    "return an integer with same value - might truncate"
+
+    |l v sign|
+
+%{  /* NOCONTEXT */
+
+    if ((_floatVal(self) >= (double)_MIN_INT)
+     && (_floatVal(self) <= (double)_MAX_INT)) {
+        RETURN ( _MKSMALLINT( (INT)_floatVal(self)) );
+    }
+%}
+.
+    "this is stupid code - rounding errors accumulate; fix later"
+
+    sign := self sign.
+    v := self abs.
+    (v >= 10) ifTrue:[
+        l := (v / 10.0) asInteger * 10
+    ] ifFalse:[
+        l := 0
+    ].
+    v := v - ((v / 10.0) floor * 10.0) floor.
+    l := l + v truncated.
+    ^ l * sign
+
+    "12345.0 asInteger"
+    "1e15 asInteger"
+! !
+
+!Float methodsFor:'double dispatching'!
+
+sumFromFraction:aFraction
+    "sent when a fraction does not know how to add the recevier, a float"
+
+    ^ (self * aFraction denominator + aFraction numerator) / aFraction denominator
+!
+
+differenceFromFraction:aFraction
+    "sent when a fraction does not know how to subtract the recevier, a float"
+
+    ^ (self * aFraction denominator - aFraction numerator) / aFraction denominator
+!
+
+productFromFraction:aFraction
+    "sent when a fraction does not know how to multiply the recevier, a float"
+
+    ^ self * aFraction numerator / aFraction denominator
+!
+
+quotientFromFraction:aFraction
+    "sent when a fraction does not know how to divide by the recevier, a float"
+
+    ^ aFraction numerator / (self * aFraction denominator)
+! !
+
+!Float methodsFor:'truncation and rounding'!
+
+truncated
+    "return the receiver truncated towards zero as Integer"
+
+    |val|
+
+%{
+    double floor(), ceil();
+    double dVal;
+
+    dVal = _floatVal(self);
+    if (dVal < 0.0) {
+        dVal = ceil(dVal);
+    } else {
+        dVal = floor(dVal);
+    }
+
+    /*
+     * mhmh it seems that ST-80 is returning integers if possible.
+     * (at least, some pd programs expect it ...)
+     */
+    if ((dVal >= (double)_MIN_INT) && (dVal <= (double)_MAX_INT)) {
+        RETURN ( _MKSMALLINT( (INT) dVal ) );
+    }
+    val = _MKFLOAT(dVal COMMA_CON);
+%}
+.
+    ^ val asInteger
+!
+
+rounded
+    "return the receiver rounded to the nearest integer as integer"
+
+    |val|
+
+%{
+    double floor();
+    double dVal;
+
+    /*
+     * mhmh it seems that ST-80 is returning integers if possible
+     * at least, some pd programs expect it ...
+     */
+    dVal = floor(_floatVal(self) + 0.5);
+    if ((dVal >= (double)_MIN_INT) && (dVal <= (double)_MAX_INT)) {
+        RETURN ( _MKSMALLINT( (INT) dVal ) );
+    }
+    val = _MKFLOAT(dVal COMMA_CON);
+%}
+.
+    ^ val asInteger
+!
+
+floor
+    "return the biggest integer-valued float less or equal to the receiver"
+
+%{  /* NOCONTEXT */
+
+    double floor();
+
+    RETURN ( _MKFLOAT(floor(_floatVal(self)) COMMA_SND) );
+%}
+!
+
+ceiling
+    "return the smallest integer-valued float greater or equal to the receiver"
+
+%{  /* NOCONTEXT */
+
+    double ceil();
+
+    RETURN ( _MKFLOAT(ceil(_floatVal(self)) COMMA_SND) );
+%}
+!
+
+fractionPart
+    "return a float with value from digits after the decimal point"
+
+    ^ self - self truncated asFloat
+
+    "1234.56789 fractionPart"
+    "1.2345e6 fractionPart"
+! !
+
+!Float methodsFor:'mathematical functions'!
+
+ln
+    "return the natural logarithm of myself"
+
+%{  /* NOCONTEXT */
+
+    double log();
+    double result;
+    extern errno;
+    extern OBJ ErrorNumber;
+
+    errno = 0;
+    result = log(_floatVal(self));
+    if (errno == 0)
+        RETURN ( _MKFLOAT(result COMMA_SND) );
+    ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+    DomainErrorSignal raise
+!
+
+raisedTo:aNumber
+    "return self raised to the power of aNumber"
+    |n|
+
+    n := aNumber asFloat.
+%{
+    double pow();
+    double result;
+    extern errno;
+    extern OBJ ErrorNumber;
+
+    if (_isFloat(n)) {
+        errno = 0;
+        result = pow(_floatVal(self), _floatVal(n));
+        errno = 0;  /* XXXX */
+        if (errno == 0)
+            RETURN ( _MKFLOAT(result COMMA_CON) );
+        ErrorNumber = _MKSMALLINT(errno);
+    }
+%}
+.
+    DomainErrorSignal raise
+!
+
+exp
+    "return e raised to the power of the receiver"
+
+%{  /* NOCONTEXT */
+
+    double exp();
+    double result;
+    extern errno;
+    extern OBJ ErrorNumber;
+
+    errno = 0;
+    result = exp(_floatVal(self));
+    if (errno == 0)
+        RETURN ( _MKFLOAT(result COMMA_SND) );
+    ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+    DomainErrorSignal raise
+!
+
+sin
+    "return the sine of myself interpreted as radians"
+
+%{  /* NOCONTEXT */
+
+    double sin();
+    double result;
+    extern errno;
+    extern OBJ ErrorNumber;
+
+    errno = 0;
+    result = sin(_floatVal(self));
+    if (errno == 0)
+        RETURN ( _MKFLOAT(result COMMA_SND) );
+    ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+    DomainErrorSignal raise
+!
+
+cos
+    "return the cosine of myself interpreted as radians"
+
+%{  /* NOCONTEXT */
+
+    double cos();
+    double result;
+    extern errno;
+    extern OBJ ErrorNumber;
+
+    errno = 0;
+    result = cos(_floatVal(self));
+    if (errno == 0)
+        RETURN ( _MKFLOAT(result COMMA_SND) );
+    ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+    DomainErrorSignal raise
+!
+
+tan
+    "return the tangent of myself interpreted as radians"
+
+%{  /* NOCONTEXT */
+
+    double tan();
+    double result;
+    extern errno;
+    extern OBJ ErrorNumber;
+
+    errno = 0;
+    result = tan(_floatVal(self));
+    if (errno == 0)
+        RETURN ( _MKFLOAT(result COMMA_SND) );
+    ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+    DomainErrorSignal raise
+!
+
+arcSin
+    "return the arcsine of myself as radians"
+
+%{  /* NOCONTEXT */
+
+    double asin();
+    double result;
+    extern errno;
+    extern OBJ ErrorNumber;
+
+    errno = 0;
+    result = asin(_floatVal(self));
+    if (errno == 0)
+        RETURN ( _MKFLOAT(result COMMA_SND) );
+    ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+    DomainErrorSignal raise
+!
+
+arcCos
+    "return the arccosine of myself as radians"
+
+%{  /* NOCONTEXT */
+
+    double acos();
+    double result;
+    extern errno;
+    extern OBJ ErrorNumber;
+
+    errno = 0;
+    result = acos(_floatVal(self));
+    if (errno == 0)
+        RETURN ( _MKFLOAT(result COMMA_SND) );
+    ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+    DomainErrorSignal raise
+!
+
+arcTan
+    "return the arctangent of myself as radians"
+
+%{  /* NOCONTEXT */
+
+    double atan();
+    double result;
+    extern errno;
+    extern OBJ ErrorNumber;
+
+    errno = 0;
+    result = atan(_floatVal(self));
+    if (errno == 0)
+        RETURN ( _MKFLOAT(result COMMA_SND) );
+    ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+    DomainErrorSignal raise
+!
+
+sqrt
+    "return the square root of myself"
+
+%{  /* NOCONTEXT */
+
+    double sqrt();
+    double result;
+    extern errno;
+    extern OBJ ErrorNumber;
+
+    errno = 0;
+    result = sqrt(_floatVal(self));
+    if (errno == 0)
+        RETURN ( _MKFLOAT(result COMMA_SND) );
+    ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+    DomainErrorSignal raise
+! !
+
+!Float methodsFor:'printing and storing'!
+
+printString
+    "return a printed representation of the receiver"
+
+%{  /* NOCONTEXT */
+
+    char buffer[256];
+    REGISTER char *cp;
+#ifdef THIS_CONTEXT
+    OBJ sav = __thisContext;
+#endif
+
+#ifdef SYSV
+    sprintf(buffer, "%.6lg", _floatVal(self));
+#else
+    sprintf(buffer, "%.6G", _floatVal(self));
+#endif
+
+#ifdef THIS_CONTEXT
+    __thisContext = sav;
+#endif
+    /* 
+     * kludge to make integral floats prints as i.0 (not i as printf does)
+     * (i.e. look if string contains '.' or 'e' and append '.0' if not)
+     */
+    for (cp = buffer; *cp; cp++) {
+        if ((*cp == '.') || (*cp == 'e')) break;
+    }
+    if (! *cp) {
+        *cp++ = '.';
+        *cp++ = '0';
+	*cp = '\0';
+    }
+
+    RETURN ( _MKSTRING(buffer COMMA_SND) );
+%}
+    "1.0 printString"
+    "1.234 printString"
+    "1e10 printString"
+    "1.2e3 printString"
+    "1.2e30 printString"
+!
+
+printfPrintString:formatString
+    "non-portable: return a printed representation of the receiver
+     as specified by formatString, which is defined by printf.
+     If you use this, be aware, that specifying doubles differes on
+     systems; on SYSV machines you have to give something like %lf, 
+     while on BSD systems the format string has to be %F."
+
+%{  /* NOCONTEXT */
+    char buffer[256];
+
+    if (_isString(formatString)) {
+#ifdef THIS_CONTEXT
+        OBJ sav = __thisContext;
+#endif
+        sprintf(buffer, _stringVal(formatString), _floatVal(self));
+#ifdef THIS_CONTEXT
+        __thisContext = sav;
+#endif
+        RETURN ( _MKSTRING(buffer COMMA_SND) );
+    }
+%}
+.
+    self primitiveFailed
+
+    "Float pi printfPrintString:'%%lg -> %lg'"
+    "Float pi printfPrintString:'%%lf -> %lf'"
+    "Float pi printfPrintString:'%%7.5lg -> %7.5lg'"
+    "Float pi printfPrintString:'%%7.5lf -> %7.5lf'"
+    "Float pi printfPrintString:'%%G -> %G'"
+    "Float pi printfPrintString:'%%F -> %F'"
+    "Float pi printfPrintString:'%%7.5G -> %7.5G'"
+    "Float pi printfPrintString:'%%7.5F -> %7.5F'"
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FloatArray.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,31 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+	      All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+ArrayedCollection variableFloatSubclass:#FloatArray
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Indexed'
+!
+
+FloatArray comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+	      All Rights Reserved
+
+FloatArrays store floats (and nothing else).
+See the comment in double array for more information.
+
+%W% %E%
+
+written june 93 by claus
+'!
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Fraction.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,391 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Number subclass:#Fraction
+       instanceVariableNames:'numerator denominator'
+       classVariableNames:'FractionOne FractionZero'
+       poolDictionaries:''
+       category:'Magnitude-Numbers'
+!
+
+Fraction comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+%W% %E%
+'!
+
+!Fraction class methodsFor:'initialization'!
+
+initialize
+    FractionZero := self numerator:0 denominator:1.
+    FractionOne := self numerator:1 denominator:1
+! !
+
+!Fraction class methodsFor:'constants'!
+
+zero
+    ^ FractionZero
+!
+
+unity
+    ^ FractionOne
+! !
+
+!Fraction class methodsFor:'instance creation'!
+
+new
+    "create and return a new fraction with value 0"
+
+    ^ self basicNew setNumerator:0 denominator:1
+!
+
+numerator:num denominator:den
+    "create and return a new fraction with numerator num and denominator den"
+
+%{  /* NOCONTEXT */
+    extern char *newNextPtr, *newEndPtr;
+
+    if (self == Fraction) {
+        if (_CanDoQuickNew(sizeof(struct fractionstruct))) {
+            OBJ newFraction;
+
+            _qCheckedAlignedNew(newFraction, sizeof(struct fractionstruct), __context);
+            _InstPtr(newFraction)->o_class = Fraction;
+            _FractionInstPtr(newFraction)->f_numerator = num;
+            _FractionInstPtr(newFraction)->f_denominator = den;
+            /* no store check needed - its definitely in newSpace */
+            RETURN ( newFraction );
+        }
+    }
+%}
+.
+    ^ self basicNew setNumerator:num denominator:den
+! !
+
+!Fraction methodsFor:'accessing'!
+
+numerator
+    "return the numerator"
+
+    ^ numerator
+!
+
+denominator
+    "return the denominator"
+
+    ^ denominator
+! !
+
+!Fraction methodsFor:'private'!
+
+setNumerator:num denominator:den
+    "set both numerator and denominator"
+
+    numerator := num.
+    denominator := den
+!
+
+reduced
+    "reduce the receiver"
+
+    |gc|
+
+    denominator == 1 ifTrue:[^ numerator].
+    numerator == 1 ifTrue:[^ self].
+
+    gc := numerator gcd:denominator.
+    (gc == 1) ifFalse:[
+        numerator := numerator // gc.
+        denominator := denominator // gc
+    ].
+    (numerator < 0) ifTrue:[
+        (denominator < 0) ifTrue:[
+            numerator := numerator negated.
+            denominator := denominator negated
+        ]
+    ].
+    (denominator == 1) ifTrue:[^ numerator].
+    ^ self
+! !
+
+!Fraction methodsFor:'coercing'!
+
+coerce:aNumber
+    ^ aNumber asFraction
+!
+
+generality
+    ^ 60
+! !
+
+!Fraction methodsFor:'converting'!
+
+asInteger
+    "return an integer with my value - will usually truncate"
+
+    ^ numerator // denominator
+!
+
+asLargeInteger
+    "return an integer with my value - will usually truncate"
+
+    ^ self asInteger asLargeInteger
+!
+
+asFloat
+    "return a float with (approximately) my value"
+
+    ^ (numerator asFloat) / (denominator asFloat)
+!
+
+asFraction
+    "return the receiver as fraction - thats itself"
+
+    ^ self
+! !
+
+!Fraction methodsFor:'comparing'!
+
+= aNumber
+    "return true, if the argument represents the same numeric value
+     as the receiver, false otherwise"
+
+    (aNumber isMemberOf:SmallInteger) ifTrue:[
+        (denominator = 1) ifFalse:[^ false].
+        ^ numerator = aNumber
+    ].
+    (aNumber isKindOf:Fraction) ifTrue:[
+        (numerator = aNumber numerator) ifFalse:[^ false].
+        ^ denominator = aNumber denominator
+    ].
+    ^ self retry:#= coercing:aNumber
+!
+
+> aNumber
+    "return true if the receiver is greater
+     than aNumber, false otherwise."
+    "optional - could use inherited method ..."
+
+    (aNumber isMemberOf:SmallInteger) ifTrue:[
+        ^ numerator > (denominator * aNumber)
+    ].
+    (aNumber isKindOf:Fraction) ifTrue:[
+        ^ (numerator * aNumber denominator) > (denominator * aNumber numerator)
+    ].
+    ^ self retry:#> coercing:aNumber
+!
+
+< aNumber
+    "return true if the receiver is less
+     than aNumber, false otherwise."
+
+    (aNumber isMemberOf:SmallInteger) ifTrue:[
+        ^ numerator < (denominator * aNumber)
+    ].
+    (aNumber isKindOf:Fraction) ifTrue:[
+        ^ (numerator * aNumber denominator) < (denominator * aNumber numerator)
+    ].
+    ^ aNumber lessFromFraction:self
+! !
+
+!Fraction methodsFor:'testing'!
+
+negative
+    "return true if the receiver is negative"
+
+    (numerator < 0) ifTrue:[
+        ^ (denominator < 0) not
+    ].
+    ^ (denominator < 0)
+! !
+
+!Fraction methodsFor:'arithmetic'!
+
++ aNumber
+    "return the sum of the receiver and the argument, aNumber"
+
+    |n d|
+
+    (aNumber isMemberOf:SmallInteger) ifTrue:[
+        ^ (self class numerator:(numerator + (denominator * aNumber))
+                    denominator:denominator) reduced
+    ].
+    (aNumber isKindOf:Fraction) ifTrue:[
+        n := aNumber numerator.
+        d := aNumber denominator.
+
+        "save a multiplication if possible"
+        denominator == d ifTrue:[
+            ^ (self class numerator:(numerator + n) denominator:d) reduced
+        ].
+
+        ^ (self class numerator:((numerator * d) + (n * denominator))
+                    denominator:(denominator * d)) reduced
+    ].
+    ^ aNumber sumFromFraction:self
+!
+
+- aNumber
+    "return the difference of the receiver and the argument, aNumber"
+
+    |n d|
+
+    (aNumber isMemberOf:SmallInteger) ifTrue:[
+        ^ (self class numerator:(numerator - (denominator * aNumber))
+                    denominator:denominator) reduced
+    ].
+    (aNumber isKindOf:Fraction) ifTrue:[
+        n := aNumber numerator.
+        d := aNumber denominator.
+
+        "save a multiplication if possible"
+        denominator == d ifTrue:[
+            ^ (self class numerator:(numerator - n) denominator:d) reduced
+        ].
+
+        ^ (self class numerator:((numerator * d) - (n * denominator)) 
+                    denominator:(denominator * d)) reduced
+    ].
+    ^ aNumber differenceFromFraction:self
+!
+
+* aNumber
+    "return the product of the receiver and the argument, aNumber"
+
+    |n d|
+
+    (aNumber isMemberOf:SmallInteger) ifTrue:[
+        ^ (self class numerator:(numerator * aNumber)
+                    denominator:denominator) reduced
+    ].
+    (aNumber isKindOf:Fraction) ifTrue:[
+        n := numerator * aNumber numerator.
+        d := denominator * aNumber denominator.
+        ^ (self class numerator:n denominator:d) reduced
+    ].
+    ^ aNumber productFromFraction:self
+!
+
+/ aNumber
+    "return the quotient of the receiver and the argument, aNumber"
+
+    |n d|
+
+    (aNumber isKindOf:Fraction) ifTrue:[
+        n := numerator * aNumber denominator.
+        d := denominator * aNumber numerator.
+        ^ (self class numerator:n denominator:d) reduced
+    ].
+    ^ aNumber quotientFromFraction:self
+!
+
+// aNumber
+    "return the integer quotient of the receiver and the argument, aNumber"
+
+    self negative ifTrue:[
+         ^ ((numerator * aNumber denominator) // (denominator * aNumber numerator)) - 1
+    ].
+    ^ (numerator * aNumber denominator) // (denominator * aNumber numerator)
+!
+
+negated
+    "optional - could use inherited method ..."
+
+    ^ self class numerator:(numerator negated)
+               denominator:denominator
+!
+
+reciprocal
+    "optional - could use inherited method ..."
+
+    numerator == 1 ifTrue:[^ denominator].
+    ^ self class numerator:denominator
+               denominator:numerator
+! !
+
+!Fraction methodsFor:'truncation and rounding'!
+
+truncated
+    "return the receiver truncated towards zero as Integer"
+
+    ^ numerator // denominator
+!
+
+rounded
+    "return the receiver rounded to the nearest integer as integer"
+
+    self negative ifTrue:[
+        ^ (self + (1/2)) truncated - 1
+    ].
+    ^ (self + (1/2)) truncated
+! !
+
+!Fraction methodsFor:'double dispatching'!
+
+sumFromInteger:anInteger
+    "sent when an integer does not know how to add the recevier, a fraction"
+
+    ^ (self class numerator:(numerator + (anInteger * denominator))
+                denominator:denominator) reduced
+!
+
+differenceFromInteger:anInteger
+    "sent when an integer does not know how to subtract the recevier, a fraction"
+
+    ^ (self class numerator:((anInteger * denominator) - numerator)
+                denominator:denominator) reduced
+!
+
+productFromInteger:anInteger
+    "sent when an integer does not know how to multiply the recevier, a fraction"
+
+    ^ (self class numerator:(anInteger * numerator)
+                denominator:denominator) reduced
+!
+
+lessFromInteger:anInteger
+    "sent when an integer does not know how to compare to the recevier, a fraction"
+
+    ^ (denominator * anInteger) < numerator
+!
+
+sumFromFloat:aFloat
+    "sent when a float does not know how to add the recevier, a fraction"
+
+    ^ (aFloat * denominator + numerator) / denominator
+!
+
+differenceFromFloat:aFloat
+    "sent when a float does not know how to subtract the recevier, a fraction"
+
+    ^ (aFloat * denominator - numerator) / denominator
+!
+
+productFromFloat:aFloat
+    "sent when a float does not know how to multiply the recevier, a fraction"
+
+    ^ aFloat * numerator / denominator
+!
+
+quotientFromFloat:aFloat
+    "sent when a float does not know how to divide by the recevier, a fraction"
+
+    ^ (aFloat * denominator) / numerator
+! !
+
+!Fraction methodsFor:'printing'!
+
+printString
+    ^ '(' , numerator printString, '/' , denominator printString, ')'
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IdDict.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,100 @@
+"
+ COPYRIGHT (c) 1992 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Dictionary subclass:#IdentityDictionary
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Unordered'
+!
+
+IdentityDictionary comment:'
+
+COPYRIGHT (c) 1992 by Claus Gittinger
+              All Rights Reserved
+
+same as a Dictionary but key must be identical - not just equal.
+Since compare is on identity keys, hashing is also done via
+identityHash instead of hash.
+
+%W% %E%
+
+written jul 92 by claus
+'!
+
+!IdentityDictionary methodsFor:'private'!
+
+findKeyOrNil:key
+    "Look for the key in the receiver.  If it is found, return
+     the index of the association containing the key, otherwise
+     return the index of the first unused slot. Grow the receiver,
+     if key was not found, and no unused slots where present"
+
+    |index  "{ Class:SmallInteger }"
+     length "{ Class:SmallInteger }"
+     startIndex
+     probe |
+
+    length := keyArray basicSize.
+    startIndex := key identityHash \\ length + 1.
+
+    index := startIndex.
+    [true] whileTrue:[
+        probe := keyArray basicAt:index.
+        (probe isNil or: [probe == key]) ifTrue:[^ index].
+
+        index == length ifTrue:[
+            index := 1
+        ] ifFalse:[
+            index := index + 1
+        ].
+        index == startIndex ifTrue:[^ self grow findKeyOrNil:key].
+    ]
+!
+
+findKey:key ifAbsent:aBlock
+    "Look for the key in the receiver.  If it is found, return
+     the index of the association containing the key, otherwise
+     return the value of evaluating aBlock."
+
+    |index  "{ Class:SmallInteger }"
+     length "{ Class:SmallInteger }"
+     startIndex
+     probe|
+
+    length := keyArray basicSize.
+    length < 10 ifTrue:[
+        "assuming, that for small dictionaries the overhead of hashing
+         is large ... maybe that proves wrong (if overhead of comparing
+         is high)"
+        index := keyArray identityIndexOf:key.
+        index == 0 ifTrue:[
+            ^ aBlock value
+        ].
+        ^ index
+    ].
+
+    startIndex := key identityHash \\ length + 1.
+
+    index := startIndex.
+    [true] whileTrue:[
+        probe := keyArray basicAt:index.
+        probe == key ifTrue:[^ index].
+
+        index == length ifTrue:[
+            index := 1
+        ] ifFalse:[
+            index := index + 1
+        ].
+        (probe isNil or:[index == startIndex]) ifTrue:[^ aBlock value]
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IdSet.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,94 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Set subclass:#IdentitySet
+         instanceVariableNames:''
+         classVariableNames:''
+         poolDictionaries:''
+         category:'Collections-Unordered'
+!
+
+IdentitySet comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+same as a Set but compares elements using == (i.e. they must be identical
+- not just equal in structure).
+Since compare is on identity, hashing is also done via
+identityHash instead of hash.
+
+%W% %E%
+
+written jan 93 by claus
+'!
+
+!IdentitySet methodsFor:'private'!
+
+find:key ifAbsent:aBlock
+    "Look for the key in the receiver.  If it is found, return
+     the index of the slot containing the key, otherwise
+     return the value of evaluating aBlock.
+     Redefined to compare for identity instead of equality"
+
+    |index      "{ Class:SmallInteger }"
+     length     "{ Class:SmallInteger }"
+     startIndex "{ Class:SmallInteger }" 
+     probe |
+
+    length := contentsArray basicSize.
+    startIndex := key identityHash \\ length + 1.
+
+    index := startIndex.
+    [true] whileTrue:[
+        probe := (contentsArray basicAt:index).
+        probe == key ifTrue:[^ index].
+        probe isNil ifTrue:[^ aBlock value].
+
+        index == length ifTrue:[
+            index := 1
+        ] ifFalse:[
+	    index := index + 1
+	].
+        index == startIndex ifTrue:[
+            ^ aBlock value
+        ]
+    ]
+!
+
+findElementOrNil:key
+    "Look for the key in the receiver.  Redefined to compare for
+     identity instead of equality"
+
+    |index      "{ Class:SmallInteger }"
+     length     "{ Class:SmallInteger }"
+     startIndex "{ Class:SmallInteger }"
+     probe |
+
+    length := contentsArray basicSize.
+    startIndex := key identityHash \\ length + 1.
+
+    index := startIndex.
+    [true] whileTrue:[
+        probe := contentsArray basicAt:index.
+        (probe isNil or: [key == probe]) ifTrue:[^ index].
+
+        index == length ifTrue:[
+            index := 1
+        ] ifFalse:[
+	    index := index + 1
+	].
+        index == startIndex ifTrue: [
+            ^ self grow findElementOrNil:key
+        ]
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IdentityDictionary.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,100 @@
+"
+ COPYRIGHT (c) 1992 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Dictionary subclass:#IdentityDictionary
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Unordered'
+!
+
+IdentityDictionary comment:'
+
+COPYRIGHT (c) 1992 by Claus Gittinger
+              All Rights Reserved
+
+same as a Dictionary but key must be identical - not just equal.
+Since compare is on identity keys, hashing is also done via
+identityHash instead of hash.
+
+%W% %E%
+
+written jul 92 by claus
+'!
+
+!IdentityDictionary methodsFor:'private'!
+
+findKeyOrNil:key
+    "Look for the key in the receiver.  If it is found, return
+     the index of the association containing the key, otherwise
+     return the index of the first unused slot. Grow the receiver,
+     if key was not found, and no unused slots where present"
+
+    |index  "{ Class:SmallInteger }"
+     length "{ Class:SmallInteger }"
+     startIndex
+     probe |
+
+    length := keyArray basicSize.
+    startIndex := key identityHash \\ length + 1.
+
+    index := startIndex.
+    [true] whileTrue:[
+        probe := keyArray basicAt:index.
+        (probe isNil or: [probe == key]) ifTrue:[^ index].
+
+        index == length ifTrue:[
+            index := 1
+        ] ifFalse:[
+            index := index + 1
+        ].
+        index == startIndex ifTrue:[^ self grow findKeyOrNil:key].
+    ]
+!
+
+findKey:key ifAbsent:aBlock
+    "Look for the key in the receiver.  If it is found, return
+     the index of the association containing the key, otherwise
+     return the value of evaluating aBlock."
+
+    |index  "{ Class:SmallInteger }"
+     length "{ Class:SmallInteger }"
+     startIndex
+     probe|
+
+    length := keyArray basicSize.
+    length < 10 ifTrue:[
+        "assuming, that for small dictionaries the overhead of hashing
+         is large ... maybe that proves wrong (if overhead of comparing
+         is high)"
+        index := keyArray identityIndexOf:key.
+        index == 0 ifTrue:[
+            ^ aBlock value
+        ].
+        ^ index
+    ].
+
+    startIndex := key identityHash \\ length + 1.
+
+    index := startIndex.
+    [true] whileTrue:[
+        probe := keyArray basicAt:index.
+        probe == key ifTrue:[^ index].
+
+        index == length ifTrue:[
+            index := 1
+        ] ifFalse:[
+            index := index + 1
+        ].
+        (probe isNil or:[index == startIndex]) ifTrue:[^ aBlock value]
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IdentitySet.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,94 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Set subclass:#IdentitySet
+         instanceVariableNames:''
+         classVariableNames:''
+         poolDictionaries:''
+         category:'Collections-Unordered'
+!
+
+IdentitySet comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+same as a Set but compares elements using == (i.e. they must be identical
+- not just equal in structure).
+Since compare is on identity, hashing is also done via
+identityHash instead of hash.
+
+%W% %E%
+
+written jan 93 by claus
+'!
+
+!IdentitySet methodsFor:'private'!
+
+find:key ifAbsent:aBlock
+    "Look for the key in the receiver.  If it is found, return
+     the index of the slot containing the key, otherwise
+     return the value of evaluating aBlock.
+     Redefined to compare for identity instead of equality"
+
+    |index      "{ Class:SmallInteger }"
+     length     "{ Class:SmallInteger }"
+     startIndex "{ Class:SmallInteger }" 
+     probe |
+
+    length := contentsArray basicSize.
+    startIndex := key identityHash \\ length + 1.
+
+    index := startIndex.
+    [true] whileTrue:[
+        probe := (contentsArray basicAt:index).
+        probe == key ifTrue:[^ index].
+        probe isNil ifTrue:[^ aBlock value].
+
+        index == length ifTrue:[
+            index := 1
+        ] ifFalse:[
+	    index := index + 1
+	].
+        index == startIndex ifTrue:[
+            ^ aBlock value
+        ]
+    ]
+!
+
+findElementOrNil:key
+    "Look for the key in the receiver.  Redefined to compare for
+     identity instead of equality"
+
+    |index      "{ Class:SmallInteger }"
+     length     "{ Class:SmallInteger }"
+     startIndex "{ Class:SmallInteger }"
+     probe |
+
+    length := contentsArray basicSize.
+    startIndex := key identityHash \\ length + 1.
+
+    index := startIndex.
+    [true] whileTrue:[
+        probe := contentsArray basicAt:index.
+        (probe isNil or: [key == probe]) ifTrue:[^ index].
+
+        index == length ifTrue:[
+            index := 1
+        ] ifFalse:[
+	    index := index + 1
+	].
+        index == startIndex ifTrue: [
+            ^ self grow findElementOrNil:key
+        ]
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Integer.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,641 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Number subclass:#Integer
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Magnitude-Numbers'
+!
+
+Integer comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+abstract superclass for all integer numbers
+
+%W% %E%
+written 88 by claus
+'!
+
+!Integer class methodsFor:'constants'!
+
+zero
+    ^ 0
+!
+
+unity
+    ^ 1
+! !
+
+!Integer methodsFor:'arithmetic'!
+
+quo:aNumber
+    "Return the integer quotient of dividing the receiver by aNumber with
+    truncation towards zero. For Integers this is same as //"
+
+    ^ self // aNumber
+
+! !
+
+!Integer methodsFor:'double dispatching'!
+
+sumFromFraction:aFraction
+    "sent when a fraction does not know how to add the recevier, an integer"
+
+    ^ (Fraction numerator:(aFraction numerator
+                           + (self * aFraction denominator))
+              denominator:aFraction denominator) reduced
+!
+
+differenceFromFraction:aFraction
+    "sent when a fraction does not know how to subtract the recevier, an integer"
+
+    ^ (Fraction numerator:((self * aFraction denominator) - aFraction numerator)
+              denominator:aFraction denominator) reduced
+!
+
+productFromFraction:aFraction
+    "sent when a fraction does not know how to multiply the recevier, an integer"
+
+    ^ (Fraction numerator:(self * aFraction numerator)
+              denominator:aFraction denominator) reduced
+! !
+
+!Integer methodsFor:'truncation & rounding'!
+
+ceiling
+    "I am my ceiling"
+
+    ^ self
+!
+
+floor
+    "I am my floor"
+
+    ^ self
+!
+
+rounded
+    "return the receiver rounded toward the next Integer -
+     for integers this is self"
+
+    ^ self
+!
+
+truncated
+    "return the receiver truncated towards zero - 
+     for integers this is self"
+
+    ^ self
+! !
+
+!Integer methodsFor:'queries'!
+
+digitLength
+    "return the number of bytes needed for the binary representation
+     of the receiver"
+
+    ^ (self log:256) ceiling asInteger
+!
+
+digitAt:n
+    "return the n-th byte of the binary representation"
+
+    |num count|
+
+    num := self.
+    count := n.
+    [count > 1] whileTrue:[
+        num := num // 256.
+        count := count - 1
+    ].
+    ^ num \\ 256
+!
+
+isInteger
+    "return true, if the receiver is some kind of integer number"
+
+    ^ true
+! !
+
+!Integer methodsFor:'misc math'!
+
+factorial
+    "return 1*2*3...*self"
+
+    (self > 1) ifTrue:[
+        ^ self * (self - 1) factorial
+    ].
+    ^ self
+!
+
+gcd:anInteger
+    "return the greatest common divisor (Euclid's algorithm)"
+
+    |ttt selfInteger temp|
+
+    ttt := anInteger.
+    selfInteger := self.
+    [ttt ~~ 0] whileTrue:[
+        temp := selfInteger \\ ttt.
+        selfInteger := ttt.
+        ttt := temp
+    ].
+    ^ selfInteger
+!
+
+lcm:anInteger
+    ^(self * anInteger) abs // (self gcd: anInteger)
+!
+
+fib
+    "dont use this method if you need fibionacci numbers -
+     this method is for benchmarking purposes only.
+     (use fastFib instead and dont ever try 60 fib ...)"
+
+    (self > 1) ifTrue:[
+        ^ (self - 1) fib + (self - 2) fib
+    ].
+    ^ 1
+
+    "Time millisecondsToRun:[30 fib]"
+!
+
+fastFib
+    "this method just to show how a changed algorithm can
+     change things much more drastic than tuning ...
+     (compare 30 fib with 30 fastFib / dont even try 60 fib)"
+
+    |fib|
+
+    self <= 1 ifTrue:[^ 1].
+
+    FibCache isNil ifTrue:[
+        FibCache := OrderedCollection new
+    ].
+    FibCache size >= self ifTrue:[
+        ^ FibCache at:self
+    ].
+    fib := (self - 2) fastFib + (self - 1) fastFib.
+
+    FibCache grow:self.
+    FibCache at:self put:fib.
+    ^ fib
+
+    "Time millisecondsToRun:[30 fastFib]"
+!
+
+acker:n
+    "return the value of acker(self, n)"
+
+    (self == 0) ifTrue:[^ n + 1].
+    (n == 0) ifTrue:[^ (self - 1) acker: 1].
+    ^ (self - 1) acker:(self acker:(n - 1))
+
+    "3 acker:2"
+! !
+
+!Integer methodsFor:'coercing and converting'!
+
+asFraction
+    "return a Fraction with same value as receiver"
+
+    ^ Fraction numerator:self denominator:1
+!
+
+asInteger
+    "return the receiver truncated towards zero - 
+     for integers this is self"
+
+    ^ self
+! !
+
+!Integer methodsFor:'printing & storing'!
+
+storeString
+    "return a string for storing - printString will do"
+
+    ^ self printString
+!
+
+printString
+    "return a string representation of the receiver"
+
+    ^ self printStringRadix:10
+!
+
+radixPrintStringRadix:aRadix
+    "return a string representation of the receiver in the specified
+     radix; prepend XXr to the string"
+
+    ^ (aRadix printString) , 'r', (self printStringRadix:aRadix)
+
+    "31 radixPrintStringRadix:2 "
+    "31 radixPrintStringRadix:3 "
+    "31 radixPrintStringRadix:36 "
+!
+
+printStringRadix:aRadix
+    "return a string representation of the receiver in the specified
+     radix (without the initial XXr)"
+
+    |leftPart|
+
+    (self = 0) ifTrue:[^ '0'].
+    (self < 0) ifTrue:[
+        ^ '-' , (self negated printStringRadix:aRadix)
+    ].
+    leftPart := self // aRadix.
+    (leftPart ~= 0) ifTrue:[
+        ^ (leftPart printStringRadix:aRadix) copyWith:(Character digitValue:(self \\ aRadix))
+    ].
+    ^ (Character digitValue:self) asString
+!
+
+printStringRadix:aRadix size:sz fill:fillCharacter
+    "return a string representation of the receiver in the specified
+     radix. The string is padded on the left with fillCharacter to make
+     its size as specified in sz."
+
+     |s|
+
+    s := self printStringRadix:aRadix.
+    s size < sz ifTrue:[
+        s := ((String new:(sz - s size)) atAllPut:fillCharacter) , s
+    ].
+    ^ s
+
+    "1024 printStringRadix:16 size:4 fill:$0"
+! !
+
+!Integer class methodsFor:'instance creation'!
+
+readFrom:aStream radix:radix
+    "return the next Integer from the (character-)stream aStream in radix;
+     (assumes that the initial XXR has already been read)
+     no whitespace-skipping; returns 0 if no number available"
+
+    |nextChar value|
+
+    nextChar := aStream peek.
+    value := 0.
+    [nextChar notNil and:[nextChar isDigitRadix:radix]] whileTrue:[
+        value := value * radix + nextChar digitValue.
+        nextChar := aStream nextPeek
+    ].
+    ^ value
+!
+
+readFrom:aStream
+    "return the next Integer from the (character-)stream aStream,
+     handling initial XXr for arbitrary radix numbers and initial
+     sign.
+     skipping all whitespace first; return nil if no number"
+
+    |nextChar value negative|
+
+    nextChar := aStream skipSeparators.
+    (nextChar == $-) ifTrue:[
+        negative := true.
+        nextChar := aStream nextPeek
+    ] ifFalse:[
+        negative := false
+    ].
+    nextChar isDigit ifFalse:[ ^ nil].
+    value := Integer readFrom:aStream radix:10.
+    nextChar := aStream peek.
+    ((nextChar == $r) or:[ nextChar == $R]) ifTrue:[
+        aStream next.
+        value := Integer readFrom:aStream radix:value
+    ].
+    negative ifTrue:[
+        ^ value negated
+    ].
+    ^ value
+! !
+
+!Integer methodsFor:'benchmarking'!
+
+sieve
+    "sieve the primes self times"
+
+    |num i k prime count flags time|
+
+    num := 8191.
+    flags := Array new:num.
+
+    Transcript show:'Sieve running ...'.
+    Transcript cr.
+
+    time := Time millisecondsToRun:[
+        self timesRepeat:[
+            count := 0.
+            flags atAllPut:1.
+            i := 1.
+            num timesRepeat:[
+                (flags at:i) == 1 ifTrue:[
+                    prime := i + i + 3.
+                    k := i + prime.
+                    [k <= num] whileTrue:[
+                        flags at:k put:0.
+                        k := k + prime
+                    ].
+                    count := count + 1
+                ].
+                i := i + 1
+            ].
+        ].
+    ].
+    Transcript show:'Sieve in Smalltalk: '.
+    Transcript show:self printString. 
+    Transcript showCr:' iteration(s).'.
+    Transcript show:'found '. 
+    Transcript show:count printString. 
+    Transcript showCr:' primes.' .
+    Transcript show:'time per run: '. 
+    Transcript show:(time / self) printString. 
+    Transcript showCr:' ms.'
+
+    "1 sieve"
+!
+
+sieveWithIntegers
+    "sieve the primes self times"
+
+    |num        "<SmallInteger>"
+     i          "<SmallInteger>"
+     k          "<SmallInteger>"
+     prime      "<SmallInteger>"
+     count      "<SmallInteger>"
+     flags time|
+
+    num := 8191.
+    flags := Array new:num.
+
+    Transcript show:'Sieve running ...'.
+    Transcript cr.
+
+    time := Time millisecondsToRun:[
+        self timesRepeat:[
+            count := 0.
+            flags atAllPut:1.
+            i := 1.
+            num timesRepeat:[
+                (flags at:i) == 1 ifTrue:[
+                    prime := i + i + 3.
+                    k := i + prime.
+                    [k <= num] whileTrue:[
+                        flags at:k put:0.
+                        k := k + prime
+                    ].
+                    count := count + 1
+                ].
+                i := i + 1
+            ].
+        ].
+    ].
+    Transcript show:'Sieve in Smalltalk: '.
+    Transcript show:self printString. 
+    Transcript showCr:' iteration(s).'.
+    Transcript show:'found '. 
+    Transcript show:count printString. 
+    Transcript showCr:' primes.' .
+    Transcript show:'time per run: '. 
+    Transcript show:(time / self) printString. 
+    Transcript showCr:' ms.'
+
+    "1 sieveWithIntegers"
+!
+
+recur1:num
+    "actual recursion method for recur1"
+
+    (num = 0) ifTrue:[^ self].
+    self recur1:(num - 1).
+    ^ self recur1:(num - 1)
+!
+
+recur1
+    "lots of recursion for testing send with arg"
+
+    |t|
+
+    t := Time millisecondsToRun:[
+        1 recur1:15
+    ].
+    Transcript showCr:(t printString)
+
+    "1 recur1"
+!
+
+recur2
+    "lots of recursion for testing send without arg"
+
+    (self > 0) ifTrue:[
+        (self - 1) recur2.
+        ^ (self - 1) recur2
+    ]
+
+    "Transcript showCr:(
+        Time millisecondsToRun:[
+            15 recur2
+        ]
+     ) printString"
+!
+
+countDown
+    |t index|
+
+    t := Time millisecondsToRun:[
+        index := 100000.
+        [index > 0] whileTrue:[
+            index := index - 1
+        ].
+    ].
+    Transcript showCr:(t printString)
+
+    "1 countDown"
+!
+
+countDown2
+    |t|
+
+    t := Time millisecondsToRun:[
+        |index|
+
+        index := 100000.
+        [index > 0] whileTrue:[
+            index := index - 1
+        ].
+    ].
+    Transcript showCr:(t printString)
+
+    "1 countDown2"
+!
+
+noop
+    ^ self
+!
+
+send:num
+    "lots of message sends"
+
+    |t|
+
+    t := Time millisecondsToRun:[
+        num timesRepeat:[
+            self noop
+        ].
+    ].
+    Transcript showCr:(t printString)
+
+    "1 send:100000"
+!
+
+memory
+    "lots of memory allocation"
+
+    |t|
+
+    t := Time millisecondsToRun:[
+        self timesRepeat:[
+            Array new:500
+        ].
+    ].
+    Transcript showCr:(t printString)
+
+    "10000 memory"
+!
+
+benchArithmetic
+    "arithmetic speed bench"
+
+    |p n m t|
+
+    n := 3.0.
+    m := 5.5.
+
+    t := Time millisecondsToRun:[
+        self timesRepeat:[
+            p := 5 / n + m
+        ]
+    ].
+    Transcript showCr:(t printString)
+
+    "10000 benchArithmetic"
+!
+
+sumTo
+    |val|
+
+    100 timesRepeat:[
+        val := 0.
+        1 to:10000 do:[:i |
+            val := val + i
+        ]
+    ].
+    "Time millisecondsToRun:[1 sumTo]"
+!
+
+fastSumTo
+    |val i|
+
+    100 timesRepeat:[
+        val := 0.
+        i := 1.
+        [i <= 10000] whileTrue:[
+            val := val + i.
+            i := i + 1
+        ].
+    ].
+    "Time millisecondsToRun:[1 fastSumTo]"
+!
+
+nestedLoop
+    |i|
+
+    100 timesRepeat:[
+        i := 0.
+        1 to:100 do:[:l1 |
+            1 to:100 do:[:l2 |
+                i := i + 1
+            ]
+        ]
+    ]
+    "Time millisecondsToRun:[1 nestedLoop]"
+!
+
+atAllPut
+    |vec t|
+
+    vec := Array new:100000.
+    t := Time millisecondsToRun:[
+        1 to:100000 do:[:i |
+            vec at:i put:7
+        ]
+    ].
+    ^ t
+
+    "1  atAllPut"
+!
+
+atAllPut2
+    |array t|
+
+    array := Array new:100000.
+    t := Time millisecondsToRun:[
+        1 to:100000 do:[:i |
+            array at:i put:7
+        ]
+    ].
+    ^ t
+
+    "1  atAllPut2"
+!
+
+sumAll 
+    |vec t s|
+
+    vec := Array new:100000.
+    1 to:100000 do:[:i |
+        vec at:i put:7
+    ].
+    s := 0.
+    t := Time millisecondsToRun:[
+        1 to:100000 do:[:i |
+            s := s + (vec at:i)
+        ]
+    ].
+    ^ t
+
+    "1  sumAll"
+!
+
+sumAll2 
+    |array t s|
+
+    array := Array new:100000.
+    1 to:100000 do:[:i |
+        array at:i put:7
+    ].
+    s := 0.
+    t := Time millisecondsToRun:[
+        1 to:100000 do:[:i |
+            s := s + (array at:i)
+        ]
+    ].
+    ^ t
+
+    "1  sumAll2"
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Interval.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,198 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+SequenceableCollection subclass:#Interval
+       instanceVariableNames:'start stop step'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Sequenceable'
+!
+
+Interval comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+Intervals represent a collection (or range) of numeric values specified by
+a startValue, an endValue and a step. The elements are computed, not stored.
+For example, the interval (1 to:5) containes the elements (1 2 3 4 5) and
+(1 to:6 by:2) contains (1 3 5).
+
+
+%W% %E%
+
+written summer 89 by claus
+'!
+
+!Interval class methodsFor:'instance creation'!
+
+from:start to:stop
+    "return a new interval with elements from start
+     to stop by 1"
+
+    ^ self new
+        setFrom:start 
+        to:stop
+        by:1
+!
+
+from:start to:stop by:step
+    "return a new interval with elements from start
+     to stop by step"
+
+    ^ self new
+        setFrom:start
+        to:stop
+        by:step
+! !
+
+!Interval methodsFor:'private'!
+
+setFrom:startInteger to:stopInteger by:stepInteger
+    start := startInteger.
+    stop := stopInteger.
+    step := stepInteger
+! !
+
+!Interval methodsFor:'accessing'!
+
+first
+    "return the first element of the collection"
+
+    ^ start
+!
+
+start
+    "return the first number of the range"
+
+    ^ start
+!
+
+start:aNumber
+    "set the first number of the range"
+
+    start := aNumber
+!
+
+stop
+    "return the end number of the range"
+
+    ^ stop
+!
+
+stop:aNumber
+    "set the end number of the range"
+
+    stop := aNumber
+!
+
+step
+    "return the step increment of the range"
+
+    ^ step
+!
+
+step:aNumber
+    "set the step increment of the range"
+
+    step := aNumber
+!
+
+size
+    "return the number of elements in the collection"
+
+    (step < 0) ifTrue:[
+        (start < stop) ifTrue:[
+            ^ 0
+        ].
+        ^ stop - start // step + 1
+    ].
+    (stop < start) ifTrue:[
+        ^ 0
+    ].
+    ^ stop - start // step + 1
+!
+
+at:index
+    (index between:1 and:self size) ifTrue:[
+        ^ start + (step * (index - 1))
+    ].
+    self errorSubscriptBounds:index
+!
+
+at:index put:anObject
+    self error:'you cannot store into an interval'
+! !
+
+!Interval methodsFor:'adding/removing elements'!
+
+add:newObject
+    self error:'elements cannot be added to an interval'
+!
+
+remove:anObject
+    self error:'elements cannot be removed from an interval'
+! !
+
+!Interval methodsFor:'private'!
+
+species
+    ^ OrderedCollection
+! !
+    
+!Interval methodsFor:'enumeration'!
+
+do:aBlock
+    |aValue|
+
+    aValue := start.
+    step < 0 ifTrue:[
+        [stop <= aValue] whileTrue:[
+            aBlock value:aValue.
+            aValue := aValue + step
+        ]
+    ] ifFalse:[
+        [stop >= aValue] whileTrue:[
+            aBlock value:aValue.
+            aValue := aValue + step
+        ]
+    ]
+!
+
+select:aBlock
+    "evaluate the argument, aBlock for every element in the collection
+     and return a collection of all elements for which the block return
+     true. redefined since SeqColl accesses the receiver with at:, which is
+     slow for intervals."
+
+    |newColl|
+
+    newColl := OrderedCollection new:self size.
+    self do:[:each |
+        (aBlock value:each) ifTrue:[newColl add:each]
+    ].
+    ^ newColl
+!
+
+collect:aBlock
+    "evaluate the argument, aBlock for every element in the collection
+     and return a collection of the results - redefined since SeqColl
+     accesses the receiver via at:, which is slow for intervals"
+
+    |newCollection|
+
+    newCollection := self species new:(self size).
+    self do:[:each |
+        newCollection add:(aBlock value:each)
+    ].
+    ^ newCollection
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LargeInt.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,723 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Integer subclass:#LargeInteger
+       instanceVariableNames:'sign digitArray'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Magnitude-Numbers'
+!
+
+LargeInteger comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+This class provides arbitrary precision integers. These are represented as:
+  sign (-1/0/+1) and, if sign ~~ 0
+  an Array of digits with 4 digits per element; 
+  least significant 4 digits at index 1 ...
+
+This is definitely not a good (fast) implementation -
+but ok for now, since LargeIntegers are not used very often.
+It will be reimplemented when everything else runs fine and a need arises
+(or someone rewrites it and sends me the code :-).
+
+%W% %E%
+'!
+
+!LargeInteger class methodsFor:'instance creation'!
+
+new:numberOfDigits
+    "catch creation message"
+
+    self error:'LargeIntegers cannot be created with new'
+!
+
+new
+    "catch creation message"
+
+    self error:'LargeIntegers cannot be created with new'
+!
+
+value:aSmallInteger
+    "create and return a new LargeInteger with value taken from
+     the argument, aSmallInteger"
+
+    ^ self basicNew value:aSmallInteger
+
+    "LargeInteger value:3689"
+!
+
+valueLow:lowBits hi:hiBits
+    "create and return a new LargeInteger with value taken from
+     the two 16-bit args"
+
+    hiBits < 0 ifTrue:[
+        ^ ((self value:hiBits negated) * 16r10000 + lowBits) negated
+    ].
+    ^ (self value:hiBits) * 16r10000 + lowBits
+! !
+
+
+!LargeInteger methodsFor:'coercing & converting'!
+
+coerce:aNumber
+    "return the argument as a LargeInteger"
+
+    ^ aNumber asLargeInteger
+!
+
+asLargeInteger
+    "return a LargeInteger with same value as myself - thats me"
+
+    ^ self
+!
+
+asSmallInteger
+    "return a SmallInteger with same value as myself - the result
+     is invalid if the receivers value cannot be represented
+     as a SmallInteger"
+
+    |value|
+
+    value := 0.
+    (sign == 0) ifFalse:[
+        digitArray reverseDo:[:aDigit |
+            value := (value times:10000) + aDigit 
+        ].
+        (sign < 0) ifTrue:[
+            value := value negated
+        ]
+    ].
+    ^ value
+!
+
+asFloat
+    "return a Float with same value as myself"
+
+    |newFloat|
+
+    newFloat := 0.0.
+    (sign == 0) ifFalse:[
+        digitArray reverseDo:[:aDigit |
+            newFloat := (newFloat * 10000.0) + aDigit asFloat
+        ].
+        (sign < 0) ifTrue:[
+            newFloat := newFloat negated
+        ]
+    ].
+    ^ newFloat
+!
+
+value:aSmallInteger
+    "return a new LargeInteger with value taken from a SmallInteger"
+
+    |absValue 
+     index "{ Class: SmallInteger }"|
+
+    (aSmallInteger == 0) ifTrue: [
+        digitArray := nil.
+        sign := 0.
+        ^ self
+    ].
+    (aSmallInteger < 0) ifTrue: [
+        sign := -1.
+        absValue := aSmallInteger negated
+    ] ifFalse: [
+        sign := 1.
+        absValue := aSmallInteger
+    ].
+    digitArray := Array new:3.
+    index := 1.
+    [absValue == 0] whileFalse: [
+        digitArray at:index put:(absValue \\ 10000).
+        absValue := absValue // 10000.
+        index := index + 1
+    ].
+    [index <= 3] whileTrue:[
+        digitArray at:index put:0.
+        index := index + 1
+    ]
+! !
+    
+!LargeInteger methodsFor:'comparing'!
+
+= aNumber
+    "return true, if the argument, aNumber has the same value as
+     the receiver"
+
+    (aNumber class == LargeInteger) ifFalse:[
+        aNumber respondsToArithmetic ifFalse:[ ^ false ].
+        ^ self retry:#= coercing:aNumber
+    ].
+    (aNumber sign == sign) ifFalse:[^ false].
+    ^ self absEq:aNumber
+!
+
+< aNumber
+    "return true, if the argument, aNumber is greater than the receiver"
+
+    |otherSign|
+
+    (aNumber class == LargeInteger) ifFalse:[
+        ^ self retry:#< coercing:aNumber
+    ].
+    otherSign := aNumber sign.
+
+    (sign > 0) ifTrue:[
+        "I am positive"
+        (otherSign > 0) ifTrue:[^ self absLess:aNumber].
+        ^ false "aNumber is <= 0"
+    ].
+    (sign == 0) ifTrue:[
+        (otherSign > 0) ifTrue:[^ true].
+        ^ false
+    ].
+    "I am negative"
+    (otherSign > 0) ifTrue:[^ true].
+    (otherSign == 0) ifTrue:[^ true].
+    ^ (self absLess:aNumber) not
+! !
+
+!LargeInteger methodsFor:'arithmetic'!
+
++ aNumber
+    "return the sum of the receiver and the argument, aNumber"
+
+    |otherSign|
+
+    (aNumber class == LargeInteger) ifFalse:[
+        ^ self retry:#+ coercing:aNumber
+    ].
+    otherSign := aNumber sign.
+
+    (sign > 0) ifTrue:[
+        "I am positive"
+        (otherSign > 0) ifTrue:[^ self absPlus:aNumber].
+        (otherSign < 0) ifTrue:[^ self absMinus:aNumber].
+        ^ self
+    ].
+    (sign == 0) ifTrue:[^ aNumber].
+    "I am negative"
+    (otherSign > 0) ifTrue:[^ aNumber absMinus:self].
+    (otherSign < 0) ifTrue:[^ (self absPlus:aNumber) negated].
+    ^ self
+!
+
+- aNumber
+    "return the difference of the receiver and the argument, aNumber"
+
+    |otherSign|
+
+    (aNumber class == LargeInteger) ifFalse:[
+        ^ self retry:#- coercing:aNumber
+    ].
+    otherSign := aNumber sign.
+    (sign > 0) ifTrue:[
+        "I am positive"
+        (otherSign > 0) ifTrue:[^ self absMinus:aNumber].
+        (otherSign < 0) ifTrue:[^ self absPlus:aNumber].
+        ^ self
+    ].
+    (sign == 0) ifTrue:[^ aNumber negated].
+    "I am negative"
+    (otherSign > 0) ifTrue:[^ (self absPlus:aNumber) negated].
+    (otherSign < 0) ifTrue:[^ (self absMinus:aNumber) negated].
+    ^ self
+!
+
+* aNumber
+    "return the product of the receiver and the argument, aNumber"
+
+    |otherSign|
+
+    (aNumber = 10) ifTrue:[
+        ^ self deepCopy mul10
+    ].
+    (aNumber class == LargeInteger) ifFalse:[
+        ^ self retry:#* coercing:aNumber
+    ].
+    otherSign := aNumber sign.
+   
+    (sign == 0) ifTrue:[^ 0].
+    (sign == otherSign) ifTrue:[^ self absMul:aNumber].
+    (otherSign == 0) ifTrue:[^ 0].
+    ^ (self absMul:aNumber) negated
+!
+
+/ aNumber
+    "this is a q&d hack - we loose lots of precision here ..."
+
+    ^ (self asFloat / aNumber asFloat)
+!
+    
+// aNumber
+    "return the quotient of the receiver and the argument, aNumber"
+
+    |otherSign|
+
+    (aNumber = 10) ifTrue:[
+        ^ self deepCopy div10
+    ].
+    (aNumber class == LargeInteger) ifFalse:[
+        ^ self retry:#// coercing:aNumber
+    ].
+    otherSign := aNumber sign.
+
+    sign < 0 ifTrue:[
+        (sign == otherSign) ifTrue:[^ (self negated absDiv:aNumber negated) at:1].
+        ^ ((self negated absDiv:aNumber) at:1) negated
+    ].
+    (sign == otherSign) ifTrue:[^ (self absDiv:aNumber) at:1].
+    ^ ((self absDiv:aNumber negated) at:1) negated
+!
+    
+\\ aNumber
+    "return the remainder of division of the receiver by the argument, aNumber"
+
+    |otherSign|
+
+    (aNumber class == LargeInteger) ifFalse:[
+        ^ self retry:#\\ coercing:aNumber
+    ].
+
+    otherSign := aNumber sign.
+    sign < 0 ifTrue:[
+        (sign == otherSign) ifTrue:[^ (self negated absDiv:aNumber negated) at:2].
+        ^ ((self negated absDiv:aNumber) at:2) negated
+    ].
+    (sign == otherSign) ifTrue:[^ (self absDiv:aNumber) at:2].
+    ^ ((self absDiv:aNumber negated) at:2) negated
+!
+
+negated
+    "return a LargeInteger with value negated from receivers value"
+
+    |newNumber|
+
+    (sign == 0) ifTrue:[^ 0].
+    newNumber := self shallowCopy.
+    newNumber sign:(sign negated).
+    ^ newNumber
+! !
+
+!LargeInteger methodsFor:'bit operations'!
+
+bitAnd:anInteger
+    "q & d hack to make Random work;
+     this works only correctly, if my value can be represented
+     as a SmallInteger"
+
+    ^ self asSmallInteger bitAnd:anInteger
+! !
+
+!LargeInteger methodsFor:'testing'!
+
+sign
+    "return the sign of the receiver"
+
+    ^ sign
+!
+
+odd
+    "return true if the receiver is odd"
+
+    ^ (digitArray at:1) even
+!
+
+even
+    "return true if the receiver is even"
+
+    ^ (digitArray at:1) even
+!
+
+negative
+    "return true, if the receiver is < 0"
+
+    ^ (sign < 0)
+!
+
+positive
+    "return true, if the receiver is >= 0"
+
+    ^ (sign >= 0)
+!
+
+strictlyPositive
+    "return true, if the receiver is > 0"
+
+    ^ (sign > 0)
+! !
+
+!LargeInteger methodsFor:'private'!
+
+absEq:aLargeInteger
+    "return true, if abs(self) = abs(theArgument)"
+
+    |len1 "{ Class: SmallInteger }"
+     len2 "{ Class: SmallInteger }"
+     d1   "{ Class: SmallInteger }"
+     d2   "{ Class: SmallInteger }"
+     otherDigits |
+
+    len1 := digitArray size.
+    otherDigits := aLargeInteger digits.
+    len2 := otherDigits size.
+
+    [(digitArray basicAt:len1) == 0] whileTrue:[
+        len1 := len1 - 1
+    ].
+    [(otherDigits basicAt:len2) == 0] whileTrue:[
+        len2 := len2 - 1
+    ].
+    (len1 ~~ len2) ifTrue:[^ false].
+    [len1 > 0] whileTrue:[
+        d1 := digitArray basicAt:len1.
+        d2 := otherDigits basicAt:len1.
+        (d1 ~~ d2) ifTrue:[^ false].
+        len1 := len1 - 1
+    ].
+    ^ true
+!
+
+absLess:aLargeInteger
+    "return true, if abs(self) < abs(theArgument)"
+
+    |len1 "{ Class: SmallInteger }"
+     len2 "{ Class: SmallInteger }"
+     d1   "{ Class: SmallInteger }"
+     d2   "{ Class: SmallInteger }"
+     otherDigits |
+
+    len1 := digitArray size.
+    otherDigits := aLargeInteger digits.
+    len2 := otherDigits size.
+
+    [(digitArray basicAt:len1) == 0] whileTrue:[
+        len1 := len1 - 1
+    ].
+    [(otherDigits basicAt:len2) == 0] whileTrue:[
+        len2 := len2 - 1
+    ].
+    (len1 < len2) ifTrue:[^ true].
+    (len1 > len2) ifTrue:[^ false].
+
+    [len1 > 0] whileTrue:[
+        d1 := digitArray basicAt:len1.
+        d2 := otherDigits basicAt:len1.
+        (d1 < d2) ifTrue:[^ true].
+        (d1 > d2) ifTrue:[^ false].
+        len1 := len1 - 1
+    ].
+    ^ false
+!
+
+absPlus:aLargeInteger
+    "return a LargeInteger representing abs(self) + abs(theArgument)"
+
+    |result done otherDigits resultDigits
+     len1  "{ Class: SmallInteger }"
+     len2  "{ Class: SmallInteger }"
+     index "{ Class: SmallInteger }"
+     carry "{ Class: SmallInteger }"
+     sum   "{ Class: SmallInteger }" |
+
+    len1 := digitArray size.
+    otherDigits := aLargeInteger digits.
+    len2 := otherDigits size.
+
+    result := LargeInteger basicNew 
+                           numberOfDigits:((len1 max: len2) + 1).
+    result sign:1.
+    resultDigits := result digits.
+
+    index := 1.
+    carry := 0.
+
+    done := false.
+    [done] whileFalse:[
+        sum := carry.
+        (index <= len1) ifTrue:[
+            sum := sum + (digitArray basicAt:index).
+            (index <= len2) ifTrue:[
+                sum := sum + (otherDigits basicAt:index)
+            ]
+        ] ifFalse:[
+            (index <= len2) ifTrue:[
+                sum := sum + (otherDigits basicAt:index)
+            ] ifFalse:[
+                "end reached"
+                done := true
+            ]
+        ].
+        (sum > 9999) ifTrue:[
+            carry := 1.
+            sum := sum - 10000
+        ] ifFalse:[
+            carry := 0
+        ].
+        resultDigits basicAt:index put:sum.
+        index := index + 1
+    ].
+    ^ result normalize
+!
+
+absMinus:aLargeInteger
+    "return a LargeInteger representing abs(self) - abs(theArgument)"
+
+    |result done
+     otherDigits resultDigits
+     len1   "{ Class: SmallInteger }"
+     len2   "{ Class: SmallInteger }"
+     index  "{ Class: SmallInteger }"
+     borrow "{ Class: SmallInteger }"
+     diff   "{ Class: SmallInteger }"
+     sum    "{ Class: SmallInteger }"
+     carry  "{ Class: SmallInteger }" |
+
+    len1 := digitArray size.
+    otherDigits := aLargeInteger digits.
+    len2 := otherDigits size.
+
+    result := LargeInteger basicNew 
+                           numberOfDigits:((len1 max: len2) + 1).
+    result sign:1.
+    resultDigits := result digits.
+
+    index := 1.
+    borrow := 0.
+
+    done := false.
+    [done] whileFalse:[
+        diff := borrow.
+        (index <= len1) ifTrue:[
+            diff := diff + (digitArray basicAt:index).
+            (index <= len2) ifTrue:[
+                diff := diff - (otherDigits basicAt:index)
+            ]
+        ] ifFalse:[
+            (index <= len2) ifTrue:[
+                diff := diff - (otherDigits basicAt:index)
+            ] ifFalse:[
+                "end reached"
+                done := true
+            ]
+        ].
+        (diff < 0) ifTrue:[
+            borrow := -1.
+            diff := diff + 10000
+        ] ifFalse:[
+            borrow := 0
+        ].
+        resultDigits basicAt:index put:diff.
+        index := index + 1
+    ].
+    (borrow ~~ 0) ifTrue:[
+        result sign: -1.
+        carry := 0.
+        1 to:(index - 1) do:[:i |
+            sum := ((resultDigits at:i) + carry - 10000) negated.
+            resultDigits at:i put:sum.
+            carry := 1
+        ]
+    ].
+    ^ result normalize
+!
+
+absMul:aLargeInteger
+    "return a LargeInteger representing abs(self) * abs(theArgument)"
+
+    |result otherDigits resultDigits
+     len1     "{ Class: SmallInteger }"
+     len2     "{ Class: SmallInteger }"
+     dstIndex "{ Class: SmallInteger }"
+     carry    "{ Class: SmallInteger }"
+     prod     "{ Class: SmallInteger }" |
+
+    len1 := digitArray size.
+    otherDigits := aLargeInteger digits.
+    len2 := otherDigits size.
+
+    result := LargeInteger basicNew numberOfDigits:(len1 + len2 + 1).
+    result sign:1.
+    resultDigits := result digits.
+
+    "clear result"
+    resultDigits atAllPut:0.
+
+    1 to:len1 do:[:index1 |
+        1 to:len2 do:[:index2 |
+            dstIndex := index1 + index2 - 1.
+            prod := (digitArray basicAt:index1) * (otherDigits basicAt:index2).
+            prod := prod + (resultDigits basicAt:dstIndex).
+            resultDigits basicAt:dstIndex put:(prod \\ 10000).
+            carry := prod // 10000.
+            (carry ~~ 0) ifTrue:[
+                resultDigits basicAt:(dstIndex + 1)
+                                 put:(resultDigits basicAt:(dstIndex + 1)) + carry
+            ]
+        ]
+    ].
+    ^ result normalize
+!
+
+absDiv:anInteger
+    "return an array with two LargeIntegers representing
+     abs(self) // abs(theArgument) and abs(self) \\ abs(theArgument)"
+
+    |tmp1 tmp2 
+     rem 
+     count "{ Class: SmallInteger }"
+     digit "{ Class: SmallInteger }" |
+
+    self == 0 ifTrue:[^ 0].
+    anInteger == 0 ifTrue:[^ self divideByZeroError].
+
+    self < anInteger ifTrue:[
+        ^ Array with:0 with:self
+    ].
+
+    tmp1 := self deepCopy.
+    tmp2 := anInteger deepCopy.
+    count := 0.
+    [tmp2 < tmp1] whileTrue:[
+        tmp2 mul10.
+        count := count + 1
+    ].
+
+    tmp2 div10.
+
+    rem := 0 asLargeInteger. 
+    [count == 0] whileFalse:[
+        digit := 0.
+        [tmp1 >= tmp2] whileTrue:[
+            digit := digit + 1.
+            tmp1 := tmp1 - tmp2
+        ].
+        rem := rem * 10 + digit.
+        tmp2 div10.
+        count := count - 1
+    ].
+    ^ Array with:rem with:tmp1 
+!
+
+mul10
+    "destructively multiply the receiver by 10.
+     private - used for division only"
+
+    |carry "{ Class: SmallInteger }"
+     prod  "{ Class: SmallInteger }"|
+
+    carry := 0.
+    1 to:(digitArray size) do:[:index |
+        prod := (digitArray at:index) * 10 + carry.
+        digitArray at:index put:prod \\ 10000.
+        carry := prod // 10000
+    ].
+    carry ~~ 0 ifTrue:[
+        digitArray := digitArray copyWith:carry
+    ]
+!
+
+div10
+    "destructively divide the receiver by 10.
+     private - used for division only"
+
+    |nDigits|
+
+    nDigits := digitArray size.
+    1 to:(nDigits - 1) do:[:index |
+        digitArray at:index put:((digitArray at:index) // 10
+                                + ((digitArray at:index + 1) \\ 10 * 1000))
+    ].
+    digitArray at:nDigits put:(digitArray at:nDigits) // 10
+!
+
+normalize
+    "if the receiver can be represented as a SmallInteger, return
+     a SmallInteger with my value; otherwise return self with leading
+     zeros removed"
+
+    |index "{ Class: SmallInteger }" |
+
+    index := digitArray size.
+    [(index > 0) and:[(digitArray at:index) == 0]] whileTrue:[
+        index := index - 1
+    ].
+    (index == 1) ifTrue:[
+        ^ (digitArray at:1) * sign
+    ].
+    (index == 2) ifTrue:[
+        ^ ((digitArray at:2) * 10000 + (digitArray at:1)) * sign
+    ].
+    (index == 0) ifTrue:[
+        ^ 0
+    ].
+    (index ~~ digitArray size) ifTrue:[
+        digitArray := digitArray copyFrom:1 to:index
+    ].
+    ^ self
+!
+
+digits
+    ^ digitArray
+!
+
+numberOfDigits
+    ^ digitArray size
+!
+
+numberOfDigits:nDigits
+    digitArray := Array new:nDigits
+!
+
+sign:aNumber
+    sign := aNumber
+! !
+
+!LargeInteger methodsFor:'printing & storing'!
+
+storeString
+    "return a string representation of the receiver, which can be
+     used to reconstruct the receiver"
+
+    ^ self printString , ' asLargeInteger'
+!
+
+printString
+    |aString index fourDigits n|
+
+    index := digitArray size.
+    [(index > 1) and:[(digitArray at:index) == 0]] whileTrue:[
+        index := index - 1
+    ].
+    (sign == 0) ifTrue: [^ '0'].
+    (sign == -1) ifTrue: [
+        aString := '-'
+    ] ifFalse: [
+        aString := ''
+    ].
+
+    aString := aString , (digitArray basicAt:index) printString.
+    index := index - 1.
+    [index > 0] whileTrue:[
+        fourDigits := (digitArray basicAt:index) printString.
+        n := fourDigits size.
+        (n < 4) ifTrue:[
+            aString := aString , ('000' copyFrom:n to:3)
+        ].
+        aString := aString , fourDigits.
+        index := index - 1
+    ].
+    ^ aString
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LargeInteger.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,723 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Integer subclass:#LargeInteger
+       instanceVariableNames:'sign digitArray'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Magnitude-Numbers'
+!
+
+LargeInteger comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+This class provides arbitrary precision integers. These are represented as:
+  sign (-1/0/+1) and, if sign ~~ 0
+  an Array of digits with 4 digits per element; 
+  least significant 4 digits at index 1 ...
+
+This is definitely not a good (fast) implementation -
+but ok for now, since LargeIntegers are not used very often.
+It will be reimplemented when everything else runs fine and a need arises
+(or someone rewrites it and sends me the code :-).
+
+%W% %E%
+'!
+
+!LargeInteger class methodsFor:'instance creation'!
+
+new:numberOfDigits
+    "catch creation message"
+
+    self error:'LargeIntegers cannot be created with new'
+!
+
+new
+    "catch creation message"
+
+    self error:'LargeIntegers cannot be created with new'
+!
+
+value:aSmallInteger
+    "create and return a new LargeInteger with value taken from
+     the argument, aSmallInteger"
+
+    ^ self basicNew value:aSmallInteger
+
+    "LargeInteger value:3689"
+!
+
+valueLow:lowBits hi:hiBits
+    "create and return a new LargeInteger with value taken from
+     the two 16-bit args"
+
+    hiBits < 0 ifTrue:[
+        ^ ((self value:hiBits negated) * 16r10000 + lowBits) negated
+    ].
+    ^ (self value:hiBits) * 16r10000 + lowBits
+! !
+
+
+!LargeInteger methodsFor:'coercing & converting'!
+
+coerce:aNumber
+    "return the argument as a LargeInteger"
+
+    ^ aNumber asLargeInteger
+!
+
+asLargeInteger
+    "return a LargeInteger with same value as myself - thats me"
+
+    ^ self
+!
+
+asSmallInteger
+    "return a SmallInteger with same value as myself - the result
+     is invalid if the receivers value cannot be represented
+     as a SmallInteger"
+
+    |value|
+
+    value := 0.
+    (sign == 0) ifFalse:[
+        digitArray reverseDo:[:aDigit |
+            value := (value times:10000) + aDigit 
+        ].
+        (sign < 0) ifTrue:[
+            value := value negated
+        ]
+    ].
+    ^ value
+!
+
+asFloat
+    "return a Float with same value as myself"
+
+    |newFloat|
+
+    newFloat := 0.0.
+    (sign == 0) ifFalse:[
+        digitArray reverseDo:[:aDigit |
+            newFloat := (newFloat * 10000.0) + aDigit asFloat
+        ].
+        (sign < 0) ifTrue:[
+            newFloat := newFloat negated
+        ]
+    ].
+    ^ newFloat
+!
+
+value:aSmallInteger
+    "return a new LargeInteger with value taken from a SmallInteger"
+
+    |absValue 
+     index "{ Class: SmallInteger }"|
+
+    (aSmallInteger == 0) ifTrue: [
+        digitArray := nil.
+        sign := 0.
+        ^ self
+    ].
+    (aSmallInteger < 0) ifTrue: [
+        sign := -1.
+        absValue := aSmallInteger negated
+    ] ifFalse: [
+        sign := 1.
+        absValue := aSmallInteger
+    ].
+    digitArray := Array new:3.
+    index := 1.
+    [absValue == 0] whileFalse: [
+        digitArray at:index put:(absValue \\ 10000).
+        absValue := absValue // 10000.
+        index := index + 1
+    ].
+    [index <= 3] whileTrue:[
+        digitArray at:index put:0.
+        index := index + 1
+    ]
+! !
+    
+!LargeInteger methodsFor:'comparing'!
+
+= aNumber
+    "return true, if the argument, aNumber has the same value as
+     the receiver"
+
+    (aNumber class == LargeInteger) ifFalse:[
+        aNumber respondsToArithmetic ifFalse:[ ^ false ].
+        ^ self retry:#= coercing:aNumber
+    ].
+    (aNumber sign == sign) ifFalse:[^ false].
+    ^ self absEq:aNumber
+!
+
+< aNumber
+    "return true, if the argument, aNumber is greater than the receiver"
+
+    |otherSign|
+
+    (aNumber class == LargeInteger) ifFalse:[
+        ^ self retry:#< coercing:aNumber
+    ].
+    otherSign := aNumber sign.
+
+    (sign > 0) ifTrue:[
+        "I am positive"
+        (otherSign > 0) ifTrue:[^ self absLess:aNumber].
+        ^ false "aNumber is <= 0"
+    ].
+    (sign == 0) ifTrue:[
+        (otherSign > 0) ifTrue:[^ true].
+        ^ false
+    ].
+    "I am negative"
+    (otherSign > 0) ifTrue:[^ true].
+    (otherSign == 0) ifTrue:[^ true].
+    ^ (self absLess:aNumber) not
+! !
+
+!LargeInteger methodsFor:'arithmetic'!
+
++ aNumber
+    "return the sum of the receiver and the argument, aNumber"
+
+    |otherSign|
+
+    (aNumber class == LargeInteger) ifFalse:[
+        ^ self retry:#+ coercing:aNumber
+    ].
+    otherSign := aNumber sign.
+
+    (sign > 0) ifTrue:[
+        "I am positive"
+        (otherSign > 0) ifTrue:[^ self absPlus:aNumber].
+        (otherSign < 0) ifTrue:[^ self absMinus:aNumber].
+        ^ self
+    ].
+    (sign == 0) ifTrue:[^ aNumber].
+    "I am negative"
+    (otherSign > 0) ifTrue:[^ aNumber absMinus:self].
+    (otherSign < 0) ifTrue:[^ (self absPlus:aNumber) negated].
+    ^ self
+!
+
+- aNumber
+    "return the difference of the receiver and the argument, aNumber"
+
+    |otherSign|
+
+    (aNumber class == LargeInteger) ifFalse:[
+        ^ self retry:#- coercing:aNumber
+    ].
+    otherSign := aNumber sign.
+    (sign > 0) ifTrue:[
+        "I am positive"
+        (otherSign > 0) ifTrue:[^ self absMinus:aNumber].
+        (otherSign < 0) ifTrue:[^ self absPlus:aNumber].
+        ^ self
+    ].
+    (sign == 0) ifTrue:[^ aNumber negated].
+    "I am negative"
+    (otherSign > 0) ifTrue:[^ (self absPlus:aNumber) negated].
+    (otherSign < 0) ifTrue:[^ (self absMinus:aNumber) negated].
+    ^ self
+!
+
+* aNumber
+    "return the product of the receiver and the argument, aNumber"
+
+    |otherSign|
+
+    (aNumber = 10) ifTrue:[
+        ^ self deepCopy mul10
+    ].
+    (aNumber class == LargeInteger) ifFalse:[
+        ^ self retry:#* coercing:aNumber
+    ].
+    otherSign := aNumber sign.
+   
+    (sign == 0) ifTrue:[^ 0].
+    (sign == otherSign) ifTrue:[^ self absMul:aNumber].
+    (otherSign == 0) ifTrue:[^ 0].
+    ^ (self absMul:aNumber) negated
+!
+
+/ aNumber
+    "this is a q&d hack - we loose lots of precision here ..."
+
+    ^ (self asFloat / aNumber asFloat)
+!
+    
+// aNumber
+    "return the quotient of the receiver and the argument, aNumber"
+
+    |otherSign|
+
+    (aNumber = 10) ifTrue:[
+        ^ self deepCopy div10
+    ].
+    (aNumber class == LargeInteger) ifFalse:[
+        ^ self retry:#// coercing:aNumber
+    ].
+    otherSign := aNumber sign.
+
+    sign < 0 ifTrue:[
+        (sign == otherSign) ifTrue:[^ (self negated absDiv:aNumber negated) at:1].
+        ^ ((self negated absDiv:aNumber) at:1) negated
+    ].
+    (sign == otherSign) ifTrue:[^ (self absDiv:aNumber) at:1].
+    ^ ((self absDiv:aNumber negated) at:1) negated
+!
+    
+\\ aNumber
+    "return the remainder of division of the receiver by the argument, aNumber"
+
+    |otherSign|
+
+    (aNumber class == LargeInteger) ifFalse:[
+        ^ self retry:#\\ coercing:aNumber
+    ].
+
+    otherSign := aNumber sign.
+    sign < 0 ifTrue:[
+        (sign == otherSign) ifTrue:[^ (self negated absDiv:aNumber negated) at:2].
+        ^ ((self negated absDiv:aNumber) at:2) negated
+    ].
+    (sign == otherSign) ifTrue:[^ (self absDiv:aNumber) at:2].
+    ^ ((self absDiv:aNumber negated) at:2) negated
+!
+
+negated
+    "return a LargeInteger with value negated from receivers value"
+
+    |newNumber|
+
+    (sign == 0) ifTrue:[^ 0].
+    newNumber := self shallowCopy.
+    newNumber sign:(sign negated).
+    ^ newNumber
+! !
+
+!LargeInteger methodsFor:'bit operations'!
+
+bitAnd:anInteger
+    "q & d hack to make Random work;
+     this works only correctly, if my value can be represented
+     as a SmallInteger"
+
+    ^ self asSmallInteger bitAnd:anInteger
+! !
+
+!LargeInteger methodsFor:'testing'!
+
+sign
+    "return the sign of the receiver"
+
+    ^ sign
+!
+
+odd
+    "return true if the receiver is odd"
+
+    ^ (digitArray at:1) even
+!
+
+even
+    "return true if the receiver is even"
+
+    ^ (digitArray at:1) even
+!
+
+negative
+    "return true, if the receiver is < 0"
+
+    ^ (sign < 0)
+!
+
+positive
+    "return true, if the receiver is >= 0"
+
+    ^ (sign >= 0)
+!
+
+strictlyPositive
+    "return true, if the receiver is > 0"
+
+    ^ (sign > 0)
+! !
+
+!LargeInteger methodsFor:'private'!
+
+absEq:aLargeInteger
+    "return true, if abs(self) = abs(theArgument)"
+
+    |len1 "{ Class: SmallInteger }"
+     len2 "{ Class: SmallInteger }"
+     d1   "{ Class: SmallInteger }"
+     d2   "{ Class: SmallInteger }"
+     otherDigits |
+
+    len1 := digitArray size.
+    otherDigits := aLargeInteger digits.
+    len2 := otherDigits size.
+
+    [(digitArray basicAt:len1) == 0] whileTrue:[
+        len1 := len1 - 1
+    ].
+    [(otherDigits basicAt:len2) == 0] whileTrue:[
+        len2 := len2 - 1
+    ].
+    (len1 ~~ len2) ifTrue:[^ false].
+    [len1 > 0] whileTrue:[
+        d1 := digitArray basicAt:len1.
+        d2 := otherDigits basicAt:len1.
+        (d1 ~~ d2) ifTrue:[^ false].
+        len1 := len1 - 1
+    ].
+    ^ true
+!
+
+absLess:aLargeInteger
+    "return true, if abs(self) < abs(theArgument)"
+
+    |len1 "{ Class: SmallInteger }"
+     len2 "{ Class: SmallInteger }"
+     d1   "{ Class: SmallInteger }"
+     d2   "{ Class: SmallInteger }"
+     otherDigits |
+
+    len1 := digitArray size.
+    otherDigits := aLargeInteger digits.
+    len2 := otherDigits size.
+
+    [(digitArray basicAt:len1) == 0] whileTrue:[
+        len1 := len1 - 1
+    ].
+    [(otherDigits basicAt:len2) == 0] whileTrue:[
+        len2 := len2 - 1
+    ].
+    (len1 < len2) ifTrue:[^ true].
+    (len1 > len2) ifTrue:[^ false].
+
+    [len1 > 0] whileTrue:[
+        d1 := digitArray basicAt:len1.
+        d2 := otherDigits basicAt:len1.
+        (d1 < d2) ifTrue:[^ true].
+        (d1 > d2) ifTrue:[^ false].
+        len1 := len1 - 1
+    ].
+    ^ false
+!
+
+absPlus:aLargeInteger
+    "return a LargeInteger representing abs(self) + abs(theArgument)"
+
+    |result done otherDigits resultDigits
+     len1  "{ Class: SmallInteger }"
+     len2  "{ Class: SmallInteger }"
+     index "{ Class: SmallInteger }"
+     carry "{ Class: SmallInteger }"
+     sum   "{ Class: SmallInteger }" |
+
+    len1 := digitArray size.
+    otherDigits := aLargeInteger digits.
+    len2 := otherDigits size.
+
+    result := LargeInteger basicNew 
+                           numberOfDigits:((len1 max: len2) + 1).
+    result sign:1.
+    resultDigits := result digits.
+
+    index := 1.
+    carry := 0.
+
+    done := false.
+    [done] whileFalse:[
+        sum := carry.
+        (index <= len1) ifTrue:[
+            sum := sum + (digitArray basicAt:index).
+            (index <= len2) ifTrue:[
+                sum := sum + (otherDigits basicAt:index)
+            ]
+        ] ifFalse:[
+            (index <= len2) ifTrue:[
+                sum := sum + (otherDigits basicAt:index)
+            ] ifFalse:[
+                "end reached"
+                done := true
+            ]
+        ].
+        (sum > 9999) ifTrue:[
+            carry := 1.
+            sum := sum - 10000
+        ] ifFalse:[
+            carry := 0
+        ].
+        resultDigits basicAt:index put:sum.
+        index := index + 1
+    ].
+    ^ result normalize
+!
+
+absMinus:aLargeInteger
+    "return a LargeInteger representing abs(self) - abs(theArgument)"
+
+    |result done
+     otherDigits resultDigits
+     len1   "{ Class: SmallInteger }"
+     len2   "{ Class: SmallInteger }"
+     index  "{ Class: SmallInteger }"
+     borrow "{ Class: SmallInteger }"
+     diff   "{ Class: SmallInteger }"
+     sum    "{ Class: SmallInteger }"
+     carry  "{ Class: SmallInteger }" |
+
+    len1 := digitArray size.
+    otherDigits := aLargeInteger digits.
+    len2 := otherDigits size.
+
+    result := LargeInteger basicNew 
+                           numberOfDigits:((len1 max: len2) + 1).
+    result sign:1.
+    resultDigits := result digits.
+
+    index := 1.
+    borrow := 0.
+
+    done := false.
+    [done] whileFalse:[
+        diff := borrow.
+        (index <= len1) ifTrue:[
+            diff := diff + (digitArray basicAt:index).
+            (index <= len2) ifTrue:[
+                diff := diff - (otherDigits basicAt:index)
+            ]
+        ] ifFalse:[
+            (index <= len2) ifTrue:[
+                diff := diff - (otherDigits basicAt:index)
+            ] ifFalse:[
+                "end reached"
+                done := true
+            ]
+        ].
+        (diff < 0) ifTrue:[
+            borrow := -1.
+            diff := diff + 10000
+        ] ifFalse:[
+            borrow := 0
+        ].
+        resultDigits basicAt:index put:diff.
+        index := index + 1
+    ].
+    (borrow ~~ 0) ifTrue:[
+        result sign: -1.
+        carry := 0.
+        1 to:(index - 1) do:[:i |
+            sum := ((resultDigits at:i) + carry - 10000) negated.
+            resultDigits at:i put:sum.
+            carry := 1
+        ]
+    ].
+    ^ result normalize
+!
+
+absMul:aLargeInteger
+    "return a LargeInteger representing abs(self) * abs(theArgument)"
+
+    |result otherDigits resultDigits
+     len1     "{ Class: SmallInteger }"
+     len2     "{ Class: SmallInteger }"
+     dstIndex "{ Class: SmallInteger }"
+     carry    "{ Class: SmallInteger }"
+     prod     "{ Class: SmallInteger }" |
+
+    len1 := digitArray size.
+    otherDigits := aLargeInteger digits.
+    len2 := otherDigits size.
+
+    result := LargeInteger basicNew numberOfDigits:(len1 + len2 + 1).
+    result sign:1.
+    resultDigits := result digits.
+
+    "clear result"
+    resultDigits atAllPut:0.
+
+    1 to:len1 do:[:index1 |
+        1 to:len2 do:[:index2 |
+            dstIndex := index1 + index2 - 1.
+            prod := (digitArray basicAt:index1) * (otherDigits basicAt:index2).
+            prod := prod + (resultDigits basicAt:dstIndex).
+            resultDigits basicAt:dstIndex put:(prod \\ 10000).
+            carry := prod // 10000.
+            (carry ~~ 0) ifTrue:[
+                resultDigits basicAt:(dstIndex + 1)
+                                 put:(resultDigits basicAt:(dstIndex + 1)) + carry
+            ]
+        ]
+    ].
+    ^ result normalize
+!
+
+absDiv:anInteger
+    "return an array with two LargeIntegers representing
+     abs(self) // abs(theArgument) and abs(self) \\ abs(theArgument)"
+
+    |tmp1 tmp2 
+     rem 
+     count "{ Class: SmallInteger }"
+     digit "{ Class: SmallInteger }" |
+
+    self == 0 ifTrue:[^ 0].
+    anInteger == 0 ifTrue:[^ self divideByZeroError].
+
+    self < anInteger ifTrue:[
+        ^ Array with:0 with:self
+    ].
+
+    tmp1 := self deepCopy.
+    tmp2 := anInteger deepCopy.
+    count := 0.
+    [tmp2 < tmp1] whileTrue:[
+        tmp2 mul10.
+        count := count + 1
+    ].
+
+    tmp2 div10.
+
+    rem := 0 asLargeInteger. 
+    [count == 0] whileFalse:[
+        digit := 0.
+        [tmp1 >= tmp2] whileTrue:[
+            digit := digit + 1.
+            tmp1 := tmp1 - tmp2
+        ].
+        rem := rem * 10 + digit.
+        tmp2 div10.
+        count := count - 1
+    ].
+    ^ Array with:rem with:tmp1 
+!
+
+mul10
+    "destructively multiply the receiver by 10.
+     private - used for division only"
+
+    |carry "{ Class: SmallInteger }"
+     prod  "{ Class: SmallInteger }"|
+
+    carry := 0.
+    1 to:(digitArray size) do:[:index |
+        prod := (digitArray at:index) * 10 + carry.
+        digitArray at:index put:prod \\ 10000.
+        carry := prod // 10000
+    ].
+    carry ~~ 0 ifTrue:[
+        digitArray := digitArray copyWith:carry
+    ]
+!
+
+div10
+    "destructively divide the receiver by 10.
+     private - used for division only"
+
+    |nDigits|
+
+    nDigits := digitArray size.
+    1 to:(nDigits - 1) do:[:index |
+        digitArray at:index put:((digitArray at:index) // 10
+                                + ((digitArray at:index + 1) \\ 10 * 1000))
+    ].
+    digitArray at:nDigits put:(digitArray at:nDigits) // 10
+!
+
+normalize
+    "if the receiver can be represented as a SmallInteger, return
+     a SmallInteger with my value; otherwise return self with leading
+     zeros removed"
+
+    |index "{ Class: SmallInteger }" |
+
+    index := digitArray size.
+    [(index > 0) and:[(digitArray at:index) == 0]] whileTrue:[
+        index := index - 1
+    ].
+    (index == 1) ifTrue:[
+        ^ (digitArray at:1) * sign
+    ].
+    (index == 2) ifTrue:[
+        ^ ((digitArray at:2) * 10000 + (digitArray at:1)) * sign
+    ].
+    (index == 0) ifTrue:[
+        ^ 0
+    ].
+    (index ~~ digitArray size) ifTrue:[
+        digitArray := digitArray copyFrom:1 to:index
+    ].
+    ^ self
+!
+
+digits
+    ^ digitArray
+!
+
+numberOfDigits
+    ^ digitArray size
+!
+
+numberOfDigits:nDigits
+    digitArray := Array new:nDigits
+!
+
+sign:aNumber
+    sign := aNumber
+! !
+
+!LargeInteger methodsFor:'printing & storing'!
+
+storeString
+    "return a string representation of the receiver, which can be
+     used to reconstruct the receiver"
+
+    ^ self printString , ' asLargeInteger'
+!
+
+printString
+    |aString index fourDigits n|
+
+    index := digitArray size.
+    [(index > 1) and:[(digitArray at:index) == 0]] whileTrue:[
+        index := index - 1
+    ].
+    (sign == 0) ifTrue: [^ '0'].
+    (sign == -1) ifTrue: [
+        aString := '-'
+    ] ifFalse: [
+        aString := ''
+    ].
+
+    aString := aString , (digitArray basicAt:index) printString.
+    index := index - 1.
+    [index > 0] whileTrue:[
+        fourDigits := (digitArray basicAt:index) printString.
+        n := fourDigits size.
+        (n < 4) ifTrue:[
+            aString := aString , ('000' copyFrom:n to:3)
+        ].
+        aString := aString , fourDigits.
+        index := index - 1
+    ].
+    ^ aString
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Link.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,44 @@
+"
+ COPYRIGHT (c) 1992-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Link
+       instanceVariableNames:'nextLink'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Support'
+!
+
+Link comment:'
+
+COPYRIGHT (c) 1992-93 by Claus Gittinger
+              All Rights Reserved
+
+this class provides the basic functionality for Link-nodes.
+Links are abstract in that they do not provide a place for storing something,
+just the link-chain. For more usability look at ValueLink.
+
+%W% %E%
+'!
+
+!Link methodsFor:'accessing'!
+
+nextLink
+    "return the next link"
+
+    ^ nextLink
+!
+
+nextLink:aLink
+    "set the next link"
+
+    nextLink := aLink
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LinkList.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,231 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+SequenceableCollection subclass:#LinkedList
+       instanceVariableNames:'firstLink lastLink nodeClass numberOfNodes'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Sequenceable'
+!
+
+LinkedList comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+this class implements an anchor to a list of Links.
+The data itself is held in the Link elements (see Link and subclasses).
+
+%W% %E%
+'!
+
+!LinkedList class methodsFor:'instance creation'!
+
+new
+    "create and return a new LinkedList"
+
+    ^ super new initialize
+! !
+
+!LinkedList methodsFor:'ininialization'!
+
+initialize
+    numberOfNodes := 0
+! !
+
+!LinkedList methodsFor:'copying'!
+
+deepCopy
+    |newList|
+    newList := self shallowCopy.
+    newList setFirstNode:(firstLink deepCopy).
+    newList setLastNode:(firstLink last).
+    ^ newList
+! !
+
+!LinkedList methodsFor:'accessing'!
+
+setFirstNode:aNode
+    "set the first node to be the argument, aNode"
+
+    firstLink := aNode
+!
+
+setLastNode:aNode
+    "set the last node to be the argument, aNode"
+
+    lastLink := aNode
+!
+
+first
+    "return the first node in the list"
+
+    ^ firstLink
+!
+
+last
+    "return last node in the list"
+
+    ^ lastLink
+!
+
+size
+    "return the size of the LinkedList i.e. the number of nodes"
+
+    ^ numberOfNodes
+! !
+
+!LinkedList methodsFor:'testing'!
+
+includes:anObject
+    "return true, if some nodes contents is anObject"
+
+    |theNode|
+
+    theNode := firstLink.
+    [theNode notNil] whileTrue:[
+        (anObject = theNode) ifTrue:[^ true].
+        theNode := theNode nextLink
+    ].
+    ^ false
+! !
+
+!LinkedList methodsFor:'adding/removing elements'!
+
+addFirst:aLink
+    "adds aLink to the beginning of the sequence. Returns aLink"
+
+    firstLink isNil ifTrue:[
+        firstLink := aLink.
+        lastLink := aLink
+    ] ifFalse: [
+        aLink nextLink:firstLink.
+        firstLink := aLink
+    ].
+    numberOfNodes := numberOfNodes + 1.
+    ^ aLink
+!
+
+add:aLink
+    "adds aLink to the end of the sequence. Returns aLink"
+
+    aLink nextLink:nil.
+    lastLink isNil ifTrue:[
+        firstLink := aLink
+    ] ifFalse: [
+        lastLink nextLink:aLink
+    ].
+    lastLink := aLink.
+    numberOfNodes := numberOfNodes + 1.
+    ^ aLink
+!
+
+add:linkToAdd after:aLink
+    "adds linkToAdd after another link, aLink. If aLink is nil,
+     linkToAdd is inserted at the beginning. Returns linkToAdd."
+
+    |this|
+
+    aLink isNil ifTrue:[ ^ self addFirst:linkToAdd ].
+
+    this := firstLink.
+    [this notNil and:[this ~~ aLink]] whileTrue:[
+        this := this nextLink
+    ].
+    this isNil ifTrue:[ ^ self addLast:linkToAdd ].
+    linkToAdd nextLink:(this nextLink).
+    this nextLink:linkToAdd.
+    ^ linkToAdd
+!
+
+removeFirst
+    "remove and return the first node from the sequence"
+
+    |link|
+
+    firstLink isNil ifTrue:[
+        self errorIsEmpty
+    ] ifFalse:[
+        link := firstLink.
+        (firstLink == lastLink) ifTrue:[
+            firstLink := nil.
+            lastLink := nil
+        ] ifFalse:[
+            firstLink := firstLink nextLink
+        ].
+        numberOfNodes := numberOfNodes - 1
+    ].
+    ^ link
+!
+
+remove:aLink ifAbsent:exceptionBlock
+    "remove the argument, aLink from the sequence; if absent,
+     evaluate the excpetionBlock"
+
+    |prevNode nextNode thisNode|
+
+    thisNode := firstLink.
+    [thisNode notNil] whileTrue:[
+        nextNode := thisNode nextLink.
+        (thisNode == aLink) ifTrue:[
+            prevNode isNil ifTrue:[
+                firstLink := thisNode nextLink
+            ] ifFalse:[
+                prevNode nextLink:(thisNode nextLink)
+            ].
+            (lastLink == thisNode) ifTrue:[
+                thisNode nextLink isNil ifTrue:[
+                    lastLink := prevNode
+                ] ifFalse:[
+                    lastLink := thisNode nextLink
+                ]
+            ].
+            numberOfNodes := numberOfNodes - 1.
+            ^ self
+        ].
+        prevNode := thisNode.
+        thisNode := nextNode
+    ].
+    ^ exceptionBlock value
+! !
+
+!LinkedList methodsFor:'enumerating'!
+
+do:aBlock
+    "evaluate the argument, aBlock with 1 arg for every element in the list"
+
+    |thisNode|
+
+    thisNode := firstLink.
+    [thisNode notNil] whileTrue:[
+        aBlock value:thisNode.
+        thisNode := thisNode nextLink
+    ]
+!
+
+reverseDo:aBlock fromNode:aNode
+    "helper for reverseDo:"
+
+    aNode notNil ifTrue:[
+        aNode nextLink notNil ifTrue:[
+            self reverseDo:aBlock fromNode:(aNode nextLink)
+        ].
+        aBlock value:aNode
+    ]
+!
+
+reverseDo:aBlock
+    "evaluate the argument, aBlock with 1 arg for every element in the list
+     in the reverse order"
+
+    self reverseDo:aBlock fromNode:firstLink
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LinkedList.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,231 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+SequenceableCollection subclass:#LinkedList
+       instanceVariableNames:'firstLink lastLink nodeClass numberOfNodes'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Sequenceable'
+!
+
+LinkedList comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+this class implements an anchor to a list of Links.
+The data itself is held in the Link elements (see Link and subclasses).
+
+%W% %E%
+'!
+
+!LinkedList class methodsFor:'instance creation'!
+
+new
+    "create and return a new LinkedList"
+
+    ^ super new initialize
+! !
+
+!LinkedList methodsFor:'ininialization'!
+
+initialize
+    numberOfNodes := 0
+! !
+
+!LinkedList methodsFor:'copying'!
+
+deepCopy
+    |newList|
+    newList := self shallowCopy.
+    newList setFirstNode:(firstLink deepCopy).
+    newList setLastNode:(firstLink last).
+    ^ newList
+! !
+
+!LinkedList methodsFor:'accessing'!
+
+setFirstNode:aNode
+    "set the first node to be the argument, aNode"
+
+    firstLink := aNode
+!
+
+setLastNode:aNode
+    "set the last node to be the argument, aNode"
+
+    lastLink := aNode
+!
+
+first
+    "return the first node in the list"
+
+    ^ firstLink
+!
+
+last
+    "return last node in the list"
+
+    ^ lastLink
+!
+
+size
+    "return the size of the LinkedList i.e. the number of nodes"
+
+    ^ numberOfNodes
+! !
+
+!LinkedList methodsFor:'testing'!
+
+includes:anObject
+    "return true, if some nodes contents is anObject"
+
+    |theNode|
+
+    theNode := firstLink.
+    [theNode notNil] whileTrue:[
+        (anObject = theNode) ifTrue:[^ true].
+        theNode := theNode nextLink
+    ].
+    ^ false
+! !
+
+!LinkedList methodsFor:'adding/removing elements'!
+
+addFirst:aLink
+    "adds aLink to the beginning of the sequence. Returns aLink"
+
+    firstLink isNil ifTrue:[
+        firstLink := aLink.
+        lastLink := aLink
+    ] ifFalse: [
+        aLink nextLink:firstLink.
+        firstLink := aLink
+    ].
+    numberOfNodes := numberOfNodes + 1.
+    ^ aLink
+!
+
+add:aLink
+    "adds aLink to the end of the sequence. Returns aLink"
+
+    aLink nextLink:nil.
+    lastLink isNil ifTrue:[
+        firstLink := aLink
+    ] ifFalse: [
+        lastLink nextLink:aLink
+    ].
+    lastLink := aLink.
+    numberOfNodes := numberOfNodes + 1.
+    ^ aLink
+!
+
+add:linkToAdd after:aLink
+    "adds linkToAdd after another link, aLink. If aLink is nil,
+     linkToAdd is inserted at the beginning. Returns linkToAdd."
+
+    |this|
+
+    aLink isNil ifTrue:[ ^ self addFirst:linkToAdd ].
+
+    this := firstLink.
+    [this notNil and:[this ~~ aLink]] whileTrue:[
+        this := this nextLink
+    ].
+    this isNil ifTrue:[ ^ self addLast:linkToAdd ].
+    linkToAdd nextLink:(this nextLink).
+    this nextLink:linkToAdd.
+    ^ linkToAdd
+!
+
+removeFirst
+    "remove and return the first node from the sequence"
+
+    |link|
+
+    firstLink isNil ifTrue:[
+        self errorIsEmpty
+    ] ifFalse:[
+        link := firstLink.
+        (firstLink == lastLink) ifTrue:[
+            firstLink := nil.
+            lastLink := nil
+        ] ifFalse:[
+            firstLink := firstLink nextLink
+        ].
+        numberOfNodes := numberOfNodes - 1
+    ].
+    ^ link
+!
+
+remove:aLink ifAbsent:exceptionBlock
+    "remove the argument, aLink from the sequence; if absent,
+     evaluate the excpetionBlock"
+
+    |prevNode nextNode thisNode|
+
+    thisNode := firstLink.
+    [thisNode notNil] whileTrue:[
+        nextNode := thisNode nextLink.
+        (thisNode == aLink) ifTrue:[
+            prevNode isNil ifTrue:[
+                firstLink := thisNode nextLink
+            ] ifFalse:[
+                prevNode nextLink:(thisNode nextLink)
+            ].
+            (lastLink == thisNode) ifTrue:[
+                thisNode nextLink isNil ifTrue:[
+                    lastLink := prevNode
+                ] ifFalse:[
+                    lastLink := thisNode nextLink
+                ]
+            ].
+            numberOfNodes := numberOfNodes - 1.
+            ^ self
+        ].
+        prevNode := thisNode.
+        thisNode := nextNode
+    ].
+    ^ exceptionBlock value
+! !
+
+!LinkedList methodsFor:'enumerating'!
+
+do:aBlock
+    "evaluate the argument, aBlock with 1 arg for every element in the list"
+
+    |thisNode|
+
+    thisNode := firstLink.
+    [thisNode notNil] whileTrue:[
+        aBlock value:thisNode.
+        thisNode := thisNode nextLink
+    ]
+!
+
+reverseDo:aBlock fromNode:aNode
+    "helper for reverseDo:"
+
+    aNode notNil ifTrue:[
+        aNode nextLink notNil ifTrue:[
+            self reverseDo:aBlock fromNode:(aNode nextLink)
+        ].
+        aBlock value:aNode
+    ]
+!
+
+reverseDo:aBlock
+    "evaluate the argument, aBlock with 1 arg for every element in the list
+     in the reverse order"
+
+    self reverseDo:aBlock fromNode:firstLink
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Magnitude.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,102 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Magnitude
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Magnitude-General'
+!
+
+Magnitude comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+This is an abstract class definining common methods for
+Objects which can be compared by a kind of greater realation.
+
+%W% %E%
+'!
+
+!Magnitude methodsFor:'comparing'!
+
+> aMagnitude
+    "Compare the receiver with the argument and return true if the
+     receiver is greater than the argument. Otherwise return false."
+
+    ^ self subclassResponsibility
+!
+
+= aMagnitude
+    "Compare the receiver with the argument and return true if the
+     receiver is equal to the argument. Otherwise return false."
+
+    ^ self subclassResponsibility
+!
+
+<= aMagnitude
+    "Compare the receiver with the argument and return true if the
+     receiver is less than or equal to the argument. Otherwise return false."
+
+    ^ (self > aMagnitude) not
+!
+
+< aMagnitude
+    "Compare the receiver with the argument and return true if the
+     receiver is less than the argument. Otherwise return false."
+
+    ^ (aMagnitude > self)
+!
+
+>= aMagnitude
+    "Compare the receiver with the argument and return true if the
+     receiver is greater than or equal to the argument.
+     Otherwise return false."
+
+    ^ (aMagnitude > self) not
+! !
+
+!Magnitude methodsFor:'testing'!
+
+between:min and:max
+    "return whether the receiver is less than or equal to the argument max
+     and greater than or equal to the argument min."
+
+    (self < min) ifTrue:[^ false].
+    (self > max) ifTrue:[^ false].
+    ^ true
+!
+
+in:anInterval
+    "return whether the receiver is within the interval bounds"
+
+    (self < anInterval start) ifTrue:[^ false].
+    (self > anInterval stop) ifTrue:[^ false].
+    ^ true
+! !
+
+!Magnitude methodsFor:'misc'!
+
+min:aMagnitude
+    "return the receiver or the argument, whichever has lesser magnitude"
+
+    (self < aMagnitude) ifTrue:[^ self].
+    ^ aMagnitude
+!
+
+max:aMagnitude
+    "return the receiver or the argument, whichever has greater magnitude"
+
+    (self > aMagnitude) ifTrue:[^ self].
+    ^ aMagnitude
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Make.proto	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,390 @@
+# %W% %E%
+#
+# -------------- no need to change anything below ----------
+
+LIBNAME=libbasic
+LIB=libbasic.$(A)
+SUBDIRS=
+
+TOP=..
+I       = $(INCLUDE)
+
+STCFLAGS= -H../include -warnGlobalAssign $(STCOPT)
+
+OBJS=   Object.$(O) Unix.$(O) Magnitude.$(O) MsgTally.$(O) Point.$(O) \
+	Rectangle.$(O) Assoc.$(O) ArithVal.$(O) \
+	Context.$(O) BContext.$(O) CCReader.$(O) Block.$(O) Message.$(O) Smalltalk.$(O) \
+	UndefObj.$(O) Behavior.$(O) MiniDebug.$(O) MiniIns.$(O) Coll.$(O) \
+	Link.$(O) ValLink.$(O) Boolean.$(O) Stream.$(O) Character.$(O) \
+        Number.$(O) ObjMem.$(O) \
+	Time.$(O) OrdColl.$(O) SeqColl.$(O) Bag.$(O) Set.$(O) \
+	Date.$(O) True.$(O) False.$(O) ClassDescr.$(O) Class.$(O) Method.$(O) \
+	FileDir.$(O) Integer.$(O) \
+	Random.$(O) PosStream.$(O) Metaclass.$(O) Float.$(O) Fraction.$(O) \
+	LargeInt.$(O) \
+	SmallInt.$(O) Interval.$(O) LinkList.$(O) Dict.$(O) ArrColl.$(O) \
+	ReadStr.$(O) WriteStr.$(O) Array.$(O) VarArray.$(O) VarString.$(O) \
+	IntArray.$(O) ByteArray.$(O) RWStream.$(O) IdDict.$(O) ShIdDict.$(O) \
+	ShadowArr.$(O) Text.$(O) String.$(O) ActorStr.$(O) \
+	ExtStream.$(O) ByteString.$(O) \
+	FileText.$(O) Symbol.$(O) NPExtStr.$(O) FileStr.$(O) PipeStr.$(O) \
+	DirStr.$(O) PrintStr.$(O) Socket.$(O) \
+	Autoload.$(O) BCCReader.$(O) ExtBytes.$(O) \
+        Process.$(O) ProcSched.$(O) SortColl.$(O) UIBytes.$(O) \
+        ChangeSet.$(O) Change.$(O) ClsDefChg.$(O) ClsComChg.$(O) MethodChg.$(O) \
+	Signal.$(O) SignalSet.$(O) Exception.$(O) Semaphore.$(O) SharedQueue.$(O) \
+	Registry.$(O) SoundStr.$(O) FloatArray.$(O) DoubleArray.$(O)
+
+AUXOBJS= Polygon.$(O) Filename.$(O) CacheDict.$(O) IdSet.$(O) Project.$(O)
+
+all::   $(OBJTARGET)
+
+objs::  level0 \
+	level1 \
+	level2 \
+	level3 \
+	level4 \
+	level5 \
+	level6 \
+	level7 \
+	level8 \
+	level9 
+
+level0:$(P)                                             \
+	    Object.$(O)
+
+level1:$(P)                                             \
+	    Unix.$(O)                                   \
+	    Autoload.$(O)                               \
+	    Magnitude.$(O)                              \
+	    MsgTally.$(O)                               \
+	    Rectangle.$(O)                              \
+	    Assoc.$(O)                                  \
+	    Context.$(O)                                \
+	    Signal.$(O)                                 \
+	    Exception.$(O)                              \
+	    CCReader.$(O)                               \
+	    Block.$(O)                                  \
+	    Message.$(O)                                \
+	    Smalltalk.$(O)                              \
+	    ObjMem.$(O)                                 \
+	    ProcSched.$(O)                              \
+	    SharedQueue.$(O)                            \
+	    UndefObj.$(O)                               \
+	    Behavior.$(O)                               \
+	    MiniDebug.$(O)                              \
+	    MiniIns.$(O)                                \
+	    Coll.$(O)                                   \
+	    Link.$(O)                                   \
+	    Boolean.$(O)                                \
+	    Stream.$(O)                                 \
+	    Change.$(O)                                 \
+	    Registry.$(O)                               \
+	    Project.$(O)                              
+
+level2:$(P)                                             \
+	    BContext.$(O)                               \
+	    Character.$(O)                              \
+	    Process.$(O)                                \
+	    ArithVal.$(O)				\
+	    Time.$(O)                                   \
+	    Date.$(O)                                   \
+	    True.$(O)                                   \
+	    False.$(O)                                  \
+	    ClassDescr.$(O)                             \
+	    Method.$(O)                                 \
+	    SeqColl.$(O)                                \
+	    Set.$(O)                                    \
+	    Bag.$(O)                                    \
+	    FileDir.$(O)                                \
+	    Random.$(O)                                 \
+	    BCCReader.$(O)                              \
+	    ValLink.$(O)                                \
+	    ClassChg.$(O)                               \
+	    PosStream.$(O)                         
+
+level3:$(P)                                             \
+	    Class.$(O)                                  \
+	    Point.$(O)                                  \
+	    Number.$(O)                                 \
+	    Interval.$(O)                               \
+	    LinkList.$(O)                               \
+	    Dict.$(O)                                   \
+	    OrdColl.$(O)                                \
+	    ArrColl.$(O)                                \
+	    ReadStr.$(O)                                \
+	    WriteStr.$(O)                     		\
+	    MethodChg.$(O)                     		\
+	    ClsDefChg.$(O)                     		\
+	    ClsComChg.$(O)                     
+
+level4:$(P)                                             \
+	    Metaclass.$(O)                              \
+	    Integer.$(O)                                \
+	    Float.$(O)                                  \
+	    Fraction.$(O)                               \
+	    Semaphore.$(O)                              \
+	    Array.$(O)                                  \
+	    VarArray.$(O)                               \
+	    VarString.$(O)                              \
+	    IdDict.$(O)                                 \
+	    IntArray.$(O)                               \
+	    FloatArray.$(O)                             \
+	    DoubleArray.$(O)                            \
+	    ExtBytes.$(O)                               \
+	    SortColl.$(O)                               \
+	    ChangeSet.$(O)                              \
+	    RWStream.$(O)
+
+level5:$(P)                                             \
+	    LargeInt.$(O)                               \
+	    SmallInt.$(O)                               \
+	    ShadowArr.$(O)                              \
+	    ShIdDict.$(O)                               \
+	    Text.$(O)                                   \
+	    ByteArray.$(O)                              \
+	    ActorStr.$(O)                               \
+	    ExtStream.$(O)                   
+
+level6:$(P)                                             \
+	    FileText.$(O)                               \
+	    String.$(O)                                 \
+	    UIBytes.$(O)                                \
+	    NPExtStr.$(O)                               \
+	    FileStr.$(O)
+
+level7:$(P)                                             \
+	    ByteString.$(O)                             \
+	    SoundStr.$(O)				\
+	    Symbol.$(O)                                 \
+	    PipeStr.$(O)                                \
+	    Socket.$(O)                                 \
+	    DirStr.$(O)                                 
+
+level8:$(P)						\
+	    PrintStr.$(O)				
+
+level9:$(P)
+
+level10:						\
+	    Polygon.$(O)                                \
+	    Filename.$(O)                               \
+	    CacheDict.$(O)                              \
+	    IdSet.$(O)                              	\
+	    Project.$(O)                              	\
+	    PSPrStr.$(O)                                \
+	    HPIIPrStr.$(O)                              \
+	    FX1PrStr.$(O)
+
+level11:						\
+	    SignalSet.$(O)
+
+auxobjs::  level10					\
+	   level11					\
+	   $(AUXOBJS)
+
+install::
+	-mkdir $(DESTLIBDIR)
+	-$(INSTALL) $(LIBNAME)$(OBJNAME) $(DESTLIBDIR)
+
+libe:   $(LIB)
+
+PSPrStr.o:
+	$(STC) -CC="$(CC)" $(STCFLAGS) +limitSuperInclude $(CFLAGS) -c $*.st
+
+HPIIPrStr.o:
+	$(STC) -CC="$(CC)" $(STCFLAGS) +limitSuperInclude $(CFLAGS) -c $*.st
+
+FX1PrStr.o:
+	$(STC) -CC="$(CC)" $(STCFLAGS) +limitSuperInclude $(CFLAGS) -c $*.st
+
+Class:
+	$(MAKE) Class.o OPT=""
+
+Metaclass:
+	$(MAKE) Metaclass.o OPT=""
+
+i_Class.o:
+	$(MAKE) I_CLASS OPT=""
+
+i_Metaclass.o:
+	$(MAKE) I_METACLASS OPT=""
+
+I_CLASS:
+	$(CC) -c $(CFLAGS) i_Class.c
+
+I_METACLASS:
+	$(CC) -c $(CFLAGS) i_Metaclass.c
+
+cleanjunk::
+	-rm -f *.c *.H
+
+clean::
+	-rm -f *.c *.H
+
+clobber::
+	-rm -f *.c *.H
+
+tar:
+	rm -f $(TOP)/DISTRIB/libbasic.tar*
+	(cd $(TOP); tar cvf DISTRIB/libbasic.tar \
+	                        libbasic/*.st \
+	                        libbasic/Make.proto \
+	                        libbasic/*.stc)
+	compress $(TOP)/DISTRIB/libbasic.tar
+
+#
+# next thing I'll build into stc is a makedepend feature for this ...
+#
+
+STCHDR=$(I)/stc.h $(I)/stcIntern.h
+OBJECT=$(I)/Object.H $(STCHDR)
+BOOLEAN=$(I)/Boolean.H $(OBJECT)
+BEHAVIOR=$(I)/Behavior.H $(OBJECT)
+CLASS=$(I)/Class.H $(I)/ClassDescr.H $(BEHAVIOR)
+
+Object.o:       Object.st $(STCHDR)
+UndefObj.o:     UndefObj.st $(OBJECT)
+
+Message.o:      Message.st $(OBJECT)
+
+Unix.o:         Unix.st $(OBJECT)
+Autoload.o:     Autoload.st $(OBJECT)
+Smalltalk.o:    Smalltalk.st $(OBJECT)
+ObjMem.o:       ObjMem.st $(OBJECT)
+Process.o:      Process.st $(OBJECT)
+ProcSched.o:    ProcSched.st $(OBJECT)
+Semaphore.o:    Semaphore.st $(OBJECT)
+SharedQueue.o:  SharedQueue.st $(OBJECT)
+MsgTally.o:     MsgTally.st  $(OBJECT)
+MiniDebug.o:    MiniDebug.st $(OBJECT)
+MiniIns.o:      MiniIns.st $(OBJECT)
+Context.o:      Context.st $(OBJECT)
+BContext.o:     BContext.st $(I)/Context.H $(OBJECT)
+Method.o:       Method.st $(OBJECT)
+Block.o:        Block.st $(OBJECT)
+BlckContext.o:  BlckContext.st $(OBJECT)
+
+Signal.o:       Signal.st $(OBJECT)
+
+Exception.o:    Exception.st $(OBJECT)
+Rectangle.o:    Rectangle.st $(OBJECT)
+
+Assoc.o:        Assoc.st $(OBJECT)
+Boolean.o:      Boolean.st $(OBJECT)
+True.o:         True.st $(BOOLEAN)
+False.o:        False.st $(BOOLEAN)
+ExtSource.o:    ExtSource.st $(OBJECT)
+
+Behavior.o:     Behavior.st $(OBJECT)
+ClassDescr.o:   ClassDescr.st $(BEHAVIOR)
+Class.o:        Class.st $(I)/ClassDescr.H $(BEHAVIOR)
+Metaclass.o:    Metaclass.st $(CLASS)
+CCReader.o:     CCReader.st $(OBJECT)
+BCCReader.o:    BCCReader.st $(I)/CCReader.H $(OBJECT)
+Project.o:      Project.st $(OBJECT)
+
+MAGNITUDE=$(I)/Magnitude.H $(OBJECT)
+ARITHVAL=$(I)/ArithVal.H $(MAGNITUDE)
+NUMBER=$(I)/Number.H $(ARITHVAL)
+INTEGER=$(I)/Integer.H $(NUMBER)
+
+Magnitude.o:    Magnitude.st $(OBJECT)
+Point.o:        Point.st $(MAGNITUDE)
+Character.o:    Character.st $(MAGNITUDE)
+ArithVal.o:     ArithVal.st $(MAGNITUDE)
+Number.o:       Number.st $(ARITHVAL)
+Integer.o:      Integer.st $(NUMBER)
+Float.o:        Float.st $(NUMBER)
+Fraction.o:     Fraction.st $(NUMBER)
+SmallInt.o:     SmallInt.st $(INTEGER)
+LargeInt.o:     LargeInt.st $(INTEGER)
+Time.o:         Time.st $(MAGNITUDE)
+Date.o:         Date.st $(MAGNITUDE)
+
+COLL=$(I)/Coll.H $(OBJECT)
+SET=$(I)/Set.H $(COLL)
+DICT=$(I)/Dict.H $(COLL)
+IDDICT=$(I)/IdDict.H $(DICT)
+SHADOWIDDICT=$(I)/ShIdDict.H $(IDDICT)
+SEQCOLL=$(I)/SeqColl.H $(COLL)
+ARRCOLL=$(I)/ArrColl.H $(SEQCOLL)
+ARRAY=$(I)/Array.H $(ARRCOLL)
+VARARRAY=$(I)/VarArray.H $(ARRCOLL)
+INTARRAY=$(I)/IntArray.H $(ARRCOLL)
+BYTEARRAY=$(I)/ByteArray.H $(INTARRAY)
+STRING=$(I)/String.H $(BYTEARRAY)
+
+Registry.o:     Registry.st $(OBJECT)
+Coll.o:         Coll.st $(OBJECT)
+FileDir.o:      FileDir.st $(COLL)
+SeqColl.o:      SeqColl.st $(COLL)
+Set.o:          Set.st $(COLL)
+IdSet.o:        IdSet.st $(SET)
+SignalSet.o:    SignalSet.st $(I)/IdSet.H $(SET)
+Bag.o:          Bag.st $(COLL)
+Dict.o:         Dict.st $(COLL)
+IdDict.o:       IdDict.st $(DICT)
+CacheDict.o:    CacheDict.st $(DICT)
+ShIdDict.o:     ShIdDict.st $(IDDICT)
+OrdColl.o:      OrdColl.st $(SEQCOLL)
+SortColl.o:     SortColl.st $(I)/OrdColl.H $(SEQCOLL)
+ChangeSet.o:    ChangeSet.st $(I)/OrdColl.H $(SEQCOLL)
+Interval.o:     Interval.st $(SEQCOLL)
+Node.o:         Node.st $(COLL)
+Tree.o:         Tree.st $(COLL)
+LinkList.o:     LinkList.st $(SEQCOLL)
+Link.o:         Link.st $(OBJECT)
+ValLink.o:      ValLink.st $(I)/Link.H $(OBJECT)
+ArrColl.o:      ArrColl.st $(SEQCOLL)
+VarArray.o:     VarArray.st $(ARRCOLL)
+VarString.o:    VarString.st $(ARRCOLL)
+Text.o:         Text.st $(VARARRAY)
+FileText.o:     FileText.st $(I)/Text.H $(VARARRAY)
+Array.o:        Array.st $(ARRCOLL)
+Polygon.o:      Polygon.st $(ARRAY)
+ShadowArr.o:    ShadowArr.st $(ARRAY)
+IntArray.o:     IntArray.st $(ARRCOLL)
+FloatArray.o:   FloatArray.st $(ARRCOLL)
+DoubleArray.o:  DoubleArray.st $(ARRCOLL)
+ExtBytes.o:     ExtBytes.st $(ARRCOLL)
+ByteArray.o:    ByteArray.st $(INTARRAY)
+UIBytes.o:      UIBytes.st $(I)/ByteArray.H $(INTARRAY)
+String.o:       String.st $(BYTEARRAY)
+ByteString.o:   ByteString.st $(STRING)
+Symbol.o:       Symbol.st $(STRING)
+Filename.o:     Filename.st $(STRING)
+
+Change.o:       Change.st $(OBJECT)
+ClassChg.o:     ClassChg.st $(OBJECT)
+ClsDefChg.o:    ClsDefChg.st $(OBJECT)
+ClsComChg.o:    ClsComChg.st $(OBJECT)
+MethodChg.o:    MethodChg.st $(OBJECT)
+
+STREAM=$(I)/Stream.H $(OBJECT)
+POSSTREAM=$(I)/PosStream.H $(STREAM)
+WRITESTREAM=$(I)/WriteStr.H $(POSSTREAM)
+RWSTREAM=$(I)/RWStream.H $(WRITESTREAM)
+EXTSTREAM=$(I)/ExtStream.H $(RWSTREAM)
+NPEXTSTREAM=$(I)/NPExtStr.H $(EXTSTREAM)
+PIPESTREAM=$(I)/PipeStr.H $(NPEXTSTREAM)
+
+Stream.o:       Stream.st $(OBJECT)
+Random.o:       Random.st $(STREAM)
+PosStream.o:    PosStream.st $(STREAM)
+ReadStr.o:      ReadStr.st $(POSSTREAM)
+WriteStr.o:     WriteStr.st $(POSSTREAM)
+RWStream.o:     RWStream.st $(WRITESTREAM)
+ActorStr.o:     ActorStr.st $(RWSTREAM)
+ExtStream.o:    ExtStream.st $(RWSTREAM)
+NPExtStr.o:     NPExtStr.st $(EXTSTREAM)
+TTYStream.o:    TTYStream.st $(NPEXTSTREAM)
+PipeStr.o:      PipeStr.st $(NPEXTSTREAM)
+Socket.o:       Socket.st $(NPEXTSTREAM)
+PrintStr.o:     PrintStr.st $(PIPESTREAM)
+PSPrStr.o:      PSPrStr.st $(I)/PrintStr.H $(PIPESTREAM)
+HPIIPrStr.o:    HPIIPrStr.st $(I)/PrintStr.H $(PIPESTREAM)
+FX1PrStr.o:     FX1PrStr.st $(I)/PrintStr.H $(PIPESTREAM)
+FileStr.o:      FileStr.st $(EXTSTREAM)
+SoundStr.o:     SoundStr.st $(I)/FileStr.H $(EXTSTREAM)
+DirStr.o:       DirStr.st $(I)/FileStr.H $(EXTSTREAM)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Message.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,94 @@
+"
+ COPYRIGHT (c) 1988-92 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Message
+       instanceVariableNames:'selector args'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Kernel-Methods'
+!
+
+Message comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+these are only created, when a message fails (i.e. is not understood) -
+the selector and arguments are put into an instance of Message, which is then
+passed to doesNotUnderstand:aMessage as argument.
+
+This allows for re-evaluation of the failed send (after some cleanup).
+As an example of its use, see the implementation of the Autoload-class.
+
+%W% %E%
+'!
+
+!Message class methodsFor:'instance creation'!
+
+selector:aSymbol
+    "return a new message object for a send without arguments"
+
+    ^ self basicNew selector:aSymbol
+                   arguments:nil
+!
+
+selector:aSymbol with:anArg
+    "return a new message object for a send with one argument"
+
+    ^ self basicNew selector:aSymbol
+                   arguments:(Array with:anArg)
+!
+
+selector:aSymbol with:arg1 with:arg2
+    "return a new message object for a send with two arguments"
+
+    ^ self basicNew selector:aSymbol
+                   arguments:(Array with:arg1 with:arg2)
+!
+
+selector:aSymbol withAll:argArray
+    "return a new message object for a send with many arguments"
+
+    ^ self basicNew selector:aSymbol
+                   arguments:argArray
+! !
+
+!Message methodsFor:'private accessing'!
+
+selector:aSymbol arguments:argArray
+    "set selector and arguments of the receiver"
+
+    selector := aSymbol.
+    args := argArray
+! !
+
+!Message methodsFor:'accessing'!
+
+selector
+    "return the selector of the message"
+
+    ^ selector
+!
+
+arguments
+    "return the arguments of the message"
+
+    ^ args
+! !
+
+!Message methodsFor:'printing'!
+
+printString
+    "return a string for printing the receiver"
+
+    ^ selector printString
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Metaclass.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,396 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Class subclass:#Metaclass
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Kernel-Classes'
+!
+
+Metaclass comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+every class-class is a subclass of Metaclass
+- this adds support for creating new subclasses or changing the definition
+of an already existing class.
+
+%W% %E%
+'!
+
+!Metaclass methodsFor:'creating classes'!
+
+name:newName inEnvironment:aSystemDictionary
+             subclassOf:aClass
+             instanceVariableNames:stringOfInstVarNames
+             variable:variableBoolean
+             words:wordsBoolean
+             pointers:pointersBoolean
+             classVariableNames:stringOfClassVarNames
+             poolDictionaries:stringOfPoolNames
+             category:categoryString
+             comment:commentString
+             changed:changed
+
+    |newClass newMetaclass nInstVars nameString classSymbol oldClass 
+     allSubclasses classVarChange instVarChange superClassChange newComment
+     upd|
+
+    nInstVars := stringOfInstVarNames countWords.
+    nameString := newName asString.
+    classSymbol := nameString asSymbol.
+    newComment := commentString.
+
+    (aSystemDictionary includesKey:classSymbol) ifTrue:[
+        oldClass := aSystemDictionary at:classSymbol.
+        (newComment isNil and:[oldClass isBehavior "isKindOf:Class"]) ifTrue:[
+            newComment := oldClass comment
+        ]
+    ].
+
+    "create the metaclass first"
+    newMetaclass := Metaclass new.
+    newMetaclass setSuperclass:(aClass class).
+    newMetaclass instSize:(aClass class instSize).
+    newMetaclass flags:0.            "not indexed"
+    newMetaclass setName:(nameString , 'class').
+    newMetaclass classVariableString:'' "stringOfClassVarNames".
+    newMetaclass setComment:newComment category:categoryString.
+
+    newClass := newMetaclass new.
+    newClass setSuperclass:aClass.
+    newClass instSize:(aClass instSize + nInstVars).
+
+    (variableBoolean == true) ifTrue:[
+        pointersBoolean ifTrue:[
+            newClass flags:4         "pointerarray"
+        ] ifFalse:[
+            wordsBoolean ifTrue:[
+                newClass flags:2     "wordarray"
+            ] ifFalse:[
+                newClass flags:1     "bytearray"
+            ]
+        ]
+    ] ifFalse:[
+        "this is a backward compatible hack"
+
+        (variableBoolean == #float) ifTrue:[
+            newClass flags:6         "float array"
+        ] ifFalse:[
+            (variableBoolean == #double) ifTrue:[
+                newClass flags:7     "double array"
+            ] ifFalse:[
+                (variableBoolean == #long) ifTrue:[
+                    newClass flags:3     "long array"
+                ] ifFalse:[
+                    newClass flags:0   
+                ]
+            ]
+        ].
+    ].
+
+    newClass setName:nameString.
+    (nInstVars ~~ 0) ifTrue:[
+        newClass instanceVariableString:stringOfInstVarNames
+    ].
+    oldClass notNil ifTrue:[
+        "setting first will make new class clear obsolete classvars"
+        newClass setClassVariableString:(oldClass classVariableString)
+    ].
+    newClass classVariableString:stringOfClassVarNames.
+
+    oldClass notNil ifTrue:[
+        "dont have to flush if class is brand-new"
+
+        ObjectMemory flushCaches.
+    ].
+
+    aSystemDictionary at:classSymbol put:newClass.
+
+    self addChangeRecordForClass:newClass.
+
+    oldClass isNil ifTrue:[
+        commentString notNil ifTrue:[
+            newClass comment:commentString
+        ]
+    ] ifFalse:[
+        "if only category/comment has changed, do not recompile .."
+
+        (oldClass superclass == newClass superclass) ifTrue:[
+          (oldClass instSize == newClass instSize) ifTrue:[
+            (oldClass flags == newClass flags) ifTrue:[
+              (oldClass name = newClass name) ifTrue:[
+                (oldClass instanceVariableString = newClass instanceVariableString) ifTrue:[
+                  (oldClass classVariableString = newClass classVariableString) ifTrue:[
+                    (newComment ~= oldClass comment) ifTrue:[
+                        oldClass comment:newComment
+                    ]. 
+                    oldClass category:categoryString. 
+                    aSystemDictionary at:classSymbol put:oldClass.
+                    oldClass changed.
+                    ^ oldClass
+                  ]
+                ]
+              ]
+            ]
+          ]
+        ].
+
+        (newComment ~= oldClass comment) ifTrue:[
+            newClass comment:newComment
+        ].
+
+        upd := Class updateChanges:false.
+
+        superClassChange := oldClass superclass ~~ newClass superclass.
+
+        classVarChange := oldClass classVariableString ~= newClass classVariableString.
+
+        classVarChange ifTrue:[
+            " no need to recompile if classvars are added "
+            classVarChange := (newClass classVariableString startsWith: oldClass classVariableString) not
+        ].
+        classVarChange := classVarChange or:[superClassChange].
+        classVarChange := classVarChange or:[self anyInvalidatedMethodsIn: oldClass class].
+
+        classVarChange ifTrue:[
+            "must recompile class-methods"
+            self copyInvalidatedMethodsFrom:(oldClass class) for:newMetaclass.
+            newMetaclass recompile
+        ] ifFalse:[
+            "class methods still work"
+            self copyMethodsFrom:(oldClass class) for:newMetaclass
+        ].
+
+        instVarChange := oldClass instanceVariableString ~= newClass instanceVariableString.
+        instVarChange ifTrue:[
+            " no need to recompile if instvars are added "
+            instVarChange := (newClass instanceVariableString startsWith: oldClass instanceVariableString) not
+        ].
+        instVarChange := instVarChange or:[superClassChange].
+        instVarChange := instVarChange or:[self anyInvalidatedMethodsIn: oldClass].
+
+        (instVarChange or:[classVarChange]) ifTrue:[
+            "must recompile instance-methods"
+            self copyInvalidatedMethodsFrom:oldClass for:newClass.
+            newClass recompile
+        ] ifFalse:[
+            "instance methods still work"
+            self copyMethodsFrom:oldClass for:newClass
+        ].
+
+        "get list of all subclasses - do before superclass is changed"
+
+        allSubclasses := oldClass allSubclasses.
+
+        "update superclass of immediate subclasses"
+
+        oldClass subclassesDo:[:aClass |
+            aClass superclass:newClass
+        ].
+
+        "update instSizes and recompile all subclasses if needed"
+
+        "for subclasses we must be strict"
+        classVarChange := oldClass classVariableString ~= newClass classVariableString.
+        classVarChange := classVarChange or:[superClassChange].
+
+        "for subclasses we must be strict since offsets change"
+        instVarChange := oldClass instanceVariableString ~= newClass instanceVariableString.
+        instVarChange := instVarChange or:[superClassChange].
+
+        allSubclasses do:[:aClass |
+            aClass instSize:(aClass instSize + (newClass instSize - oldClass instSize)).
+            (classVarChange or:[self anyInvalidatedMethodsIn:aClass class]) ifTrue:[
+                aClass class recompileAll
+            ].
+            (classVarChange or:[instVarChange or:[self anyInvalidatedMethodsIn: aClass]]) ifTrue:[
+                aClass recompileAll
+            ]
+        ].
+
+        ObjectMemory flushCaches.
+        Class updateChanges:upd
+    ].
+    oldClass isNil ifTrue:[
+        Smalltalk changed
+    ] ifFalse:[
+        oldClass setName:(oldClass name , '-old')
+    ].
+    ^ newClass
+!
+
+new
+    "returs a new class class"
+    |newClass|
+
+    newClass := self basicNew.
+    newClass setSuperclass:(Object class)
+                 selectors:(Array new:0)
+                   methods:(Array new:0)
+                  instSize:0
+                     flags:0.
+    newClass setComment:(self comment) category:(self category).
+    ^ newClass
+! !
+
+!Metaclass methodsFor:'class instance variables'!
+
+instanceVariableNames:aString
+    "changing / adding class-inst vars -
+     this actually creates a new metaclass and class"
+
+    |newClass newMetaclass nClassInstVars oldClass 
+     allSubclasses upd t oldVars sizeChange|
+
+    oldVars := self instanceVariableString.
+    aString = oldVars ifTrue:[^ self].
+
+    nClassInstVars := aString countWords.
+    sizeChange := nClassInstVars ~~ oldVars countWords.
+
+    "create the new metaclass"
+    newMetaclass := Metaclass new.
+    newMetaclass setSuperclass:superclass.
+    newMetaclass instSize:(superclass instSize + nClassInstVars).
+    (nClassInstVars ~~ 0) ifTrue:[
+        newMetaclass instanceVariableString:aString
+    ].
+    newMetaclass flags:0.            "not indexed"
+    newMetaclass setName:name.
+    newMetaclass classVariableString:classvars.
+    newMetaclass category:category.
+    newMetaclass setComment:comment.
+
+    "find the class which is my sole instance"
+
+    t := Smalltalk allClasses select:[:element | element class == self].
+    (t size ~~ 1) ifTrue:[
+        self error:'oops - I should have exactly one instance'.
+        ^ nil
+    ].
+    oldClass := t anElement.
+
+    "create a new class"
+    newClass := newMetaclass new.
+    newClass setSuperclass:(oldClass superclass).
+    newClass instSize:(oldClass instSize).
+    newClass flags:(oldClass flags).
+    newClass setName:(oldClass name).
+    newClass instanceVariableString:(oldClass instanceVariableString).
+    newClass classVariableString:(oldClass classVariableString).
+    newClass comment:(oldClass comment).
+    newClass category:(oldClass category).
+
+    ObjectMemory flushCaches.
+
+    Smalltalk at:(oldClass name asSymbol) put:newClass.
+
+    upd := Class updateChanges:false.
+
+    (oldVars isBlank 
+    or:[aString startsWith:oldVars]) ifTrue:[
+        "there where none before or a new var has been added
+         - methods still work"
+        self copyMethodsFrom:self for:newMetaclass.
+        self copyMethodsFrom:oldClass for:newClass
+    ] ifFalse:[
+        "recompile class-methods"
+        self copyInvalidatedMethodsFrom:self for:newMetaclass.
+        newMetaclass recompile.
+
+        "recompile instance-methods"
+        self copyInvalidatedMethodsFrom:oldClass for:newClass.
+        newClass recompile
+    ].
+
+    "get list of all subclasses - do before superclass is changed"
+
+    allSubclasses := oldClass allSubclasses.
+
+    "update superclass of immediate subclasses"
+
+    oldClass subclassesDo:[:aClass |
+        aClass superclass:newClass
+    ].
+
+    "update instSizes and recompile all subclasses if needed"
+
+    allSubclasses do:[:aClass |
+        aClass class recompileAll.
+        aClass recompileAll
+    ].
+
+    ObjectMemory flushCaches.
+    Class updateChanges:upd.
+    ^ newMetaclass
+! !
+
+!Metaclass methodsFor:'queries'!
+
+isMeta
+    "return true, if the receiver is some kind of metaclass;
+     true is returned here. Redefines isMeta in Object"
+
+    ^ true
+! !
+
+!Metaclass methodsFor:'private'!
+
+copyMethodsFrom:oldClass for:newClass
+    "when a class has changed, but metaclass is unaffected (i.e. classVars
+     have not changed) there is no need to recompile them"
+
+    newClass selectors:(oldClass selectors) methods:(oldClass methodDictionary)
+!
+
+copyInvalidatedMethodsFrom:oldClass for:newClass
+    "when a class has been changed, copy all old methods into the new class
+     - changing code to a trap method giving an error message;
+     this allows us to keep the source while trapping uncompilable (due to
+     now undefined instvars) methods"
+
+    |trap trapCode trapByteCode|
+
+    trap := Method compiledMethodAt:#invalidMethod.
+    trapCode := trap code.
+    trapByteCode := trap byteCode.
+
+    newClass selectors:(oldClass selectors) methods:(oldClass methodDictionary).
+    newClass methodDictionary do:[:aMethod |
+        aMethod code:trapCode.
+        aMethod literals:nil.
+        aMethod byteCode:trapByteCode
+    ]
+!
+
+anyInvalidatedMethodsIn:aClass
+    "return true, if aClass has any invalidated methods in it"
+
+    |trap trapCode trapByteCode|
+
+    trap := Method compiledMethodAt:#invalidMethod.
+    trapCode := trap code.
+    trapByteCode := trap byteCode.
+
+    aClass methodDictionary do:[:aMethod |
+        trapCode notNil ifTrue:[
+            (aMethod code == trapCode) ifTrue:[^ true]
+        ].
+        trapByteCode notNil ifTrue:[
+            (aMethod byteCode == trapByteCode) ifTrue:[^ true]
+        ]
+    ].
+    ^ false
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Method.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,629 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Method
+       instanceVariableNames:'code flags byteCode literals
+                              source sourcePosition category'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Kernel-Methods'
+!
+
+Method comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+             All Rights Reserved
+
+this class defines protocol for executable methods;
+both compiled and interpreted methods are represented by this class.
+Compiled code has a non-nil code field, while interpreted methods have
+a nil code field and non-nil byteCode field.
+
+The methods source-code is represented by source and sourcePosition: 
+if sourcePosition is a Number, the source-field is the fileName and
+sourcePosition is the character offset of the source-chunk in this source file.
+If sourcePosition is nil, the source is the string in the source field.
+
+The flags field defines things like the number of method-locals,
+method arguments and stack size need (for interpreted methods).
+
+WARNING: layout known by compiler and runtime system - dont change
+
+%W% %E%
+written spring 89 by claus
+'!
+
+!Method methodsFor:'accessing'!
+
+instVarAt:index
+    "redefined to catch access to code-field - it is a non-object"
+
+    index == 1 ifTrue:[^ nil].
+    ^ super instVarAt:index
+!
+
+instVarAt:index put:value
+    "redefined to catch access to code-field - it is a non-object"
+
+    index == 1 ifTrue:[^ nil].
+    ^ super instVarAt:index put:value
+!
+
+code
+    "return code field - since its a non-object return address as integer"
+%{
+    RETURN ( _MKSMALLINT((int)(_MethodInstPtr(self)->m_code)) );
+%}
+!
+
+code:anAddress
+    "set the code field - you should know what you do if doing this -
+     should only be done by compiler.
+     Smalltalk can crash badly if playing around here ..."
+%{
+    if (_isSmallInteger(anAddress)) {
+        _MethodInstPtr(self)->m_code = (OBJFUNC)_intVal(anAddress);
+        RETURN ( self );
+    }
+%}
+.
+    self primitiveFailed
+!
+
+source
+    "return the sourcestring for the receiver"
+
+    |aStream junk|
+
+    source notNil ifTrue:[
+        sourcePosition isNil ifTrue:[^ source].
+        aStream := Smalltalk systemFileStreamFor:('source/' , source).
+        aStream notNil ifTrue:[
+            aStream position:sourcePosition.
+            junk := aStream nextChunk.
+            aStream close
+        ]
+    ].
+    ^ junk
+!
+
+source:aString
+    "set the methods sourcestring"
+
+    source := aString.
+    sourcePosition := nil
+!
+
+sourceFileName
+    "return the sourcefilename if source is extern; nil otherwise"
+
+    sourcePosition notNil ifTrue:[^ source].
+    ^ nil
+!
+
+sourcePosition
+    "return the sourceposition if source is extern; nil otherwise"
+
+    ^ sourcePosition
+!
+
+sourceFileName:aFileName position:aNumber
+    "set the methods sourcefile/position"
+
+    source := aFileName.
+    sourcePosition := aNumber
+!
+
+literals
+    "return the methods literal array"
+
+    ^ literals
+!
+
+literals:anArray
+    "set the methods literal array"
+
+    literals := anArray
+!
+
+byteCode
+    "return the methods byteCode array"
+
+    ^ byteCode
+!
+
+byteCode:aByteArray
+    "set the methods byteCode array"
+
+    byteCode := aByteArray
+!
+
+category
+    "return the methods category or nil"
+
+    ^ category
+!
+
+category:aStringOrSymbol
+    "set the methods category"
+
+    category := aStringOrSymbol asSymbol
+!
+
+flags
+    "return the flags (number of method variables, stacksize)"
+
+    ^ flags
+!
+
+flags:newFlags
+    "set the flags (number of method variables, stacksize)
+     - should only be done by the compiler"
+
+    flags := newFlags
+!
+
+numberOfMethodVars:aNumber
+    "set the number of method variables
+     - should only be done by the compiler"
+
+    |newFlags|
+
+    newFlags := flags.
+%{
+    /* made this a primitive to get define in stc.h */
+    newFlags = _MKSMALLINT((_intVal(newFlags) & ~F_NVARS)
+                           | (_intVal(aNumber) << F_NVARSHIFT));
+%}
+.
+    flags := newFlags
+!
+
+stackSize:aNumber
+    "set the depth of the local stack
+     - should only be done by the compiler"
+
+    |newFlags|
+
+    newFlags := flags.
+%{
+    /* made this a primitive to get define in stc.h */
+    newFlags = _MKSMALLINT((_intVal(newFlags) & ~F_NSTACK) 
+                           | (_intVal(aNumber) << F_NSTACKSHIFT));
+%}
+.
+    flags := newFlags
+! !
+
+!Method methodsFor:'queries'!
+
+containingClass
+    "return the class I am defined in"
+
+    Smalltalk allClassesDo:[:aClass |
+        (aClass containsMethod:self) ifTrue:[^ aClass]
+    ].
+    ^ nil
+!
+
+methodArgNames
+    "return a collection with the methods argument names.
+     Uses Parser to parse methods source."
+
+    |parser sourceString|
+
+    sourceString := self source.
+    sourceString notNil ifTrue:[
+        parser := Parser parseMethodSpecification:sourceString.
+        parser isNil ifTrue:[^ nil].
+        ^ parser methodArgs
+    ].
+    ^ nil
+
+    "(Method compiledMethodAt:#printOn:) methodArgNames"
+!
+
+methodVarNames
+    "return a collection with the methods local-variable names.
+     Uses Parser to parse methods source."
+
+    |parser sourceString|
+
+    sourceString := self source.
+    sourceString notNil ifTrue:[
+        parser := Parser parseMethodArgAndVarSpecification:sourceString.
+        parser isNil ifTrue:[^ nil].
+        ^ parser methodVars
+    ].
+    ^ nil
+
+    "(Method compiledMethodAt:#printOn:) methodVarNames"
+!
+
+methodArgAndVarNames
+    "return a collection with the methods argument and variable names.
+     Uses Parser to parse methods source."
+
+    |parser sourceString argNames varNames|
+
+    sourceString := self source.
+    sourceString notNil ifTrue:[
+        parser := Parser parseMethodArgAndVarSpecification:sourceString.
+        parser isNil ifTrue:[^ nil].
+        argNames := parser methodArgs.
+        varNames := parser methodVars.
+        argNames isNil ifTrue:[^ varNames].
+        varNames isNil ifTrue:[^ argNames].
+        ^ (argNames , varNames)
+    ].
+    ^ nil
+
+    "(Method compiledMethodAt:#printOn:) methodArgAndVarNames"
+!
+
+methodComment
+    "return the methods comment, nil if there is none"
+
+    |text line nQuote index qIndex qIndex2 comment|
+
+    text := self source asText.
+    (text size < 2) ifTrue:[^nil].
+
+    line := (text at:2).
+    nQuote := line occurrencesOf:(Character doubleQuote).
+    (nQuote == 2) ifTrue:[
+        qIndex := line indexOf:(Character doubleQuote).
+        qIndex2 := line indexOf:(Character doubleQuote) startingAt:(qIndex + 1).
+        ^ line copyFrom:(qIndex + 1) to:(qIndex2 - 1)
+    ].
+    (nQuote == 1) ifTrue:[
+        qIndex := line indexOf:(Character doubleQuote).
+        comment := line copyFrom:(qIndex + 1).
+
+        index := 3.
+        line := text at:index.
+        nQuote := line occurrencesOf:(Character doubleQuote).
+        [nQuote ~~ 1] whileTrue:[
+            comment := comment , Character cr asString , line withoutSpaces.
+            index := index + 1.
+            line := text at:index.
+            nQuote := line occurrencesOf:(Character doubleQuote)
+        ].
+        qIndex := line indexOf:(Character doubleQuote).
+        ^ comment , Character cr asString , (line copyTo:(qIndex - 1)) withoutSpaces
+    ].
+    ^ nil
+
+    "(Method compiledMethodAt:#methodComment) methodComment"
+!
+
+referencesGlobal:aGlobalSymbol
+    "return true, if this method references the global
+     bound to aGlobalSymbol."
+
+    literals isNil ifTrue:[^ false].
+    ^ (literals identityIndexOf:aGlobalSymbol startingAt:1) ~~ 0
+!
+
+sends:aSelectorSymbol
+    "return true, if this method contains a message-send
+     with aSelectorSymbol as selector. 
+     - due to the simple check in the literal array, also simple uses 
+     of aSelectorSymbol as symbol will return true."
+
+    ^ self referencesGlobal:aSelectorSymbol
+! !
+
+!Method methodsFor:'error handling'!
+
+invalidMethod
+    "this error is triggered by the interpreter when an invalid method
+     is about to be executed.
+     When recompiling classes after a definition-change, all
+     uncompilable methods will be bound to this method here,
+     so that evaluating such an uncompilable method will trigger an error.
+     Can also happen when Compiler/runtime system is broken."
+
+    self error:'invalid method - not executable'
+!
+
+invalidByteCode
+    "this error is triggered when the bytecode-interpreter tries to
+     execute an invalid bytecode.
+     Can only happen when Compiler/runtime system is broken."
+
+    self error:'invalid byteCode in method - not executable'
+!
+
+receiverNotBoolean
+    "this error is triggered when the bytecode-interpreter tries to
+     execute ifTrue:/ifFalse or whileTrue: type of expressions where the
+     receiver is neither true nor false."
+
+    self error:'if/while on non-boolean receiver'
+! !
+
+!Method methodsFor:'executing'!
+
+valueWithReceiver:anObject arguments:argArray
+    "low level call of a methods code - big danger alert.
+     This method is provided for debugging- and breakpoint-support;
+     not for general use. The receiver must be a method compiled in
+     anObjects class or one of its superclasses 
+     - otherwise strange things (and also strange crashes) can occur. 
+     Be warned."
+
+    |numArgs a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12|
+
+    argArray class == Array ifFalse:[
+        ^ self error:'argumentArray must be an Array'
+    ].
+    numArgs := argArray size.
+    numArgs > 12 ifTrue:[
+        ^ self error:'too many arguments'
+    ].
+%{
+    OBJFUNC code;
+    OBJ searchClass;
+    static struct inlineCache dummy = _DUMMYILC0;
+
+    code = _MethodInstPtr(self)->m_code;
+    searchClass = dummy.ilc_class = _Class(anObject);
+    switch (_intVal(numArgs)) {
+        case 0:
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy) );
+
+        case 1:
+            a1 = _ArrayInstPtr(argArray)->a_element[0];
+#ifdef PASS_ARG_REF
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1) );
+#endif
+
+        case 2:
+            a1 = _ArrayInstPtr(argArray)->a_element[0];
+            a2 = _ArrayInstPtr(argArray)->a_element[1];
+#ifdef PASS_ARG_REF
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2) );
+#endif
+
+        case 3:
+            a1 = _ArrayInstPtr(argArray)->a_element[0];
+            a2 = _ArrayInstPtr(argArray)->a_element[1];
+            a3 = _ArrayInstPtr(argArray)->a_element[2];
+#ifdef PASS_ARG_REF
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3) );
+#endif
+
+        case 4:
+            a1 = _ArrayInstPtr(argArray)->a_element[0];
+            a2 = _ArrayInstPtr(argArray)->a_element[1];
+            a3 = _ArrayInstPtr(argArray)->a_element[2];
+            a4 = _ArrayInstPtr(argArray)->a_element[3];
+#ifdef PASS_ARG_REF
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3, a4) );
+#endif
+
+        case 5:
+            a1 = _ArrayInstPtr(argArray)->a_element[0];
+            a2 = _ArrayInstPtr(argArray)->a_element[1];
+            a3 = _ArrayInstPtr(argArray)->a_element[2];
+            a4 = _ArrayInstPtr(argArray)->a_element[3];
+            a5 = _ArrayInstPtr(argArray)->a_element[4];
+#ifdef PASS_ARG_REF
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3, a4, a5) );
+#endif
+
+        case 6:
+            a1 = _ArrayInstPtr(argArray)->a_element[0];
+            a2 = _ArrayInstPtr(argArray)->a_element[1];
+            a3 = _ArrayInstPtr(argArray)->a_element[2];
+            a4 = _ArrayInstPtr(argArray)->a_element[3];
+            a5 = _ArrayInstPtr(argArray)->a_element[4];
+            a6 = _ArrayInstPtr(argArray)->a_element[5];
+#ifdef PASS_ARG_REF
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3, a4, a5, a6) );
+#endif
+
+        case 7:
+            a1 = _ArrayInstPtr(argArray)->a_element[0];
+            a2 = _ArrayInstPtr(argArray)->a_element[1];
+            a3 = _ArrayInstPtr(argArray)->a_element[2];
+            a4 = _ArrayInstPtr(argArray)->a_element[3];
+            a5 = _ArrayInstPtr(argArray)->a_element[4];
+            a6 = _ArrayInstPtr(argArray)->a_element[5];
+            a7 = _ArrayInstPtr(argArray)->a_element[6];
+#ifdef PASS_ARG_REF
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3, a4, a5, a6, a7) );
+#endif
+
+        case 8:
+            a1 = _ArrayInstPtr(argArray)->a_element[0];
+            a2 = _ArrayInstPtr(argArray)->a_element[1];
+            a3 = _ArrayInstPtr(argArray)->a_element[2];
+            a4 = _ArrayInstPtr(argArray)->a_element[3];
+            a5 = _ArrayInstPtr(argArray)->a_element[4];
+            a6 = _ArrayInstPtr(argArray)->a_element[5];
+            a7 = _ArrayInstPtr(argArray)->a_element[6];
+            a8 = _ArrayInstPtr(argArray)->a_element[7];
+#ifdef PASS_ARG_REF
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3, a4, a5, a6, a7, a8) );
+#endif
+
+        case 9:
+            a1 = _ArrayInstPtr(argArray)->a_element[0];
+            a2 = _ArrayInstPtr(argArray)->a_element[1];
+            a3 = _ArrayInstPtr(argArray)->a_element[2];
+            a4 = _ArrayInstPtr(argArray)->a_element[3];
+            a5 = _ArrayInstPtr(argArray)->a_element[4];
+            a6 = _ArrayInstPtr(argArray)->a_element[5];
+            a7 = _ArrayInstPtr(argArray)->a_element[6];
+            a8 = _ArrayInstPtr(argArray)->a_element[7];
+            a9 = _ArrayInstPtr(argArray)->a_element[8];
+#ifdef PASS_ARG_REF
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3, a4, a5, a6, a7, a8, a9) );
+#endif
+
+        case 10:
+            a1 = _ArrayInstPtr(argArray)->a_element[0];
+            a2 = _ArrayInstPtr(argArray)->a_element[1];
+            a3 = _ArrayInstPtr(argArray)->a_element[2];
+            a4 = _ArrayInstPtr(argArray)->a_element[3];
+            a5 = _ArrayInstPtr(argArray)->a_element[4];
+            a6 = _ArrayInstPtr(argArray)->a_element[5];
+            a7 = _ArrayInstPtr(argArray)->a_element[6];
+            a8 = _ArrayInstPtr(argArray)->a_element[7];
+            a9 = _ArrayInstPtr(argArray)->a_element[8];
+            a10 = _ArrayInstPtr(argArray)->a_element[9];
+#ifdef PASS_ARG_REF
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) );
+#endif
+
+        case 11:
+            a1 = _ArrayInstPtr(argArray)->a_element[0];
+            a2 = _ArrayInstPtr(argArray)->a_element[1];
+            a3 = _ArrayInstPtr(argArray)->a_element[2];
+            a4 = _ArrayInstPtr(argArray)->a_element[3];
+            a5 = _ArrayInstPtr(argArray)->a_element[4];
+            a6 = _ArrayInstPtr(argArray)->a_element[5];
+            a7 = _ArrayInstPtr(argArray)->a_element[6];
+            a8 = _ArrayInstPtr(argArray)->a_element[7];
+            a9 = _ArrayInstPtr(argArray)->a_element[8];
+            a10 = _ArrayInstPtr(argArray)->a_element[9];
+            a11 = _ArrayInstPtr(argArray)->a_element[10];
+#ifdef PASS_ARG_REF
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) );
+#endif
+
+        case 12:
+            a1 = _ArrayInstPtr(argArray)->a_element[0];
+            a2 = _ArrayInstPtr(argArray)->a_element[1];
+            a3 = _ArrayInstPtr(argArray)->a_element[2];
+            a4 = _ArrayInstPtr(argArray)->a_element[3];
+            a5 = _ArrayInstPtr(argArray)->a_element[4];
+            a6 = _ArrayInstPtr(argArray)->a_element[5];
+            a7 = _ArrayInstPtr(argArray)->a_element[6];
+            a8 = _ArrayInstPtr(argArray)->a_element[7];
+            a9 = _ArrayInstPtr(argArray)->a_element[8];
+            a10 = _ArrayInstPtr(argArray)->a_element[9];
+            a11 = _ArrayInstPtr(argArray)->a_element[10];
+            a12 = _ArrayInstPtr(argArray)->a_element[11];
+#ifdef PASS_ARG_REF
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, &a1) );
+#else
+            RETURN ( (*code)(anObject, nil, SND_COMMA searchClass, &dummy, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) );
+#endif
+    }
+%}
+    "(String compiledMethodAt:#print) valueWithReceiver:'hello' arguments:#()"
+    "(Float compiledMethodAt:#+) valueWithReceiver:1.0 arguments:#(2.0)"
+! !
+
+!Method methodsFor:'printing'!
+
+printOn:aStream
+    "put a printed representation of the receiver onto aStream"
+
+    |homeClass|
+
+    homeClass := self containingClass.
+    homeClass notNil ifTrue:[
+        aStream nextPutAll:'a Method in '.
+        homeClass name printOn:aStream.
+        aStream nextPutAll:' '.
+        (homeClass selectorForMethod:self) printOn:aStream
+    ] ifFalse:[
+        aStream nextPutAll:'a Method'
+    ]
+! !
+
+!Method methodsFor:'binary fileOut'!
+
+binaryFileOutLiteralsOn:aStream
+    |index n|
+
+    literals isNil ifTrue:[
+        aStream nextPutAll:'0'.
+        aStream nextPut:$!!.
+        ^ self
+    ].
+    aStream nextPutAll:literals size printString.
+    aStream nextPut:$!!.
+
+    index := 1.
+    literals do:[:lit |
+        (lit isKindOf:Number) ifTrue:[
+            lit storeOn:aStream
+        ] ifFalse:[
+            ((lit isKindOf:String) or:[lit isKindOf:Character]) ifTrue:[
+                lit storeOn:aStream
+            ] ifFalse:[
+                (lit isKindOf:Array) ifTrue:[
+                    aStream nextPut:$(.
+                    lit storeOn:aStream.
+                    aStream nextPut:$)
+                ] ifFalse:[
+                    (lit isBehavior "isKindOf:Class") ifTrue:[
+                        aStream nextPutAll:'(Smalltalk at:#'.
+                        n := lit name.
+                        (lit isMeta "isKindOf:Metaclass") ifTrue:[
+                            n := (n copyFrom:1 to:(n size - 5)) , ') class'
+                        ] ifFalse:[
+                            n := n , ')'
+                        ].
+                        aStream nextPutAll:n
+                    ] ifFalse:[
+                        self error:('invalid literal ' , lit class name)
+                    ]
+                ]
+            ]
+        ].
+        aStream nextPut:$!!.
+        index := index + 1
+    ]
+!
+
+binaryFileOutOn:aStream
+    byteCode isNil ifTrue:[
+        self notify:'no bytecodes to fileout'.
+        ^ self
+    ].
+    self binaryFileOutLiteralsOn:aStream.
+
+    flags storeOn:aStream.
+    aStream nextPut:$!!.
+
+    byteCode size storeOn:aStream.
+    aStream nextPut:$!!.
+    aStream nextPutBytes:(byteCode size) from:byteCode
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MiniDebug.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,297 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#MiniDebugger
+       instanceVariableNames:'tracing stepping traceBlock'
+       classVariableNames:   'theOneAndOnlyDebugger'
+       poolDictionaries:''
+       category:'System-Support'
+!
+
+MiniDebugger comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+a primitive (non graphical) debugger for use on systems without
+graphics or when the real debugger dies (i.e. an error occurs in
+the graphical debugger).
+
+%W% %E%
+'!
+
+!MiniDebugger class methodsFor: 'instance creation'!
+
+new
+    theOneAndOnlyDebugger printNL.
+    theOneAndOnlyDebugger isNil ifTrue:[
+        theOneAndOnlyDebugger := self basicNew initialize
+    ].
+    ^ theOneAndOnlyDebugger
+!
+
+singleStep:aBlock
+    |aDebugger|
+
+    aDebugger := self new stepping.
+    StepInterruptHandler := aDebugger.
+    StepInterruptPending := true.
+    InterruptPending := true.
+    aBlock value.
+    StepInterruptPending := nil
+!
+
+trace:aBlock
+    self trace:aBlock with:[:where | where printNewline]
+!
+
+trace:aBlock on:aStream
+    self trace:aBlock with:[:where | where printString printOn:aStream.
+                                     aStream cr]
+!
+
+trace:aBlock with:aTraceBlock
+    |aDebugger|
+
+    aDebugger := self new tracingWith:aTraceBlock.
+    ObjectMemory flushInlineCaches.
+    StepInterruptHandler := aDebugger.
+    StepInterruptPending := true.
+    InterruptPending := true.
+    aBlock value.
+    StepInterruptPending := nil.
+    ^ nil
+!
+
+enterWithMessage:aString
+    |aDebugger|
+
+    StepInterruptPending := nil.
+    aString printNewline.
+    aDebugger := self new.
+    aDebugger enter.
+    ^ nil
+! !
+
+!MiniDebugger methodsFor: 'initialization'!
+
+initialize
+    traceBlock := nil.
+    tracing := false.
+    stepping := false
+! !
+
+!MiniDebugger methodsFor: 'private'!
+
+stepping
+    traceBlock := nil.
+    tracing := false.
+    stepping := true
+!
+
+tracingWith:aBlockOrNil
+    traceBlock := aBlockOrNil.
+    stepping := false.
+    tracing := true
+!
+
+getContext
+    |backtrace|
+    backtrace := thisContext.
+    (backtrace notNil) ifTrue: [
+        "remove Context getContext frame"
+        backtrace := backtrace sender.
+        "remove Debugger showContext frame"
+        backtrace := backtrace sender.
+        "remove Debugger commandLoop frame"
+        backtrace := backtrace sender.
+        "remove Debugger enter frame"
+        backtrace := backtrace sender
+    ].
+    ^ backtrace
+! !
+
+!MiniDebugger methodsFor: 'interrupt handling'!
+
+stepInterrupt
+    |where|
+
+    where := thisContext.        "where is stepInterrupt context"
+    where notNil ifTrue:[
+        where := where sender    "where is now interrupted methods context"
+    ].
+    stepping ifTrue:[
+        where notNil ifTrue:[
+            where fullPrint
+        ] ifFalse:[
+            'stepInterrupt: no context' printNewline
+        ].
+        self enter
+    ] ifFalse:[
+        where notNil ifTrue:[
+            traceBlock notNil ifTrue:[
+                traceBlock value:where
+            ]
+        ] ifFalse:[
+            'traceInterrupt: no context' printNewline
+        ].
+	ObjectMemory flushInlineCaches.
+        StepInterruptPending := true.
+        InterruptPending := true
+    ]
+!
+
+enter
+    |cmd|
+
+    cmd := self commandLoop.
+    (cmd == $s) ifTrue: [
+        self stepping.
+	ObjectMemory flushInlineCaches.
+        StepInterruptHandler := self.
+        StepInterruptPending := true.
+        InterruptPending := true
+    ].
+    (cmd == $t) ifTrue: [
+        traceBlock := [:where | where fullPrint].
+	ObjectMemory flushInlineCaches.
+        StepInterruptHandler := self.
+        StepInterruptPending := true.
+        InterruptPending := true
+    ].
+    (cmd == $c) ifTrue: [
+        stepping := false.
+        tracing := false.
+        StepInterruptPending := nil.
+        InterruptPending := nil
+    ].
+    ^ nil
+! !
+
+!MiniDebugger methodsFor: 'user commands'!
+
+commandLoop
+    |cmd done valid context|
+
+    done := false.
+    [done] whileFalse:[
+        valid := false.
+        cmd := self getCommand.
+        (cmd == $p) ifTrue:[
+            valid := true.
+            context isNil ifTrue: [
+                context := self getContext
+            ].
+            context notNil ifTrue:[
+                context fullPrintAll
+            ] ifFalse:[
+                'no context' printNewline
+            ]
+        ].
+        (cmd == $r) ifTrue:[
+            valid := true.
+            context isNil ifTrue: [
+                context := self getContext
+            ].
+            context notNil ifTrue:[
+                "remove Debugger stepinterrupt/halt frame"
+                context sender receiver printNewline
+            ] ifFalse:[
+                'no context - dont know receiver' printNewline
+            ]
+        ].
+        (cmd == $R) ifTrue:[
+            valid := true.
+            context isNil ifTrue: [
+                context := self getContext
+            ].
+            context notNil ifTrue:[
+                "remove Debugger stepinterrupt/halt frame"
+                context sender receiver storeOn:Stdout
+            ] ifFalse:[
+                'no context - dont know receiver' printNewline
+            ]
+        ].
+        (cmd == $i) ifTrue:[
+            valid := true.
+            context isNil ifTrue: [
+                context := self getContext
+            ].
+            context notNil ifTrue:[
+                "remove Debugger stepinterrupt/halt frame"
+                context sender receiver inspect
+            ] ifFalse:[
+                'no context - dont know receiver' printNewline
+            ]
+        ].
+        (cmd == $I) ifTrue:[
+            valid := true.
+            context isNil ifTrue: [
+                context := self getContext
+            ].
+            context notNil ifTrue:[
+                "remove Debugger stepinterrupt/halt frame"
+                self interpreterLoopWith:(context sender receiver)
+            ] ifFalse:[
+                'no context - dont know receiver' printNewline.
+                self interpreterLoopWith:nil
+            ]
+        ].
+	context := nil.
+        (cmd == $c) ifTrue:[valid := true. done := true].
+        (cmd == $s) ifTrue:[valid := true. done := true].
+        (cmd == $t) ifTrue:[valid := true. done := true].
+        (cmd == $a) ifTrue:[Smalltalk fatalAbort].
+        (cmd == $x) ifTrue:[Smalltalk exit].
+        valid ifFalse: [
+            'valid commands:' printNewline.
+            '   (c)ontinue'     printNewline.
+            '   (s)tep'         printNewline.
+            '   (t)race'        printNewline.
+            '   (p)rintContext' printNewline.
+            '   (r)eceiver'     printNewline.
+            '   (R)eceiver'     printNewline.
+            '   (i)nspect'      printNewline.
+            '   (I)nterpreter'  printNewline.
+            '   (a)bort'        printNewline.
+            '   (x)exit Smalltalk'  printNewline
+        ]
+    ].
+    ^ cmd
+!
+
+getCommand
+    |cmd c|
+    'MiniDebugger> ' print.
+    cmd := Character fromUser.
+    c := cmd.
+    [ c isEndOfLineCharacter ] whileFalse: [
+        c := Character fromUser
+    ].
+    ^ cmd
+!
+
+interpreterLoopWith:anObject
+    |line done|
+    'read-eval-print loop; exit with empty line' printNewline.
+    done := false.
+    [done] whileFalse:[
+        line := Stdin nextLine.
+        (line size == 0) ifTrue:[
+            done := true
+        ] ifFalse:[
+            (Compiler evaluate:line 
+                      receiver:anObject
+                     notifying:nil) printNewline
+        ]
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MiniDebugger.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,297 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#MiniDebugger
+       instanceVariableNames:'tracing stepping traceBlock'
+       classVariableNames:   'theOneAndOnlyDebugger'
+       poolDictionaries:''
+       category:'System-Support'
+!
+
+MiniDebugger comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+a primitive (non graphical) debugger for use on systems without
+graphics or when the real debugger dies (i.e. an error occurs in
+the graphical debugger).
+
+%W% %E%
+'!
+
+!MiniDebugger class methodsFor: 'instance creation'!
+
+new
+    theOneAndOnlyDebugger printNL.
+    theOneAndOnlyDebugger isNil ifTrue:[
+        theOneAndOnlyDebugger := self basicNew initialize
+    ].
+    ^ theOneAndOnlyDebugger
+!
+
+singleStep:aBlock
+    |aDebugger|
+
+    aDebugger := self new stepping.
+    StepInterruptHandler := aDebugger.
+    StepInterruptPending := true.
+    InterruptPending := true.
+    aBlock value.
+    StepInterruptPending := nil
+!
+
+trace:aBlock
+    self trace:aBlock with:[:where | where printNewline]
+!
+
+trace:aBlock on:aStream
+    self trace:aBlock with:[:where | where printString printOn:aStream.
+                                     aStream cr]
+!
+
+trace:aBlock with:aTraceBlock
+    |aDebugger|
+
+    aDebugger := self new tracingWith:aTraceBlock.
+    ObjectMemory flushInlineCaches.
+    StepInterruptHandler := aDebugger.
+    StepInterruptPending := true.
+    InterruptPending := true.
+    aBlock value.
+    StepInterruptPending := nil.
+    ^ nil
+!
+
+enterWithMessage:aString
+    |aDebugger|
+
+    StepInterruptPending := nil.
+    aString printNewline.
+    aDebugger := self new.
+    aDebugger enter.
+    ^ nil
+! !
+
+!MiniDebugger methodsFor: 'initialization'!
+
+initialize
+    traceBlock := nil.
+    tracing := false.
+    stepping := false
+! !
+
+!MiniDebugger methodsFor: 'private'!
+
+stepping
+    traceBlock := nil.
+    tracing := false.
+    stepping := true
+!
+
+tracingWith:aBlockOrNil
+    traceBlock := aBlockOrNil.
+    stepping := false.
+    tracing := true
+!
+
+getContext
+    |backtrace|
+    backtrace := thisContext.
+    (backtrace notNil) ifTrue: [
+        "remove Context getContext frame"
+        backtrace := backtrace sender.
+        "remove Debugger showContext frame"
+        backtrace := backtrace sender.
+        "remove Debugger commandLoop frame"
+        backtrace := backtrace sender.
+        "remove Debugger enter frame"
+        backtrace := backtrace sender
+    ].
+    ^ backtrace
+! !
+
+!MiniDebugger methodsFor: 'interrupt handling'!
+
+stepInterrupt
+    |where|
+
+    where := thisContext.        "where is stepInterrupt context"
+    where notNil ifTrue:[
+        where := where sender    "where is now interrupted methods context"
+    ].
+    stepping ifTrue:[
+        where notNil ifTrue:[
+            where fullPrint
+        ] ifFalse:[
+            'stepInterrupt: no context' printNewline
+        ].
+        self enter
+    ] ifFalse:[
+        where notNil ifTrue:[
+            traceBlock notNil ifTrue:[
+                traceBlock value:where
+            ]
+        ] ifFalse:[
+            'traceInterrupt: no context' printNewline
+        ].
+	ObjectMemory flushInlineCaches.
+        StepInterruptPending := true.
+        InterruptPending := true
+    ]
+!
+
+enter
+    |cmd|
+
+    cmd := self commandLoop.
+    (cmd == $s) ifTrue: [
+        self stepping.
+	ObjectMemory flushInlineCaches.
+        StepInterruptHandler := self.
+        StepInterruptPending := true.
+        InterruptPending := true
+    ].
+    (cmd == $t) ifTrue: [
+        traceBlock := [:where | where fullPrint].
+	ObjectMemory flushInlineCaches.
+        StepInterruptHandler := self.
+        StepInterruptPending := true.
+        InterruptPending := true
+    ].
+    (cmd == $c) ifTrue: [
+        stepping := false.
+        tracing := false.
+        StepInterruptPending := nil.
+        InterruptPending := nil
+    ].
+    ^ nil
+! !
+
+!MiniDebugger methodsFor: 'user commands'!
+
+commandLoop
+    |cmd done valid context|
+
+    done := false.
+    [done] whileFalse:[
+        valid := false.
+        cmd := self getCommand.
+        (cmd == $p) ifTrue:[
+            valid := true.
+            context isNil ifTrue: [
+                context := self getContext
+            ].
+            context notNil ifTrue:[
+                context fullPrintAll
+            ] ifFalse:[
+                'no context' printNewline
+            ]
+        ].
+        (cmd == $r) ifTrue:[
+            valid := true.
+            context isNil ifTrue: [
+                context := self getContext
+            ].
+            context notNil ifTrue:[
+                "remove Debugger stepinterrupt/halt frame"
+                context sender receiver printNewline
+            ] ifFalse:[
+                'no context - dont know receiver' printNewline
+            ]
+        ].
+        (cmd == $R) ifTrue:[
+            valid := true.
+            context isNil ifTrue: [
+                context := self getContext
+            ].
+            context notNil ifTrue:[
+                "remove Debugger stepinterrupt/halt frame"
+                context sender receiver storeOn:Stdout
+            ] ifFalse:[
+                'no context - dont know receiver' printNewline
+            ]
+        ].
+        (cmd == $i) ifTrue:[
+            valid := true.
+            context isNil ifTrue: [
+                context := self getContext
+            ].
+            context notNil ifTrue:[
+                "remove Debugger stepinterrupt/halt frame"
+                context sender receiver inspect
+            ] ifFalse:[
+                'no context - dont know receiver' printNewline
+            ]
+        ].
+        (cmd == $I) ifTrue:[
+            valid := true.
+            context isNil ifTrue: [
+                context := self getContext
+            ].
+            context notNil ifTrue:[
+                "remove Debugger stepinterrupt/halt frame"
+                self interpreterLoopWith:(context sender receiver)
+            ] ifFalse:[
+                'no context - dont know receiver' printNewline.
+                self interpreterLoopWith:nil
+            ]
+        ].
+	context := nil.
+        (cmd == $c) ifTrue:[valid := true. done := true].
+        (cmd == $s) ifTrue:[valid := true. done := true].
+        (cmd == $t) ifTrue:[valid := true. done := true].
+        (cmd == $a) ifTrue:[Smalltalk fatalAbort].
+        (cmd == $x) ifTrue:[Smalltalk exit].
+        valid ifFalse: [
+            'valid commands:' printNewline.
+            '   (c)ontinue'     printNewline.
+            '   (s)tep'         printNewline.
+            '   (t)race'        printNewline.
+            '   (p)rintContext' printNewline.
+            '   (r)eceiver'     printNewline.
+            '   (R)eceiver'     printNewline.
+            '   (i)nspect'      printNewline.
+            '   (I)nterpreter'  printNewline.
+            '   (a)bort'        printNewline.
+            '   (x)exit Smalltalk'  printNewline
+        ]
+    ].
+    ^ cmd
+!
+
+getCommand
+    |cmd c|
+    'MiniDebugger> ' print.
+    cmd := Character fromUser.
+    c := cmd.
+    [ c isEndOfLineCharacter ] whileFalse: [
+        c := Character fromUser
+    ].
+    ^ cmd
+!
+
+interpreterLoopWith:anObject
+    |line done|
+    'read-eval-print loop; exit with empty line' printNewline.
+    done := false.
+    [done] whileFalse:[
+        line := Stdin nextLine.
+        (line size == 0) ifTrue:[
+            done := true
+        ] ifFalse:[
+            (Compiler evaluate:line 
+                      receiver:anObject
+                     notifying:nil) printNewline
+        ]
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MiniIns.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,123 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#MiniInspector
+       instanceVariableNames:'inspectedObject'
+       classVariableNames:   ''
+       poolDictionaries:''
+       category:'System-Support'
+!
+
+MiniInspector comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+a primitive (non graphical) inspector for use on systems without
+graphics or when the real inspector dies.
+
+%W% %E%
+'!
+
+!MiniInspector class methodsFor:'instance creation'!
+
+openOn:anObject
+    |anInspector|
+    anInspector := (self new) initializeFor:anObject.
+    anInspector enter
+! !
+
+!MiniInspector methodsFor:'private'!
+
+initializeFor:anObject
+    inspectedObject := anObject.
+    ^self
+!
+
+enter
+    self commandLoop.
+    ^ nil
+!
+
+commandLoop
+    |cmd done valid|
+
+    'Inspector:' printNewline.
+    ' ' printNewline.
+    done := false.
+    [done] whileFalse:[
+        valid := false.
+        cmd := self getCommand.
+        (cmd == $0) ifTrue:[
+            valid := true.
+            self inspectInstvar:0 of:inspectedObject
+        ].
+        (cmd == $1) ifTrue:[
+            valid := true.
+            self inspectInstvar:1 of:inspectedObject
+        ].
+        (cmd == $2) ifTrue:[
+            valid := true.
+            self inspectInstvar:2 of:inspectedObject
+        ].
+        (cmd == $3) ifTrue:[
+            valid := true.
+            self inspectInstvar:3 of:inspectedObject
+        ].
+        (cmd == $i) ifTrue:[
+            valid := true.
+            self printInstVarsOf:inspectedObject
+        ].
+        (cmd == $p) ifTrue:[
+            valid := true.
+            inspectedObject printNewline
+        ].
+        (cmd == $q) ifTrue:[valid := true. done := true ].
+        valid ifFalse: [
+            'valid commands:' printNewline.
+            '   (i)nstvars'   printNewline.
+            '   (p)rint'      printNewline.
+            '   (1-9) inspect instvar'      printNewline.
+            '   (q)uit'       printNewline
+        ]
+    ].
+    ^ cmd
+!
+
+getCommand
+    |cmd c|
+    'inspector> ' print.
+    cmd := Character fromUser.
+    c := cmd.
+    [ c isEndOfLineCharacter ] whileFalse: [
+        c := Character fromUser
+    ].
+    ^ cmd
+!
+
+printInstVarsOf:anObject
+    |n|
+    n := anObject class instSize.
+    'number of instvars: ' print. n printNewline.
+    1 to:n do:[:i |
+        '  ' print. i print. ': ' print.
+        (anObject instVarAt:i) printNewline
+    ]
+!
+
+inspectInstvar:which of:anObject
+    which > (anObject class instSize) ifTrue:[
+        'invalid instvar' printNewline
+    ] ifFalse: [
+        (anObject instVarAt:which) inspect
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MiniInspector.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,123 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#MiniInspector
+       instanceVariableNames:'inspectedObject'
+       classVariableNames:   ''
+       poolDictionaries:''
+       category:'System-Support'
+!
+
+MiniInspector comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+a primitive (non graphical) inspector for use on systems without
+graphics or when the real inspector dies.
+
+%W% %E%
+'!
+
+!MiniInspector class methodsFor:'instance creation'!
+
+openOn:anObject
+    |anInspector|
+    anInspector := (self new) initializeFor:anObject.
+    anInspector enter
+! !
+
+!MiniInspector methodsFor:'private'!
+
+initializeFor:anObject
+    inspectedObject := anObject.
+    ^self
+!
+
+enter
+    self commandLoop.
+    ^ nil
+!
+
+commandLoop
+    |cmd done valid|
+
+    'Inspector:' printNewline.
+    ' ' printNewline.
+    done := false.
+    [done] whileFalse:[
+        valid := false.
+        cmd := self getCommand.
+        (cmd == $0) ifTrue:[
+            valid := true.
+            self inspectInstvar:0 of:inspectedObject
+        ].
+        (cmd == $1) ifTrue:[
+            valid := true.
+            self inspectInstvar:1 of:inspectedObject
+        ].
+        (cmd == $2) ifTrue:[
+            valid := true.
+            self inspectInstvar:2 of:inspectedObject
+        ].
+        (cmd == $3) ifTrue:[
+            valid := true.
+            self inspectInstvar:3 of:inspectedObject
+        ].
+        (cmd == $i) ifTrue:[
+            valid := true.
+            self printInstVarsOf:inspectedObject
+        ].
+        (cmd == $p) ifTrue:[
+            valid := true.
+            inspectedObject printNewline
+        ].
+        (cmd == $q) ifTrue:[valid := true. done := true ].
+        valid ifFalse: [
+            'valid commands:' printNewline.
+            '   (i)nstvars'   printNewline.
+            '   (p)rint'      printNewline.
+            '   (1-9) inspect instvar'      printNewline.
+            '   (q)uit'       printNewline
+        ]
+    ].
+    ^ cmd
+!
+
+getCommand
+    |cmd c|
+    'inspector> ' print.
+    cmd := Character fromUser.
+    c := cmd.
+    [ c isEndOfLineCharacter ] whileFalse: [
+        c := Character fromUser
+    ].
+    ^ cmd
+!
+
+printInstVarsOf:anObject
+    |n|
+    n := anObject class instSize.
+    'number of instvars: ' print. n printNewline.
+    1 to:n do:[:i |
+        '  ' print. i print. ': ' print.
+        (anObject instVarAt:i) printNewline
+    ]
+!
+
+inspectInstvar:which of:anObject
+    which > (anObject class instSize) ifTrue:[
+        'invalid instvar' printNewline
+    ] ifFalse: [
+        (anObject instVarAt:which) inspect
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NPExtStr.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,107 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+	      All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+ExternalStream subclass:#NonPositionableExternalStream
+       instanceVariableNames:'typeIfStandard'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Streams-External'
+!
+
+NonPositionableExternalStream comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+	      All Rights Reserved
+
+this one stands all non-positionable external streams; there are
+terminal streams, pipe streams etc.
+
+%W% %E%
+'!
+
+%{
+#include <stdio.h>
+%}
+
+!NonPositionableExternalStream class methodsFor:'instance creation'!
+
+forStdin
+    ^ self basicNew initializeForStdin
+!
+
+forStdout
+    ^ self basicNew initializeForStdout
+!
+
+forStderr
+    ^ self basicNew initializeForStderr
+! !
+
+!NonPositionableExternalStream methodsFor:'private'!
+
+initializeForStdin
+    mode := #readonly.
+    typeIfStandard := #Stdin.
+%{
+    _INST(filePointer) = MKOBJ(stdin);
+%}
+!
+
+initializeForStdout
+    mode := #readwrite.
+    unBuffered := true.
+    typeIfStandard := #Stdout.
+%{
+    _INST(filePointer) = MKOBJ(stdout);
+%}
+!
+
+initializeForStderr
+    mode := #readwrite.
+    unBuffered := true.
+    typeIfStandard := #Stderr.
+%{
+    _INST(filePointer) = MKOBJ(stderr);
+%}
+!
+
+reOpen
+    "if I am one of the standard streams, reopen is easy"
+
+'reopen NPExtStr' printNewline.
+    (typeIfStandard == #Stdin) ifTrue:[
+	^ self initializeForStdin
+    ].
+    (typeIfStandard == #Stdout) ifTrue:[
+	^ self initializeForStdout
+    ].
+    (typeIfStandard == #Stderr) ifTrue:[
+	^ self initializeForStderr
+    ].
+    ^ super reOpen
+! !
+
+!NonPositionableExternalStream methodsFor:'error handling'!
+
+errorNotPositionable
+    ^ self error:'positioning not allowd'
+! !
+
+!NonPositionableExternalStream methodsFor:'positioning'!
+
+position
+    ^ 0
+!
+
+position:anInteger
+    self errorNotPositionable
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NonPositionableExternalStream.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,107 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+	      All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+ExternalStream subclass:#NonPositionableExternalStream
+       instanceVariableNames:'typeIfStandard'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Streams-External'
+!
+
+NonPositionableExternalStream comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+	      All Rights Reserved
+
+this one stands all non-positionable external streams; there are
+terminal streams, pipe streams etc.
+
+%W% %E%
+'!
+
+%{
+#include <stdio.h>
+%}
+
+!NonPositionableExternalStream class methodsFor:'instance creation'!
+
+forStdin
+    ^ self basicNew initializeForStdin
+!
+
+forStdout
+    ^ self basicNew initializeForStdout
+!
+
+forStderr
+    ^ self basicNew initializeForStderr
+! !
+
+!NonPositionableExternalStream methodsFor:'private'!
+
+initializeForStdin
+    mode := #readonly.
+    typeIfStandard := #Stdin.
+%{
+    _INST(filePointer) = MKOBJ(stdin);
+%}
+!
+
+initializeForStdout
+    mode := #readwrite.
+    unBuffered := true.
+    typeIfStandard := #Stdout.
+%{
+    _INST(filePointer) = MKOBJ(stdout);
+%}
+!
+
+initializeForStderr
+    mode := #readwrite.
+    unBuffered := true.
+    typeIfStandard := #Stderr.
+%{
+    _INST(filePointer) = MKOBJ(stderr);
+%}
+!
+
+reOpen
+    "if I am one of the standard streams, reopen is easy"
+
+'reopen NPExtStr' printNewline.
+    (typeIfStandard == #Stdin) ifTrue:[
+	^ self initializeForStdin
+    ].
+    (typeIfStandard == #Stdout) ifTrue:[
+	^ self initializeForStdout
+    ].
+    (typeIfStandard == #Stderr) ifTrue:[
+	^ self initializeForStderr
+    ].
+    ^ super reOpen
+! !
+
+!NonPositionableExternalStream methodsFor:'error handling'!
+
+errorNotPositionable
+    ^ self error:'positioning not allowd'
+! !
+
+!NonPositionableExternalStream methodsFor:'positioning'!
+
+position
+    ^ 0
+!
+
+position:anInteger
+    self errorNotPositionable
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Number.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,275 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+ArithmeticValue subclass:#Number
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Magnitude-Numbers'
+!
+
+Number comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+abstract superclass for all kinds of numbers
+
+%W% %E%
+'!
+
+! Number methodsFor:'converting' !
+
+@ aNumber
+    "return a Point with the receiver as x-coordinate and the argument
+     as y-coordinate"
+
+%{  /* NOCONTEXT */
+
+    extern char *newNextPtr, *newEndPtr;
+
+    if (_CanDoQuickNew(sizeof(struct point))) {
+        OBJ newPoint;
+
+        _qCheckedAlignedNew(newPoint, sizeof(struct point), __context);
+        _InstPtr(newPoint)->o_class = Point;
+        _PointInstPtr(newPoint)->p_x = self;
+        _PointInstPtr(newPoint)->p_y = aNumber;
+	/* no store check needed - its definitely in newSpace */
+        RETURN ( newPoint );
+    }
+%}
+.
+    ^ Point x:self y:aNumber
+!
+
+asPoint
+    "return a new Point with the receiver as all coordinates;  
+     often used to supply the same value in two dimensions, as with 
+     symmetrical gridding or scaling."
+
+%{  /* NOCONTEXT */
+
+    extern char *newNextPtr, *newEndPtr;
+
+    if (_CanDoQuickNew(sizeof(struct point))) {
+        OBJ newPoint;
+
+	_qCheckedAlignedNew(newPoint, sizeof(struct point), __context);
+	_InstPtr(newPoint)->o_class = Point;
+	_PointInstPtr(newPoint)->p_x = self;
+	_PointInstPtr(newPoint)->p_y = self;
+	/* no store check needed - its definitely in newSpace */
+	RETURN ( newPoint );
+    }
+%}
+.
+    ^ Point x:self y:self
+!
+
+degreesToRadians
+    "interpreting the receiver as radians, return the degrees"
+
+    ^ (self * (Float pi)) / 180.0
+!
+
+radiansToDegrees
+    "interpreting the receiver as degrees, return the radians"
+
+    ^ (self * 180.0) / (Float pi)
+!
+
+coerce:aNumber
+    ^ self subclassResponsibility
+!
+
+generality
+    ^ 40
+!
+
+retry:aSymbol coercing:aNumber
+    "Arithmetic represented by the binary operator, aSymbol,
+    could not be performed with the receiver and the argument,
+    aNumber, because of the differences in representation.  Coerce either
+    the receiver or the argument, depending on which has higher generality, and
+    try again.  If the operation is comapre for same value (=), return false if
+    the argument is not a Number. 
+    If the generalities are the same, create an error message."
+
+    |myGenerality otherGenerality|
+
+    (aSymbol == #=) ifTrue:[
+        (aNumber respondsTo:#generality) ifFalse:[^ false]
+    ] ifFalse:[
+        (aNumber respondsTo:#generality) ifFalse:[
+            self error:'retry:coercing: argument is not a number'.
+            ^ self
+        ]
+    ].
+    myGenerality := self generality.
+    otherGenerality := aNumber generality.
+    (myGenerality > otherGenerality) ifTrue:[
+        ^ self perform:aSymbol with:(self coerce:aNumber)
+    ].
+    (myGenerality < otherGenerality) ifTrue:[
+        ^ (aNumber coerce:self) perform:aSymbol with:aNumber
+    ].
+    self error:'retry:coercing: oops - same generality'
+! !
+
+!Number methodsFor:'printing & storing'!
+
+storeString
+    ^ self printString
+! !
+
+!Number methodsFor:'intervals'!
+
+to:stop
+    "return an interval from receiver up to the argument, incrementing by 1"
+
+    ^ Interval from:self to:stop
+!
+
+to:stop by:step
+    "return an interval from receiver up to the argument, incrementing by step"
+
+    ^ Interval from:self to:stop by:step
+! !
+
+!Number methodsFor:'iteration'!
+
+timesRepeat:aBlock
+    "evaluate the argument, aBlock self times"
+
+    |count|
+
+    count := self.
+    [count > 0] whileTrue:[
+        aBlock value.
+        count := count - 1
+    ]
+!
+
+to:stop do:aBlock
+    "create an interval from the receiver up to the argument, incrementing by 1.
+     For each element of the interval, evaluate aBlock"
+
+    |tmp|
+
+    tmp := self.
+    [tmp <= stop] whileTrue:[
+        aBlock value:tmp.
+        tmp := tmp+1
+    ]
+!
+
+to:stop by:incr do:aBlock
+    "create an interval from the receiver up to the argument stop, incrementing
+     by step. For each element of the interval, evaluate aBlock"
+
+    |tmp|
+
+    tmp := self.
+    (incr > 0) ifTrue:[
+        [tmp <= stop] whileTrue:[
+            aBlock value:tmp.
+            tmp := tmp+incr
+        ]
+    ] ifFalse:[
+        [tmp >= stop] whileTrue:[
+            aBlock value:tmp.
+            tmp := tmp+incr
+        ]
+    ]
+! !
+
+!Number class methodsFor:'private'!
+
+readMantissaFrom:aStream radix:radix
+    "helper for readFrom: -
+     return the mantissa from the (character-)stream aStream;
+     no whitespace-skipping; error if no number available"
+
+    |nextChar value factor|
+
+    value := 0.0.
+    factor := 1.0 / radix.
+    nextChar := aStream peek.
+    [nextChar notNil and:[nextChar isDigitRadix:radix]] whileTrue:[
+        value := value + (nextChar digitValue * factor).
+        factor := factor / radix.
+        nextChar := aStream nextPeek
+    ].
+    ^ value
+! !
+
+!Number class methodsFor:'instance creation'!
+
+readFrom:aStream
+    "return the next Number from the (character-)stream aStream;
+     skipping all whitespace first; return nil if no number"
+
+    |nextChar radix value negative signExp|
+
+    nextChar := aStream skipSeparators.
+    (nextChar == $-) ifTrue:[
+        negative := true.
+        nextChar := aStream nextPeek
+    ] ifFalse:[
+        negative := false
+    ].
+    nextChar isDigit ifFalse:[ ^ nil].
+    value := Integer readFrom:aStream radix:10.
+    nextChar := aStream peek.
+    ((nextChar == $r) or:[ nextChar == $R]) ifTrue:[
+        aStream next.
+        radix := value.
+        value := Integer readFrom:aStream radix:radix
+    ] ifFalse:[
+        radix := 10
+    ].
+    (nextChar == $.) ifTrue:[
+        nextChar := aStream nextPeek.
+        (nextChar notNil and:[nextChar isDigitRadix:radix]) ifTrue:[
+            value := value asFloat + (Number readMantissaFrom:aStream
+                                                        radix:radix).
+            nextChar := aStream peek
+        ]
+    ].
+    ((nextChar == $e) or:[nextChar == $E]) ifTrue:[
+        nextChar := aStream nextPeek.
+        signExp := 1.
+        (nextChar == $+) ifTrue:[
+            nextChar := aStream nextPeek
+        ] ifFalse:[
+            (nextChar == $-) ifTrue:[
+                nextChar := aStream nextPeek.
+                signExp := -1
+            ]
+        ].
+        (nextChar notNil and:[(nextChar isDigitRadix:radix)]) ifTrue:[
+            value := value asFloat 
+                     * (10.0 raisedToInteger:
+                                ((Integer readFrom:aStream 
+                                             radix:radix) * signExp))
+        ]
+    ].
+    negative ifTrue:[
+        ^ value negated
+    ].
+    ^ value
+
+    "Number readFromString:'54.32e-01'"
+    "Number readFromString:'12345678901234567890'"
+    "Number readFromString:'16rAAAAFFFFAAAAFFFF'"
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ObjMem.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,367 @@
+"
+ COPYRIGHT (c) 1992-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#ObjectMemory
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'System-Support'
+!
+
+ObjectMemory comment:'
+
+COPYRIGHT (c) 1992 -93 by Claus Gittinger
+             All Rights Reserved
+
+This class contains access methods to the system memory -
+in previous versions this stuff used to be in the Smalltalk class.
+It has been separated for better overall structure.
+
+%W% %E%
+'!
+
+!ObjectMemory class methodsFor:'cache management'!
+
+flushInlineCachesForClass:aClass
+    "flush inlinecaches for calls to aClass"
+
+%{  /* NOCONTEXT */
+    __flushInlineCachesFor(aClass);
+%}
+!
+
+flushInlineCachesWithArgs:nargs
+    "flush inlinecaches for calls with nargs arguments"
+
+%{  /* NOCONTEXT */
+    __flushInlineCaches(_intVal(nargs));
+%}
+!
+
+flushInlineCachesFor:aClass withArgs:nargs
+    "flush inlinecaches for calls to aClass with nargs arguments"
+
+%{  /* NOCONTEXT */
+    __flushInlineCachesForAndNargs(aClass, _intVal(nargs));
+%}
+!
+
+flushInlineCaches
+    "flush all inlinecaches"
+
+%{  /* NOCONTEXT */
+    __flushAllInlineCaches();
+%}
+!
+
+flushMethodCacheFor:aClass
+    "flush the method cache for sends to aClass"
+
+%{  /* NOCONTEXT */
+    __flushMethodCacheFor(aClass);
+%}
+!
+
+flushMethodCache
+    "flush the method cache"
+
+%{  /* NOCONTEXT */
+    __flushMethodCache();
+%}
+!
+
+flushCaches
+    "flush method and inline caches"
+
+%{  /* NOCONTEXT */
+    __flushMethodCache();
+    __flushAllInlineCaches();
+%}
+! !
+
+!ObjectMemory class methodsFor:'enumeration'!
+
+allObjectsDo:aBlock
+    "evaluate the argument, aBlock for all objects in the system"
+%{
+#ifdef THIS_CONTEXT
+    __allObjectsDo(&aBlock);
+#else
+    __allObjectsDo(&aBlock, __context);
+#endif
+%}
+! !
+
+!ObjectMemory class methodsFor:'queries'!
+
+newSpaceUsed
+    "return the number of bytes allocated for new objects"
+
+%{  /* NOCONTEXT */
+    RETURN ( _MKSMALLINT(__newSpaceUsed()) );
+%}
+    "ObjectMemory newSpaceUsed"
+!
+
+oldSpaceUsed
+    "return the number of bytes allocated for old objects"
+
+%{  /* NOCONTEXT */
+    RETURN ( _MKSMALLINT(__oldSpaceUsed()) );
+%}
+    "ObjectMemory oldSpaceUsed"
+!
+
+bytesUsed
+    "return the number of bytes allocated for objects -
+     this number is not exact, since some objects may be dead"
+
+%{  /* NOCONTEXT */
+    RETURN ( _MKSMALLINT(__oldSpaceUsed() + __newSpaceUsed()) );
+%}
+    "ObjectMemory bytesUsed"
+!
+
+numberOfObjects
+    "return the number of objects in the system"
+
+    |tally|
+
+    tally := 0.
+    self allObjectsDo:[:obj | tally := tally + 1].
+    ^ tally
+
+    "ObjectMemory numberOfObjects"
+!
+
+printReferences:anObject
+    "debugging: print referents to anObject"
+
+%{
+    _printRefChain(__context, anObject);
+%}
+!
+
+whoReferences:anObject
+    "return a collection of objects referencing the argument, anObject"
+
+    |aCollection|
+
+    aCollection := IdentitySet new.
+    self allObjectsDo:[:o |
+        (o references:anObject) ifTrue:[
+            aCollection add:o
+        ]
+    ].
+    (aCollection size == 0) ifTrue:[
+        "actually this cannot happen - there is always one"
+        ^ nil
+    ].
+    ^ aCollection
+! !
+
+!ObjectMemory class methodsFor:'garbage collector control'!
+
+garbageCollect
+    "search for and free garbage in the oldSpace
+     (newSpace is cleaned automatically)
+     - can take a long time if paging is involved
+     - when no paging is involved, its faster than I thought :-)"
+%{
+    __garbageCollect(__context);
+%}
+
+    "ObjectMemory garbageCollect"
+!
+
+scavenge
+    "for debugging only - collect newspace stuff"
+%{
+    nonTenuringScavenge(__context);
+%}
+
+    "ObjectMemory scavenge"
+!
+
+tenure
+    "forcae all new stuff into old-space"
+%{
+    tenure(__context);
+%}
+
+    "ObjectMemory tenure"
+!
+
+markAndSweep
+    "mark/sweep garbage collector"
+
+%{
+    markAndSweep(__context);
+%}
+
+    "ObjectMemory markAndSweep"
+!
+
+gcStep
+    "one incremental garbage collect step"
+%{
+    incrGCstep(__context);
+%}
+!
+
+turnOffGarbageCollector
+    "turn off garbage collector.
+     this method is somewhat dangerous: if collector is turned off,
+     and too many objects are created, the system may run into trouble.
+     Use this only for measurement purposes or when realtime behavior
+     is required for a limited time period. No waranty"
+%{
+    allocForceSpace(0);
+%}
+!
+
+turnOnGarbageCollector
+    "turn it on again"
+
+%{
+    allocForceSpace(1);
+%}
+! !
+
+!ObjectMemory class methodsFor:'system management'!
+
+loadClassBinary:aClassName
+    "find the object file for aClassName and -if found - load it;
+     this one loads precompiled object files"
+
+    |fName newClass upd|
+
+    fName := self fileNameForClass:aClassName.
+    fName notNil ifTrue:[
+        upd := Class updateChanges:false.
+        [
+            self loadBinary:(fName , '.o')
+        ] valueNowOrOnUnwindDo:[
+            Class updateChanges:upd
+        ].
+        newClass := self at:(aClassName asSymbol).
+        (newClass notNil and:[newClass implements:#initialize]) ifTrue:[
+            newClass initialize
+        ]
+    ]
+!
+
+snapShot
+    "create a snapshot"
+
+    ImageName isNil ifTrue:[
+        ImageName := 'st.img'
+    ].
+    self snapShotOn:ImageName
+
+    "ObjectMemory snapShot"
+!
+
+snapShotOn:aFileName
+    "create a snapshot in the given file"
+
+    "give others a chance to fix things"
+    self changed:#save.
+%{
+    OBJ __snapShotOn();
+
+    if (_isString(aFileName)) {
+        RETURN ( __snapShotOn(__context, _stringVal(aFileName)) );
+    }
+%}
+.
+    ^ self primitiveFailed
+
+    "ObjectMemory snapShotOn:'myimage.img'"
+!
+
+applicationImageOn:aFileName for:startupClass selector:startupSelector
+    "create a snapshot which will come up without any views 
+     but starts up an application by sending startupClass the startupSelector"
+
+    |viewsKnown savedIdleBlocks savedTimeoutBlocks savedTranscript|
+
+    viewsKnown := Display knownViews.
+    savedIdleBlocks := Display idleBlocks.
+    savedTimeoutBlocks := Display timeOutBlocks.
+    savedTranscript := Transcript.
+
+    "a kludge: save image with modified knownViews ..."
+
+    Display knownViews:nil.
+    Display idleBlocks:nil.
+    Display timeOutBlocks:nil.
+    Transcript := Stderr.
+    StartupClass := startupClass.
+    StartupSelector := startupSelector.
+
+    self snapShotOn:aFileName.
+
+    StartupClass := nil.
+    StartupSelector := nil.
+    Transcript := savedTranscript.
+    Display knownViews:viewsKnown.
+    Display idleBlocks:savedIdleBlocks.
+    Display timeOutBlocks:savedTimeoutBlocks
+
+    "ObjectMemory applicationImageOn:'draw.img' for:DrawTool selector:#start"
+    "ObjectMemory applicationImageOn:'pm.img' for:PMSimulator selector:#start"
+!
+
+minimumApplicationImageOn:aFileName for:startupClass selector:startupSelector
+    "create a snapshot which will come up without any views 
+     but starts up an application by sending startupClass the startupSelector.
+     All unneeded info is stripped from the saved image."
+
+    "create a temporary image, for continuation"
+    self snapShotOn:'temp.img'.
+
+    Display knownViews do:[:aView |
+        aView notNil ifTrue:[
+            aView superView isNil ifTrue:[
+                aView destroy
+            ]
+        ]
+    ].
+
+    self stripImage.
+
+    self applicationImageOn:aFileName for:startupClass selector:startupSelector.
+
+    "continue in old image"
+
+    OperatingSystem exec:(Arguments at:1)
+           withArguments:#('smalltalk' '-i' 'temp.img') , (Arguments copyFrom:2)
+
+    "ObjectMemory minimumApplicationImageOn:'clock1.img' for:Clock selector:#start"
+    "ObjectMemory applicationImageOn:'clock2.img' for:Clock selector:#start"
+!
+
+stripImage
+    "remove all unneeded stuff from the image - much more is possible here"
+
+    "remove all class comments"
+
+    Smalltalk allClassesDo:[:aClass |
+        aClass setComment:nil.
+        aClass methodDictionary do:[:aMethod |
+            aMethod source:''.
+            aMethod category:#none 
+        ]
+    ].
+    self garbageCollect
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Object.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,1863 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Object
+       instanceVariableNames:''
+       classVariableNames:'ErrorSignal HaltSignal 
+                           MessageNotUnderstoodSignal UserInterruptSignal
+                           RecursionInterruptSignal ExceptionInterruptSignal
+                           SubscriptOutOfBoundSignal NonIntegerIndexSignal
+                           InformationSignal'
+       poolDictionaries:''
+       category:'Kernel-Objects'
+!
+
+Object comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+Class Object is the superclass of all other classes. Protocol common to
+all objects is defined here.
+Also some utility stuff (like notify) and error handling is implemented here.
+
+%W% %E%
+'!
+
+Smalltalk at:#ErrorRecursion put:false!
+Smalltalk at:#ErrorActive put:false!
+Smalltalk at:#ErrorHandler put:nil!
+Smalltalk at:#Dependencies put:nil!
+Smalltalk at:#SystemNotifier put:nil!
+Smalltalk at:#SystemWarningBox put:nil!
+Smalltalk at:#SystemInfoBox put:nil!
+Smalltalk at:#SystemConfirmer put:nil!
+
+!Object class methodsFor:'initialization'!
+
+initialize
+    "called only once - initialize signals"
+
+    ErrorSignal isNil ifTrue:[
+        ErrorSignal := (Signal new) mayProceed:true.
+        ErrorSignal notifierString:'error'.
+
+        HaltSignal := (Signal new) mayProceed:true.
+        HaltSignal notifierString:'halt encountered'.
+
+        MessageNotUnderstoodSignal := (Signal new) mayProceed:true.
+        MessageNotUnderstoodSignal notifierString:'message not understood'.
+
+        UserInterruptSignal := (Signal new) mayProceed:true.
+        UserInterruptSignal notifierString:'user Interrupt'.
+
+        RecursionInterruptSignal := (Signal new) mayProceed:false.
+        RecursionInterruptSignal notifierString:'recursion interrupt'.
+
+        ExceptionInterruptSignal := (Signal new) mayProceed:true.
+        ExceptionInterruptSignal notifierString:'exception Interrupt'.
+
+        SubscriptOutOfBoundSignal := (Signal new) mayProceed:false.
+        SubscriptOutOfBoundSignal notifierString:'subscript out of bounds'.
+
+        NonIntegerIndexSignal := (Signal new) mayProceed:false.
+        NonIntegerIndexSignal notifierString:'index must be integer'.
+
+        InformationSignal := (Signal new) mayProceed:true.
+        InformationSignal notifierString:'information'.
+
+        Dependencies := IdentityDictionary new.
+    ]
+
+    "Object initialize"
+! !
+
+!Object class methodsFor:'signal access'!
+
+errorSignal
+    "return the signal used for error/error: - messages"
+
+    ^ ErrorSignal
+!
+
+haltSignal
+    "return the signal used for halt/halt: - messages"
+
+    ^ HaltSignal
+!
+
+messageNotUnderstoodSignal
+    "return the signal used for doesNotUnderstand: - messages"
+
+    ^ MessageNotUnderstoodSignal
+!
+
+userInterruptSignal
+    "return the signal used for ^C interrupts"
+
+    ^ UserInterruptSignal
+!
+
+recursionInterruptSignal 
+    "return the signal used for recursion overflow reporting"
+
+    ^ RecursionInterruptSignal
+!
+
+exceptionInterruptSignal
+    "return the signal used for exception (display errors) reporting"
+
+    ^ ExceptionInterruptSignal
+!
+
+subscriptOutOfBoundSignal
+    "return the signal used for subscript error reporting"
+
+    ^ SubscriptOutOfBoundSignal
+!
+
+nonIntegerIndexSignal 
+    "return the signal used for bad subscript error reporting"
+
+    ^ NonIntegerIndexSignal
+!
+
+informationSignal 
+    "return the signal used for informations"
+
+    ^ InformationSignal
+! !
+
+!Object methodsFor:'initialization'!
+
+initialize
+    "just to ignore initialize to objects which do not need it"
+
+    ^ self
+! !
+
+!Object methodsFor:'instance creation'!
+
+readFromString:aString
+    "create an object from its printed representation"
+
+    ^ self readFrom:(ReadStream on:aString)
+!
+
+readFrom:aStream
+    "read an objects printed representation from the argument,
+     aStream and return it."
+
+    |newObject|
+    newObject := Compiler evaluate:aStream.
+    (newObject isKindOf:self) ifFalse:[
+        self error:('expected ' , self name)
+    ].
+    ^ newObject
+! !
+
+!Object methodsFor:'system primitives'!
+
+become:anotherObject
+    "make all references to the receiver become references to anotherObject and vice-versa.
+     This may be an expensive (i.e. slow) operation, since in the worst case, the whole memory
+     has to be searched for references to the two objects. In general, using become: should be
+     avoided if possible since it may produce many strange effects.
+     This method fails, if the receiver or the argument is a SmallInteger or nil, or is a context
+     of a living method (i.e. one that has not already returned).
+     (notice that become: is not used by the system - the Collection classes have been rewritten
+      to not use it.)"
+%{
+    if (primBecome(self, anotherObject))
+        RETURN ( self );
+%}
+.
+    self primitiveFailed
+!
+
+address
+    "return the core address as an integer 
+     - since objects may move around the returned value is invalid after the
+     next scavenge/collect, therefore use only for debugging."
+
+%{  /* NOCONTEXT */
+
+    if (! _isNonNilObject(self)) {
+        RETURN ( nil );
+    }
+    if ((_qSpace(self) != OLDSPACE) && (_qSpace(self) != STACKSPACE)) {
+        RETURN ( nil );
+    }
+    RETURN ( _MKSMALLINT( (int)self ) );
+%}
+! !
+
+!Object methodsFor:'queries'!
+
+size
+    "return the number of the receivers indexed instance variables;
+     this method may be redefined in subclasses"
+
+    ^ self basicSize
+!
+
+basicSize
+    "return the number of the receivers indexed instance variables
+     this method should NOT be redefined in any subclass"
+
+%{  /* NOCONTEXT */
+
+    register int nbytes;
+    register OBJ myClass;
+
+    /*
+     * notice the missing test for self beeing a nonNilObject -
+     * this can be done since basicSize is defined both in UndefinedObject
+     * and SmallInteger
+     */
+    myClass = _qClass(self);
+    nbytes = _qSize(self) 
+              - OHDR_SIZE 
+              - _intVal(_ClassInstPtr(myClass)->c_ninstvars) * sizeof(OBJ);
+
+    switch (_intVal(_ClassInstPtr(myClass)->c_flags) & ARRAYMASK) {
+        case BYTEARRAY:
+            RETURN ( _MKSMALLINT(nbytes / sizeof(char)) );
+
+        case WORDARRAY:
+            RETURN ( _MKSMALLINT(nbytes / sizeof(short)) );
+
+        case LONGARRAY:
+            RETURN ( _MKSMALLINT(nbytes / sizeof(long)) );
+
+        case FLOATARRAY:
+            RETURN ( _MKSMALLINT(nbytes / sizeof(float)) );
+
+        case DOUBLEARRAY:
+            RETURN ( _MKSMALLINT(nbytes / sizeof(double)) );
+
+        case WKPOINTERARRAY:
+        case POINTERARRAY:
+            RETURN ( _MKSMALLINT(nbytes / sizeof(OBJ)) );
+    }
+%}
+.
+    ^ 0
+!
+
+objectSize
+    "return the size of the receiver in bytes - for debugging only"
+
+%{  /* NOCONTEXT */
+
+    RETURN ( _isNonNilObject(self) ? _MKSMALLINT(_qSize(self)) 
+                                   : _MKSMALLINT(0) )
+%}
+!
+
+isVariable
+    "return true if the receiver has indexed instance variables,
+     false otherwise"
+
+    ^ self class isVariable
+!
+
+isFixedSize
+    "return true if the receiver cannot grow - this will vanish once things
+     like Array and String learn how to grow ..."
+
+    ^ true
+!
+
+class
+    "return the receivers class"
+
+%{  /* NOCONTEXT */
+
+    RETURN ( _Class(self) );
+%}
+!
+
+species
+    "return a class which is similar to (or the same as) the receivers class.
+     This is used to create an appropriate object when creating derived
+     copies in the Collection classes (sometimes redefined)."
+
+    ^ self class
+!
+
+yourself
+    "return the receiver - used for cascades to return self at the end"
+
+    ^ self
+!
+
+isBehavior
+    "return true, if the receiver is some kind of class (i.e. behavior);
+     false is returned here - the method is redefined in Behavior"
+
+    ^ false
+!
+
+isMeta
+    "return true, if the receiver is some kind of metaclass;
+     false is returned here - the method is redefined in Metaclass"
+
+    ^ false
+!
+
+isBlock
+    "return true, iff the receiver is some kind of Block;
+     false returned here - the method is redefined in Block."
+
+    ^ false
+!
+
+isContext
+    "return true, iff the receiver is some kind of Context;
+     false returned here - the method is redefined in Context."
+
+    ^ false
+!
+
+isStream
+    "return true, if the receiver is some kind of stream;
+     false is returned here - the method is redefined in Stream"
+
+    ^ false
+!
+
+isInteger
+    "return true, if the receiver is some kind of integer number;
+     false is returned here - the method is redefined in Integer"
+
+    ^ false
+!
+
+respondsToArithmetic
+    "return true, if the receiver responds to arithmetic messages.
+     false is returned here - the method is redefined in ArithmeticValue"
+
+    ^ false
+!
+
+isMemberOf:aClass
+    "return true, if the receiver is an instance of aClass, false otherwise"
+
+    ^ (self class) == aClass
+!
+
+isKindOf:aClass
+    "return true, if the receiver is an instance of aClass or one of its
+     subclasses, false otherwise"
+
+%{  /* NOCONTEXT */
+
+    register OBJ thisClass;
+
+    thisClass = _Class(self);
+    while (thisClass != nil) {
+        if (thisClass == aClass) {
+            RETURN ( true );
+        }
+        thisClass = _ClassInstPtr(thisClass)->c_superclass;
+    }
+%}
+.
+    ^ false
+!
+
+respondsTo:aSelector
+    "return true, if the receiver implements a method with selector equal
+     to aSelector; i.e. if there is a method for aSelector in either the
+     receivers class or one of its superclasses"
+
+%{  /* NOCONTEXT */
+
+    extern OBJ lookup();
+
+    if (lookup(_Class(self), aSelector) == nil) {
+        RETURN ( false );
+    }
+    RETURN ( true );
+%}
+.
+    ^ self class canUnderstand:aSelector
+!
+
+references:anObject
+    "return true, if the receiver refers to the argument, anObject.
+     - for debugging only"
+
+    |myClass 
+     numInst "{ Class: SmallInteger }" |
+
+    myClass := self class.
+
+    "check the class"
+    (myClass == anObject) ifTrue:[^ true].
+
+    "check the instance variables"
+    numInst := myClass instSize.
+    1 to:numInst do:[:i | 
+        ((self instVarAt:i) == anObject) ifTrue:[^ true]
+    ].
+
+    "check the indexed variables"
+    myClass isVariable ifTrue:[
+        numInst := myClass basicSize.
+        1 to:numInst do:[:i | 
+            ((self basicAt:i) == anObject) ifTrue:[^ true]
+        ]
+    ].
+    ^ false
+!
+
+allOwners
+    "return a collection of all objects referencing the receiver"
+
+    ^ ObjectMemory whoReferences:self
+! !
+
+!Object methodsFor:'misc'!
+
+-> anObject
+    "return an association with the receiver as key and
+     the argument as value"
+
+    ^ Association key:self value:anObject
+! !
+
+!Object methodsFor:'copying'!
+
+copy
+    "return a copy of the receiver - defaults to shallowcopy here"
+
+    ^ self shallowCopy
+!
+
+shallowCopy
+    "return a copy of the object with shared subobjects i.e. shallow copies
+     of its instance objects.
+     This method does NOT handle cycles"
+
+    |myClass aCopy 
+     sz "{ Class: SmallInteger }" |
+
+    myClass := self class.
+    myClass isVariable ifTrue:[
+        sz := self basicSize.
+        aCopy := myClass basicNew:sz.
+
+        "copy the indexed variables"
+        1 to:sz do:[:i | 
+            aCopy basicAt:i put:(self basicAt:i) 
+        ]
+    ] ifFalse:[
+        aCopy := myClass basicNew
+    ].
+
+    "copy the instance variables"
+    sz := myClass instSize.
+    1 to:sz do:[:i | 
+        aCopy instVarAt:i put:(self instVarAt:i) 
+    ].
+
+    ^ aCopy
+!
+
+deepCopy
+    "return a copy of the object with all subobjects also copied.
+     This method does NOT handle cycles"
+
+    |myClass aCopy 
+     sz "{ Class: SmallInteger }" |
+
+    myClass := self class.
+    myClass isVariable ifTrue:[
+        sz := self basicSize.
+        aCopy := myClass basicNew:sz.
+
+        "copy the indexed variables"
+        1 to:sz do:[:i | 
+            aCopy basicAt:i put:((self basicAt:i) deepCopy)
+        ]
+    ] ifFalse:[
+        aCopy := myClass basicNew 
+    ].
+
+    "copy the instance variables"
+    sz := myClass instSize.
+    1 to:sz do:[:i | 
+        aCopy instVarAt:i put:((self instVarAt:i) deepCopy)
+    ].
+
+    ^ aCopy
+! !
+
+!Object methodsFor:'comparing'!
+
+== anObject
+    "return true, if the receiver and the arg are the same object"
+
+%{  /* NOCONTEXT */
+
+    RETURN ( (self == anObject) ? true : false );
+%}
+!
+
+~~ anObject
+    "return true, if the receiver and the arg are not the same object"
+
+%{  /* NOCONTEXT */
+
+    RETURN ( (self == anObject) ? false : true );
+%}
+!
+
+= anObject
+    "return true, if the receiver and the arg have the same structure"
+
+    ^ self == anObject
+!
+
+~= anObject
+    "return true, if the receiver and the arg do not have the same structure"
+
+    ^ (self = anObject) not
+!
+
+isNil
+    "return true, if the receiver is nil"
+
+    ^ false
+!
+
+notNil
+    "return true, if the receiver is not nil"
+
+    ^ true
+!
+
+hash
+    "return an Integer useful as a hash key for the receiver.
+     This hash should return same values for objects with same
+     contents (i.e. use this to hash on structure)"
+
+    ^ self identityHash
+!
+
+identityHash
+    "return an Integer useful as a hash key for the receiver.
+     This hash should return same values for the same object (i.e. use
+     this to hash on identity of objects).
+
+     We cannot use the Objects address (as other smalltalks do) since
+     no object-table exists and the hashval must not change when objects
+     are moved by the collector. Therefore we assign each object a unique
+     Id in the object header itself as its hashed upon.
+     (luckily we have 12 bits spare to do this - unluckily its only 12 bits).
+     To expand the range a bit, these 12 hashBits are concatenated to the
+     receivers class hashBits, to form a 24bit hashvalue (which will not
+     help, if many objects of the same class are hashed upon ...)"
+
+%{  /* NOCONTEXT */
+
+    REGISTER int v1, v2;
+    static int nextHash = 0;
+    OBJ cls;
+
+    if (_isObject(self)) {
+        v1 = ((self->o_age & ~AGE_MASK) >> 5) << 8;
+        v1 |= (self->o_hashLow);
+        if (v1 == 0) {
+            v1 = nextHash++;
+            if (v1 == 0)
+                v1 = nextHash++;
+            self->o_hashLow = v1 & 0xFF;
+            self->o_age |= (v1 >> 8) << 5;
+        }
+
+        cls = _qClass(self);
+        v2 = ((cls->o_age & ~AGE_MASK) >> 5) << 8;
+        v2 |= (cls->o_hashLow);
+        if (v2 == 0) {
+            v2 = nextHash++;
+            if (v2 == 0)
+                v2 = nextHash++;
+            cls->o_hashLow = v2 & 0xFF;
+            cls->o_age |= (v2 >> 8) << 5;
+        }
+        RETURN ( _MKSMALLINT((v2<<12) | v1) );
+    }
+%}
+.
+    ^ 0
+! !
+
+!Object methodsFor:'interrupt handling'!
+
+userInterrupt
+    "user (^c) interrupt - enter debugger"
+
+    self error:'user Interrupt'
+!
+
+ioInterrupt
+    "io (SIGIO/SIGPOLL) interrupt and no handler - enter debugger"
+
+    self error:'user Interrupt'
+!
+
+spyInterrupt
+    "spy interrupt and no handler - enter debugger"
+
+    self error:'spy Interrupt'
+!
+
+timerInterrupt
+    "timer interrupt and no handler - enter debugger"
+
+    self error:'timer Interrupt'
+!
+
+errorInterrupt
+    "x-error interrupt and no handler - enter debugger"
+
+    self error:'error Interrupt:' , (Display lastError)
+!
+
+memoryInterrupt
+    "out-of-memory interrupt and no handler - enter debugger"
+
+    self error:'almost out of memory'
+!
+
+fpExceptionInterrupt
+    "a floating point exception occured - this one
+     has to be handled differently since they come asynchronous
+     on some machines"
+
+    self error:'a floating point exception occured'
+!
+
+signalInterrupt:signalNumber
+    "unix signal occured"
+
+    |box|
+
+    (Smalltalk at:#SignalCatchBlock) notNil ifTrue:[
+        box := OptionBox title:('Signal ' , 
+                                signalNumber printString ,
+                                ' cought')
+               numberOfOptions:5.
+
+        box buttonTitles:#('ignore' 'debug' 'restart' 'dump' 'exit').
+        box actions:(Array with:[^ nil]
+                           with:[Debugger enterWithMessage:'Signal ', signalNumber printString. ^nil]
+                           with:[SignalCatchBlock value. ^nil]
+                           with:[Smalltalk fatalAbort]
+                           with:[Smalltalk exit]).
+        box showAtPointer
+    ].
+
+    self error:('signal ' , signalNumber printString)
+!
+
+recursionInterrupt
+    "recursion limit interrupt - enter debugger"
+
+    self error:'recursion limit reached'
+!
+
+exceptionInterrupt
+    "exception interrupt - enter debugger"
+
+    self error:'exception Interrupt'
+! !
+
+!Object methodsFor:'error handling'!
+
+subscriptBoundsError:badIndex
+    "report error that badIndex is out of bounds"
+
+    SubscriptOutOfBoundSignal raise
+"
+    ^ self error:('index ' , badIndex printString , ' is out of bounds')
+"
+!
+
+indexNotInteger
+    "report error that index is not an Integer"
+
+    NonIntegerIndexSignal raise
+"
+    ^ self error:'index must be integer'
+"
+!
+
+elementNotInteger
+    "report error that object to be stored is no Integer"
+
+    ^ self error:'element must be an Integer'
+!
+
+elementNotCharacter
+    "report error that object to be stored is no Character"
+
+    ^ self error:'element must be a Character'
+!
+
+elementOutOfBounds
+    "report error that object to be stored is not valid"
+
+    ^ self error:'element out of bounds'
+!
+
+mustBeRectangle
+    "report an argument-not-rectangle-error"
+
+    ^ self error:'argument must be a Rectangle'
+!
+
+mustBeString
+    "report an argument-not-string-error"
+
+    ^ self error:'argument must be a String'
+!
+
+notIndexed
+    "report error that receiver has no indexed instance variables"
+
+    ^ self error:'receiver has no indexed variables'
+!
+
+typeCheckError
+    "generated when a variable declared with a type hint gets a bad
+     value assigned"
+
+    ^ self error:'bad assign to typed variable'
+!
+
+primitiveFailed
+    "report error that primitive code failed"
+
+    ^ self error:'primitive failed'
+!
+
+subclassResponsibility
+    "report error that this message should have been reimplemented in a
+     subclass"
+
+    ^ self error:'method must be reimplemented in subclass'
+!
+
+shouldNotImplement
+    "report error that this message should not be implemented"
+
+    ^ self error:'method not appropriate for this class'
+!
+
+error
+    "report error that an error occured"
+
+    ^ self error:'error encountered'
+!
+
+halt
+    "enter debugger with halt-message"
+
+    ^ self halt:'halt encountered'
+!
+
+fatalError:aMessage
+    "report a fatal-error; system dumps a backtrace and exits with core dump"
+%{
+    /*
+     * do not use any message calls here
+     * - since this might lead to infinite recursion ...
+     */
+    if (_isString(aMessage))
+        printf("%s\n", _stringVal(aMessage));
+    printStack(__context);
+    exit(1);
+%}
+!
+
+checkForRecursiveError
+    "helper for all error-methods; catch error while in Debugger.
+     If Debugger is DebugView, try switching to MiniDebugger (as
+     a last chance) otherwise abort.
+     There should not be an error in the debugger, this will only
+     happen if some classes has been changed badly."
+
+    ErrorActive ifTrue:[
+        (Debugger == MiniDebugger) ifTrue:[
+            ErrorRecursion ifFalse:[
+%{
+                printf("recursive error ...\n");
+                printStack(__context);
+                mainExit(0);
+%}
+            ]
+        ].
+        "set to MiniDebugger - and go on"
+        ^ MiniDebugger
+    ].
+    ^ Debugger
+!
+
+error:aString
+    "enter debugger with error-message aString;
+     if nonNil, the global ErrorHandler is informed instead -
+     this gives a chance for private error handling and error catching
+     within debugger"
+
+    |retVal debugger|
+
+    ErrorHandler notNil ifTrue:[
+        ^ ErrorHandler catch:#error: with:aString for:self
+    ].
+    Debugger isNil ifTrue:[
+        'error: ' print. aString printNewline.
+        self fatalError:'no Debugger defined'
+    ].
+    debugger := self checkForRecursiveError.
+    ErrorActive := true.
+    retVal := debugger enterWithMessage:aString.
+    ErrorActive := false.
+    ^ retVal
+!
+
+doesNotUnderstand:aMessage
+    "enter debugger with does-not-understand-message;
+     if nonNil, the global ErrorHandler is informed instead -
+     this gives a chance for private error handling and error catching
+     within debugger"
+
+    |retVal debugger|
+
+    ErrorHandler notNil ifTrue:[
+        ^ ErrorHandler catch:#doesNotUnderstand: with:aMessage for:self
+    ].
+    Debugger isNil ifTrue:[
+        'doesNotUnderstand:' print. aMessage selector printNewline.
+        self fatalError:'no Debugger defined'
+    ].
+    debugger := self checkForRecursiveError.
+    ErrorActive := true.
+    retVal := debugger enterWithMessage:(self class name , 
+                                         ' does not understand:' ,
+                                         aMessage printString).
+    ErrorActive := false.
+    ^ retVal
+!
+
+halt:aString
+    "enter debugger with halt-message;
+     the global ErrorHandler if nonNil is informed instead -
+     this gives a chance for private error handling and error catching
+     within debugger"
+
+    |retVal debugger|
+
+    ErrorHandler notNil ifTrue:[
+        ^ ErrorHandler catch:#halt: with:aString for:self
+    ].
+    Debugger isNil ifTrue:[
+        'halt encountered:' print. aString printNewline.
+        self fatalError:'no Debugger defined'
+    ].
+    debugger := self checkForRecursiveError.
+    ErrorActive := true.
+    retVal := debugger enterWithMessage:aString.
+    ErrorActive := false.
+    ^ retVal
+! !
+
+!Object methodsFor:'debugging'!
+
+notify:aString
+    "launch a Notifier, telling user something"
+
+    SystemNotifier isNil ifTrue:[
+        Notifier isNil ifTrue:[
+            Transcript showCr:aString.
+            ^ self
+        ].
+        SystemNotifier := Notifier new
+    ].
+    SystemNotifier title:aString.
+    SystemNotifier showAtPointer
+!
+
+information:aString
+    "launch an InfoBox, telling user something"
+
+    SystemInfoBox isNil ifTrue:[
+        InfoBox isNil ifTrue:[
+            Transcript showCr:aString.
+            ^ self
+        ].
+        SystemInfoBox := InfoBox new
+    ].
+    SystemInfoBox title:aString.
+    SystemInfoBox showAtPointer
+!
+
+warn:aString
+    "launch a WarningBox, telling user something"
+
+    SystemWarningBox isNil ifTrue:[
+        WarningBox isNil ifTrue:[
+            Transcript showCr:aString.
+            ^ self
+        ].
+        SystemWarningBox := WarningBox new
+    ].
+    SystemWarningBox title:aString.
+    SystemWarningBox showAtPointer
+!
+
+confirm:aString
+    "launch a confirmer, which allows user to enter yes or no.
+     return true for yes, false for no"
+
+    SystemConfirmer isNil ifTrue:[
+        YesNoBox isNil ifTrue:[
+            Transcript show:'no YesNoBox. '.
+            Transcript showCr:aString.
+            ^ true
+        ].
+        SystemConfirmer := YesNoBox new
+    ].
+    SystemConfirmer title:aString.
+    SystemConfirmer yesAction:[^ true] noAction:[^ false].
+    SystemConfirmer showAtPointer
+!
+
+basicInspect
+    "launch an inspector on the receiver.
+     this method should NOT be redefined in subclasses."
+
+    Inspector isNil ifTrue:[
+        Transcript showCr:'no Inspector defined'
+    ] ifFalse:[
+        Inspector openOn:self
+    ]
+!
+
+inspect
+    "launch an inspector on the receiver.
+     this method can be redefined in subclasses."
+
+    ^ self basicInspect
+! !
+
+!Object methodsFor:'accessing'!
+
+at:index
+    "return the indexed instance variable with index, anInteger;
+     this method can be redefined in subclasses."
+
+    ^ self basicAt:index
+!
+
+basicAt:index
+    "return the indexed instance variable with index, anInteger.
+     Trigger an error if the receiver has no indexed instance variables.
+     This method should NOT be redefined in any subclass"
+
+%{  /* NOCONTEXT */
+
+    register int nbytes, indx;
+    OBJ myClass;
+    register char *pFirst;
+    unsigned char *cp;
+    unsigned short *sp;
+    long *lp;
+    OBJ *op;
+    int nInstBytes, ninstvars;
+    extern OBJ _makeLarge();
+
+
+    /*
+     * notice the missing test for self beeing a nonNilObject -
+     * this can be done since basicAt: is defined both in UndefinedObject
+     * and SmallInteger
+     */
+    if (_isSmallInteger(index)) {
+        myClass = _qClass(self);
+        indx = _intVal(index) - 1;
+        ninstvars = _intVal(_ClassInstPtr(myClass)->c_ninstvars);
+        nInstBytes = OHDR_SIZE + ninstvars * sizeof(OBJ);
+        nbytes = _qSize(self) - nInstBytes;
+        pFirst = (char *)(_InstPtr(self)) + nInstBytes;
+
+        switch (_intVal(_ClassInstPtr(myClass)->c_flags) & ARRAYMASK) {
+            case BYTEARRAY:
+                if ((indx >= 0) && (indx < (nbytes / sizeof(char)))) {
+                    cp = (unsigned char *)pFirst + indx;
+                    RETURN ( _MKSMALLINT(*cp & 0xFF) );
+                }
+                break;
+
+            case WORDARRAY:
+                if ((indx >= 0) && (indx < (nbytes / sizeof(short)))) {
+                    sp = (unsigned short *)pFirst + indx;
+                    RETURN ( _MKSMALLINT(*sp & 0xFFFF) );
+                }
+                break;
+
+            case LONGARRAY:
+                if ((indx >= 0) && (indx < (nbytes / sizeof(long)))) {
+                    lp = (long *)pFirst + indx;
+                    if ((*lp >= _MIN_INT) && (*lp <= _MAX_INT))
+                        RETURN ( _MKSMALLINT(*lp) );
+                    RETURN ( _makeLarge(*lp) );
+                }
+                break;
+
+            case FLOATARRAY:
+                if ((indx >= 0) && (indx < (nbytes / sizeof(float)))) {
+                    float *fp;
+
+                    fp = (float *)pFirst + indx;
+                    RETURN ( _MKFLOAT((double)(*fp)) COMMA_CON );
+                }
+                break;
+
+            case DOUBLEARRAY:
+                if ((indx >= 0) && (indx < (nbytes / sizeof(double)))) {
+                    double *dp;
+
+                    dp = (double *)pFirst + indx;
+                    RETURN ( _MKFLOAT(*dp) COMMA_CON );
+                }
+                break;
+
+            case WKPOINTERARRAY:
+            case POINTERARRAY:
+                if ((indx >= 0) && (indx < (nbytes / sizeof(OBJ)))) {
+                    op = (OBJ *)pFirst + indx;
+                    RETURN ( *op );
+                }
+                break;
+        }
+    }
+%}
+.
+    (index isMemberOf:SmallInteger) ifTrue:[
+        ^ self subscriptBoundsError:index
+    ].
+    ^ self indexNotInteger
+!
+
+at:index put:anObject
+    "store the 2nd arg, anObject as indexed instvar with index, anInteger.
+     this method can be redefined in subclasses."
+
+    ^ self basicAt:index put:anObject
+!
+
+basicAt:index put:anObject
+    "store the 2nd arg, anObject as indexed instvar with index, anInteger.
+     Trigger an error if the receiver has no indexed instance variables.
+     This method should NOT be redefined in any subclass"
+
+%{  /* NOCONTEXT */
+
+    register int nbytes, indx;
+    OBJ myClass;
+    register char *pFirst;
+    char *cp;
+    short *sp;
+    long *lp;
+    OBJ *op;
+    int nInstBytes, ninstvars;
+    int val;
+
+    /* notice the missing test for self beeing a nonNilObject -
+       this an be done since basicAt: is defined both in UndefinedObject
+       and SmallInteger */
+
+    if (_isSmallInteger(index)) {
+        indx = _intVal(index) - 1;
+        myClass = _qClass(self);
+        ninstvars = _intVal(_ClassInstPtr(myClass)->c_ninstvars);
+        nInstBytes = OHDR_SIZE + ninstvars * sizeof(OBJ);
+        nbytes = _qSize(self) - nInstBytes;
+        pFirst = (char *)(_InstPtr(self)) + nInstBytes;
+
+        switch (_intVal(_ClassInstPtr(myClass)->c_flags) & ARRAYMASK) {
+            case BYTEARRAY:
+                if (_isSmallInteger(anObject)) {
+                    val = _intVal(anObject);
+                    if ((val >= 0) && (val <= 255)) {
+                        if ((indx >= 0) && (indx < (nbytes / sizeof(char)))) {
+                            cp = pFirst + indx;
+                            *cp = val;
+                            RETURN ( anObject );
+                        }
+                    }
+                }
+                break;
+
+            case WORDARRAY:
+                if (_isSmallInteger(anObject)) {
+                    val = _intVal(anObject);
+                    if ((val >= 0) && (val <= 0xFFFF)) {
+                        if ((indx >= 0) && (indx < (nbytes / sizeof(short)))) {
+                            sp = (short *)pFirst + indx;
+                            *sp = val;
+                            RETURN ( anObject );
+                        }
+                    }
+                }
+                break;
+
+            case LONGARRAY:
+                if (_isSmallInteger(anObject)) {
+                    if ((indx >= 0) && (indx < (nbytes / sizeof(long)))) {
+                        lp = (long *)pFirst + indx;
+                        *lp = _intVal(anObject);
+                        RETURN ( anObject );
+                    }
+                }
+                /* XXX
+                 * XXX must add possibility to put in a large number here
+                 * XXX
+                 */
+                break;
+
+            case FLOATARRAY:
+                if (_isFloat(anObject)) {
+                    if ((indx >= 0) && (indx < (nbytes / sizeof(float)))) {
+                        float *fp;
+
+                        fp = (float *)pFirst + indx;
+                        *fp = _floatVal(anObject);
+                        RETURN ( anObject );
+                    }
+                }
+                break;
+
+            case DOUBLEARRAY:
+                if (_isFloat(anObject)) {
+                    if ((indx >= 0) && (indx < (nbytes / sizeof(double)))) {
+                        double *dp;
+
+                        dp = (double *)pFirst + indx;
+                        *dp = _floatVal(anObject);
+                        RETURN ( anObject );
+                    }
+                }
+                break;
+
+            case WKPOINTERARRAY:
+            case POINTERARRAY:
+                if ((indx >= 0) && (indx < (nbytes / sizeof(OBJ)))) {
+                    op = (OBJ *)pFirst + indx;
+                    *op = anObject;
+                    __STORE(self, anObject);
+                    RETURN ( anObject );
+                }
+                break;
+
+            default:
+                break;
+        }
+    }
+%}
+.
+    (index isMemberOf:SmallInteger) ifFalse:[
+        ^ self indexNotInteger
+    ].
+    (index between:1 and:self size) ifFalse:[
+        ^ self subscriptBoundsError:index
+    ].
+    ^ self elementNotInteger
+!
+
+instVarAt:index
+    "return a non-indexed instance variable;
+     this is not very object oriented - use with care (needed for inspector)"
+
+%{  /* NOCONTEXT */
+
+    OBJ myClass;
+    int idx, ninstvars;
+
+    if (_isSmallInteger(index)) {
+        myClass = _Class(self);
+        ninstvars = _intVal(_ClassInstPtr(myClass)->c_ninstvars);
+        idx = _intVal(index) - 1;
+        if ((idx >= 0) && (idx < ninstvars)) {
+            RETURN ( _InstPtr(self)->i_instvars[idx] );
+        }
+    }
+%}
+.
+    index isInteger ifFalse:[
+        ^ self indexNotInteger
+    ].
+    ^ self subscriptBoundsError:index
+!
+
+instVarAt:index put:value
+    "change a non-indexed instance variable;
+     this is not very object oriented - use with care (needed for inspector)"
+
+%{  /* NOCONTEXT */
+
+    OBJ myClass;
+    int idx, ninstvars;
+
+    if (_isSmallInteger(index)) {
+        myClass = _Class(self);
+        ninstvars = _intVal(_ClassInstPtr(myClass)->c_ninstvars);
+        idx = _intVal(index) - 1;
+        if ((idx >= 0) && (idx < ninstvars)) {
+            _InstPtr(self)->i_instvars[idx] = value;
+            __STORE(self, value);
+            RETURN ( value );
+        }
+    }
+%}
+.
+    index isInteger ifFalse:[
+        ^ self indexNotInteger
+    ].
+    ^ self subscriptBoundsError:index
+! !
+
+!Object methodsFor:'dependents access'!
+
+dependents
+    "return a Collection of dependents - nil if there is none.
+     The default implementation here uses a global Dictionary to store
+     dependents - some classes (Model) redefine this for better performance."
+
+    ^ Dependencies at:self ifAbsent:[]
+!
+
+dependents:aCollection
+    "set the collection of dependents.
+     The default implementation here uses a global Dictionary to store
+     dependents - some classes (Model) redefine this for better performance."
+
+    (aCollection isNil or:[aCollection isEmpty]) ifTrue:[
+        Dependencies removeKey:self ifAbsent:[]
+    ] ifFalse:[
+        Dependencies at:self put:aCollection
+    ]
+!
+
+addDependent:anObject
+    "make the argument, anObject be a dependent of the receiver"
+
+    |deps|
+
+    deps := self dependents.
+    deps isNil ifTrue:[
+        deps := IdentitySet with:anObject.
+        self dependents:deps
+    ] ifFalse:[
+        deps add:anObject
+    ]
+!
+
+removeDependent:anObject
+    "make the argument, anObject be independent of the receiver"
+
+    |deps|
+
+    deps := self dependents.
+    deps notNil ifTrue:[
+        deps remove:anObject ifAbsent:[].
+        deps isEmpty ifTrue:[
+            self dependents:nil
+        ]
+    ]
+!
+
+release
+    "remove all dependencies from the receiver"
+
+    self dependents:nil
+! !
+
+!Object methodsFor:'change and update'!
+
+changed
+    "notify all dependents that the receiver has changed.
+     Each dependent gets a '#update' message."
+
+    |deps|
+
+    deps := self dependents.
+    deps notNil ifTrue:[
+        deps do:[:dependent | 
+            dependent update:self
+        ]
+    ]
+!
+
+changed:aParameter
+    "notify all dependents that the receiver has changed somehow.
+     Each dependent gets a '#update:aParameter' message."
+
+    |deps|
+
+    deps := self dependents.
+    deps notNil ifTrue:[
+        deps do:[:dependent | 
+            dependent update:aParameter
+        ]
+    ]
+!
+
+changed:aParameter with:arguments
+    "notify all dependents that the receiver has changed somehow.
+     sending update:with: to each dependent with an additional arguments"
+
+    |deps|
+
+    deps := self dependents.
+    deps notNil ifTrue:[
+        deps do:[:dependent | 
+            dependent update:aParameter with:arguments
+        ]
+    ]
+!
+
+broadcast:aSymbol
+    "send the argument, aSelectorSymbol to all my dependents"
+
+    |deps|
+
+    deps := self dependents.
+    deps notNil ifTrue:[
+        deps do:[:dependent | 
+            dependent perform:aSymbol
+        ]
+    ]
+!
+
+broadcast:aSymbol with:anObject
+    "send the argument, aSelectorSymbol of a 1 argument message
+     to all my dependents with the second argument, anObject as argument"
+
+    |deps|
+
+    deps := self dependents.
+    deps notNil ifTrue:[
+        deps do:[:dependent | 
+            dependent perform:aSymbol with:anObject
+        ]
+    ]
+!
+
+update:aParameter
+    "dependent is notified of some change -
+     Default behavior is to do nothing"
+
+    ^ self
+!
+
+update:aParameter with:anArgument
+    "dependent is notified of some change -
+     Default is to try simple update"
+
+    ^ self update:aParameter
+!
+
+update:aParameter with:anArgument from:sender
+    "dependent is notified of some change -
+     Default is to try simple update"
+
+    ^ self update:aParameter with:anArgument
+! !
+
+!Object methodsFor:'message sending'!
+
+perform:aSelector
+    "send the message aSelector to the receiver"
+
+%{  /* NOCONTEXT */
+
+    static struct inlineCache ilc = _ILC0;
+    static OBJ lastSelector = nil;
+
+#ifdef XXXTHIS_CONTEXT
+    /*
+     * must set lineno by hand here ...
+     */
+    if (_intVal(__pilc->ilc_lineNo) > 0)
+        _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+
+    if (aSelector != lastSelector) {
+        ilc.ilc_func = _SEND0;
+        lastSelector = aSelector;
+    }
+#ifdef THIS_CONTEXT
+    ilc.ilc_lineNo = __pilc->ilc_lineNo;
+#endif
+
+    RETURN ( (*ilc.ilc_func)(self, aSelector, CON_COMMA nil, &ilc) );
+%}
+!
+
+perform:aSelector with:anObject
+    "send the one-arg-message aSelector to the receiver"
+
+%{  /* NOCONTEXT */
+
+    static struct inlineCache ilc = _ILC1;
+    static OBJ lastSelector = nil;
+
+#ifdef XXXTHIS_CONTEXT
+    /*
+     * must set lineno by hand here ...
+     */
+    if (_intVal(__pilc->ilc_lineNo) > 0)
+        _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+    if (aSelector != lastSelector) {
+        ilc.ilc_func = _SEND1;
+        lastSelector = aSelector;
+    }
+#ifdef THIS_CONTEXT
+    ilc.ilc_lineNo = __pilc->ilc_lineNo;
+#endif
+
+#ifdef PASS_ARG_REF
+    RETURN ( (*ilc.ilc_func)(self, aSelector, CON_COMMA nil, &ilc, &anObject) );
+#else
+    RETURN ( (*ilc.ilc_func)(self, aSelector, CON_COMMA nil, &ilc, anObject) );
+#endif
+%}
+!
+
+perform:aSelector with:firstObject with:secondObject
+    "send the two-arg-message aSelector to the receiver"
+
+%{  /* NOCONTEXT */
+
+    static struct inlineCache ilc = _ILC2;
+    static OBJ lastSelector = nil;
+
+#ifdef XXXTHIS_CONTEXT
+    /*
+     * must set lineno by hand here ...
+     */
+    if (_intVal(__pilc->ilc_lineNo) > 0)
+        _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+    if (aSelector != lastSelector) {
+        ilc.ilc_func = _SEND2;
+        lastSelector = aSelector;
+    }
+#ifdef THIS_CONTEXT
+    ilc.ilc_lineNo = __pilc->ilc_lineNo;
+#endif
+
+#ifdef PASS_ARG_REF
+    RETURN ( (*ilc.ilc_func)(self, aSelector, CON_COMMA nil, &ilc, &firstObject) );
+#else
+    RETURN ( (*ilc.ilc_func)(self, aSelector, CON_COMMA nil, &ilc, firstObject, secondObject) );
+#endif
+%}
+!
+
+perform:aSelector with:firstObject with:secondObject with:thirdObject
+    "send the three-arg-message aSelector to the receiver"
+
+%{  /* NOCONTEXT */
+
+    static struct inlineCache ilc = _ILC3;
+    static OBJ lastSelector = nil;
+
+#ifdef XXXTHIS_CONTEXT
+    /*
+     * must set lineno by hand here ...
+     */
+    if (_intVal(__pilc->ilc_lineNo) > 0)
+        _ContextInstPtr(__thisContext)->c_lineno = __pilc->ilc_lineNo;
+#endif
+    if (aSelector != lastSelector) {
+        ilc.ilc_func = _SEND3;
+        lastSelector = aSelector;
+    }
+#ifdef THIS_CONTEXT
+    ilc.ilc_lineNo = __pilc->ilc_lineNo;
+#endif
+
+#ifdef PASS_ARG_REF
+    RETURN ( (*ilc.ilc_func)(self, aSelector, CON_COMMA nil, &ilc, &firstObject) );
+#else
+    RETURN ( (*ilc.ilc_func)(self, aSelector, CON_COMMA nil, &ilc, firstObject, secondObject, thirdObject) );
+#endif
+%}
+!
+
+perform:aSelector withArguments:argArray
+    "send the message aSelector with all args taken from argArray 
+     to the receiver"
+
+    |numberOfArgs a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12|
+
+    numberOfArgs := argArray size.
+%{
+    extern OBJ Array;
+    REGISTER OBJ *argP;
+    OBJ T;
+    int nargs, i;
+    static OBJ last0 = nil; static struct inlineCache ilc0 = _ILC0;
+    static OBJ last1 = nil; static struct inlineCache ilc1 = _ILC1;
+    static OBJ last2 = nil; static struct inlineCache ilc2 = _ILC2;
+    static OBJ last3 = nil; static struct inlineCache ilc3 = _ILC3;
+    static OBJ last4 = nil; static struct inlineCache ilc4 = _ILC4;
+    static OBJ last5 = nil; static struct inlineCache ilc5 = _ILC5;
+    static OBJ last6 = nil; static struct inlineCache ilc6 = _ILC6;
+    static OBJ last7 = nil; static struct inlineCache ilc7 = _ILC7;
+    static OBJ last8 = nil; static struct inlineCache ilc8 = _ILC8;
+    static OBJ last9 = nil; static struct inlineCache ilc9 = _ILC9;
+    static OBJ last10 = nil; static struct inlineCache ilc10 = _ILC10;
+    static OBJ last11 = nil; static struct inlineCache ilc11 = _ILC11;
+    static OBJ last12 = nil; static struct inlineCache ilc12 = _ILC12;
+
+    if (_isSmallInteger(numberOfArgs)) {
+        nargs = _intVal(numberOfArgs);
+        if (nargs) {
+            argP = (OBJ *)(&a1);
+            if (_Class(argArray) == Array) {
+                for (i=0; i < nargs; i++) {
+                    *argP++ = _ArrayInstPtr(argArray)->a_element[i];
+                }
+            } else {
+                for (i=1; i <= nargs; i++) {
+#ifdef PASS_ARG_REF
+                    T = _MKSMALLINT(i);
+                    *argP++ = _AT_(argArray, CON_COMMA &T);
+#else
+                    *argP++ = _AT_(argArray, CON_COMMA _MKSMALLINT(i));
+#endif
+                }
+            }
+        }
+#ifdef XXXTHIS_CONTEXT
+        /*
+         * must set lineno by hand here ...
+         */
+        if (_intVal(__pilc->ilc_lineNo) > 0)
+            _ContextInstPtr(__context->c_sender)->c_lineno = __pilc->ilc_lineNo;
+#endif
+        switch (nargs) {
+            case 0: 
+                if (aSelector != last0) {
+                    ilc0.ilc_func = _SEND0;
+                    last0 = aSelector;
+                }
+#ifdef PASS_ARG_REF
+                RETURN ( (*ilc0.ilc_func)(self, aSelector, CON_COMMA nil, &ilc0, &a1) );
+#else
+                RETURN ( (*ilc0.ilc_func)(self, aSelector, CON_COMMA nil, &ilc0, a1, a2) );
+#endif
+
+            case 1: 
+                if (aSelector != last1) {
+                    ilc1.ilc_func = _SEND1;
+                    last1 = aSelector;
+                }
+#ifdef PASS_ARG_REF
+                RETURN ( (*ilc1.ilc_func)(self, aSelector, CON_COMMA nil, &ilc1, &a1));
+#else
+                RETURN ( (*ilc1.ilc_func)(self, aSelector, CON_COMMA nil, &ilc1, a1, a2));
+#endif
+
+            case 2: 
+                if (aSelector != last2) {
+                    ilc2.ilc_func = _SEND2;
+                    last2 = aSelector;
+                }
+#ifdef PASS_ARG_REF
+                RETURN ( (*ilc2.ilc_func)(self, aSelector, CON_COMMA nil, &ilc2, &a1));
+#else
+                RETURN ( (*ilc2.ilc_func)(self, aSelector, CON_COMMA nil, &ilc2, a1, a2));
+#endif
+
+            case 3: 
+                if (aSelector != last3) {
+                    ilc3.ilc_func = _SEND3;
+                    last3 = aSelector;
+                }
+#ifdef PASS_ARG_REF
+                RETURN ( (*ilc3.ilc_func)(self, aSelector, CON_COMMA nil, &ilc3, &a1));
+#else
+                RETURN ( (*ilc3.ilc_func)(self, aSelector, CON_COMMA nil, &ilc3, a1, a2, a3));
+#endif
+
+            case 4: 
+                if (aSelector != last4) {
+                    ilc4.ilc_func = _SEND4;
+                    last4 = aSelector;
+                }
+#ifdef PASS_ARG_REF
+                RETURN ( (*ilc4.ilc_func)(self, aSelector, CON_COMMA nil, &ilc4, &a1));
+#else
+                RETURN ( (*ilc4.ilc_func)(self, aSelector, CON_COMMA nil, &ilc4, a1, a2, a3, a4));
+#endif
+
+            case 5: 
+                if (aSelector != last5) {
+                    ilc5.ilc_func = _SEND5;
+                    last5 = aSelector;
+                }
+#ifdef PASS_ARG_REF
+                RETURN ( (*ilc5.ilc_func)(self, aSelector, CON_COMMA nil, &ilc5, &a1));
+#else
+                RETURN ( (*ilc5.ilc_func)(self, aSelector, CON_COMMA nil, &ilc5, a1, a2, a3, a4, a5));
+#endif
+
+            case 6: 
+                if (aSelector != last6) {
+                    ilc6.ilc_func = _SEND6;
+                    last6 = aSelector;
+                }
+#ifdef PASS_ARG_REF
+                RETURN ( (*ilc6.ilc_func)(self, aSelector, CON_COMMA nil, &ilc6, &a1));
+#else
+                RETURN ( (*ilc6.ilc_func)(self, aSelector, CON_COMMA nil, &ilc6, a1, a2, a3, a4, a5, a6));
+#endif
+
+            case 7: 
+                if (aSelector != last7) {
+                    ilc7.ilc_func = _SEND7;
+                    last7 = aSelector;
+                }
+#ifdef PASS_ARG_REF
+                RETURN ( (*ilc7.ilc_func)(self, aSelector, CON_COMMA nil, &ilc7, &a1));
+#else
+                RETURN ( (*ilc7.ilc_func)(self, aSelector, CON_COMMA nil, &ilc7, a1, a2, a3, a4, a5, a6, a7));
+#endif
+
+            case 8: 
+                if (aSelector != last8) {
+                    ilc8.ilc_func = _SEND8;
+                    last8 = aSelector;
+                }
+#ifdef PASS_ARG_REF
+                RETURN ( (*ilc8.ilc_func)(self, aSelector, CON_COMMA nil, &ilc8, &a1));
+#else
+                RETURN ( (*ilc8.ilc_func)(self, aSelector, CON_COMMA nil, &ilc8, a1, a2, a3, a4, a5, a6, a7, a8));
+#endif
+
+            case 9: 
+                if (aSelector != last9) {
+                    ilc9.ilc_func = _SEND9;
+                    last9 = aSelector;
+                }
+#ifdef PASS_ARG_REF
+                RETURN ( (*ilc9.ilc_func)(self, aSelector, CON_COMMA nil, &ilc9, &a1));
+#else
+                RETURN ( (*ilc9.ilc_func)(self, aSelector, CON_COMMA nil, &ilc9, a1, a2, a3, a4, a5, a6, a7, a8, a9));
+#endif
+
+            case 10: 
+                if (aSelector != last10) {
+                    ilc10.ilc_func = _SEND10;
+                    last10 = aSelector;
+                }
+#ifdef PASS_ARG_REF
+                RETURN ( (*ilc10.ilc_func)(self, aSelector, CON_COMMA nil, &ilc10, &a1));
+#else
+                RETURN ( (*ilc10.ilc_func)(self, aSelector, CON_COMMA nil, &ilc10, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10));
+#endif
+
+            case 11: 
+                if (aSelector != last11) {
+                    ilc11.ilc_func = _SEND11;
+                    last11 = aSelector;
+                }
+#ifdef PASS_ARG_REF
+                RETURN ( (*ilc11.ilc_func)(self, aSelector, CON_COMMA nil, &ilc11, &a1));
+#else
+                RETURN ( (*ilc11.ilc_func)(self, aSelector, CON_COMMA nil, &ilc11, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11));
+#endif
+
+            case 12: 
+                if (aSelector != last12) {
+                    ilc12.ilc_func = _SEND12;
+                    last12 = aSelector;
+                }
+#ifdef PASS_ARG_REF
+                RETURN ( (*ilc12.ilc_func)(self, aSelector, CON_COMMA nil, &ilc12, &a1));
+#else
+                RETURN ( (*ilc12.ilc_func)(self, aSelector, CON_COMMA nil, &ilc12, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12));
+#endif
+        }
+    }
+%}
+.
+    ^ self primitiveFailed
+! !
+
+!Object methodsFor:'printing & storing'!
+
+className
+    "return the classname of the receivers class"
+
+    ^ self class name
+!
+
+classNameWithArticle
+    "return a string consisting of classname preceeded by an article"
+
+    |article classname firstChar|
+
+    classname := self className.
+    firstChar := (classname at:1) asLowercase. 
+    (firstChar isVowel or:[firstChar == $x]) ifTrue:[
+         article := 'an '
+    ] ifFalse:[
+         article := 'a '
+    ].
+    ^ (article , classname)
+!
+
+printString
+    "return a string for printing the receiver.
+     Default printString is the classname preceeded by an article -
+     is redefined in many subclasses"
+
+    ^ self classNameWithArticle
+!
+
+print
+    "print the receiver on the standard output stream"
+
+    self printString print
+!
+
+printOn:aStream
+    "print the receiver on the argument-stream"
+
+    aStream nextPutAll:(self printString)
+!
+
+printStringRightAdjustLen:fieldSize
+    "return my printString as a right-adjusted string of length fieldSize"
+
+    |thePrintString len spaces|
+
+    thePrintString := self printString.
+    len := thePrintString size.
+    (len < fieldSize) ifTrue:[
+         spaces := String new:(fieldSize - len).
+         ^ spaces , thePrintString
+    ].
+    ^ thePrintString
+!
+
+printRightAdjustLen:fieldSize
+    "print the receiver right adjusted in a field of fieldSize
+     characters"
+
+    (self printStringRightAdjustLen:fieldSize) printOn:Stdout
+!
+
+printNL
+    "print the receiver followed by a cr
+     - for GNU Smalltalk compatibility"
+
+    ^ self printNewline
+!
+
+printNewline
+    "print the receiver followed by a cr"
+
+    self print.
+    Character nl print
+!
+
+displayString
+    "return a string used when displaying the receiver in a view,
+     for example an Inspector. This is usually the same as printString"
+
+    ^ self printString
+!
+
+storeString
+    "return a string representing an expression to reconstruct the receiver"
+
+    | stream myClass hasSemi 
+      sz "{ Class: SmallInteger }" |
+
+    myClass := self class.
+    stream := WriteStream on:(String new).
+    stream nextPut:$(.
+    stream nextPutAll:self class name.
+    hasSemi := false.
+    myClass isVariable ifTrue:[
+        stream nextPutAll:' basicNew:'.
+        self basicSize printOn:stream
+    ] ifFalse:[
+        stream nextPutAll:' basicNew'
+    ].
+    sz := myClass instSize.
+    1 to:sz do:[:i | 
+        stream nextPutAll:' instVarAt:'.
+        i printOn:stream.
+        stream nextPutAll:' put:'.
+        (self instVarAt:i) storeOn:stream.
+        stream nextPut:$;.
+        hasSemi := true
+    ].
+    myClass isVariable ifTrue:[
+        sz := self basicSize.
+        1 to:sz do:[:i | 
+            stream nextPutAll:' basicAt:'.
+            i printOn:stream.
+            stream nextPutAll:' put:'.
+            (self basicAt:i) storeOn:stream.
+            stream nextPut:$;.
+            hasSemi := true
+        ]
+    ].
+    hasSemi ifTrue:[
+        stream nextPutAll:' yourself'
+    ].
+    stream nextPut:$).
+    ^ stream contents
+!
+
+storeOn:aStream
+    "store the receiver on aStream; i.e. print an expression which will
+     reconstruct the receiver"
+
+    aStream nextPutAll:(self storeString)
+!
+
+store
+    "store the receiver on standard output"
+
+    self storeOn:Stdout
+!
+
+storeNl
+    "store the receiver on standard output; append a newline"
+
+    self store.
+    Character nl print
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ObjectMemory.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,367 @@
+"
+ COPYRIGHT (c) 1992-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#ObjectMemory
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'System-Support'
+!
+
+ObjectMemory comment:'
+
+COPYRIGHT (c) 1992 -93 by Claus Gittinger
+             All Rights Reserved
+
+This class contains access methods to the system memory -
+in previous versions this stuff used to be in the Smalltalk class.
+It has been separated for better overall structure.
+
+%W% %E%
+'!
+
+!ObjectMemory class methodsFor:'cache management'!
+
+flushInlineCachesForClass:aClass
+    "flush inlinecaches for calls to aClass"
+
+%{  /* NOCONTEXT */
+    __flushInlineCachesFor(aClass);
+%}
+!
+
+flushInlineCachesWithArgs:nargs
+    "flush inlinecaches for calls with nargs arguments"
+
+%{  /* NOCONTEXT */
+    __flushInlineCaches(_intVal(nargs));
+%}
+!
+
+flushInlineCachesFor:aClass withArgs:nargs
+    "flush inlinecaches for calls to aClass with nargs arguments"
+
+%{  /* NOCONTEXT */
+    __flushInlineCachesForAndNargs(aClass, _intVal(nargs));
+%}
+!
+
+flushInlineCaches
+    "flush all inlinecaches"
+
+%{  /* NOCONTEXT */
+    __flushAllInlineCaches();
+%}
+!
+
+flushMethodCacheFor:aClass
+    "flush the method cache for sends to aClass"
+
+%{  /* NOCONTEXT */
+    __flushMethodCacheFor(aClass);
+%}
+!
+
+flushMethodCache
+    "flush the method cache"
+
+%{  /* NOCONTEXT */
+    __flushMethodCache();
+%}
+!
+
+flushCaches
+    "flush method and inline caches"
+
+%{  /* NOCONTEXT */
+    __flushMethodCache();
+    __flushAllInlineCaches();
+%}
+! !
+
+!ObjectMemory class methodsFor:'enumeration'!
+
+allObjectsDo:aBlock
+    "evaluate the argument, aBlock for all objects in the system"
+%{
+#ifdef THIS_CONTEXT
+    __allObjectsDo(&aBlock);
+#else
+    __allObjectsDo(&aBlock, __context);
+#endif
+%}
+! !
+
+!ObjectMemory class methodsFor:'queries'!
+
+newSpaceUsed
+    "return the number of bytes allocated for new objects"
+
+%{  /* NOCONTEXT */
+    RETURN ( _MKSMALLINT(__newSpaceUsed()) );
+%}
+    "ObjectMemory newSpaceUsed"
+!
+
+oldSpaceUsed
+    "return the number of bytes allocated for old objects"
+
+%{  /* NOCONTEXT */
+    RETURN ( _MKSMALLINT(__oldSpaceUsed()) );
+%}
+    "ObjectMemory oldSpaceUsed"
+!
+
+bytesUsed
+    "return the number of bytes allocated for objects -
+     this number is not exact, since some objects may be dead"
+
+%{  /* NOCONTEXT */
+    RETURN ( _MKSMALLINT(__oldSpaceUsed() + __newSpaceUsed()) );
+%}
+    "ObjectMemory bytesUsed"
+!
+
+numberOfObjects
+    "return the number of objects in the system"
+
+    |tally|
+
+    tally := 0.
+    self allObjectsDo:[:obj | tally := tally + 1].
+    ^ tally
+
+    "ObjectMemory numberOfObjects"
+!
+
+printReferences:anObject
+    "debugging: print referents to anObject"
+
+%{
+    _printRefChain(__context, anObject);
+%}
+!
+
+whoReferences:anObject
+    "return a collection of objects referencing the argument, anObject"
+
+    |aCollection|
+
+    aCollection := IdentitySet new.
+    self allObjectsDo:[:o |
+        (o references:anObject) ifTrue:[
+            aCollection add:o
+        ]
+    ].
+    (aCollection size == 0) ifTrue:[
+        "actually this cannot happen - there is always one"
+        ^ nil
+    ].
+    ^ aCollection
+! !
+
+!ObjectMemory class methodsFor:'garbage collector control'!
+
+garbageCollect
+    "search for and free garbage in the oldSpace
+     (newSpace is cleaned automatically)
+     - can take a long time if paging is involved
+     - when no paging is involved, its faster than I thought :-)"
+%{
+    __garbageCollect(__context);
+%}
+
+    "ObjectMemory garbageCollect"
+!
+
+scavenge
+    "for debugging only - collect newspace stuff"
+%{
+    nonTenuringScavenge(__context);
+%}
+
+    "ObjectMemory scavenge"
+!
+
+tenure
+    "forcae all new stuff into old-space"
+%{
+    tenure(__context);
+%}
+
+    "ObjectMemory tenure"
+!
+
+markAndSweep
+    "mark/sweep garbage collector"
+
+%{
+    markAndSweep(__context);
+%}
+
+    "ObjectMemory markAndSweep"
+!
+
+gcStep
+    "one incremental garbage collect step"
+%{
+    incrGCstep(__context);
+%}
+!
+
+turnOffGarbageCollector
+    "turn off garbage collector.
+     this method is somewhat dangerous: if collector is turned off,
+     and too many objects are created, the system may run into trouble.
+     Use this only for measurement purposes or when realtime behavior
+     is required for a limited time period. No waranty"
+%{
+    allocForceSpace(0);
+%}
+!
+
+turnOnGarbageCollector
+    "turn it on again"
+
+%{
+    allocForceSpace(1);
+%}
+! !
+
+!ObjectMemory class methodsFor:'system management'!
+
+loadClassBinary:aClassName
+    "find the object file for aClassName and -if found - load it;
+     this one loads precompiled object files"
+
+    |fName newClass upd|
+
+    fName := self fileNameForClass:aClassName.
+    fName notNil ifTrue:[
+        upd := Class updateChanges:false.
+        [
+            self loadBinary:(fName , '.o')
+        ] valueNowOrOnUnwindDo:[
+            Class updateChanges:upd
+        ].
+        newClass := self at:(aClassName asSymbol).
+        (newClass notNil and:[newClass implements:#initialize]) ifTrue:[
+            newClass initialize
+        ]
+    ]
+!
+
+snapShot
+    "create a snapshot"
+
+    ImageName isNil ifTrue:[
+        ImageName := 'st.img'
+    ].
+    self snapShotOn:ImageName
+
+    "ObjectMemory snapShot"
+!
+
+snapShotOn:aFileName
+    "create a snapshot in the given file"
+
+    "give others a chance to fix things"
+    self changed:#save.
+%{
+    OBJ __snapShotOn();
+
+    if (_isString(aFileName)) {
+        RETURN ( __snapShotOn(__context, _stringVal(aFileName)) );
+    }
+%}
+.
+    ^ self primitiveFailed
+
+    "ObjectMemory snapShotOn:'myimage.img'"
+!
+
+applicationImageOn:aFileName for:startupClass selector:startupSelector
+    "create a snapshot which will come up without any views 
+     but starts up an application by sending startupClass the startupSelector"
+
+    |viewsKnown savedIdleBlocks savedTimeoutBlocks savedTranscript|
+
+    viewsKnown := Display knownViews.
+    savedIdleBlocks := Display idleBlocks.
+    savedTimeoutBlocks := Display timeOutBlocks.
+    savedTranscript := Transcript.
+
+    "a kludge: save image with modified knownViews ..."
+
+    Display knownViews:nil.
+    Display idleBlocks:nil.
+    Display timeOutBlocks:nil.
+    Transcript := Stderr.
+    StartupClass := startupClass.
+    StartupSelector := startupSelector.
+
+    self snapShotOn:aFileName.
+
+    StartupClass := nil.
+    StartupSelector := nil.
+    Transcript := savedTranscript.
+    Display knownViews:viewsKnown.
+    Display idleBlocks:savedIdleBlocks.
+    Display timeOutBlocks:savedTimeoutBlocks
+
+    "ObjectMemory applicationImageOn:'draw.img' for:DrawTool selector:#start"
+    "ObjectMemory applicationImageOn:'pm.img' for:PMSimulator selector:#start"
+!
+
+minimumApplicationImageOn:aFileName for:startupClass selector:startupSelector
+    "create a snapshot which will come up without any views 
+     but starts up an application by sending startupClass the startupSelector.
+     All unneeded info is stripped from the saved image."
+
+    "create a temporary image, for continuation"
+    self snapShotOn:'temp.img'.
+
+    Display knownViews do:[:aView |
+        aView notNil ifTrue:[
+            aView superView isNil ifTrue:[
+                aView destroy
+            ]
+        ]
+    ].
+
+    self stripImage.
+
+    self applicationImageOn:aFileName for:startupClass selector:startupSelector.
+
+    "continue in old image"
+
+    OperatingSystem exec:(Arguments at:1)
+           withArguments:#('smalltalk' '-i' 'temp.img') , (Arguments copyFrom:2)
+
+    "ObjectMemory minimumApplicationImageOn:'clock1.img' for:Clock selector:#start"
+    "ObjectMemory applicationImageOn:'clock2.img' for:Clock selector:#start"
+!
+
+stripImage
+    "remove all unneeded stuff from the image - much more is possible here"
+
+    "remove all class comments"
+
+    Smalltalk allClassesDo:[:aClass |
+        aClass setComment:nil.
+        aClass methodDictionary do:[:aMethod |
+            aMethod source:''.
+            aMethod category:#none 
+        ]
+    ].
+    self garbageCollect
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrdColl.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,375 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+SequenceableCollection subclass:#OrderedCollection
+         instanceVariableNames:'contentsArray firstIndex lastIndex'
+         classVariableNames:''
+         poolDictionaries:''
+         category:'Collections-Sequenceable'
+!
+
+OrderedCollection comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+OrderedCollection have ordered elements. Insertion and removal at both ends
+is possible - therefore they can be used for queues and stacks.
+
+Instance variables:
+
+contentsArray   <Array>         the actual contents
+firstIndex      <SmallInteger>  index of first valid element
+lastIndex       <SmallInteger>  index of last valid element
+
+%W% %E%
+written spring 89 by claus
+'!
+
+!OrderedCollection class methodsFor:'instance creation'!
+
+new:size
+    "create a new OrderedCollection"
+
+    ^ (self basicNew) initContents:size
+!
+
+new
+    "create a new OrderedCollection"
+
+    ^ (self basicNew) initContents:10
+! !
+
+!OrderedCollection methodsFor:'testing'!
+
+size
+    "return the number of elements in the collection"
+
+    ^ lastIndex - firstIndex + 1
+!
+
+isFixedSize
+    "return true if the receiver cannot grow - this will vanish once
+     Arrays and Strings learn how to grow ..."
+
+    ^ false
+! !
+
+!OrderedCollection methodsFor:'copying'!
+
+reverse
+    "return a new collection with all elements of the receiver
+     in reverse order"
+
+    |newCollection
+     size     "{ Class:SmallInteger }"
+     dstIndex "{ Class:SmallInteger }" |
+
+    size := self size.
+    newCollection := self species new:size.
+    newCollection setFirstIndex:firstIndex lastIndex:lastIndex.
+    dstIndex := size.
+    1 to:size do:[:srcIndex |
+        newCollection at:dstIndex put:(self at:srcIndex).
+        dstIndex := dstIndex - 1
+    ].
+    ^ newCollection
+!
+
+, aCollection
+    "return a new collection formed from concatenating the receiver with
+     the argument"
+
+    |newCollection|
+
+    newCollection := self species new:(self size + aCollection size).
+    self do:[:element |
+        newCollection add:element
+    ].
+    aCollection do:[:element |
+        newCollection add:element
+    ].
+    ^ newCollection
+! !
+
+!OrderedCollection methodsFor:'adding & removing'!
+
+removeFirst
+    "remove the first element from the collection; return the element"
+
+    |anObject |
+
+    anObject := contentsArray at:firstIndex.
+    firstIndex := firstIndex + 1.
+    ^ anObject
+!
+
+removeLast
+    "remove the last element from the collection; return the element"
+
+    |anObject |
+
+    anObject := contentsArray at:lastIndex.
+    lastIndex := lastIndex - 1.
+    ^ anObject
+!
+
+remove:anObject ifAbsent:exceptionBlock
+    "remove the first occurrence of anObject from the collection;
+     return the value of exceptionBlock if anObject is not in
+     the collection"
+
+    |index "{ Class:SmallInteger }"|
+
+    index := firstIndex.
+    [index <= lastIndex] whileTrue:[
+        anObject = (contentsArray at:index) ifTrue:[
+            contentsArray replaceFrom:index to:(contentsArray size - 1)
+                            with:contentsArray startingAt:(index + 1).
+            lastIndex := lastIndex - 1.
+            ^ anObject
+        ].
+        index := index + 1
+    ].
+    ^ exceptionBlock value
+!
+
+add:anObject
+    "add the argument, anObject to the end of the collection
+     Return the argument, anObject."
+
+    ^ self addLast:anObject
+!
+
+addLast:anObject
+    "add the argument, anObject to the end of the collection
+     Return the argument, anObject."
+
+    (lastIndex == contentsArray size) ifTrue:[
+        self makeRoomAtLast
+    ].
+    lastIndex := lastIndex + 1.
+    contentsArray at:lastIndex put:anObject.
+    ^ anObject
+!
+
+addFirst:anObject
+    "add the argument, anObject to the beginning of the collection.
+     Return the argument, anObject."
+
+    (firstIndex == 1) ifTrue:[
+        self makeRoomAtFront
+    ].
+    firstIndex := firstIndex - 1.
+    contentsArray at:firstIndex put:anObject.
+    ^ anObject
+!
+
+add:anObject beforeIndex:index
+    "insert the argument, anObject to become located at index.
+     Return the argument, anObject."
+
+    self makeRoomAtIndex:(index - firstIndex + 1).
+    contentsArray at:(index - firstIndex + 1) put:anObject.
+    ^ anObject
+! !
+
+!OrderedCollection methodsFor:'grow & shrink'!
+
+grow:newSize
+    "return the number of elements in the collection"
+
+    |newContents|
+
+    newSize <= (lastIndex - firstIndex + 1) ifTrue:[
+        lastIndex := firstIndex + newSize - 1
+    ] ifFalse:[
+        newContents := Array new:newSize.
+        newContents replaceFrom:1 to:(lastIndex - firstIndex + 1) with:contentsArray.
+        contentsArray := newContents.
+        firstIndex := 1.
+        lastIndex := newSize
+    ]
+! !
+
+!OrderedCollection methodsFor:'accessing'!
+
+at:anInteger
+    "return the element at index, anInteger"
+
+    ((anInteger < 1) or:[(anInteger + firstIndex - 1) > lastIndex])
+    ifTrue:[
+        self errorNoSuchElement
+    ] ifFalse:[
+        ^ contentsArray at:(anInteger + firstIndex - 1)
+    ]
+!
+
+at:anInteger put:anObject
+    "set the element at index, to be anInteger"
+
+    ((anInteger < 1) or:[(anInteger + firstIndex - 1) > lastIndex])
+    ifTrue:[
+        self errorNoSuchElement
+    ] ifFalse:[
+        ^ contentsArray at:(anInteger + firstIndex - 1) put:anObject
+    ]
+! !
+
+!OrderedCollection methodsFor:'private'!
+
+errorNoSuchElement
+    self error:'indexing non existing element'
+!
+
+setFirstIndex:newFirstIndex lastIndex:newLastIndex
+    firstIndex := newFirstIndex.
+    lastIndex := newLastIndex.
+!
+
+makeRoomAtLast
+    "grow the contents for more room at the end"
+
+    |newContents 
+     oldSize "{ Class:SmallInteger }" |
+
+    oldSize := contentsArray size.
+    newContents := Array new:(oldSize * 2).
+    newContents replaceFrom:1 to:oldSize with:contentsArray.
+    contentsArray := newContents
+!
+
+makeRoomAtFront
+    "grow the contents for more room at the beginning"
+
+    |newContents
+     oldSize "{ Class:SmallInteger }" |
+
+    oldSize := contentsArray size.
+    newContents := Array new:(oldSize * 2).
+    newContents replaceFrom:(oldSize + 1) to:(oldSize * 2)
+                       with:contentsArray startingAt:1.
+    contentsArray := newContents.
+    firstIndex := firstIndex + oldSize.
+    lastIndex := lastIndex + oldSize
+!
+
+makeRoomAtIndex:index
+    "grow the contents for inserting at index
+     i.e.
+     #(1 2 3 4 5 6) asOrderedCollection makeRoomAtIndex:3 -> #(1 2 nil 3 4 5 6)
+     #(1 2 3 4 5 6) asOrderedCollection makeRoomAtIndex:1 -> #(nil 1 2 3 4 5 6)
+     #(1 2 3 4 5 6) asOrderedCollection makeRoomAtIndex:7 -> #(1 2 3 4 5 6 nil)"
+
+    |newContents
+     newSize "{ Class:SmallInteger }" 
+     oldSize "{ Class:SmallInteger }" |
+
+    oldSize := contentsArray size.
+    newSize := oldSize + 1.
+    newContents := Array new:newSize.
+    index == 1 ifFalse:[
+        newContents replaceFrom:1 to:index-1 with:contentsArray startingAt:1.
+    ].
+    index == newSize ifFalse:[
+        newContents replaceFrom:index + 1 to:newSize with:contentsArray startingAt:index.
+    ].
+    contentsArray := newContents.
+    lastIndex := lastIndex + 1
+!
+
+initContents:size
+    contentsArray := Array new:size.
+    firstIndex := 1.
+    lastIndex := 0 
+! !
+
+!OrderedCollection methodsFor:'testing'!
+
+includes:anObject
+    "return true if the object is in the collection"
+
+    |index|
+
+    index := contentsArray indexOf:anObject.
+    ^ index between:firstIndex and:lastIndex
+!
+
+identityIndexOf:anObject
+    "return the index of the object, 0 if not in the collection"
+
+    |index|
+
+    index := contentsArray identityIndexOf:anObject.
+    index < firstIndex ifTrue:[^ 0].
+    index > lastIndex ifTrue:[^ 0].
+    ^ index - firstIndex + 1
+!
+
+identityIndexOf:anObject startingAt:startIndex
+    "return the index of the object, 0 if not in the collection"
+
+    |index|
+
+    index := contentsArray identityIndexOf:anObject startingAt:(startIndex + firstIndex - 1).
+    index < firstIndex ifTrue:[^ 0].
+    index > lastIndex ifTrue:[^ 0].
+    ^ index - firstIndex + 1
+!
+
+indexOf:anObject
+    "return the index of the object, 0 if not in the collection"
+
+    |index|
+
+    index := contentsArray indexOf:anObject.
+    index < firstIndex ifTrue:[^ 0].
+    index > lastIndex ifTrue:[^ 0].
+    ^ index - firstIndex + 1
+!
+
+indexOf:anObject startingAt:startIndex
+    "return the index of the object, 0 if not in the collection"
+
+    |index|
+
+    index := contentsArray indexOf:anObject startingAt:(startIndex + firstIndex - 1).
+    index < firstIndex ifTrue:[^ 0].
+    index > lastIndex ifTrue:[^ 0].
+    ^ index - firstIndex + 1
+! !
+
+!OrderedCollection methodsFor:'enumeration'!
+
+do:aBlock
+    "evaluate the argument, aBlock for every element in the collection."
+
+    contentsArray from:firstIndex to:lastIndex do:aBlock
+!
+
+collect:aBlock
+    "evaluate the argument, aBlock for every element in the collection
+     and return a collection of the results"
+
+    |newCollection
+     index  "{ Class:SmallInteger }"
+     end    "{ Class:SmallInteger }" |
+
+    end := lastIndex.
+    newCollection := (self species new).
+    index := firstIndex.
+    [index <= end] whileTrue:[
+        newCollection add:(aBlock value:(contentsArray at:index)).
+        index := index + 1
+    ].
+    ^ newCollection
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrderedCollection.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,375 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+SequenceableCollection subclass:#OrderedCollection
+         instanceVariableNames:'contentsArray firstIndex lastIndex'
+         classVariableNames:''
+         poolDictionaries:''
+         category:'Collections-Sequenceable'
+!
+
+OrderedCollection comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+OrderedCollection have ordered elements. Insertion and removal at both ends
+is possible - therefore they can be used for queues and stacks.
+
+Instance variables:
+
+contentsArray   <Array>         the actual contents
+firstIndex      <SmallInteger>  index of first valid element
+lastIndex       <SmallInteger>  index of last valid element
+
+%W% %E%
+written spring 89 by claus
+'!
+
+!OrderedCollection class methodsFor:'instance creation'!
+
+new:size
+    "create a new OrderedCollection"
+
+    ^ (self basicNew) initContents:size
+!
+
+new
+    "create a new OrderedCollection"
+
+    ^ (self basicNew) initContents:10
+! !
+
+!OrderedCollection methodsFor:'testing'!
+
+size
+    "return the number of elements in the collection"
+
+    ^ lastIndex - firstIndex + 1
+!
+
+isFixedSize
+    "return true if the receiver cannot grow - this will vanish once
+     Arrays and Strings learn how to grow ..."
+
+    ^ false
+! !
+
+!OrderedCollection methodsFor:'copying'!
+
+reverse
+    "return a new collection with all elements of the receiver
+     in reverse order"
+
+    |newCollection
+     size     "{ Class:SmallInteger }"
+     dstIndex "{ Class:SmallInteger }" |
+
+    size := self size.
+    newCollection := self species new:size.
+    newCollection setFirstIndex:firstIndex lastIndex:lastIndex.
+    dstIndex := size.
+    1 to:size do:[:srcIndex |
+        newCollection at:dstIndex put:(self at:srcIndex).
+        dstIndex := dstIndex - 1
+    ].
+    ^ newCollection
+!
+
+, aCollection
+    "return a new collection formed from concatenating the receiver with
+     the argument"
+
+    |newCollection|
+
+    newCollection := self species new:(self size + aCollection size).
+    self do:[:element |
+        newCollection add:element
+    ].
+    aCollection do:[:element |
+        newCollection add:element
+    ].
+    ^ newCollection
+! !
+
+!OrderedCollection methodsFor:'adding & removing'!
+
+removeFirst
+    "remove the first element from the collection; return the element"
+
+    |anObject |
+
+    anObject := contentsArray at:firstIndex.
+    firstIndex := firstIndex + 1.
+    ^ anObject
+!
+
+removeLast
+    "remove the last element from the collection; return the element"
+
+    |anObject |
+
+    anObject := contentsArray at:lastIndex.
+    lastIndex := lastIndex - 1.
+    ^ anObject
+!
+
+remove:anObject ifAbsent:exceptionBlock
+    "remove the first occurrence of anObject from the collection;
+     return the value of exceptionBlock if anObject is not in
+     the collection"
+
+    |index "{ Class:SmallInteger }"|
+
+    index := firstIndex.
+    [index <= lastIndex] whileTrue:[
+        anObject = (contentsArray at:index) ifTrue:[
+            contentsArray replaceFrom:index to:(contentsArray size - 1)
+                            with:contentsArray startingAt:(index + 1).
+            lastIndex := lastIndex - 1.
+            ^ anObject
+        ].
+        index := index + 1
+    ].
+    ^ exceptionBlock value
+!
+
+add:anObject
+    "add the argument, anObject to the end of the collection
+     Return the argument, anObject."
+
+    ^ self addLast:anObject
+!
+
+addLast:anObject
+    "add the argument, anObject to the end of the collection
+     Return the argument, anObject."
+
+    (lastIndex == contentsArray size) ifTrue:[
+        self makeRoomAtLast
+    ].
+    lastIndex := lastIndex + 1.
+    contentsArray at:lastIndex put:anObject.
+    ^ anObject
+!
+
+addFirst:anObject
+    "add the argument, anObject to the beginning of the collection.
+     Return the argument, anObject."
+
+    (firstIndex == 1) ifTrue:[
+        self makeRoomAtFront
+    ].
+    firstIndex := firstIndex - 1.
+    contentsArray at:firstIndex put:anObject.
+    ^ anObject
+!
+
+add:anObject beforeIndex:index
+    "insert the argument, anObject to become located at index.
+     Return the argument, anObject."
+
+    self makeRoomAtIndex:(index - firstIndex + 1).
+    contentsArray at:(index - firstIndex + 1) put:anObject.
+    ^ anObject
+! !
+
+!OrderedCollection methodsFor:'grow & shrink'!
+
+grow:newSize
+    "return the number of elements in the collection"
+
+    |newContents|
+
+    newSize <= (lastIndex - firstIndex + 1) ifTrue:[
+        lastIndex := firstIndex + newSize - 1
+    ] ifFalse:[
+        newContents := Array new:newSize.
+        newContents replaceFrom:1 to:(lastIndex - firstIndex + 1) with:contentsArray.
+        contentsArray := newContents.
+        firstIndex := 1.
+        lastIndex := newSize
+    ]
+! !
+
+!OrderedCollection methodsFor:'accessing'!
+
+at:anInteger
+    "return the element at index, anInteger"
+
+    ((anInteger < 1) or:[(anInteger + firstIndex - 1) > lastIndex])
+    ifTrue:[
+        self errorNoSuchElement
+    ] ifFalse:[
+        ^ contentsArray at:(anInteger + firstIndex - 1)
+    ]
+!
+
+at:anInteger put:anObject
+    "set the element at index, to be anInteger"
+
+    ((anInteger < 1) or:[(anInteger + firstIndex - 1) > lastIndex])
+    ifTrue:[
+        self errorNoSuchElement
+    ] ifFalse:[
+        ^ contentsArray at:(anInteger + firstIndex - 1) put:anObject
+    ]
+! !
+
+!OrderedCollection methodsFor:'private'!
+
+errorNoSuchElement
+    self error:'indexing non existing element'
+!
+
+setFirstIndex:newFirstIndex lastIndex:newLastIndex
+    firstIndex := newFirstIndex.
+    lastIndex := newLastIndex.
+!
+
+makeRoomAtLast
+    "grow the contents for more room at the end"
+
+    |newContents 
+     oldSize "{ Class:SmallInteger }" |
+
+    oldSize := contentsArray size.
+    newContents := Array new:(oldSize * 2).
+    newContents replaceFrom:1 to:oldSize with:contentsArray.
+    contentsArray := newContents
+!
+
+makeRoomAtFront
+    "grow the contents for more room at the beginning"
+
+    |newContents
+     oldSize "{ Class:SmallInteger }" |
+
+    oldSize := contentsArray size.
+    newContents := Array new:(oldSize * 2).
+    newContents replaceFrom:(oldSize + 1) to:(oldSize * 2)
+                       with:contentsArray startingAt:1.
+    contentsArray := newContents.
+    firstIndex := firstIndex + oldSize.
+    lastIndex := lastIndex + oldSize
+!
+
+makeRoomAtIndex:index
+    "grow the contents for inserting at index
+     i.e.
+     #(1 2 3 4 5 6) asOrderedCollection makeRoomAtIndex:3 -> #(1 2 nil 3 4 5 6)
+     #(1 2 3 4 5 6) asOrderedCollection makeRoomAtIndex:1 -> #(nil 1 2 3 4 5 6)
+     #(1 2 3 4 5 6) asOrderedCollection makeRoomAtIndex:7 -> #(1 2 3 4 5 6 nil)"
+
+    |newContents
+     newSize "{ Class:SmallInteger }" 
+     oldSize "{ Class:SmallInteger }" |
+
+    oldSize := contentsArray size.
+    newSize := oldSize + 1.
+    newContents := Array new:newSize.
+    index == 1 ifFalse:[
+        newContents replaceFrom:1 to:index-1 with:contentsArray startingAt:1.
+    ].
+    index == newSize ifFalse:[
+        newContents replaceFrom:index + 1 to:newSize with:contentsArray startingAt:index.
+    ].
+    contentsArray := newContents.
+    lastIndex := lastIndex + 1
+!
+
+initContents:size
+    contentsArray := Array new:size.
+    firstIndex := 1.
+    lastIndex := 0 
+! !
+
+!OrderedCollection methodsFor:'testing'!
+
+includes:anObject
+    "return true if the object is in the collection"
+
+    |index|
+
+    index := contentsArray indexOf:anObject.
+    ^ index between:firstIndex and:lastIndex
+!
+
+identityIndexOf:anObject
+    "return the index of the object, 0 if not in the collection"
+
+    |index|
+
+    index := contentsArray identityIndexOf:anObject.
+    index < firstIndex ifTrue:[^ 0].
+    index > lastIndex ifTrue:[^ 0].
+    ^ index - firstIndex + 1
+!
+
+identityIndexOf:anObject startingAt:startIndex
+    "return the index of the object, 0 if not in the collection"
+
+    |index|
+
+    index := contentsArray identityIndexOf:anObject startingAt:(startIndex + firstIndex - 1).
+    index < firstIndex ifTrue:[^ 0].
+    index > lastIndex ifTrue:[^ 0].
+    ^ index - firstIndex + 1
+!
+
+indexOf:anObject
+    "return the index of the object, 0 if not in the collection"
+
+    |index|
+
+    index := contentsArray indexOf:anObject.
+    index < firstIndex ifTrue:[^ 0].
+    index > lastIndex ifTrue:[^ 0].
+    ^ index - firstIndex + 1
+!
+
+indexOf:anObject startingAt:startIndex
+    "return the index of the object, 0 if not in the collection"
+
+    |index|
+
+    index := contentsArray indexOf:anObject startingAt:(startIndex + firstIndex - 1).
+    index < firstIndex ifTrue:[^ 0].
+    index > lastIndex ifTrue:[^ 0].
+    ^ index - firstIndex + 1
+! !
+
+!OrderedCollection methodsFor:'enumeration'!
+
+do:aBlock
+    "evaluate the argument, aBlock for every element in the collection."
+
+    contentsArray from:firstIndex to:lastIndex do:aBlock
+!
+
+collect:aBlock
+    "evaluate the argument, aBlock for every element in the collection
+     and return a collection of the results"
+
+    |newCollection
+     index  "{ Class:SmallInteger }"
+     end    "{ Class:SmallInteger }" |
+
+    end := lastIndex.
+    newCollection := (self species new).
+    index := firstIndex.
+    [index <= end] whileTrue:[
+        newCollection add:(aBlock value:(contentsArray at:index)).
+        index := index + 1
+    ].
+    ^ newCollection
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PipeStr.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,135 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+NonPositionableExternalStream subclass:#PipeStream
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Streams-External'
+!
+
+PipeStream comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+%W% %E%
+'!
+
+%{
+#include <stdio.h>
+#ifndef transputer
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif
+%}
+
+!PipeStream class methodsFor:'instance creation'!
+
+writingTo:command
+    "create and return a new pipeStream which can write to the unix command
+     given by command."
+
+    ^ (self basicNew) writingTo:command
+
+    "PipeStream writingTo:'sort'"
+!
+
+readingFrom:command
+    "create and return a new pipeStream which can read from the unix command
+     given by command."
+
+    ^ (self basicNew) readingFrom:command
+
+    "PipeStream readingFrom:'ls'"
+! !
+
+!PipeStream methodsFor:'instance release'!
+
+closeFile
+    "low level close - redefined since we close a pipe here"
+
+%{  /* NOCONTEXT */
+#ifndef transputer
+    pclose(MKFD(_INST(filePointer)));
+#endif
+%}
+! !
+
+!PipeStream methodsFor:'private'!
+
+openPipeFor:commandString withMode:mode
+    "open a pipe to commandString; mode may be 'r' or 'w'"
+
+    |retVal|
+
+    unBuffered := true.
+%{
+#ifndef transputer
+    {
+        FILE *f;
+        extern OBJ ErrorNumber;
+        extern errno;
+
+        if (_isString(commandString) && _isString(mode)) {
+            f = (FILE *)popen((char *) _stringVal(commandString),
+			      (char *) _stringVal(mode));
+            if (f == NULL) {
+                ErrorNumber = _MKSMALLINT(errno);
+            } else {
+                _INST(filePointer) = MKOBJ(f);
+                retVal = self;
+            }
+        }
+    }
+#endif
+%}
+.
+    retVal notNil ifTrue:[
+        lobby register:self
+    ].
+    ^ retVal
+!
+
+readingFrom:command
+    "setup the receiver to read from command"
+
+    self readonly.
+    ^ self openPipeFor:command withMode:'r'
+!
+
+writingTo:command
+    "setup the receiver to write to command"
+
+    self writeonly.
+    ^ self openPipeFor:command withMode:'w'
+! !
+
+!PipeStream methodsFor:'redefined basic'!
+
+size
+    "redefined since pipes have no size"
+
+    ^ self shouldNotImplement
+!
+
+position:newpos
+    "redefined since pipes cannot be positioned"
+
+    ^ self shouldNotImplement
+!
+
+position
+    "redefined since pipes have no position"
+
+    ^ self shouldNotImplement
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PipeStream.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,135 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+NonPositionableExternalStream subclass:#PipeStream
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Streams-External'
+!
+
+PipeStream comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+%W% %E%
+'!
+
+%{
+#include <stdio.h>
+#ifndef transputer
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif
+%}
+
+!PipeStream class methodsFor:'instance creation'!
+
+writingTo:command
+    "create and return a new pipeStream which can write to the unix command
+     given by command."
+
+    ^ (self basicNew) writingTo:command
+
+    "PipeStream writingTo:'sort'"
+!
+
+readingFrom:command
+    "create and return a new pipeStream which can read from the unix command
+     given by command."
+
+    ^ (self basicNew) readingFrom:command
+
+    "PipeStream readingFrom:'ls'"
+! !
+
+!PipeStream methodsFor:'instance release'!
+
+closeFile
+    "low level close - redefined since we close a pipe here"
+
+%{  /* NOCONTEXT */
+#ifndef transputer
+    pclose(MKFD(_INST(filePointer)));
+#endif
+%}
+! !
+
+!PipeStream methodsFor:'private'!
+
+openPipeFor:commandString withMode:mode
+    "open a pipe to commandString; mode may be 'r' or 'w'"
+
+    |retVal|
+
+    unBuffered := true.
+%{
+#ifndef transputer
+    {
+        FILE *f;
+        extern OBJ ErrorNumber;
+        extern errno;
+
+        if (_isString(commandString) && _isString(mode)) {
+            f = (FILE *)popen((char *) _stringVal(commandString),
+			      (char *) _stringVal(mode));
+            if (f == NULL) {
+                ErrorNumber = _MKSMALLINT(errno);
+            } else {
+                _INST(filePointer) = MKOBJ(f);
+                retVal = self;
+            }
+        }
+    }
+#endif
+%}
+.
+    retVal notNil ifTrue:[
+        lobby register:self
+    ].
+    ^ retVal
+!
+
+readingFrom:command
+    "setup the receiver to read from command"
+
+    self readonly.
+    ^ self openPipeFor:command withMode:'r'
+!
+
+writingTo:command
+    "setup the receiver to write to command"
+
+    self writeonly.
+    ^ self openPipeFor:command withMode:'w'
+! !
+
+!PipeStream methodsFor:'redefined basic'!
+
+size
+    "redefined since pipes have no size"
+
+    ^ self shouldNotImplement
+!
+
+position:newpos
+    "redefined since pipes cannot be positioned"
+
+    ^ self shouldNotImplement
+!
+
+position
+    "redefined since pipes have no position"
+
+    ^ self shouldNotImplement
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Point.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,398 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+ArithmeticValue subclass:#Point
+       instanceVariableNames:'x y'
+       classVariableNames:'PointZero PointOne'
+       poolDictionaries:''
+       category:'Graphics-Primitives'
+!
+
+Point comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+I represent a point in 2D space. Or I can be used to represent
+an extent (of a rectangle for example), in which case my x-coordinate 
+represents the width, and y-coordinate the height of something.
+
+The x and y coordinate are usually numbers.
+
+Instance variables:
+
+x              <Number>        the x-coordinate of myself
+y              <Number>        the y-coordinate of myself
+
+%W% %E%
+written 89 by claus
+'!
+
+!Point class methodsFor:'initialization'!
+
+initialize
+    PointZero := 0 @ 0.
+    PointOne  := 1 @ 1
+! !
+
+!Point class methodsFor:'constants'!
+
+zero
+    ^ PointZero
+!
+
+unity
+    ^ PointOne
+! !
+
+!Point class methodsFor:'instance creation'!
+
+x:newX y:newY
+    "create and return a new point with coordinates newX and newY"
+
+%{  /* NOCONTEXT */
+    extern char *newNextPtr, *newEndPtr;
+
+    if (self == Point) {
+        if (_CanDoQuickNew(sizeof(struct point))) {
+            OBJ newPoint;
+
+            _qCheckedAlignedNew(newPoint, sizeof(struct point), __context);
+            _InstPtr(newPoint)->o_class = Point;
+            _PointInstPtr(newPoint)->p_x = newX;
+            _PointInstPtr(newPoint)->p_y = newY;
+	    /* no store check needed - its definitely in newSpace */
+            RETURN ( newPoint );
+        }
+    }
+%}
+.
+    ^ (self basicNew) x:newX y:newY
+!
+
+readFrom:aStream
+    "return the next Point from the (character-)stream aStream;
+     skipping all whitespace first; return nil if no point"
+
+    |newX newY|
+
+    newX := Number readFrom:aStream.
+    newX isNil ifTrue:[^ nil].
+    (aStream skipSeparators ~~ $@) ifTrue:[^nil].
+    aStream next.
+    newY := Number readFrom:aStream.
+    newY isNil ifTrue:[^ nil].
+    ^ (self basicNew) x:newX y:newY
+! !
+
+!Point methodsFor:'accessing'!
+
+x
+    "return the x coordinate"
+
+    ^x
+!
+
+y
+    "return the y coordinate"
+
+    ^y
+!
+
+x:newX
+    "set the x coordinate to be the argument, aNumber"
+
+    x := newX
+!
+
+y:newY
+    "set the y coordinate to be the argument, aNumber"
+
+    y := newY
+!
+
+x:newX y:newY
+    "set both the x and y coordinates"
+
+    x := newX.
+    y := newY
+! !
+
+!Point methodsFor:'comparing'!
+
+hash
+    "return a number for hashing"
+
+    ^ (x hash) bitXor:(y hash)
+!
+
+< aPoint
+    "return true if the receiver is above and to the left
+     of the argument, aPoint"
+
+    |p|
+
+    p := aPoint asPoint.
+    x >= (p x) ifTrue:[^ false].
+    y >= (p y) ifTrue:[^ false].
+    ^ true
+!
+
+> aPoint
+    "return true if  the receiver is below and to the right
+     of the argument, aPoint"
+
+    |p|
+
+    p := aPoint asPoint.
+    x <= (p x) ifTrue:[^ false].
+    y <= (p y) ifTrue:[^ false].
+    ^ true
+!
+
+= aPoint
+    "return true if the receiver represents the same point as
+     the argument, aPoint"
+
+    |p|
+
+    (aPoint isMemberOf:Point) ifTrue:[
+	x ~~ (aPoint x) ifTrue:[^ false].
+	y ~~ (aPoint y) ifTrue:[^ false].
+	^ true
+    ].
+    aPoint respondsToArithmetic ifFalse:[ ^ false].
+    p := aPoint asPoint.
+    x ~~ (p x) ifTrue:[^ false].
+    y ~~ (p y) ifTrue:[^ false].
+    ^ true
+!
+
+max:aPoint
+    "return the lower right corner of the rectangle uniquely defined by
+     the receiver and the argument, aPoint"
+
+    |p maxX maxY|
+
+    p := aPoint asPoint.
+    maxX := x max:(p x).
+    maxY := y max:(p y).
+    ^ maxX @ maxY
+!
+
+min:aPoint
+    "return the upper left corner of the rectangle uniquely defined by
+     the receiver and the argument, aPoint"
+
+    |p minX minY|
+
+    p := aPoint asPoint.
+    minX := x min:(p x).
+    minY := y min:(p y).
+    ^ minX @ minY
+! !
+
+!Point methodsFor:'converting'!
+
+generality
+    ^ 120
+!
+
+coerce:anObject
+    ^ anObject asPoint
+!
+
+asPoint
+    "return the receiver as Point - this is the receiver"
+
+    ^ self
+!
+
+asRectangle
+    "return a zero-width rectangle consisting of origin 
+     and corner beeing the receiver"
+
+    ^ self corner:self
+! !
+
+!Point methodsFor:'creating rectangles'!
+
+corner:aPoint
+    "return a rectangle whose origin is self and corner is aPoint"
+
+    ^ Rectangle origin:self corner:aPoint
+!
+
+extent:aPoint
+    "return a rectangle whose origin is self and extent is aPoint"
+
+    ^ Rectangle origin:self extent:aPoint
+! !
+
+!Point methodsFor:'transformations'!
+
++ scale 
+    "Return a new Point that is the sum of the 
+     receiver and scale (which is a Point or Number)."
+
+    |scalePoint|
+
+    scalePoint := scale asPoint.
+    ^ (x + scalePoint x) @ (y + scalePoint y)
+!
+
+- scale 
+    "Return a new Point that is the difference of the 
+     receiver and scale (which is a Point or Number)."
+
+    |scalePoint|
+
+    scalePoint := scale asPoint.
+    ^ (x - scalePoint x) @ (y - scalePoint y)
+!
+
+* scale 
+    "Return a new Point that is the product of the 
+     receiver and scale (which is a Point or Number)."
+
+    |scalePoint|
+
+    scalePoint := scale asPoint.
+    ^ (x * scalePoint x) @ (y * scalePoint y)
+!
+
+/ scale 
+    "Return a new Point that is the integer quotient of the 
+     receiver and scale (which is a Point or Number)."
+
+    |scalePoint|
+
+    scalePoint := scale asPoint.
+    ^ (x / scalePoint x) @ (y / scalePoint y)
+!
+
+// scale 
+    "Return a new Point that is the quotient of the 
+     receiver and scale (which is a Point or Number)."
+
+    |scalePoint|
+
+    scalePoint := scale asPoint.
+    ^ (x // scalePoint x) @ (y // scalePoint y)
+!
+
+reciprocal
+    "return a new point where the coordinates are
+     the reciproce of mine"
+
+    ^ (1 / x) @ (1 / y)
+!
+
+negated
+    "return a new point with my coordinates negated i.e.
+     the receiver mirrored at the origin"
+
+    ^ (x negated) @ (y negated)
+!
+
+scaleBy:aScale
+    "Return a new Point that is the product of the 
+     receiver and scale (which is a Point or Number)."
+
+    ^ self * aScale
+!
+
+translateBy:anOffset
+    "Return a new Point that is the sum of the 
+     receiver and scale (which is a Point or Number)."
+
+    ^ self + anOffset
+! !
+
+!Point methodsFor:'misc'!
+
+dist:aPoint 
+    "return the distance between aPoint and the receiver."
+
+    ^ (aPoint - self) r
+!
+
+dotProduct:aPoint 
+    "return a Number that is the dot product of the receiver and
+     the argument, aPoint.  That is, the two points are
+     multipled and the coordinates of the result summed."
+
+    |temp|
+
+    temp := self * aPoint.
+    ^ temp x abs + temp y abs
+!
+
+r
+    "return the receiver's radius in polar coordinate system."
+
+    ^ (self dotProduct:self) sqrt
+!
+
+abs
+    "return a new point with my coordinates taken from the absolute values"
+
+    ^ (x abs) @ (y abs)
+!
+
+truncated
+    "return a new point with my coordinates truncated as integer"
+
+    ^ (x truncated) @ (y truncated)
+!
+
+rounded
+    "return a new point with my coordinates rounded to the next integer
+     coordinated (use for gridding)"
+
+    ^ (x rounded) @ (y rounded)
+!
+
+grid:gridPoint
+    "return a new point with coordinates grided (i.e. rounded to the
+     nearest point on the grid)"
+
+    |newX newY gridX gridY|
+
+    gridX := gridPoint x.
+    (gridX <= 1) ifTrue:[
+        newX := x asInteger
+    ] ifFalse:[
+        newX := ((x + (gridX // 2)) // gridX) * gridX
+    ].
+    gridY := gridPoint y.
+    (gridY <= 1) ifTrue:[
+        newY := y asInteger
+    ] ifFalse:[
+        newY := ((y + (gridY // 2)) // gridY) * gridY
+    ].
+    ^ newX @ newY
+! !
+
+!Point methodsFor:'printing & storing'!
+
+printString
+    "return my printString"
+
+    ^ x printString , '@' , y printString
+!
+
+storeString
+    "return my storeString"
+
+    ^ self printString
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PosStream.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,486 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Stream subclass:#PositionableStream
+       instanceVariableNames:'collection position readLimit continueBlock abortBlock'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Streams'
+!
+
+PositionableStream comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+Instances of myself allow positioning the read pointer.
+I also add methods for source-chunk reading and writing
+and for filing-in/out of source code.
+
+%W% %E%
+
+TODO
+   change to use signals for error handling during fileIn
+   (get rid of continue/abort blocks)
+'!
+
+!PositionableStream class methodsFor:'constants'!
+
+chunkSeparator
+    "return the chunk-separation character"
+
+    ^ $!!
+! !
+
+!PositionableStream class methodsFor:'instance creation'!
+
+on:aCollection
+    "return a new PositionableStream streaming on aCollection"
+
+    ^ (self basicNew) on:aCollection
+!
+
+on:aCollection from:first to:last
+    "return a new PositionableStream streaming on aCollection
+     from first to last"
+
+    |newStream|
+
+    newStream := (self basicNew) on:aCollection.
+    newStream position:first.
+    newStream readLimit:last.
+    ^ newStream
+! !
+
+!PositionableStream methodsFor:'private'!
+
+on:aCollection
+    "setup for streaming on aCollection"
+
+    collection := aCollection.
+    readLimit := aCollection size.
+    position := 1
+!
+
+positionError
+    "report an error when positioning past the end"
+
+    ^ self error:'cannot position past end of collection'
+! !
+
+!PositionableStream methodsFor:'accessing'!
+
+contents
+    "return the entire contents of the stream"
+
+    ^ collection
+!
+
+peek
+    "look ahead for and return the next element"
+
+    |peekObject|
+
+    peekObject := self next.
+    self position:(self position - 1).
+    ^ peekObject
+!
+
+peekFor:something
+    "return true and move past if next == something"
+
+    self next == something ifTrue:[
+        ^ true
+    ].
+    self position:(self position - 1).
+    ^ false
+!
+
+readLimit:aNumber
+    "set the read-limit"
+
+    readLimit := aNumber
+!
+
+upTo:element
+    "return a collection of the elements up-to
+     (but excluding) the argument, element.
+     Return nil if the stream-end is reached before."
+
+    |newColl e|
+
+    newColl := collection species new.
+    e := self next.
+    [e = element] whileFalse:[
+        newColl := newColl copyWith:e.
+        e := self next.
+        self atEnd ifTrue:[^ nil]
+    ].
+    ^ newColl
+
+    "(ReadStream on:'1234567890') upTo:$5"
+    "(ReadStream on:'123456') upTo:$7"
+! !
+
+!PositionableStream methodsFor:'testing'!
+
+atEnd
+    "return true, if the read-position is at the end"
+
+    ^ position > readLimit
+!
+
+isEmpty
+    "return true, if the contents of the stream is empty"
+
+    ^ readLimit == 0
+! !
+
+!PositionableStream methodsFor:'positioning'!
+
+position
+    "return the read position"
+
+    ^ position
+!
+
+position:index
+    "set the read position"
+
+    (index > (readLimit + 1)) ifTrue: [^ self positionError].
+    position := index
+!
+
+reset
+    "set the read position to the beginning of the collection"
+
+    position := 1
+!
+
+setToEnd
+    "set the read position to the end of the collection"
+
+    position := readLimit
+!
+
+skip:numberToSkip
+    "skip the next numberToSkip elements"
+
+    self position:(position + numberToSkip)
+! !
+
+!PositionableStream methodsFor:'fileIn-Out'!
+
+skipSeparators
+    "skip all whitespace; so that next will return next non-white-space
+     element"
+
+    |nextOne|
+
+    nextOne := self peek.
+    [(nextOne notNil) and:[nextOne isSeparator]] whileTrue:[
+        self next.
+        nextOne := self peek
+    ].
+    ^ nextOne
+!
+
+skipSeparatorsExceptCR
+    "skip all whitespace except newlines;
+     next will return next non-white-space element"
+
+    |nextOne|
+
+    nextOne := self peek.
+    [(nextOne notNil) and:[nextOne isSeparator]] whileTrue:[
+        nextOne isEndOfLineCharacter ifTrue:[^ nextOne].
+        self next.
+        nextOne := self peek
+    ].
+    ^ nextOne
+!
+
+skipFor:anObject
+    "skip all objects up-to and including anObject; return the element
+     after"
+
+    |nextOne|
+
+    nextOne := self next.
+    [nextOne ~~ anObject] whileTrue:[
+        self atEnd ifTrue:[^ nil].
+        self next.
+        nextOne := self peek 
+    ].
+    ^ self next
+!
+
+nextChunk
+    "return the next chunk, i.e. all characters up to the next
+     non-doubled exclamation mark; undouble doubled exclamation marks"
+
+    |theString sep newString done thisChar nextChar inPrimitive
+     index    "{ Class:SmallInteger }"
+     currSize "{ Class:SmallInteger }" |
+
+    sep := self class chunkSeparator.
+    theString := String new:500.
+    currSize := 500.
+    thisChar := self skipSeparators.
+    thisChar := self next.
+    index := 0.
+    done := false.
+    inPrimitive := false.
+
+    [done] whileFalse:[
+        ((index + 2) <= currSize) ifFalse:[
+            newString := String new:(currSize * 2).
+            newString replaceFrom:1 to:currSize with:theString.
+            currSize := currSize * 2.
+            theString := newString
+        ].
+        thisChar isNil ifTrue:[
+            done := true
+        ] ifFalse:[
+            (thisChar == $% ) ifTrue:[
+                nextChar := self peek.
+                (nextChar == ${ ) ifTrue:[
+                    inPrimitive := true.
+                    index := index + 1.
+                    theString at:index put:thisChar.
+                    thisChar := self next
+                ] ifFalse:[
+                    (nextChar == $} ) ifTrue:[
+                        inPrimitive := false.
+                        index := index + 1.
+                        theString at:index put:thisChar.
+                        thisChar := self next
+                    ]
+                ]
+            ] ifFalse:[
+                inPrimitive ifFalse:[
+                    (thisChar == sep) ifTrue:[
+                        (self peek == sep) ifFalse:[
+                            done := true
+                        ] ifTrue:[
+                            self next
+                        ]
+                    ]
+                ]
+            ]
+        ].
+        done ifFalse:[
+            index := index + 1.
+            theString at:index put:thisChar.
+            thisChar := self next
+        ]
+    ].
+    (index == 0) ifTrue:[^ ''].
+    ^ theString copyFrom:1 to:index
+!
+
+nextChunkPut:aString
+    "put aString as a chunk onto the receiver;
+     double all exclamation marks and append an exclamation mark"
+
+    |sep gotPercent inPrimitive character
+     index    "{ Class:SmallInteger }"
+     endIndex "{ Class:SmallInteger }"
+     next     "{ Class:SmallInteger }" |
+
+    sep := self class chunkSeparator.
+    inPrimitive := false.
+    gotPercent := false.
+    index := 1.
+    endIndex := aString size.
+
+    [index <= endIndex] whileTrue:[
+        next := aString indexOf:$% startingAt:index ifAbsent:[endIndex + 1].
+        next := next min:
+                (aString indexOf:${ startingAt:index ifAbsent:[endIndex + 1]).
+        next := next min:
+                (aString indexOf:$} startingAt:index ifAbsent:[endIndex + 1]).
+        next := next min:
+                (aString indexOf:sep startingAt:index ifAbsent:[endIndex + 1]).
+
+        ((index == 1) and:[next == (endIndex + 1)]) ifTrue:[
+            self nextPutAll:aString
+        ] ifFalse:[
+            self nextPutAll:(aString copyFrom:index to:(next - 1))
+        ].
+
+        index := next.
+        (index <= endIndex) ifTrue:[
+            character := aString at:index.
+            (character == $% ) ifTrue:[
+                gotPercent := true
+            ] ifFalse:[
+                (character == ${ ) ifTrue:[
+                    gotPercent ifTrue:[
+                        inPrimitive := true
+                    ]
+                ] ifFalse:[
+                    (character == $} ) ifTrue:[
+                        gotPercent ifTrue:[
+                            inPrimitive := false
+                        ]
+                    ] ifFalse:[
+                        inPrimitive ifFalse:[
+                            (character == sep) ifTrue:[
+                                self nextPut:sep
+                            ]
+                        ]
+                    ]
+                ].
+                gotPercent := false
+            ].
+            self nextPut:character.
+            index := index + 1
+        ]
+    ].
+    self nextPut:sep
+!
+
+fileInNextChunkNotifying:someone
+    "read next chunk, evaluate it and return the result;
+     someone is notified of errors"
+
+    |aString sawExcla sep|
+
+    sep := self class chunkSeparator.
+    self skipSeparators.
+    self atEnd ifFalse:[
+        sawExcla := self peekFor:sep.
+        aString := self nextChunk.
+        aString size ~~ 0 ifTrue:[
+            sawExcla ifFalse:[
+                ^ Compiler evaluate:aString notifying:someone
+            ].
+            ^ (Compiler evaluate:aString notifying:someone) 
+                       fileInFrom:self notifying:someone
+        ]
+    ].
+    ^ nil
+!
+
+fileInNextChunk
+    "read next chunk, evaluate it and return the result"
+
+    ^ self fileInNextChunkNotifying:nil
+!
+
+fileInNotifying:someone
+    "file in from the receiver, i.e. read chunks and evaluate them -
+     return the value of the last chunk; someone is notified of errors"
+
+    |lastValue|
+
+    self position:1.
+    abortBlock := [^ nil].
+    continueBlock := [].
+    Smalltalk at:#ErrorHandler put:self.
+    [self atEnd] whileFalse:[
+        lastValue := self fileInNextChunkNotifying:someone
+    ].
+    Smalltalk at:#ErrorHandler put:nil.
+    ^ lastValue
+!
+
+fileIn
+    "file in from the receiver, i.e. read chunks and evaluate them -
+     return the value of the last chunk"
+
+    ^ self fileInNotifying:self
+!
+
+askForDebug:message
+    |box|
+
+    box := OptionBox title:message numberOfOptions:3.
+    box actions:(Array with:[^ #abort]
+                       with:[^ #debug]
+                       with:[^ #continue]).
+    box buttonTitles:#('abort' 'debug' 'continue').
+    box showAtPointer.
+    ^ #abort
+!
+
+catch:aSymbol with:aMessage for:anObject
+    "this one is sent when an error occurs while filing in -
+     we dont want a debugger to come up but simply notify
+     the error (also on the Transcript so you have a trace of it)"
+
+    |message action|
+
+    Smalltalk at:#ErrorHandler put:nil.
+    (aSymbol == #doesNotUnderstand:) ifTrue:[
+        anObject isNil ifTrue:[
+            "try to give a bit more detail on what went wrong"
+            (Metaclass respondsTo:(aMessage selector)) ifTrue:[
+                ('subclass:*' match:(aMessage selector)) ifTrue:[
+                    message := 'no superclass for ' , (aMessage arguments at:1)
+                ] ifFalse:[
+                    message := 'definitions for nonexisting class'
+                ]
+            ] ifFalse:[
+                message := 'bad message: ' , aMessage selector, ' to UndefinedObject'
+            ]
+        ] ifFalse:[
+            message := 'bad message: ' , aMessage selector ,
+                      ' to ' , anObject classNameWithArticle
+        ]
+    ] ifFalse:[
+        (aSymbol == #error:) ifTrue:[
+            message := aMessage
+        ] ifFalse:[
+            message := 'during fileIn'
+        ]
+    ].
+    message := 'Error: ' , message.
+    Transcript showCr:message.
+
+    YesNoBox notNil ifTrue:[
+        action := self askForDebug:message.
+        action == #debug ifTrue:[
+            Debugger enterWithMessage:message
+        ].
+        action == #continue ifTrue:[
+            continueBlock value
+        ].
+    ] ifFalse:[
+        self notify:message
+    ].
+
+    abortBlock value.
+    ^ nil
+!
+
+error:aMessage position:position to:endPos
+    "error notification during fileIn with no requestor"
+
+    position printOn:Transcript.
+    Transcript show:' '.
+    Transcript showCr:aMessage.
+    ^ false
+!
+
+correctableError:aMessage position:position to:endPos
+    "error notification during fileIn with no requestor"
+
+    ^ self error:aMessage position:position to:endPos
+!
+
+warning:aMessage position:position to:endPos
+    "warning notification during fileIn with no requestor - ignore it"
+
+    ^ self
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PositionableStream.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,486 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Stream subclass:#PositionableStream
+       instanceVariableNames:'collection position readLimit continueBlock abortBlock'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Streams'
+!
+
+PositionableStream comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+Instances of myself allow positioning the read pointer.
+I also add methods for source-chunk reading and writing
+and for filing-in/out of source code.
+
+%W% %E%
+
+TODO
+   change to use signals for error handling during fileIn
+   (get rid of continue/abort blocks)
+'!
+
+!PositionableStream class methodsFor:'constants'!
+
+chunkSeparator
+    "return the chunk-separation character"
+
+    ^ $!!
+! !
+
+!PositionableStream class methodsFor:'instance creation'!
+
+on:aCollection
+    "return a new PositionableStream streaming on aCollection"
+
+    ^ (self basicNew) on:aCollection
+!
+
+on:aCollection from:first to:last
+    "return a new PositionableStream streaming on aCollection
+     from first to last"
+
+    |newStream|
+
+    newStream := (self basicNew) on:aCollection.
+    newStream position:first.
+    newStream readLimit:last.
+    ^ newStream
+! !
+
+!PositionableStream methodsFor:'private'!
+
+on:aCollection
+    "setup for streaming on aCollection"
+
+    collection := aCollection.
+    readLimit := aCollection size.
+    position := 1
+!
+
+positionError
+    "report an error when positioning past the end"
+
+    ^ self error:'cannot position past end of collection'
+! !
+
+!PositionableStream methodsFor:'accessing'!
+
+contents
+    "return the entire contents of the stream"
+
+    ^ collection
+!
+
+peek
+    "look ahead for and return the next element"
+
+    |peekObject|
+
+    peekObject := self next.
+    self position:(self position - 1).
+    ^ peekObject
+!
+
+peekFor:something
+    "return true and move past if next == something"
+
+    self next == something ifTrue:[
+        ^ true
+    ].
+    self position:(self position - 1).
+    ^ false
+!
+
+readLimit:aNumber
+    "set the read-limit"
+
+    readLimit := aNumber
+!
+
+upTo:element
+    "return a collection of the elements up-to
+     (but excluding) the argument, element.
+     Return nil if the stream-end is reached before."
+
+    |newColl e|
+
+    newColl := collection species new.
+    e := self next.
+    [e = element] whileFalse:[
+        newColl := newColl copyWith:e.
+        e := self next.
+        self atEnd ifTrue:[^ nil]
+    ].
+    ^ newColl
+
+    "(ReadStream on:'1234567890') upTo:$5"
+    "(ReadStream on:'123456') upTo:$7"
+! !
+
+!PositionableStream methodsFor:'testing'!
+
+atEnd
+    "return true, if the read-position is at the end"
+
+    ^ position > readLimit
+!
+
+isEmpty
+    "return true, if the contents of the stream is empty"
+
+    ^ readLimit == 0
+! !
+
+!PositionableStream methodsFor:'positioning'!
+
+position
+    "return the read position"
+
+    ^ position
+!
+
+position:index
+    "set the read position"
+
+    (index > (readLimit + 1)) ifTrue: [^ self positionError].
+    position := index
+!
+
+reset
+    "set the read position to the beginning of the collection"
+
+    position := 1
+!
+
+setToEnd
+    "set the read position to the end of the collection"
+
+    position := readLimit
+!
+
+skip:numberToSkip
+    "skip the next numberToSkip elements"
+
+    self position:(position + numberToSkip)
+! !
+
+!PositionableStream methodsFor:'fileIn-Out'!
+
+skipSeparators
+    "skip all whitespace; so that next will return next non-white-space
+     element"
+
+    |nextOne|
+
+    nextOne := self peek.
+    [(nextOne notNil) and:[nextOne isSeparator]] whileTrue:[
+        self next.
+        nextOne := self peek
+    ].
+    ^ nextOne
+!
+
+skipSeparatorsExceptCR
+    "skip all whitespace except newlines;
+     next will return next non-white-space element"
+
+    |nextOne|
+
+    nextOne := self peek.
+    [(nextOne notNil) and:[nextOne isSeparator]] whileTrue:[
+        nextOne isEndOfLineCharacter ifTrue:[^ nextOne].
+        self next.
+        nextOne := self peek
+    ].
+    ^ nextOne
+!
+
+skipFor:anObject
+    "skip all objects up-to and including anObject; return the element
+     after"
+
+    |nextOne|
+
+    nextOne := self next.
+    [nextOne ~~ anObject] whileTrue:[
+        self atEnd ifTrue:[^ nil].
+        self next.
+        nextOne := self peek 
+    ].
+    ^ self next
+!
+
+nextChunk
+    "return the next chunk, i.e. all characters up to the next
+     non-doubled exclamation mark; undouble doubled exclamation marks"
+
+    |theString sep newString done thisChar nextChar inPrimitive
+     index    "{ Class:SmallInteger }"
+     currSize "{ Class:SmallInteger }" |
+
+    sep := self class chunkSeparator.
+    theString := String new:500.
+    currSize := 500.
+    thisChar := self skipSeparators.
+    thisChar := self next.
+    index := 0.
+    done := false.
+    inPrimitive := false.
+
+    [done] whileFalse:[
+        ((index + 2) <= currSize) ifFalse:[
+            newString := String new:(currSize * 2).
+            newString replaceFrom:1 to:currSize with:theString.
+            currSize := currSize * 2.
+            theString := newString
+        ].
+        thisChar isNil ifTrue:[
+            done := true
+        ] ifFalse:[
+            (thisChar == $% ) ifTrue:[
+                nextChar := self peek.
+                (nextChar == ${ ) ifTrue:[
+                    inPrimitive := true.
+                    index := index + 1.
+                    theString at:index put:thisChar.
+                    thisChar := self next
+                ] ifFalse:[
+                    (nextChar == $} ) ifTrue:[
+                        inPrimitive := false.
+                        index := index + 1.
+                        theString at:index put:thisChar.
+                        thisChar := self next
+                    ]
+                ]
+            ] ifFalse:[
+                inPrimitive ifFalse:[
+                    (thisChar == sep) ifTrue:[
+                        (self peek == sep) ifFalse:[
+                            done := true
+                        ] ifTrue:[
+                            self next
+                        ]
+                    ]
+                ]
+            ]
+        ].
+        done ifFalse:[
+            index := index + 1.
+            theString at:index put:thisChar.
+            thisChar := self next
+        ]
+    ].
+    (index == 0) ifTrue:[^ ''].
+    ^ theString copyFrom:1 to:index
+!
+
+nextChunkPut:aString
+    "put aString as a chunk onto the receiver;
+     double all exclamation marks and append an exclamation mark"
+
+    |sep gotPercent inPrimitive character
+     index    "{ Class:SmallInteger }"
+     endIndex "{ Class:SmallInteger }"
+     next     "{ Class:SmallInteger }" |
+
+    sep := self class chunkSeparator.
+    inPrimitive := false.
+    gotPercent := false.
+    index := 1.
+    endIndex := aString size.
+
+    [index <= endIndex] whileTrue:[
+        next := aString indexOf:$% startingAt:index ifAbsent:[endIndex + 1].
+        next := next min:
+                (aString indexOf:${ startingAt:index ifAbsent:[endIndex + 1]).
+        next := next min:
+                (aString indexOf:$} startingAt:index ifAbsent:[endIndex + 1]).
+        next := next min:
+                (aString indexOf:sep startingAt:index ifAbsent:[endIndex + 1]).
+
+        ((index == 1) and:[next == (endIndex + 1)]) ifTrue:[
+            self nextPutAll:aString
+        ] ifFalse:[
+            self nextPutAll:(aString copyFrom:index to:(next - 1))
+        ].
+
+        index := next.
+        (index <= endIndex) ifTrue:[
+            character := aString at:index.
+            (character == $% ) ifTrue:[
+                gotPercent := true
+            ] ifFalse:[
+                (character == ${ ) ifTrue:[
+                    gotPercent ifTrue:[
+                        inPrimitive := true
+                    ]
+                ] ifFalse:[
+                    (character == $} ) ifTrue:[
+                        gotPercent ifTrue:[
+                            inPrimitive := false
+                        ]
+                    ] ifFalse:[
+                        inPrimitive ifFalse:[
+                            (character == sep) ifTrue:[
+                                self nextPut:sep
+                            ]
+                        ]
+                    ]
+                ].
+                gotPercent := false
+            ].
+            self nextPut:character.
+            index := index + 1
+        ]
+    ].
+    self nextPut:sep
+!
+
+fileInNextChunkNotifying:someone
+    "read next chunk, evaluate it and return the result;
+     someone is notified of errors"
+
+    |aString sawExcla sep|
+
+    sep := self class chunkSeparator.
+    self skipSeparators.
+    self atEnd ifFalse:[
+        sawExcla := self peekFor:sep.
+        aString := self nextChunk.
+        aString size ~~ 0 ifTrue:[
+            sawExcla ifFalse:[
+                ^ Compiler evaluate:aString notifying:someone
+            ].
+            ^ (Compiler evaluate:aString notifying:someone) 
+                       fileInFrom:self notifying:someone
+        ]
+    ].
+    ^ nil
+!
+
+fileInNextChunk
+    "read next chunk, evaluate it and return the result"
+
+    ^ self fileInNextChunkNotifying:nil
+!
+
+fileInNotifying:someone
+    "file in from the receiver, i.e. read chunks and evaluate them -
+     return the value of the last chunk; someone is notified of errors"
+
+    |lastValue|
+
+    self position:1.
+    abortBlock := [^ nil].
+    continueBlock := [].
+    Smalltalk at:#ErrorHandler put:self.
+    [self atEnd] whileFalse:[
+        lastValue := self fileInNextChunkNotifying:someone
+    ].
+    Smalltalk at:#ErrorHandler put:nil.
+    ^ lastValue
+!
+
+fileIn
+    "file in from the receiver, i.e. read chunks and evaluate them -
+     return the value of the last chunk"
+
+    ^ self fileInNotifying:self
+!
+
+askForDebug:message
+    |box|
+
+    box := OptionBox title:message numberOfOptions:3.
+    box actions:(Array with:[^ #abort]
+                       with:[^ #debug]
+                       with:[^ #continue]).
+    box buttonTitles:#('abort' 'debug' 'continue').
+    box showAtPointer.
+    ^ #abort
+!
+
+catch:aSymbol with:aMessage for:anObject
+    "this one is sent when an error occurs while filing in -
+     we dont want a debugger to come up but simply notify
+     the error (also on the Transcript so you have a trace of it)"
+
+    |message action|
+
+    Smalltalk at:#ErrorHandler put:nil.
+    (aSymbol == #doesNotUnderstand:) ifTrue:[
+        anObject isNil ifTrue:[
+            "try to give a bit more detail on what went wrong"
+            (Metaclass respondsTo:(aMessage selector)) ifTrue:[
+                ('subclass:*' match:(aMessage selector)) ifTrue:[
+                    message := 'no superclass for ' , (aMessage arguments at:1)
+                ] ifFalse:[
+                    message := 'definitions for nonexisting class'
+                ]
+            ] ifFalse:[
+                message := 'bad message: ' , aMessage selector, ' to UndefinedObject'
+            ]
+        ] ifFalse:[
+            message := 'bad message: ' , aMessage selector ,
+                      ' to ' , anObject classNameWithArticle
+        ]
+    ] ifFalse:[
+        (aSymbol == #error:) ifTrue:[
+            message := aMessage
+        ] ifFalse:[
+            message := 'during fileIn'
+        ]
+    ].
+    message := 'Error: ' , message.
+    Transcript showCr:message.
+
+    YesNoBox notNil ifTrue:[
+        action := self askForDebug:message.
+        action == #debug ifTrue:[
+            Debugger enterWithMessage:message
+        ].
+        action == #continue ifTrue:[
+            continueBlock value
+        ].
+    ] ifFalse:[
+        self notify:message
+    ].
+
+    abortBlock value.
+    ^ nil
+!
+
+error:aMessage position:position to:endPos
+    "error notification during fileIn with no requestor"
+
+    position printOn:Transcript.
+    Transcript show:' '.
+    Transcript showCr:aMessage.
+    ^ false
+!
+
+correctableError:aMessage position:position to:endPos
+    "error notification during fileIn with no requestor"
+
+    ^ self error:aMessage position:position to:endPos
+!
+
+warning:aMessage position:position to:endPos
+    "warning notification during fileIn with no requestor - ignore it"
+
+    ^ self
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ProcSched.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,599 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#ProcessorScheduler
+         instanceVariableNames:'runnable zombie
+                                currentProcess currentPriority
+                                fileDescriptors fileHandlers fileSelectors
+                                timeoutTimes timeHandlers timeSelectors'
+         classVariableNames:'KnownProcesses KnownProcessIds'
+         poolDictionaries:''
+         category:'Kernel-Processes'
+!
+
+ProcessorScheduler comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+             All Rights Reserved
+
+%W% %E%
+'!
+
+Smalltalk at:#Processor put:nil!
+
+!ProcessorScheduler class methodsFor:'initialization'!
+
+initialize
+    KnownProcesses isNil ifTrue:[
+        KnownProcesses := ShadowArray new:5.
+        KnownProcesses watcher:self.
+        KnownProcessIds := OrderedCollection new.
+
+        "want to get informed when returning from snapshot"
+        ObjectMemory addDependent:self
+    ].
+
+    "create the one and only processor"
+
+    Processor := self new
+!
+
+update:something
+    something == #returnFromSnapshot ifTrue:[
+        self reinstallProcesses
+    ]
+!
+
+reinstallProcesses
+    "recreate all processes after a snapShot load"
+
+   KnownProcesses do:[:p |
+        p notNil ifTrue:[
+            "how, exactly should this be done ?"
+
+            p id ~~ 0 ifTrue:[
+                Transcript showCr:'process restart in preparation'
+            ]
+        ]
+    ]
+! !
+
+!ProcessorScheduler class methodsFor:'private process primitives'!
+
+threadCreate:aBlock
+    "physical creation of a process executing aBlock.
+     (warning: low level entry, no administration done)"
+%{
+    int tid;
+    extern int __threadCreate();
+
+    tid = __threadCreate(aBlock);
+    if (tid != 0) {
+        RETURN ( _MKSMALLINT(tid));
+    }
+    RETURN (nil);
+%}
+!
+
+threadDestroy:id
+    "physical destroy other process ...
+     (warning: low level entry, no administration done)"
+
+%{
+    if (_isSmallInteger(id)) {
+        __threadDestroy(_intVal(id));
+    }
+%}
+.
+    self primitiveFailed
+! !
+
+!ProcessorScheduler class methodsFor:'instance release'!
+
+informDispose
+    "some Process has been collected - terminate the underlying thread"
+
+    |id
+     index "<SmallInteger>"
+     sz    "<SmallInteger>"|
+
+    index := 1.
+    sz := KnownProcessIds size.
+    [index <= sz] whileTrue:[
+        (KnownProcesses at:index) isNil ifTrue:[
+            id := KnownProcessIds at:index.
+            id notNil ifTrue:[
+                Transcript showCr:('terminate thread (no longer refd) ', id printString).
+                self threadDestroy:id.
+                KnownProcessIds at:index put:nil.
+            ]
+        ].
+        index := index + 1
+    ]
+! !
+
+!ProcessorScheduler class methodsFor:'instance creation'!
+
+new
+    "there is (currently) only one processor ..."
+
+    Processor notNil ifTrue:[^ Processor].
+    ^ self basicNew initialize.
+! !
+
+!ProcessorScheduler methodsFor:'constants'!
+
+minPriority
+    ^ 1
+!
+
+maxPriority
+    ^ 31
+!
+
+userInterruptPriority
+    ^ 24
+!
+
+timingPriority
+    ^ 16
+!
+
+userSchedulingPriority
+    ^ 8
+!
+
+userBackgroundPriority
+    ^ 6
+!
+
+systemBackgroundPriority
+    ^ 4
+! !
+
+!ProcessorScheduler methodsFor:'private initializing'!
+
+initialize
+    "initialize the one-and-only ProcessorScheduler"
+
+    |nPrios l|
+
+    nPrios := self maxPriority - self minPriority + 1.
+
+    runnable := Array new:nPrios.
+
+    "setup the first (init-) process"
+    currentProcess := Process new.
+    currentProcess id:0.
+    currentProcess state:#running.
+    currentPriority := self userSchedulingPriority.
+    currentProcess setPriority:currentPriority.
+
+    l := LinkedList new.
+    l add:currentProcess.
+
+    runnable at:currentPriority put:l.
+
+    IOInterruptHandler := self.
+    OperatingSystem enableIOInterrupts.
+! !
+
+!ProcessorScheduler methodsFor:'private'!
+
+remember:aProcess
+    |newShadow newSize oldSize oldId
+     index "<SmallInteger>"
+     sz    "<SmallInteger>" |
+
+    index := 1.
+    sz := KnownProcessIds size.
+    [index <= sz] whileTrue:[
+        (KnownProcesses at:index) isNil ifTrue:[
+            oldId := KnownIds at:index.
+            oldId notNil ifTrue:[
+                self class terminateProcess:oldId
+            ].
+            KnownProcesses at:index put:aProcess.
+            KnownProcessIds at:index put:aProcess id.
+            ^ self
+        ].
+        index := index + 1
+    ].
+
+    KnownProcessIds grow:index.
+    KnownProcessIds at:index put:aProcess id.
+
+    oldSize := KnownProcesses size.
+    (index > oldSize) ifTrue:[
+        newShadow := ShadowArray new:(oldSize * 2).
+        newShadow watcher:(KnownProcesses watcher).
+        newShadow replaceFrom:1 with:KnownProcesses.
+        KnownProcesses := newShadow
+    ].
+    KnownProcesses at:index put:aProcess 
+! !
+
+!ProcessorScheduler methodsFor:'process creation'!
+
+newProcessFor:aBlock
+    "create a new process executing aBlock. Return a process (or
+     nil if fail). The new process is not scheduled. To start it 
+     running, it needs a Process>>resume."
+
+    |id p|
+
+    id := self class threadCreate:aBlock.
+    id notNil ifTrue:[
+        p := Process new.
+        p id:id.
+        p startBlock:aBlock.
+        p state:#suspended.
+        p setPriority:currentPriority.
+        self remember:p.
+    ].
+    ^ p
+! !
+
+!ProcessorScheduler methodsFor:'scheduling'!
+
+switchTo:aProcess
+    "continue execution in aProcess."
+
+    |id pri|
+
+    aProcess isNil ifTrue:[^ self].
+    aProcess == currentProcess ifTrue:[^ self].
+
+    id := aProcess id.
+    pri := aProcess priority.
+    currentProcess state:#runnable.
+
+    "no interrupts now ..."
+    currentProcess := aProcess.
+    currentProcess state:#running.
+    currentPriority := pri.
+%{
+    __threadSwitch(__context, _intVal(id));
+%}
+.
+    zombie notNil ifTrue:[
+        self class threadDestroy:zombie.
+        zombie := nil
+    ]
+!
+
+reschedule
+    "switch to the highest prio runnable process"
+
+    |l|
+
+    self maxPriority to:self minPriority by:-1 do:[:prio |
+        l := runnable at:prio.
+        l notNil ifTrue:[
+            ^ self switchTo:(l first)
+        ]
+    ].
+    "no process to run - wait to next time event"
+
+    'wait' printNewline.
+    self waitForNextTimeout
+!
+
+yield
+    "move the currently running process to the end of the currentList
+     and reschedule to the first in the list, thus switching to the 
+     next same-prio-process."
+
+    |l|
+
+    l := runnable at:currentPriority.
+    l isNil ifTrue:[
+        'oops - nil runnable list' printNewline.
+        ^ self
+    ].
+    l removeFirst.
+    l addLast:currentProcess.
+    self reschedule
+!
+
+suspend:aProcess
+    "remove the argument, aProcess from the list of runnable processes
+     and put it to the list of suspended ones. If the process is the 
+     currentProcess, reschedule."
+
+    |pri l|
+
+    aProcess isNil ifTrue:[self error:'nil suspend'. ^ self].
+    pri := aProcess priority.
+
+    l := runnable at:pri.
+    l isNil ifTrue:[self error:'bad suspend'. ^ self].
+
+    aProcess state:#suspended.
+    l remove:aProcess ifAbsent:[self error:'bad suspend'. ^ self].
+
+    (aProcess == currentProcess) ifTrue:[
+        self reschedule
+    ]
+!
+
+resume:aProcess
+    "set aProcess runnable - if its prio is higher than the currently running prio,
+     reschedule."
+
+    |l pri|
+
+    aProcess == currentProcess ifTrue:[^ self].
+    aProcess isNil ifTrue:[^ self].
+    pri := aProcess priority.
+
+    aProcess state:#runnable.
+    l := runnable at:pri.
+    l isNil ifTrue:[
+        l := LinkedList new.
+        runnable at:pri put:l
+    ].
+    l addLast:aProcess.
+
+    (pri > currentPriority) ifTrue:[
+        self reschedule
+    ]
+!
+
+processTermination
+    "current process finished its startup block without termination,
+     lay him to rest now"
+
+    self terminate:currentProcess
+!
+
+terminate:aProcess
+    "terminate aProcess. If its not the current process, its simply
+     removed from its list and destroyed. Otherwise, a switch is forced
+     and the process is destroyed by the next running process."
+
+    |pri id l|
+
+    aProcess isNil ifTrue:[^ self].
+    id := aProcess id.
+    id isNil ifTrue:[^ self].   "already dead"
+
+    pri := aProcess priority.
+
+    "easy, if currently suspended"
+    ((aProcess state ~~ #runnable) and:[aProcess state ~~ #running]) ifTrue:[
+         aProcess id:nil.
+         aProcess state:#dead.
+         aProcess startBlock:nil.
+         self class threadDestroy:id.
+         ^ self
+    ].
+
+    (aProcess state ~~ #runnable) ifTrue:[
+        l := runnable at:pri.
+        (l notNil and:[l includes:aProcess]) ifTrue:[
+            l remove:aProcess.
+            aProcess state:#dead.
+            l isEmpty ifTrue:[runnable at:pri put:nil].
+            aProcess == currentProcess ifFalse:[
+                self class threadDestroy:id.
+            ]
+        ].
+        ^ self
+    ].
+
+    (aProcess state ~~ #running) ifTrue:[
+        "hard case - its the currently running process
+         we must have the next active process destroy this one
+        "
+        aProcess state:#dead.
+        zombie := id.
+        self reschedule
+    ]
+!
+
+changePriority:newPrio for:aProcess
+    "change the priority of aProcess"
+
+    |oldList newList oldPrio s|
+
+    oldPrio := aProcess priority.
+    oldPrio == newPrio ifTrue:[^ self].
+    aProcess setPriority:newPrio.
+    s := aProcess state.
+    s == #runnable ifTrue:[
+        oldList := runnable at:oldPrio.
+        (oldList includes:aProcess) ifTrue:[
+            oldList remove:aProcess
+        ].
+
+        newList := runnable at:newPrio.
+        newList isNil ifTrue:[
+            newList := LinkedList new.
+            runnable at:newPrio put:newList
+        ].
+        newList addLast:aProcess.
+        (aProcess ~~ currentProcess and:[newPrio > currentPriority]) ifTrue:[
+            self reschedule.
+        ].
+        ^ self
+    ]
+! !
+
+!ProcessorScheduler class methodsFor:'testing'!
+
+test1
+    |scheduler|
+
+    scheduler := ProcessorScheduler new.
+    scheduler addFileDescriptor:(Stdin fileDescriptor) withHandler:self selector:#inputAvailable.
+    scheduler addFileDescriptor:(Display displayFileDescriptor) withHandler:self selector:#xInputAvailable.
+    scheduler loop
+
+    "ProcessorScheduler test1"
+!
+
+inputAvailable
+    Transcript showCr:(Stdin nextLine)
+!
+
+xInputAvailable
+    Transcript showCr:'x event'.
+    Display dispatchEvent
+! !
+
+!ProcessorScheduler methodsFor:'scheduling'!
+
+loop
+    |looping nextTime waitTime fd index|
+
+    looping := true.
+    [looping] whileTrue:[
+        "look if any timeouts are due to be evaluated"
+
+        nextTime := nil.
+        timeoutTimes notNil ifTrue:[
+            nextTime := self evaluateTimeouts
+        ].
+
+        nextTime notNil ifTrue:[
+            waitTime := OperatingSystem millisecondTimeDeltaBetween:nextTime
+                                                                and:OperatingSystem getMillisecondTime
+        ] ifFalse:[
+            waitTime := nil
+        ].
+
+        (fileDescriptors size == 0) ifTrue:[
+            waitTime isNil ifTrue:[
+                Transcript showCr:'nothing to schedule'.
+                ^ self
+            ].
+
+            "no fd to wait for - hard wait till next timeout has to come"
+            OperatingSystem millisecondDelay:waitTime
+        ] ifFalse:[
+            "wait for any fd to become ready or next timeout has to come"
+            waitTime isNil ifTrue:[waitTime := 10000].
+            fd := OperatingSystem selectOnAnyReadable:fileDescriptors withTimeOut:(waitTime / 1000).
+            fd isNil ifTrue:[
+                "an interrupt or timeout occured"
+                Transcript showCr:'interrupt or timeout'
+            ] ifFalse:[
+                "notify the handler"
+                index := fileDescriptors identityIndexOf:fd.
+                (fileHandlers at:index) perform:(fileSelectors at:index)
+            ]
+        ]
+    ]
+!
+
+evaluateTimeouts
+    "evaluate all timeouts that need to be .. and return the time of the
+     next pending timeout"
+
+    |now thisTime index endIndex handler selector nextTime|
+
+    nextTime := nil.
+    endIndex := timeoutTimes size.
+    (endIndex ~~ 0) ifTrue:[
+        now := OperatingSystem getMillisecondTime.
+        index := 1.
+        [index <= endIndex] whileTrue:[
+            thisTime := timeoutTimes at:index.
+            (OperatingSystem millisecondTime:thisTime isAfter:now) ifFalse:[
+                handler := timeHandlers at:index.
+                selector := timeSelectors at:index.
+                timeoutTimes at:index put:nil.
+                timeHandlers at:index put:nil.
+                timeSelectors at:index put:nil.
+                handler perform:selector
+            ] ifTrue:[
+                nextTime isNil ifTrue:[
+                    nextTime := thisTime
+                ] ifFalse:[
+                    (OperatingSystem millisecondTime:nextTime isAfter:thisTime) ifTrue:[
+                        nextTime := thisTime
+                    ]
+                ]
+            ].
+            index := index + 1
+        ]
+    ].
+    ^ nextTime
+! !
+
+!ProcessorScheduler methodsFor:'accessing'!
+
+currentPriority
+    ^ currentPriority
+
+    "Processor currentPriority"
+!
+
+currentProcess
+    ^ currentProcess
+
+    "Processor currentProcess"
+!
+
+addFileDescriptor:fd withHandler:handler selector:selector
+    |index|
+
+    fileDescriptors isNil ifTrue:[
+        fileDescriptors := Array with:fd.
+        fileHandlers := Array with:handler.
+        fileSelectors := Array with:selector
+    ] ifFalse:[
+        index := fileDescriptors indexOf:nil.
+        (index ~~ 0) ifTrue:[
+            fileDescriptors at:index put:fd.
+            fileHandlers at:index put:handler.
+            fileSelectors at:index put:selector
+        ] ifFalse:[
+            fileDescriptors := fileDescriptors copyWith:fd.
+            fileHandlers := fileHandlers copyWith:handler.
+            fileSelectors := fileSelectors copyWith:selector
+        ]
+    ]
+!
+
+addTimeoutAfter:millis withHandler:handler selector:selector
+    |index|
+
+    fileDescriptors isNil ifTrue:[
+        timeoutTimes := Array with:millis.
+        timeHandlers := Array with:handler.
+        timeSelectors := Array with:selector
+    ] ifFalse:[
+        index := timeoutTimes indexOf:nil.
+        (index ~~ 0) ifTrue:[
+            timeoutTimes at:index put:millis.
+            timeHandlers at:index put:handler.
+            timeSelectors at:index put:selector
+        ] ifFalse:[
+            timeoutTimes := fileDescriptors copyWith:millis.
+            timeHandlers := fileHandlers copyWith:handler.
+            timeSelectors := fileSelectors copyWith:selector
+        ]
+    ]
+!
+
+removeFileDescriptor:fd
+    |index|
+
+    index := fileDescriptors indexOf:nil.
+    (index ~~ 0) ifTrue:[
+        fileDescriptors at:index put:nil.
+        fileHandlers at:index put:nil.
+        fileSelectors at:index put:nil
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Process.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,94 @@
+"
+ COPYRIGHT (c) 1992-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Link subclass:#Process
+         instanceVariableNames:'id prio state startBlock'
+         classVariableNames:''
+         poolDictionaries:''
+         category:'Kernel-Processes'
+!
+
+Process comment:'
+
+COPYRIGHT (c) 1992-93 by Claus Gittinger
+             All Rights Reserved
+
+%W% %E%
+'!
+
+!Process methodsFor:'accessing'!
+
+state
+    ^ state
+!
+
+state:aSymbol
+    state := aSymbol
+!
+
+startBlock:aBlock
+    startBlock := aBlock
+!
+
+priority
+    "return the receivers priority"
+
+    ^ prio
+!
+
+priority:aNumber
+    "set my priority"
+
+    Processor changePriority:aNumber for:self
+!
+
+setPriority:aNumber
+    "set priority without telling processor - no public use"
+
+    prio := aNumber
+!
+
+id
+    ^ id
+!
+
+id:aNumber
+    id := aNumber
+!
+
+suspendedContext
+%{
+    extern OBJ __threadContext();
+
+    RETURN (__threadContext(_intVal(_INST(id))));
+%}
+! !
+
+!Process methodsFor:'suspend / resume'!
+
+suspend
+    Processor suspend:self
+!
+
+resume
+    Processor resume:self
+!
+
+terminate
+    Processor terminate:self
+! !
+
+!Process methodsFor:'printing'!
+
+printString
+    ^ 'a Process with id:' , id printString
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ProcessorScheduler.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,599 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#ProcessorScheduler
+         instanceVariableNames:'runnable zombie
+                                currentProcess currentPriority
+                                fileDescriptors fileHandlers fileSelectors
+                                timeoutTimes timeHandlers timeSelectors'
+         classVariableNames:'KnownProcesses KnownProcessIds'
+         poolDictionaries:''
+         category:'Kernel-Processes'
+!
+
+ProcessorScheduler comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+             All Rights Reserved
+
+%W% %E%
+'!
+
+Smalltalk at:#Processor put:nil!
+
+!ProcessorScheduler class methodsFor:'initialization'!
+
+initialize
+    KnownProcesses isNil ifTrue:[
+        KnownProcesses := ShadowArray new:5.
+        KnownProcesses watcher:self.
+        KnownProcessIds := OrderedCollection new.
+
+        "want to get informed when returning from snapshot"
+        ObjectMemory addDependent:self
+    ].
+
+    "create the one and only processor"
+
+    Processor := self new
+!
+
+update:something
+    something == #returnFromSnapshot ifTrue:[
+        self reinstallProcesses
+    ]
+!
+
+reinstallProcesses
+    "recreate all processes after a snapShot load"
+
+   KnownProcesses do:[:p |
+        p notNil ifTrue:[
+            "how, exactly should this be done ?"
+
+            p id ~~ 0 ifTrue:[
+                Transcript showCr:'process restart in preparation'
+            ]
+        ]
+    ]
+! !
+
+!ProcessorScheduler class methodsFor:'private process primitives'!
+
+threadCreate:aBlock
+    "physical creation of a process executing aBlock.
+     (warning: low level entry, no administration done)"
+%{
+    int tid;
+    extern int __threadCreate();
+
+    tid = __threadCreate(aBlock);
+    if (tid != 0) {
+        RETURN ( _MKSMALLINT(tid));
+    }
+    RETURN (nil);
+%}
+!
+
+threadDestroy:id
+    "physical destroy other process ...
+     (warning: low level entry, no administration done)"
+
+%{
+    if (_isSmallInteger(id)) {
+        __threadDestroy(_intVal(id));
+    }
+%}
+.
+    self primitiveFailed
+! !
+
+!ProcessorScheduler class methodsFor:'instance release'!
+
+informDispose
+    "some Process has been collected - terminate the underlying thread"
+
+    |id
+     index "<SmallInteger>"
+     sz    "<SmallInteger>"|
+
+    index := 1.
+    sz := KnownProcessIds size.
+    [index <= sz] whileTrue:[
+        (KnownProcesses at:index) isNil ifTrue:[
+            id := KnownProcessIds at:index.
+            id notNil ifTrue:[
+                Transcript showCr:('terminate thread (no longer refd) ', id printString).
+                self threadDestroy:id.
+                KnownProcessIds at:index put:nil.
+            ]
+        ].
+        index := index + 1
+    ]
+! !
+
+!ProcessorScheduler class methodsFor:'instance creation'!
+
+new
+    "there is (currently) only one processor ..."
+
+    Processor notNil ifTrue:[^ Processor].
+    ^ self basicNew initialize.
+! !
+
+!ProcessorScheduler methodsFor:'constants'!
+
+minPriority
+    ^ 1
+!
+
+maxPriority
+    ^ 31
+!
+
+userInterruptPriority
+    ^ 24
+!
+
+timingPriority
+    ^ 16
+!
+
+userSchedulingPriority
+    ^ 8
+!
+
+userBackgroundPriority
+    ^ 6
+!
+
+systemBackgroundPriority
+    ^ 4
+! !
+
+!ProcessorScheduler methodsFor:'private initializing'!
+
+initialize
+    "initialize the one-and-only ProcessorScheduler"
+
+    |nPrios l|
+
+    nPrios := self maxPriority - self minPriority + 1.
+
+    runnable := Array new:nPrios.
+
+    "setup the first (init-) process"
+    currentProcess := Process new.
+    currentProcess id:0.
+    currentProcess state:#running.
+    currentPriority := self userSchedulingPriority.
+    currentProcess setPriority:currentPriority.
+
+    l := LinkedList new.
+    l add:currentProcess.
+
+    runnable at:currentPriority put:l.
+
+    IOInterruptHandler := self.
+    OperatingSystem enableIOInterrupts.
+! !
+
+!ProcessorScheduler methodsFor:'private'!
+
+remember:aProcess
+    |newShadow newSize oldSize oldId
+     index "<SmallInteger>"
+     sz    "<SmallInteger>" |
+
+    index := 1.
+    sz := KnownProcessIds size.
+    [index <= sz] whileTrue:[
+        (KnownProcesses at:index) isNil ifTrue:[
+            oldId := KnownIds at:index.
+            oldId notNil ifTrue:[
+                self class terminateProcess:oldId
+            ].
+            KnownProcesses at:index put:aProcess.
+            KnownProcessIds at:index put:aProcess id.
+            ^ self
+        ].
+        index := index + 1
+    ].
+
+    KnownProcessIds grow:index.
+    KnownProcessIds at:index put:aProcess id.
+
+    oldSize := KnownProcesses size.
+    (index > oldSize) ifTrue:[
+        newShadow := ShadowArray new:(oldSize * 2).
+        newShadow watcher:(KnownProcesses watcher).
+        newShadow replaceFrom:1 with:KnownProcesses.
+        KnownProcesses := newShadow
+    ].
+    KnownProcesses at:index put:aProcess 
+! !
+
+!ProcessorScheduler methodsFor:'process creation'!
+
+newProcessFor:aBlock
+    "create a new process executing aBlock. Return a process (or
+     nil if fail). The new process is not scheduled. To start it 
+     running, it needs a Process>>resume."
+
+    |id p|
+
+    id := self class threadCreate:aBlock.
+    id notNil ifTrue:[
+        p := Process new.
+        p id:id.
+        p startBlock:aBlock.
+        p state:#suspended.
+        p setPriority:currentPriority.
+        self remember:p.
+    ].
+    ^ p
+! !
+
+!ProcessorScheduler methodsFor:'scheduling'!
+
+switchTo:aProcess
+    "continue execution in aProcess."
+
+    |id pri|
+
+    aProcess isNil ifTrue:[^ self].
+    aProcess == currentProcess ifTrue:[^ self].
+
+    id := aProcess id.
+    pri := aProcess priority.
+    currentProcess state:#runnable.
+
+    "no interrupts now ..."
+    currentProcess := aProcess.
+    currentProcess state:#running.
+    currentPriority := pri.
+%{
+    __threadSwitch(__context, _intVal(id));
+%}
+.
+    zombie notNil ifTrue:[
+        self class threadDestroy:zombie.
+        zombie := nil
+    ]
+!
+
+reschedule
+    "switch to the highest prio runnable process"
+
+    |l|
+
+    self maxPriority to:self minPriority by:-1 do:[:prio |
+        l := runnable at:prio.
+        l notNil ifTrue:[
+            ^ self switchTo:(l first)
+        ]
+    ].
+    "no process to run - wait to next time event"
+
+    'wait' printNewline.
+    self waitForNextTimeout
+!
+
+yield
+    "move the currently running process to the end of the currentList
+     and reschedule to the first in the list, thus switching to the 
+     next same-prio-process."
+
+    |l|
+
+    l := runnable at:currentPriority.
+    l isNil ifTrue:[
+        'oops - nil runnable list' printNewline.
+        ^ self
+    ].
+    l removeFirst.
+    l addLast:currentProcess.
+    self reschedule
+!
+
+suspend:aProcess
+    "remove the argument, aProcess from the list of runnable processes
+     and put it to the list of suspended ones. If the process is the 
+     currentProcess, reschedule."
+
+    |pri l|
+
+    aProcess isNil ifTrue:[self error:'nil suspend'. ^ self].
+    pri := aProcess priority.
+
+    l := runnable at:pri.
+    l isNil ifTrue:[self error:'bad suspend'. ^ self].
+
+    aProcess state:#suspended.
+    l remove:aProcess ifAbsent:[self error:'bad suspend'. ^ self].
+
+    (aProcess == currentProcess) ifTrue:[
+        self reschedule
+    ]
+!
+
+resume:aProcess
+    "set aProcess runnable - if its prio is higher than the currently running prio,
+     reschedule."
+
+    |l pri|
+
+    aProcess == currentProcess ifTrue:[^ self].
+    aProcess isNil ifTrue:[^ self].
+    pri := aProcess priority.
+
+    aProcess state:#runnable.
+    l := runnable at:pri.
+    l isNil ifTrue:[
+        l := LinkedList new.
+        runnable at:pri put:l
+    ].
+    l addLast:aProcess.
+
+    (pri > currentPriority) ifTrue:[
+        self reschedule
+    ]
+!
+
+processTermination
+    "current process finished its startup block without termination,
+     lay him to rest now"
+
+    self terminate:currentProcess
+!
+
+terminate:aProcess
+    "terminate aProcess. If its not the current process, its simply
+     removed from its list and destroyed. Otherwise, a switch is forced
+     and the process is destroyed by the next running process."
+
+    |pri id l|
+
+    aProcess isNil ifTrue:[^ self].
+    id := aProcess id.
+    id isNil ifTrue:[^ self].   "already dead"
+
+    pri := aProcess priority.
+
+    "easy, if currently suspended"
+    ((aProcess state ~~ #runnable) and:[aProcess state ~~ #running]) ifTrue:[
+         aProcess id:nil.
+         aProcess state:#dead.
+         aProcess startBlock:nil.
+         self class threadDestroy:id.
+         ^ self
+    ].
+
+    (aProcess state ~~ #runnable) ifTrue:[
+        l := runnable at:pri.
+        (l notNil and:[l includes:aProcess]) ifTrue:[
+            l remove:aProcess.
+            aProcess state:#dead.
+            l isEmpty ifTrue:[runnable at:pri put:nil].
+            aProcess == currentProcess ifFalse:[
+                self class threadDestroy:id.
+            ]
+        ].
+        ^ self
+    ].
+
+    (aProcess state ~~ #running) ifTrue:[
+        "hard case - its the currently running process
+         we must have the next active process destroy this one
+        "
+        aProcess state:#dead.
+        zombie := id.
+        self reschedule
+    ]
+!
+
+changePriority:newPrio for:aProcess
+    "change the priority of aProcess"
+
+    |oldList newList oldPrio s|
+
+    oldPrio := aProcess priority.
+    oldPrio == newPrio ifTrue:[^ self].
+    aProcess setPriority:newPrio.
+    s := aProcess state.
+    s == #runnable ifTrue:[
+        oldList := runnable at:oldPrio.
+        (oldList includes:aProcess) ifTrue:[
+            oldList remove:aProcess
+        ].
+
+        newList := runnable at:newPrio.
+        newList isNil ifTrue:[
+            newList := LinkedList new.
+            runnable at:newPrio put:newList
+        ].
+        newList addLast:aProcess.
+        (aProcess ~~ currentProcess and:[newPrio > currentPriority]) ifTrue:[
+            self reschedule.
+        ].
+        ^ self
+    ]
+! !
+
+!ProcessorScheduler class methodsFor:'testing'!
+
+test1
+    |scheduler|
+
+    scheduler := ProcessorScheduler new.
+    scheduler addFileDescriptor:(Stdin fileDescriptor) withHandler:self selector:#inputAvailable.
+    scheduler addFileDescriptor:(Display displayFileDescriptor) withHandler:self selector:#xInputAvailable.
+    scheduler loop
+
+    "ProcessorScheduler test1"
+!
+
+inputAvailable
+    Transcript showCr:(Stdin nextLine)
+!
+
+xInputAvailable
+    Transcript showCr:'x event'.
+    Display dispatchEvent
+! !
+
+!ProcessorScheduler methodsFor:'scheduling'!
+
+loop
+    |looping nextTime waitTime fd index|
+
+    looping := true.
+    [looping] whileTrue:[
+        "look if any timeouts are due to be evaluated"
+
+        nextTime := nil.
+        timeoutTimes notNil ifTrue:[
+            nextTime := self evaluateTimeouts
+        ].
+
+        nextTime notNil ifTrue:[
+            waitTime := OperatingSystem millisecondTimeDeltaBetween:nextTime
+                                                                and:OperatingSystem getMillisecondTime
+        ] ifFalse:[
+            waitTime := nil
+        ].
+
+        (fileDescriptors size == 0) ifTrue:[
+            waitTime isNil ifTrue:[
+                Transcript showCr:'nothing to schedule'.
+                ^ self
+            ].
+
+            "no fd to wait for - hard wait till next timeout has to come"
+            OperatingSystem millisecondDelay:waitTime
+        ] ifFalse:[
+            "wait for any fd to become ready or next timeout has to come"
+            waitTime isNil ifTrue:[waitTime := 10000].
+            fd := OperatingSystem selectOnAnyReadable:fileDescriptors withTimeOut:(waitTime / 1000).
+            fd isNil ifTrue:[
+                "an interrupt or timeout occured"
+                Transcript showCr:'interrupt or timeout'
+            ] ifFalse:[
+                "notify the handler"
+                index := fileDescriptors identityIndexOf:fd.
+                (fileHandlers at:index) perform:(fileSelectors at:index)
+            ]
+        ]
+    ]
+!
+
+evaluateTimeouts
+    "evaluate all timeouts that need to be .. and return the time of the
+     next pending timeout"
+
+    |now thisTime index endIndex handler selector nextTime|
+
+    nextTime := nil.
+    endIndex := timeoutTimes size.
+    (endIndex ~~ 0) ifTrue:[
+        now := OperatingSystem getMillisecondTime.
+        index := 1.
+        [index <= endIndex] whileTrue:[
+            thisTime := timeoutTimes at:index.
+            (OperatingSystem millisecondTime:thisTime isAfter:now) ifFalse:[
+                handler := timeHandlers at:index.
+                selector := timeSelectors at:index.
+                timeoutTimes at:index put:nil.
+                timeHandlers at:index put:nil.
+                timeSelectors at:index put:nil.
+                handler perform:selector
+            ] ifTrue:[
+                nextTime isNil ifTrue:[
+                    nextTime := thisTime
+                ] ifFalse:[
+                    (OperatingSystem millisecondTime:nextTime isAfter:thisTime) ifTrue:[
+                        nextTime := thisTime
+                    ]
+                ]
+            ].
+            index := index + 1
+        ]
+    ].
+    ^ nextTime
+! !
+
+!ProcessorScheduler methodsFor:'accessing'!
+
+currentPriority
+    ^ currentPriority
+
+    "Processor currentPriority"
+!
+
+currentProcess
+    ^ currentProcess
+
+    "Processor currentProcess"
+!
+
+addFileDescriptor:fd withHandler:handler selector:selector
+    |index|
+
+    fileDescriptors isNil ifTrue:[
+        fileDescriptors := Array with:fd.
+        fileHandlers := Array with:handler.
+        fileSelectors := Array with:selector
+    ] ifFalse:[
+        index := fileDescriptors indexOf:nil.
+        (index ~~ 0) ifTrue:[
+            fileDescriptors at:index put:fd.
+            fileHandlers at:index put:handler.
+            fileSelectors at:index put:selector
+        ] ifFalse:[
+            fileDescriptors := fileDescriptors copyWith:fd.
+            fileHandlers := fileHandlers copyWith:handler.
+            fileSelectors := fileSelectors copyWith:selector
+        ]
+    ]
+!
+
+addTimeoutAfter:millis withHandler:handler selector:selector
+    |index|
+
+    fileDescriptors isNil ifTrue:[
+        timeoutTimes := Array with:millis.
+        timeHandlers := Array with:handler.
+        timeSelectors := Array with:selector
+    ] ifFalse:[
+        index := timeoutTimes indexOf:nil.
+        (index ~~ 0) ifTrue:[
+            timeoutTimes at:index put:millis.
+            timeHandlers at:index put:handler.
+            timeSelectors at:index put:selector
+        ] ifFalse:[
+            timeoutTimes := fileDescriptors copyWith:millis.
+            timeHandlers := fileHandlers copyWith:handler.
+            timeSelectors := fileSelectors copyWith:selector
+        ]
+    ]
+!
+
+removeFileDescriptor:fd
+    |index|
+
+    index := fileDescriptors indexOf:nil.
+    (index ~~ 0) ifTrue:[
+        fileDescriptors at:index put:nil.
+        fileHandlers at:index put:nil.
+        fileSelectors at:index put:nil
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Project.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,78 @@
+'From Smalltalk/X, Version:2.6.4 on 27-Apr-1993 at 20:02:37'!
+
+Object subclass:#Project
+         instanceVariableNames:'name changeSet views'
+         classVariableNames:'CurrentProject'
+         poolDictionaries:''
+         category:'System-Support'
+!
+
+!Project class methodsFor:'accessing'!
+
+current
+    ^ CurrentProject
+
+    "Project current"
+!
+
+current:aProject
+    CurrentProject := aProject
+! !
+
+!Project class methodsFor:'instance creation'!
+
+new
+    |newProject|
+
+    newProject := self basicNew.
+    newProject views:(OrderedCollection new).
+    newProject name:'a new Project'.
+    newProject changeSet:(ChangeSet new).
+    ^ newProject
+! !
+
+!Project class methodsFor:'initialization'!
+
+initialize
+    CurrentProject isNil ifTrue:[
+        CurrentProject := self new name:'System'
+    ]
+
+    "Project initialize"
+! !
+
+!Project methodsFor:'accessing'!
+
+views
+    ^ views
+!
+
+views:aSetOfViews
+    views := aSetOfViews
+!
+
+addView:aView
+    views add:aView
+!
+
+removeView:aView
+    views remove:aView ifAbsent:[]
+!
+
+changeSet
+    ^ changeSet
+!
+
+changeSet:aChangeSet
+    changeSet := aChangeSet
+!
+
+name
+    ^ name
+!
+
+name:aString
+    name := aString
+! !
+
+Project initialize!
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/RWStream.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,41 @@
+"
+ COPYRIGHT (c) 1989/90/91 by Claus Gittinger
+	      All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+WriteStream subclass:#ReadWriteStream
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Streams'
+!
+
+ReadWriteStream comment:'
+
+COPYRIGHT (c) 1989/90/91 by Claus Gittinger
+	      All Rights Reserved
+
+@(#)RWStream.st	2.3 92/06/06
+'!
+
+!ReadWriteStream methodsFor: 'access-reading'!
+
+peek
+    (position > readLimit) ifTrue: [ ^ nil ].
+    ^ collection at:position
+!
+
+next
+    |p|
+    (position > readLimit) ifTrue: [ ^ nil ].
+    p := position.
+    position := position + 1.
+    ^ collection at:p
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ReadStr.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,407 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+PositionableStream subclass:#ReadStream
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Streams'
+!
+
+ReadStream comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ReadStream defines protocol for reading streamwise over collections. Code here
+is specially tuned for streaming over strings.
+
+%W% %E%
+'!
+
+!ReadStream methodsFor:'access-reading'!
+
+peek
+    "return the next element; do NOT advance read pointer.
+     - reimplemented for speed on String-Streams"
+
+%{  /* NOCONTEXT */
+
+    REGISTER int pos;
+    unsigned ch;
+
+    if (_isString(_INST(collection))
+     && _isSmallInteger(_INST(position))
+     && _isSmallInteger(_INST(readLimit))) {
+
+        pos = _intVal(_INST(position));
+        if (pos > _intVal(_INST(readLimit))) {
+            RETURN ( nil );
+        }
+        if ((pos > 0)
+         && (pos < _qSize(_INST(collection)) - OHDR_SIZE)) {
+            ch = _stringVal(_INST(collection))[pos-1];
+            RETURN ( _MKCHARACTER(ch) );
+        }
+    }
+%}
+.
+    (position > readLimit) ifTrue:[^ nil].
+    ^ collection at:position
+!
+
+next
+    "return the next element; advance read pointer.
+     - reimplemented for speed on String-Streams"
+
+    |ret|
+
+%{  /* NOCONTEXT */
+
+    REGISTER int pos;
+    unsigned ch;
+
+    if (_isString(_INST(collection))
+     && _isSmallInteger(_INST(position))
+     && _isSmallInteger(_INST(readLimit))) {
+
+        pos = _intVal(_INST(position));
+        if (pos > _intVal(_INST(readLimit))) {
+            RETURN ( nil );
+        }
+        if ((pos > 0)
+         && (pos < _qSize(_INST(collection)) - OHDR_SIZE)) {
+            ch = _stringVal(_INST(collection))[pos-1];
+            _INST(position) = _MKSMALLINT(pos + 1);
+            RETURN ( _MKCHARACTER(ch) );
+        }
+    }
+%}
+.
+    (position > readLimit) ifTrue:[^ nil].
+    ret := collection at:position.
+    position := position + 1.
+    ^ ret
+!
+
+nextPeek
+    "advance read pointer return the peek element.
+     this is equivalent to (self next; peek).
+     - reimplemented for speed on String-Streams"
+
+%{  /* NOCONTEXT */
+
+    if (_isString(_INST(collection))
+     && _isSmallInteger(_INST(position))
+     && _isSmallInteger(_INST(readLimit))) {
+        REGISTER int pos;
+        unsigned ch;
+
+        pos = _intVal(_INST(position));
+        if (pos > _intVal(_INST(readLimit))) {
+            RETURN ( nil );
+        }
+        if ((pos > 0)
+         && (pos < _qSize(_INST(collection)) - OHDR_SIZE)) {
+            _INST(position) = _MKSMALLINT(pos + 1);
+            pos = pos + 1;
+            if (pos < _qSize(_INST(collection)) - OHDR_SIZE) {
+                ch = _stringVal(_INST(collection))[pos-1];
+                RETURN ( _MKCHARACTER(ch) );
+            }
+            RETURN ( nil );
+        }
+    }
+%}
+.
+    (position > readLimit) ifTrue:[^ nil].
+    position := position + 1.
+    (position > readLimit) ifTrue:[^ nil].
+    ^ collection at:position
+!
+
+nextDecimalInteger
+    "read the next integer in radix 10. dont skip whitespace.
+     - reimplemented for speed on String-Streams"
+
+    |value|
+%{
+    int pos, limit, sz;
+    register unsigned char *cp;
+    register unsigned ch;
+    int val = 0;
+
+    if (_isString(_INST(collection))
+     && _isSmallInteger(_INST(position))
+     && _isSmallInteger(_INST(readLimit))) {
+        pos = _intVal(_INST(position));
+        limit = _intVal(_INST(readLimit));
+        sz = _qSize(_INST(collection)) - OHDR_SIZE;
+        if (sz < limit)
+            limit = sz; 
+        cp = _stringVal(_INST(collection)) + pos - 1;
+
+        for (;;) {
+            if (pos > limit) break;
+            ch = *cp;
+
+            if ((ch < '0') || (ch > '9')) break;
+            val = val * 10 + (ch - '0');
+            pos++;
+            if (val > (_MAX_INT / 10)) goto oops;
+            cp++;
+        }
+        _INST(position) = _MKSMALLINT(pos);
+        return _MKSMALLINT(val);
+    }
+oops:
+    value = _MKSMALLINT(val);
+%}
+.
+    [self peek notNil and:[self peek isDigitRadix:10]] whileTrue:[
+        value = (value * 10) + self peek digitValue.
+        self next
+    ].
+    ^ value
+!
+
+nextWord
+    "read the next word (i.e. up to non letter-or-digit).
+     return a string containing those characters.
+     - reimplemented for speed on String-Streams"
+%{
+    /* speedup, if collection is a string */
+
+    int pos, limit, sz;
+    int len;
+    char buffer[1024];
+    register unsigned char *cp;
+    register unsigned ch;
+
+    if (_isString(_INST(collection))
+     && _isSmallInteger(_INST(position))
+     && _isSmallInteger(_INST(readLimit))) {
+        pos = _intVal(_INST(position));
+        limit = _intVal(_INST(readLimit));
+        sz = _qSize(_INST(collection)) - OHDR_SIZE;
+        if (sz < limit)
+            limit = sz; 
+        cp = _stringVal(_INST(collection)) + pos - 1;
+
+        for (;;) {
+            if (pos > limit) break;
+            ch = *cp;
+
+            if (ch > ' ') break;
+            if ((ch != ' ') && (ch != '\t') && (ch != '\r')
+             && (ch != '\n') && (ch != 0x0b)) break;
+            cp++;
+            pos++;
+        }
+
+        len = 0;
+        for (;;) {
+            if (pos > limit) break;
+            ch = *cp & 0xFF;
+
+            if (! (((ch >= 'a') && (ch <= 'z')) ||
+                   ((ch >= 'A') && (ch <= 'Z')) ||
+                   ((ch >= '0') && (ch <= '9'))))
+                break;
+            buffer[len++] = ch;
+            if (len >= (sizeof(buffer)-1)) {
+                /* emergency */
+                break;
+            }
+            pos++;
+            cp++;
+        }
+
+        _INST(position) = _MKSMALLINT(pos);
+        buffer[len] = '\0';
+        RETURN ( (len != 0) ? _MKSTRING(buffer COMMA_CON) : nil );
+    }
+%}
+.
+    ^ super nextWord
+!
+
+nextSymbol
+    "read the next selector-symbol (i.e. up to non letter-or-digit).
+     return a string containing those characters.
+     - reimplemented for speed on String-Streams"
+%{
+    int pos, limit, sz;
+    int len;
+    char buffer[1024];
+    register unsigned char *cp;
+    register unsigned ch;
+
+    if (_isString(_INST(collection))
+     && _isSmallInteger(_INST(position))
+     && _isSmallInteger(_INST(readLimit))) {
+        pos = _intVal(_INST(position));
+        limit = _intVal(_INST(readLimit));
+        sz = _qSize(_INST(collection)) - OHDR_SIZE;
+        if (sz < limit)
+            limit = sz; 
+        cp = _stringVal(_INST(collection)) + pos - 1;
+
+        len = 0;
+        for (;;) {
+            if (pos > limit) break;
+            ch = *cp;
+
+            if (! (((ch >= 'a') && (ch <= 'z')) ||
+                   ((ch >= 'A') && (ch <= 'Z')) ||
+                   ((ch >= '0') && (ch <= '9')) ||
+                   (ch == ':')))
+                break;
+            buffer[len++] = ch;
+            if (len >= (sizeof(buffer)-1)) {
+                /* emergency */
+                break;
+            }
+            pos++;
+            cp++;
+        }
+
+        _INST(position) = _MKSMALLINT(pos);
+        buffer[len] = '\0';
+        RETURN ( (len != 0) ? _MKSTRING(buffer COMMA_CON) : nil );
+    }
+%}
+.
+    ^ super nextSymbol
+!
+
+skipFor:anObject
+    "skip all objects up-to and including anObject;
+     return the element after anObject.
+     - reimplemented for speed on String-Streams"
+
+%{  /* NOCONTEXT */
+
+    if (_isString(_INST(collection))
+     && _isCharacter(anObject)
+     && _isSmallInteger(_INST(position))
+     && _isSmallInteger(_INST(readLimit))) {
+        REGISTER unsigned char *chars;
+        REGISTER int pos, limit;
+	unsigned ch;
+
+        pos = _intVal(_INST(position));
+        if (pos <= 0) {
+            RETURN ( nil );
+        }
+
+        limit = _intVal(_INST(readLimit));
+        if (limit > (_qSize(_INST(collection)) - OHDR_SIZE))
+            limit = _qSize(_INST(collection)) - OHDR_SIZE;
+
+        chars = (unsigned char *)(_stringVal(_INST(collection)) + pos - 1);
+        ch = _intVal(_characterVal(anObject)) & 0xFF;
+        while (pos < limit) {
+            if (*chars == ch) {
+                ch = *++chars;
+                pos++;
+                _INST(position) = _MKSMALLINT(pos);
+                RETURN ( _MKCHARACTER(ch) );
+            }
+            chars++;
+            pos++;
+        }
+        _INST(position) = _MKSMALLINT(pos);
+        RETURN ( nil );
+    }
+%}
+.
+    ^ super skipFor:anObject
+!
+
+
+skipSeparators
+    "skip all whitespace; next will return next non-white-space element.
+     - reimplemented for speed on String-Streams"
+
+%{  /* NOCONTEXT */
+
+    if (_isString(_INST(collection))
+     && _isSmallInteger(_INST(position))
+     && _isSmallInteger(_INST(readLimit))) {
+        REGISTER unsigned char *chars;
+        REGISTER unsigned ch;
+        REGISTER int pos;
+        int limit;
+
+        pos = _intVal(_INST(position));
+        if (pos <= 0) {
+            RETURN ( nil );
+        }
+
+        limit = _intVal(_INST(readLimit));
+        if (limit > (_qSize(_INST(collection)) - OHDR_SIZE))
+            limit = _qSize(_INST(collection)) - OHDR_SIZE;
+
+        chars = (unsigned char *)(_stringVal(_INST(collection)) + pos - 1);
+        while (pos <= limit) {
+            ch = *chars++;
+            if ((ch != ' ') && (ch != '\t') && (ch != '\r')
+             && (ch != '\n') && (ch != 0x0B)) {
+                _INST(position) = _MKSMALLINT(pos);
+                RETURN ( _MKCHARACTER(ch) );
+            }
+            pos++;
+        }
+        _INST(position) = _MKSMALLINT(pos);
+        RETURN ( nil );
+    }
+%}
+.
+    ^ super skipSeparators
+!
+
+skipToAll:aCollection
+    "skip for the sequence given by the argument, aCollection;
+     return nil if not found, self otherwise. On a successful match, next read
+     will return elements of aCollection."
+
+    |oldPos buffer l first idx|
+
+    oldPos := self position.
+    l := aCollection size.
+    first := aCollection at:1.
+    [self atEnd] whileFalse:[
+        buffer := self next:l.
+        buffer = aCollection ifTrue:[
+            self position:(self position - l).
+            ^ self
+        ].
+        idx := buffer indexOf:first startingAt:2.
+        idx == 0 ifFalse:[
+            self position:(self position - l + idx - 1)
+        ]
+    ].
+    self position:oldPos.
+    ^ nil
+
+    "|s|
+     s := ReadStream on:'12345678901234567890'.
+     s skipToAll:'901'.
+     s next:4"
+! !
+
+!ReadStream methodsFor:'access-writing'!
+
+nextPut:anElement
+    ^ self error:'ReadStreams cannot write'
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ReadStream.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,407 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+PositionableStream subclass:#ReadStream
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Streams'
+!
+
+ReadStream comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ReadStream defines protocol for reading streamwise over collections. Code here
+is specially tuned for streaming over strings.
+
+%W% %E%
+'!
+
+!ReadStream methodsFor:'access-reading'!
+
+peek
+    "return the next element; do NOT advance read pointer.
+     - reimplemented for speed on String-Streams"
+
+%{  /* NOCONTEXT */
+
+    REGISTER int pos;
+    unsigned ch;
+
+    if (_isString(_INST(collection))
+     && _isSmallInteger(_INST(position))
+     && _isSmallInteger(_INST(readLimit))) {
+
+        pos = _intVal(_INST(position));
+        if (pos > _intVal(_INST(readLimit))) {
+            RETURN ( nil );
+        }
+        if ((pos > 0)
+         && (pos < _qSize(_INST(collection)) - OHDR_SIZE)) {
+            ch = _stringVal(_INST(collection))[pos-1];
+            RETURN ( _MKCHARACTER(ch) );
+        }
+    }
+%}
+.
+    (position > readLimit) ifTrue:[^ nil].
+    ^ collection at:position
+!
+
+next
+    "return the next element; advance read pointer.
+     - reimplemented for speed on String-Streams"
+
+    |ret|
+
+%{  /* NOCONTEXT */
+
+    REGISTER int pos;
+    unsigned ch;
+
+    if (_isString(_INST(collection))
+     && _isSmallInteger(_INST(position))
+     && _isSmallInteger(_INST(readLimit))) {
+
+        pos = _intVal(_INST(position));
+        if (pos > _intVal(_INST(readLimit))) {
+            RETURN ( nil );
+        }
+        if ((pos > 0)
+         && (pos < _qSize(_INST(collection)) - OHDR_SIZE)) {
+            ch = _stringVal(_INST(collection))[pos-1];
+            _INST(position) = _MKSMALLINT(pos + 1);
+            RETURN ( _MKCHARACTER(ch) );
+        }
+    }
+%}
+.
+    (position > readLimit) ifTrue:[^ nil].
+    ret := collection at:position.
+    position := position + 1.
+    ^ ret
+!
+
+nextPeek
+    "advance read pointer return the peek element.
+     this is equivalent to (self next; peek).
+     - reimplemented for speed on String-Streams"
+
+%{  /* NOCONTEXT */
+
+    if (_isString(_INST(collection))
+     && _isSmallInteger(_INST(position))
+     && _isSmallInteger(_INST(readLimit))) {
+        REGISTER int pos;
+        unsigned ch;
+
+        pos = _intVal(_INST(position));
+        if (pos > _intVal(_INST(readLimit))) {
+            RETURN ( nil );
+        }
+        if ((pos > 0)
+         && (pos < _qSize(_INST(collection)) - OHDR_SIZE)) {
+            _INST(position) = _MKSMALLINT(pos + 1);
+            pos = pos + 1;
+            if (pos < _qSize(_INST(collection)) - OHDR_SIZE) {
+                ch = _stringVal(_INST(collection))[pos-1];
+                RETURN ( _MKCHARACTER(ch) );
+            }
+            RETURN ( nil );
+        }
+    }
+%}
+.
+    (position > readLimit) ifTrue:[^ nil].
+    position := position + 1.
+    (position > readLimit) ifTrue:[^ nil].
+    ^ collection at:position
+!
+
+nextDecimalInteger
+    "read the next integer in radix 10. dont skip whitespace.
+     - reimplemented for speed on String-Streams"
+
+    |value|
+%{
+    int pos, limit, sz;
+    register unsigned char *cp;
+    register unsigned ch;
+    int val = 0;
+
+    if (_isString(_INST(collection))
+     && _isSmallInteger(_INST(position))
+     && _isSmallInteger(_INST(readLimit))) {
+        pos = _intVal(_INST(position));
+        limit = _intVal(_INST(readLimit));
+        sz = _qSize(_INST(collection)) - OHDR_SIZE;
+        if (sz < limit)
+            limit = sz; 
+        cp = _stringVal(_INST(collection)) + pos - 1;
+
+        for (;;) {
+            if (pos > limit) break;
+            ch = *cp;
+
+            if ((ch < '0') || (ch > '9')) break;
+            val = val * 10 + (ch - '0');
+            pos++;
+            if (val > (_MAX_INT / 10)) goto oops;
+            cp++;
+        }
+        _INST(position) = _MKSMALLINT(pos);
+        return _MKSMALLINT(val);
+    }
+oops:
+    value = _MKSMALLINT(val);
+%}
+.
+    [self peek notNil and:[self peek isDigitRadix:10]] whileTrue:[
+        value = (value * 10) + self peek digitValue.
+        self next
+    ].
+    ^ value
+!
+
+nextWord
+    "read the next word (i.e. up to non letter-or-digit).
+     return a string containing those characters.
+     - reimplemented for speed on String-Streams"
+%{
+    /* speedup, if collection is a string */
+
+    int pos, limit, sz;
+    int len;
+    char buffer[1024];
+    register unsigned char *cp;
+    register unsigned ch;
+
+    if (_isString(_INST(collection))
+     && _isSmallInteger(_INST(position))
+     && _isSmallInteger(_INST(readLimit))) {
+        pos = _intVal(_INST(position));
+        limit = _intVal(_INST(readLimit));
+        sz = _qSize(_INST(collection)) - OHDR_SIZE;
+        if (sz < limit)
+            limit = sz; 
+        cp = _stringVal(_INST(collection)) + pos - 1;
+
+        for (;;) {
+            if (pos > limit) break;
+            ch = *cp;
+
+            if (ch > ' ') break;
+            if ((ch != ' ') && (ch != '\t') && (ch != '\r')
+             && (ch != '\n') && (ch != 0x0b)) break;
+            cp++;
+            pos++;
+        }
+
+        len = 0;
+        for (;;) {
+            if (pos > limit) break;
+            ch = *cp & 0xFF;
+
+            if (! (((ch >= 'a') && (ch <= 'z')) ||
+                   ((ch >= 'A') && (ch <= 'Z')) ||
+                   ((ch >= '0') && (ch <= '9'))))
+                break;
+            buffer[len++] = ch;
+            if (len >= (sizeof(buffer)-1)) {
+                /* emergency */
+                break;
+            }
+            pos++;
+            cp++;
+        }
+
+        _INST(position) = _MKSMALLINT(pos);
+        buffer[len] = '\0';
+        RETURN ( (len != 0) ? _MKSTRING(buffer COMMA_CON) : nil );
+    }
+%}
+.
+    ^ super nextWord
+!
+
+nextSymbol
+    "read the next selector-symbol (i.e. up to non letter-or-digit).
+     return a string containing those characters.
+     - reimplemented for speed on String-Streams"
+%{
+    int pos, limit, sz;
+    int len;
+    char buffer[1024];
+    register unsigned char *cp;
+    register unsigned ch;
+
+    if (_isString(_INST(collection))
+     && _isSmallInteger(_INST(position))
+     && _isSmallInteger(_INST(readLimit))) {
+        pos = _intVal(_INST(position));
+        limit = _intVal(_INST(readLimit));
+        sz = _qSize(_INST(collection)) - OHDR_SIZE;
+        if (sz < limit)
+            limit = sz; 
+        cp = _stringVal(_INST(collection)) + pos - 1;
+
+        len = 0;
+        for (;;) {
+            if (pos > limit) break;
+            ch = *cp;
+
+            if (! (((ch >= 'a') && (ch <= 'z')) ||
+                   ((ch >= 'A') && (ch <= 'Z')) ||
+                   ((ch >= '0') && (ch <= '9')) ||
+                   (ch == ':')))
+                break;
+            buffer[len++] = ch;
+            if (len >= (sizeof(buffer)-1)) {
+                /* emergency */
+                break;
+            }
+            pos++;
+            cp++;
+        }
+
+        _INST(position) = _MKSMALLINT(pos);
+        buffer[len] = '\0';
+        RETURN ( (len != 0) ? _MKSTRING(buffer COMMA_CON) : nil );
+    }
+%}
+.
+    ^ super nextSymbol
+!
+
+skipFor:anObject
+    "skip all objects up-to and including anObject;
+     return the element after anObject.
+     - reimplemented for speed on String-Streams"
+
+%{  /* NOCONTEXT */
+
+    if (_isString(_INST(collection))
+     && _isCharacter(anObject)
+     && _isSmallInteger(_INST(position))
+     && _isSmallInteger(_INST(readLimit))) {
+        REGISTER unsigned char *chars;
+        REGISTER int pos, limit;
+	unsigned ch;
+
+        pos = _intVal(_INST(position));
+        if (pos <= 0) {
+            RETURN ( nil );
+        }
+
+        limit = _intVal(_INST(readLimit));
+        if (limit > (_qSize(_INST(collection)) - OHDR_SIZE))
+            limit = _qSize(_INST(collection)) - OHDR_SIZE;
+
+        chars = (unsigned char *)(_stringVal(_INST(collection)) + pos - 1);
+        ch = _intVal(_characterVal(anObject)) & 0xFF;
+        while (pos < limit) {
+            if (*chars == ch) {
+                ch = *++chars;
+                pos++;
+                _INST(position) = _MKSMALLINT(pos);
+                RETURN ( _MKCHARACTER(ch) );
+            }
+            chars++;
+            pos++;
+        }
+        _INST(position) = _MKSMALLINT(pos);
+        RETURN ( nil );
+    }
+%}
+.
+    ^ super skipFor:anObject
+!
+
+
+skipSeparators
+    "skip all whitespace; next will return next non-white-space element.
+     - reimplemented for speed on String-Streams"
+
+%{  /* NOCONTEXT */
+
+    if (_isString(_INST(collection))
+     && _isSmallInteger(_INST(position))
+     && _isSmallInteger(_INST(readLimit))) {
+        REGISTER unsigned char *chars;
+        REGISTER unsigned ch;
+        REGISTER int pos;
+        int limit;
+
+        pos = _intVal(_INST(position));
+        if (pos <= 0) {
+            RETURN ( nil );
+        }
+
+        limit = _intVal(_INST(readLimit));
+        if (limit > (_qSize(_INST(collection)) - OHDR_SIZE))
+            limit = _qSize(_INST(collection)) - OHDR_SIZE;
+
+        chars = (unsigned char *)(_stringVal(_INST(collection)) + pos - 1);
+        while (pos <= limit) {
+            ch = *chars++;
+            if ((ch != ' ') && (ch != '\t') && (ch != '\r')
+             && (ch != '\n') && (ch != 0x0B)) {
+                _INST(position) = _MKSMALLINT(pos);
+                RETURN ( _MKCHARACTER(ch) );
+            }
+            pos++;
+        }
+        _INST(position) = _MKSMALLINT(pos);
+        RETURN ( nil );
+    }
+%}
+.
+    ^ super skipSeparators
+!
+
+skipToAll:aCollection
+    "skip for the sequence given by the argument, aCollection;
+     return nil if not found, self otherwise. On a successful match, next read
+     will return elements of aCollection."
+
+    |oldPos buffer l first idx|
+
+    oldPos := self position.
+    l := aCollection size.
+    first := aCollection at:1.
+    [self atEnd] whileFalse:[
+        buffer := self next:l.
+        buffer = aCollection ifTrue:[
+            self position:(self position - l).
+            ^ self
+        ].
+        idx := buffer indexOf:first startingAt:2.
+        idx == 0 ifFalse:[
+            self position:(self position - l + idx - 1)
+        ]
+    ].
+    self position:oldPos.
+    ^ nil
+
+    "|s|
+     s := ReadStream on:'12345678901234567890'.
+     s skipToAll:'901'.
+     s next:4"
+! !
+
+!ReadStream methodsFor:'access-writing'!
+
+nextPut:anElement
+    ^ self error:'ReadStreams cannot write'
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ReadWriteStream.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,41 @@
+"
+ COPYRIGHT (c) 1989/90/91 by Claus Gittinger
+	      All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+WriteStream subclass:#ReadWriteStream
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Streams'
+!
+
+ReadWriteStream comment:'
+
+COPYRIGHT (c) 1989/90/91 by Claus Gittinger
+	      All Rights Reserved
+
+@(#)RWStream.st	2.3 92/06/06
+'!
+
+!ReadWriteStream methodsFor: 'access-reading'!
+
+peek
+    (position > readLimit) ifTrue: [ ^ nil ].
+    ^ collection at:position
+!
+
+next
+    |p|
+    (position > readLimit) ifTrue: [ ^ nil ].
+    p := position.
+    position := position + 1.
+    ^ collection at:p
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Rectangle.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,616 @@
+"
+ COPYRIGHT (c) 1989-92 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Rectangle
+       instanceVariableNames:'left top width height'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Graphics-Primitives'
+!
+
+Rectangle comment:'
+
+COPYRIGHT (c) 1989-92 by Claus Gittinger
+              All Rights Reserved
+
+Rectangles represent a rectangular area in 2D space.
+
+notice, my implementation does not use origin/corner as instance objects
+but left/top/width/height to save space and allocations. This means, that my
+Rectangles cannot be used to represent Rectangles in a higher than 2D
+space. (i.e. only valid if origin/corner are 2D Points)
+
+(aside from that, you will not see any difference from the outside)
+
+Instance variables:
+
+left           <Number>        the left coordinate (i.e origin x)
+top            <Number>        the top coordinate (i.e origin y)
+width          <Number>        the width of the rectangle
+height         <Number>        the height of the rectangle
+
+%W% %E%
+
+written 89 by claus
+'!
+
+!Rectangle class methodsFor:'instance creation'!
+
+origin:origin corner:corner
+    "create and return a new Rectangle giving top-left and bottom-right points"
+
+    ^ (self basicNew) origin:origin corner:corner
+!
+
+origin:origin extent:extent
+    "create and return a new Rectangle giving top-left point and extent point"
+
+%{  /* NOCONTEXT */
+    OBJ newRect;
+    extern char *newNextPtr, *newEndPtr;
+    extern OBJ Point;
+
+    /* short cut - rectangles are created so often ... */
+    if (_CanDoQuickNew(OHDR_SIZE + 4*sizeof(OBJ))) {
+        if ((self == Rectangle) 
+         && _isPoint(origin) 
+         && _isPoint(extent)) {
+            _qCheckedAlignedNew(newRect, OHDR_SIZE + 4*sizeof(OBJ), __context);
+            _InstPtr(newRect)->o_class = Rectangle;
+            _InstPtr(newRect)->i_instvars[0] = _PointInstPtr(origin)->p_x;
+            _InstPtr(newRect)->i_instvars[1] = _PointInstPtr(origin)->p_y;
+            _InstPtr(newRect)->i_instvars[2] = _PointInstPtr(extent)->p_x;
+            _InstPtr(newRect)->i_instvars[3] = _PointInstPtr(extent)->p_y;
+            /* no STOREs needed - newRect is in newSpace */
+            RETURN ( newRect );
+        }
+    }
+%}
+.
+    ^ (self basicNew) origin:origin extent:extent
+!
+
+left:left top:top width:w height:h
+    "create and return a new Rectangle giving left and top coordinates
+     and width, height"
+
+%{  /* NOCONTEXT */
+    OBJ newRect;
+    extern char *newNextPtr, *newEndPtr;
+
+    if (_CanDoQuickNew(OHDR_SIZE + 4*sizeof(OBJ))) {
+        /* short cut - rectangles are created so often ... */
+        if (self == Rectangle) {
+            _qCheckedAlignedNew(newRect, OHDR_SIZE + 4*sizeof(OBJ), __context);
+            _InstPtr(newRect)->o_class = Rectangle;
+            _InstPtr(newRect)->i_instvars[0] = left;
+            _InstPtr(newRect)->i_instvars[1] = top;
+            _InstPtr(newRect)->i_instvars[2] = w;
+            _InstPtr(newRect)->i_instvars[3] = h;
+            /* no STOREs needed - newRect is in newSpace */
+            RETURN ( newRect );
+        }
+    }
+%}
+.
+    ^ (self basicNew) left:left top:top width:w height:h
+!
+
+left:left right:right top:top bottom:bottom
+    "create and return a new Rectangle giving left, top, right and bottom coordinates"
+
+    ^ (self basicNew) left:left right:right top:top bottom:bottom
+!
+
+left:left top:top right:right bottom:bottom
+    "create and return a new Rectangle giving left, top, right and bottom coordinates"
+
+    ^ (self basicNew) left:left top:top right:right bottom:bottom
+! !
+
+!Rectangle methodsFor:'accessing'!
+
+top
+    "return the y-coordinate of the top-left origin"
+
+    ^ top
+!
+
+left
+    "return the x-coordinate of the top-left origin"
+
+    ^ left
+!
+
+width
+    "return the width of the rectangle"
+
+    ^ width
+!
+
+height
+    "return the height of the rectangle"
+
+    ^ height
+!
+
+left:newLeft right:right top:newTop bottom:bottom
+    "set the rectangle given left, top, right and bottom coordinates"
+
+    left := newLeft.
+    top := newTop.
+    width := right - left.
+    height := bottom - top
+!
+
+left:newLeft top:newTop right:right bottom:bottom
+    "set the rectangle given left, top, right and bottom coordinates"
+
+    left := newLeft.
+    top := newTop.
+    width := right - left.
+    height := bottom - top
+!
+
+left:newLeft top:newTop width:newWidth height:newHeight
+    "set the rectangle given left, top coordinates and width, height"
+
+    left := newLeft.
+    top := newTop.
+    width := newWidth.
+    height := newHeight
+!
+
+origin:aPoint
+    "set the top-left origin"
+
+    left := aPoint x.
+    top := aPoint y
+!
+
+corner:aPoint
+    "set the bottom-right corner"
+
+    width := aPoint x - left.
+    height := aPoint y - top
+!
+
+top:aNumber
+    "set the top edge"
+
+    height := height + (top - aNumber).
+    top := aNumber
+!
+
+bottom:aNumber
+    "set the bottom edge"
+
+    height := aNumber - top
+!
+
+origin:origin corner:corner
+    "set both origin and corner"
+
+    left := origin x.
+    top := origin y.
+    width := corner x - left.
+    height := corner y - top
+!
+
+extent:aPoint
+    "set the extent from the argument, aPoint with width taken from aPoint x
+     and height taken from aPoint y"
+
+    width := aPoint x.
+    height := aPoint y
+!
+
+origin:origin extent:extent
+    "set both origin and extent"
+
+    left := origin x.
+    top := origin y.
+    width := extent x.
+    height := extent y
+!
+
+origin
+    "return the origin"
+
+    ^ Point x:left y:top
+!
+
+left:aNumber
+    "set the left edge"
+
+    width := width + (left - aNumber).
+    left := aNumber
+!
+
+right:aNumber
+    "set the right edge"
+
+    width := aNumber - left
+!
+
+corner
+    "return the corner"
+
+    ^ Point x:(left + width) y:(top + height)
+!
+
+extent
+    "return the extent"
+
+    ^ Point x:width y:height
+!
+
+topLeft
+    "return the top-left point"
+
+    ^ Point x:left y:top
+!
+
+topRight
+    "return the top-right point"
+
+    ^ Point x:(left + width) y:top
+!
+
+bottomLeft
+    "return the bottom-left point"
+
+    ^ Point x:left y:(top + height)
+!
+
+bottomRight
+    "return the bottom-right point"
+
+    ^ Point x:(left + width) y:(top + height)
+!
+
+bottom
+    "return the y coordinate of the bottom"
+
+    ^ (top + height)
+!
+
+right
+    "return the x coordinate of the right"
+
+    ^ (left + width)
+!
+
+center
+    "return the point in the center of the receiver"
+
+    ^ Point x:(left + (width // 2)) y:(top + (height // 2))
+!
+
+leftCenter
+    "return the left center point"
+
+    ^ Point x:left y:(top + (height // 2))
+!
+
+rightCenter
+    "return the right center point"
+
+    ^ Point x:(left + width) y:(top + (height // 2))
+!
+
+area
+    "return the area (- for screen Rectangles this is the number
+     of pixels)"
+
+    ^ width * height
+! !
+
+!Rectangle methodsFor:'comparing'!
+
+= aRectangle
+    "return true, if the argument aRectangle represents the same
+     rectangle as the receiver"
+
+%{  /* NOCONTEXT */
+    static struct inlineCache eq = _ILC1;
+
+    if (_isNonNilObject(aRectangle) && _qClass(aRectangle) == Rectangle) {
+        if (( _InstPtr(self)->i_instvars[0] == _InstPtr(aRectangle)->i_instvars[0] )
+         && ( _InstPtr(self)->i_instvars[1] == _InstPtr(aRectangle)->i_instvars[1] )
+         && ( _InstPtr(self)->i_instvars[2] == _InstPtr(aRectangle)->i_instvars[2] )
+         && ( _InstPtr(self)->i_instvars[3] == _InstPtr(aRectangle)->i_instvars[3] )) {
+            RETURN ( true );
+        }
+    }
+%}
+.
+    (aRectangle isKindOf:Rectangle) ifFalse:[^ false].
+
+    left = aRectangle left ifFalse:[^ false].
+    top = aRectangle top ifFalse:[^ false].
+    width = aRectangle width ifFalse:[^ false].
+    height = aRectangle height ifFalse:[^ false].
+    ^ true
+! !
+
+!Rectangle methodsFor:'testing'!
+
+containsPoint:aPoint
+    "return true, if the argument, aPoint is contained in the receiver"
+
+    |px py|
+
+    px := aPoint x.
+    (px < left)           ifTrue:[^ false].
+    (px > (left + width)) ifTrue:[^ false].
+    py := aPoint y.
+    (py < top)            ifTrue:[^ false].
+    (py > (top + height)) ifTrue:[^ false].
+    ^ true
+!
+
+intersects:aRectangle
+    "return true, if the intersection between the argument, aRectangle
+     and the receiver is not empty"
+
+    |b r|
+
+    (aRectangle right)  < left ifTrue:[^ false].
+    (aRectangle bottom) < top  ifTrue:[^ false].
+    r := left + width.
+    (aRectangle left)   > r    ifTrue:[^ false].
+    b := top + height.
+    (aRectangle top)    > b    ifTrue:[^ false].
+    ^ true
+!
+
+contains:aRectangle
+    "return true, if the argument, aRectangle is equal to or
+     is contained fully within the receiver"
+
+    (left <= aRectangle left) ifTrue:[
+      ((left + width) >= aRectangle right) ifTrue:[
+        (top <= aRectangle top) ifTrue:[
+          ((top + height) >= aRectangle bottom) ifTrue:[
+            ^ true
+          ]
+        ]
+      ]
+    ].
+    ^ false
+!
+
+isContainedIn:aRectangle
+    "return true, if the receiver is fully contained within 
+     the argument, aRectangle"
+
+    (aRectangle left <= left) ifTrue:[
+      (aRectangle right >= (left + width)) ifTrue:[
+        (aRectangle top <= top) ifTrue:[
+          (aRectangle bottom >= (top + height)) ifTrue:[
+            ^ true
+          ]
+        ]
+      ]
+    ].
+    ^ false
+! !
+
+!Rectangle methodsFor:'rectangle operations'!
+
+intersect:aRectangle
+    "return a new rectangle covering the intersection of the receiver
+     and the argument, aRectangle.
+     the rectangles must intersect for a valid return"
+
+    ^ Rectangle left:(left max:(aRectangle left))
+               right:((left + width) min:(aRectangle right))
+                 top:(top max:(aRectangle top))
+              bottom:((top + height) min:(aRectangle bottom))
+!
+
+merge:aRectangle
+    "return a new rectangle covering both the receiver 
+     and the argument, aRectangle"
+
+    ^ Rectangle left:(left min:(aRectangle left))
+               right:((left + width) max:(aRectangle right))
+                 top:(top min:(aRectangle top))
+              bottom:((top + height) max:(aRectangle bottom))
+!
+
++ aPoint
+    "return a Rectangle with same extent as receiver but
+     origin translated by the argument, aPoint"
+
+    ^ Rectangle origin:(self origin + aPoint) extent:(self extent)
+!
+
+rounded
+    ^ Rectangle left:(left rounded) top:(top rounded)
+               width:(width rounded) height:(height rounded)
+!
+
+expandBy:amount
+    "return a new rectangle which is expanded in all directions
+     by amount, aPoint or Number"
+
+    |amountPoint|
+
+    amountPoint := amount asPoint.
+    ^ Rectangle left:(left - amountPoint x) top:(top - amountPoint y)
+               width:(width + (2 * amountPoint x))
+              height:(height + (2 * amountPoint y))
+!
+
+insetBy: delta
+    "return a new rectangle which is inset in all directions
+     by delta, aPoint or Rectangle"
+
+    | newrect |
+
+    newrect := delta asRectangle.
+    ^Rectangle origin: (self origin + (newrect origin))
+               corner: (self corner - (newrect corner))
+!
+
+translateBy:amount
+    "return a new rectangle which is translated (i.e. moved)
+     by amount, aPoint or Number"
+
+    |amountPoint|
+
+    amountPoint := amount asPoint.
+    ^ Rectangle left:(left + amountPoint x) top:(top + amountPoint y)
+               width:(width)
+              height:(height)
+!
+
+moveTo: aPoint
+    "destructively translate the rectangle"
+
+    | diff |
+
+    diff := aPoint - self origin.
+    self origin:aPoint corner:self corner + diff
+!
+
+moveBy: aPoint
+    "destructively translate the rectangle
+     sorry for the name inconsistency - but GNU-ST named it that way"
+
+    left := left + aPoint x.
+    top := top + aPoint y
+!
+
+scaleBy:scale
+    "destructively scale the receiver rectangle by scale"
+
+    |scalePoint|
+
+    scalePoint := scale asPoint.
+    width := (width * scalePoint x).
+    height := (height * scalePoint y)
+!
+
+scaledBy:scale
+    "return a new rectangle which is the receiver
+     scaled by scale"
+
+    |scalePoint|
+
+    scalePoint := scale asPoint.
+    ^ Rectangle left:left top:top
+               width:(width * scalePoint x)
+              height:(height * scalePoint y)
+!
+
+amountToTranslateWithin: aRectangle
+    "for GNU-ST compatibility"
+
+    ^(aRectangle origin) - self origin
+!
+
+areasOutside: aRectangle
+    "----------------------------------------------------------------
+    | added for GNU-ST compatibility
+    |
+    | author: Doug McCallum <uunet!!ico.isc.com!!dougm>
+    |
+    |areasOutside: aRectangle
+    | most complicated of the Rectangle primitives
+    | The basic methodology is to first determine that there is an 
+    | intersection by finding the overlapping rectangle.  From the
+    | overlapping rectangle, first determine if it runs along an edge.
+    | If it doesn't, extend the rectangle up to the top edge and add
+    | the new rectangle to the collection and start the rest of the
+    | process.  If the left edge does not touch the left edge of self,
+    | extend it to the edge saving the new rectangle.  Then do the 
+    | same to the right edge.  Then check top and bottom edges.  Most
+    | of the time only 2 or 3 rectangles get formed, occasionally 4.
+    | It should be possible to never get more than 3 but requires more
+    | work.
+     ----------------------------------------------------------------"
+
+    | collect iRect tmp |
+    iRect _ self intersect: aRectangle.
+    (iRect = nil) ifTrue: [^nil]. "case of no intersection"
+                                "the collect collection gathers Rectangles"
+    collect _ OrderedCollection new: 4.
+                                "is it floating or on the edge?"
+    (((((iRect top) ~= self top) 
+         and: [ (iRect bottom) ~= self bottom ])
+         and: [ (iRect left) ~= self left ])
+         and: [ (iRect right) ~= self right ] )
+        ifTrue: "entirely in the center."
+            [tmp _ Rectangle origin: (Point x: iRect left y: self top)
+                             corner: iRect bottomRight.
+             collect add: tmp.
+             iRect _ iRect merge: tmp].
+    ((iRect left) ~= self left)
+        ifTrue:                 "doesn't touch left edge so make it touch"
+            [tmp _ Rectangle origin: (Point x: self left y: iRect top)
+                                 corner: iRect bottomLeft.
+                 collect add: tmp.
+                                "merge new (tmp) with overlap to keep track"
+                 iRect _ iRect merge: tmp].
+    ((iRect right) ~= self right)
+        ifTrue:                 "doesn't touch right edge so extend it"
+            [tmp _ Rectangle origin: iRect topRight
+                                 corner: (Point x: self right y: iRect bottom).
+                 collect add: tmp.
+                 iRect _ iRect merge: tmp].
+    (((iRect left) ~= self left) || [(iRect top) ~= self top])
+        ifTrue:                 "whole top part can be taken now"
+            [tmp _ Rectangle origin: self origin corner: iRect topRight.
+                 collect add: tmp].
+    (((iRect right) ~= self right) || [(iRect bottom) ~= self bottom])
+        ifTrue:                 "whole bottom open and can be taken"
+            [tmp _ Rectangle origin: iRect bottomLeft corner: self corner.
+                 collect add: tmp].
+    ^collect
+
+! !
+
+!Rectangle methodsFor:'printing'!
+
+printString
+    "return a string for printing"
+
+    ^ 'Rectangle origin:'
+      , self origin printString
+      , ' corner:'
+      , self corner printString
+!
+
+printOn:aStream
+    "print the receiver on aStream"
+
+    aStream nextPutAll:'Rectangle origin:'.
+    (self origin) printOn:aStream.
+    aStream nextPutAll:' corner:'.
+    (self corner) printOn:aStream
+!
+
+storeOn:aStream
+    "store the receiver on aStream; i.e. print an expression which will
+     reconstruct the receiver"
+
+    aStream nextPutAll:'('.
+    aStream nextPutAll:(self class name).
+    aStream nextPutAll:' new origin:'.
+    aStream nextPutAll:(self origin printString).
+    aStream nextPutAll:' corner:'.
+    aStream nextPutAll:(self corner printString).
+    aStream nextPutAll:'('
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Registry.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,165 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Registry
+       instanceVariableNames:'registeredObjects phantoms'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'System-Support'
+!
+
+Registry comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+%W% %E%
+written jun 93 by claus
+'!
+
+!Registry class methodsFor:'documentation'!
+
+documentation
+"
+Registries provide an easy interface to using ShadowArrays. 
+A class, which wants to be informed of instance-death, can put a created object
+into a registry. The registry will create a copy of the object, and
+watch out for death of the registered object. When it dies, the copy will
+be sent the message >>disposed.
+The trick with the shallow copy is especially nice, you can think of it as
+beeing the original object which died.
+
+All objects, which keep external resources (such as fileDescriptors, fonts, 
+colormap-entries etc.) should be registered, so that the underlying resource
+can be freed when the object goes away.
+
+Of course, you too can use it to do whatever you need to do in case of the
+death of an object.
+"
+! !
+
+!Registry methodsFor:'dispose handling'!
+
+informDispose
+    "an instance has been destroyed - look which one it was"
+
+    |phantom|
+
+    1 to:phantoms size do:[:index |
+        (registeredObjects at:index) isNil ifTrue:[
+            (phantoms at:index) notNil ifTrue:[
+                phantom := phantoms at:index.
+                phantoms at:index put:nil.
+                phantom disposed
+            ]
+        ]
+    ]
+! !
+
+!Registry methodsFor:'enumeration'!
+
+contentsDo:aBlock
+    "evaluate aBlock for each registered object"
+
+    registeredObjects notNil ifTrue:[
+        registeredObjects do:[:o |
+            o notNil ifTrue:[
+                aBlock value:o
+            ]
+        ]
+    ]
+! !
+
+!Registry methodsFor:'accessing'!
+
+contents
+    "return the collection of registered objects"
+
+    ^ registeredObjects
+!
+
+changed:anObject
+    "a registered object has changed, create a new phantom"
+
+    |index|
+
+    index := registeredObjects identityIndexOf:anObject ifAbsent:[0].
+    index ~~ 0 ifTrue:[
+        phantoms at:index put:anObject shallowCopy.
+    ]
+!
+
+register:anObject
+    "register anObject, so that a copy of it gets the disposed message
+     when anObject dies (somewhere in the future)"
+
+    |phantom newColl count p index|
+
+    phantom := anObject shallowCopy.
+
+    registeredObjects isNil ifTrue:[
+        registeredObjects := ShadowArray new:10.
+        registeredObjects watcher:self.
+        registeredObjects at:1 put:anObject.
+        phantoms := VariableArray with:phantom.
+        ^ self
+    ].
+
+    index := registeredObjects identityIndexOf:anObject ifAbsent:[0].
+    index ~~ 0 ifTrue:[
+	self error:'bad register'.
+	phantoms at:index put:phantom.
+	^ self
+    ].
+
+    "search for a free slot, on the fly look for leftovers"
+    count := phantoms size.
+    1 to:count do:[:index |
+        (registeredObjects at:index) isNil ifTrue:[
+            "is there a leftover ?"
+            (phantoms at:index) notNil ifTrue:[
+                p := phantoms at:index.
+                phantoms at:index put:nil.
+                p disposed
+            ].
+            (phantoms at:index) isNil ifTrue:[
+                registeredObjects at:index put:anObject.
+                phantoms at:index put:phantom.
+                ^ self
+            ]
+        ]
+    ].
+
+    "no free slot, add at the end"
+
+    newColl := ShadowArray new:(count * 2).
+    newColl replaceFrom:1 to:count with:registeredObjects.
+    registeredObjects := newColl.
+    registeredObjects watcher:self.
+
+    registeredObjects at:(count + 1) put:anObject.
+    phantoms add:phantom
+!
+
+unregister:anObject
+    "remove registration of anObject, without telling the phantom;
+     should be sent, if we are no more interrested in destruction of
+     anObject (i.e. it no longer holds external resources)."
+
+    |index|
+
+    index := registeredObjects identityIndexOf:anObject ifAbsent:[0].
+    index ~~ 0 ifTrue:[
+        phantoms at:index put:nil.
+        registeredObjects at:index put:nil
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Semaphore.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,112 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+LinkedList subclass:#Semaphore
+         instanceVariableNames:'count waitingProcesses'
+         classVariableNames:''
+         poolDictionaries:''
+         category:'Kernel-Processes'!
+
+Semaphore comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+Semaphores are used to synchronize processes providing a nonBusy wait
+mechanism. A process can wait for the availability of some resource by
+performing a Semaphore-wait, which will suspend the process until the
+resource becomes available which is signalled by (another process performing)
+Semaphore-signal.
+If the resource has been alrady available before the wait, no suspending is
+done, but the resource immediately allocated.
+
+See samples in doc/coding.
+
+%W% %E%
+'!
+
+!Semaphore class methodsFor:'instance creation'!
+
+new
+    "create & return a new semaphore which blocks until a signal is sent"
+
+    ^ super new setCount:0
+!
+
+new:n
+    "create & return a new semaphore which allows n waits before
+     blocking"
+
+    ^ super new setCount:n
+!
+
+forMutualExclusion
+    "create & return a new semaphore which allows exactly one process to
+     wait on it without blocking"
+
+    ^ self new:1
+! !
+
+!Semaphore methodsFor:'private accessing'!
+
+setCount:n
+    count := n
+! !
+
+!Semaphore methodsFor:'wait & signal'!
+
+wait
+    "wait for the semaphore"
+
+    |current|
+
+    "
+     need a while-loop here, since more than one process may
+     wait for it and another one may also wake up.
+     Thus count is not always non-zero after returning from
+     suspend.
+    "
+
+    [count == 0] whileTrue:[
+        current := Processor currentProcess.
+        waitingProcesses isNil ifTrue:[
+            waitingProcesses := OrderedCollection with:current
+        ] ifFalse:[
+            waitingProcesses add:current
+        ].
+        current suspend
+    ].
+    count := count - 1
+!
+
+signal
+    "waking up waiters"
+
+    count := count + 1.
+    waitingProcesses notNil ifTrue:[
+        waitingProcesses isEmpty ifFalse:[
+            waitingProcesses removeFirst resume
+        ]
+    ]
+!
+
+critical:aBlock
+    "evaluate aBlock as a critical region; the receiver must be
+     created using Semaphore-forMutualExclusion"
+
+    |value|
+
+    self wait.
+    value := aBlock value.
+    self signal.
+    ^ value
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SeqColl.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,955 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Collection subclass:#SequenceableCollection
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Abstract'
+!
+
+SequenceableCollection comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+SequenceableCollections have ordered elements which can be accessed via
+an index. SequenceableCollection is an abstract class - there are no
+instances of it in the system.
+
+%W% %E%
+
+written spring 89 by claus
+'!
+
+!SequenceableCollection class methodsFor:'instance creation'!
+
+new:size withAll:element
+    "return a new Collection of size, where all elements are
+     initialized to element"
+
+    |newCollection|
+
+    newCollection := self new:size.
+    newCollection atAllPut:element.
+    ^ newCollection
+! !
+
+!SequenceableCollection methodsFor:'accessing'!
+
+first
+    "return the first element"
+
+    ^ self at:1
+!
+
+last
+    "return the last element"
+
+    ^ self at:(self size)
+! !
+
+!SequenceableCollection methodsFor:'comparing'!
+
+= aCollection
+    "return true if the receiver and aCollection represent collections
+     with equal contents."
+
+    |index "{ Class: SmallInteger }"
+     stop  "{ Class: SmallInteger }" |
+
+    (aCollection == self) ifTrue:[^true].
+    (aCollection isKindOf:SequenceableCollection) ifFalse:[^false].
+
+    stop := self size.
+    stop == (aCollection size) ifFalse:[^false].
+    index := 1.
+    [index <= stop] whileTrue:[
+        (self at:index) = (aCollection at:index) ifFalse:[^false].
+        index := index + 1
+    ].
+    ^ true
+!
+
+startsWith:aCollection
+    "return true, if the receivers first elements match those
+     of aCollection"
+
+    |index "{ Class: SmallInteger }"
+     stop  "{ Class: SmallInteger }" |
+
+    (aCollection == self) ifTrue:[^true].
+    (aCollection isKindOf:SequenceableCollection) ifFalse:[^false].
+
+    stop := aCollection size.
+    stop > self size ifTrue:[^false].
+
+    index := 1.
+    [index <= stop] whileTrue:[
+        (self at:index) = (aCollection at:index) ifFalse:[^false].
+        index := index + 1
+    ].
+    ^ true
+
+    "'abcde' startsWith:#($a $b $c)"
+    "#[1 2 3 4] startsWith:#(1 2 3)"
+    "#(1 2 3 4) asOrderedCollection startsWith:#(1 2 3)"
+!
+
+endsWith:aCollection
+    "return true, if the receivers last elements match those
+     of aCollection"
+
+    |index1 "{ Class: SmallInteger }"
+     index2 "{ Class: SmallInteger }" 
+     stop   "{ Class: SmallInteger }" |
+
+    (aCollection == self) ifTrue:[^true].
+    (aCollection isKindOf:SequenceableCollection) ifFalse:[^false].
+
+    stop := aCollection size.
+    stop > self size ifTrue:[^false].
+
+    index1 := self size.
+    index2 := aCollection size.
+    [index2 > 0] whileTrue:[
+        (self at:index1) = (aCollection at:index2) ifFalse:[^false].
+        index1 := index1 - 1.
+        index2 := index2 - 1
+    ].
+    ^ true
+
+    "'abcde' endsWith:#($d $e)"
+    "#[1 2 3 4] endsWith:#(3 4)"
+    "#(1 2 3 4) asOrderedCollection endsWith:#(3 4)"
+! !
+
+!SequenceableCollection methodsFor:'testing'!
+
+size
+    "return the number of elements in the collection.
+     concrete implementations must define this"
+
+    ^ self subclassResponsibility
+! !
+
+!SequenceableCollection methodsFor:'copying'!
+
+, aCollection
+    "return a new collection formed from concatenating the receiver with
+     the argument"
+
+    |newCollection 
+     mySize    "{ Class: SmallInteger }"
+     newSize   "{ Class: SmallInteger }"
+     otherSize "{ Class: SmallInteger }"
+     dstIndex  "{ Class: SmallInteger }"|
+
+    mySize := self size.
+    otherSize := aCollection size.
+    newSize := mySize + otherSize.
+    newCollection := self species new:newSize.
+
+    newCollection replaceFrom:1 to:mySize with:self startingAt:1.
+    dstIndex := mySize + 1.
+    (aCollection isKindOf:SequenceableCollection) ifTrue:[
+        "yes, aCollection has indexed elements"
+        newCollection replaceFrom:dstIndex to:newSize
+                             with:aCollection startingAt:1.
+        ^ newCollection
+    ] ifFalse:[
+        "no, enumerate aCollection"
+        aCollection do:[:element |
+            newCollection at:dstIndex put:element.
+            dstIndex := dstIndex + 1
+        ]
+    ].
+    ^ newCollection
+!
+
+copyWith:newElement
+    "return a new collection consisting of receivers elements
+     plus the argument"
+
+    |newCollection mySize newSize|
+
+    mySize := self size.
+    newSize := mySize + 1.
+    newCollection := self species new:newSize.
+    newCollection replaceFrom:1 to:mySize with:self startingAt:1.
+    newCollection at:newSize put:newElement.
+    ^newCollection
+!
+
+copyWithout:anElement
+    "return a new collection consisting of receivers elements
+     without anElement (if it was present)"
+
+    |newCollection skipIndex 
+     dstIndex "{ Class: SmallInteger }"
+     index    "{ Class: SmallInteger }"
+     stop     "{ Class: SmallInteger }" |
+
+    skipIndex := self indexOf:anElement startingAt:1.
+    (skipIndex == 0) ifTrue:[^ self copy].
+    stop := self size.
+    newCollection := self class new:(stop - 1).
+    dstIndex := 1.
+    index := 1.
+    [index <= stop] whileTrue:[
+        (index ~~ skipIndex) ifTrue:[
+            newCollection at:dstIndex put:(self at:index).
+            dstIndex := dstIndex + 1
+        ].
+        index := index + 1
+    ].
+    ^ newCollection
+!
+
+copyFrom:start to:stop
+    "return a new collection consisting of receivers elements
+     between start and stop"
+
+    |newCollection newSize|
+
+    newSize := stop - start + 1.
+    newCollection := self class new:newSize.
+    newCollection replaceFrom:1 to:newSize with:self startingAt:start.
+    ^ newCollection
+!
+
+copyFrom:start
+    "return a new collection consisting of receivers elements
+     from start to the end of the collection"
+
+    ^ self copyFrom:start to:(self size)
+!
+
+copyTo:stop
+    "return a new collection consisting of receivers elements
+     from 1 up to index stop"
+
+    ^ self copyFrom:1 to:stop
+!
+
+copyWithoutIndex:omitIndex
+    "return a new collection consisting of receivers elements
+     without the argument stored at omitIndex"
+
+    |copy|
+
+    copy := self class new:(self size - 1).
+    copy replaceFrom:1 to:(omitIndex - 1) with:self startingAt:1.
+    copy replaceFrom:omitIndex to:(copy size) 
+                with:self startingAt:(omitIndex + 1).
+    ^ copy
+! !
+
+!SequenceableCollection methodsFor:'filling and replacing'!
+
+from:index1 to:index2 put:anObject
+    "replace the elements from index1 to index2 of the collection
+     by the argument, anObject"
+
+    |index "{ Class: SmallInteger }"
+     end   "{ Class: SmallInteger }"|
+
+    index := index1.
+    end := index2.
+    [index <= end] whileTrue:[
+        self at:index put:anObject.
+        index := index + 1
+    ]
+!
+
+atAllPut:anObject
+    "replace all elements of the collection by the argument, anObject"
+
+    self from:1 to:(self size) put:anObject
+!
+
+atAll:indexCollection put:anObject
+    "put anObject into all indexes from indexCollection in the receiver"
+
+    indexCollection do:[:index | self at:index put:anObject]
+
+    "(Array new:10) atAll:(1 to:5) put:0"
+    "(Array new:10) atAll:#(1 5 6 9) put:0"
+!
+
+replaceAll:oldObject by:newObject
+    "replace all oldObjects by newObject in the receiver"
+
+    1 to:self size do:[:index |
+        (self at:index) = oldObject ifTrue:[
+            self at:index put:newObject
+        ]
+    ]
+!
+
+replaceFrom:start with:replacementCollection
+    "replace elements starting at start with elements
+     taken from replacementCollection (starting at 1)"
+
+    ^ self replaceFrom:start 
+                    to:(start + replacementCollection size - 1)
+                  with:replacementCollection
+            startingAt:1
+!
+
+replaceFrom:start to:stop with:replacementCollection
+    "replace elements between index start and stop with elements
+     taken from replacementCollection (starting at 1)"
+
+    ^ self replaceFrom:start
+                    to:stop
+                  with:replacementCollection
+            startingAt:1
+!
+
+replaceFrom:start to:stop with:replacementCollection startingAt:repStart
+    "replace elements between index start and stop with elements
+     taken from replacementCollection (starting at repStart)"
+
+    |srcIndex "{ Class: SmallInteger }"
+     dstIndex "{ Class: SmallInteger }"
+     end      "{ Class: SmallInteger }" |
+
+    (replacementCollection == self) ifTrue:[
+        (repStart < start) ifTrue:[
+            " must do reverse copy "
+            srcIndex := repStart + (stop - start).
+            dstIndex := stop.
+            end := start.
+            [dstIndex >= end] whileTrue:[
+                self at:dstIndex put:(replacementCollection at:srcIndex).
+                srcIndex := srcIndex - 1.
+                dstIndex := dstIndex - 1
+            ].
+            ^ self
+        ]
+    ].
+
+    srcIndex := repStart.
+    dstIndex := start.
+    end := stop.
+    [dstIndex <= end] whileTrue:[
+        self at:dstIndex put:(replacementCollection at:srcIndex).
+        srcIndex := srcIndex + 1.
+        dstIndex := dstIndex + 1
+    ]
+!
+
+withCRs
+    "return a new collection consisting of receivers elements
+     with all \-characters replaced by cr-characters"
+
+    |newCollection
+     size "{ Class: SmallInteger }" |
+
+    newCollection := self copy.
+    size := self size.
+    1 to:size do:[:index |
+        ((self at:index) == $\) ifTrue:[
+            newCollection at:index put:(Character cr)
+        ]
+    ].
+    ^ newCollection
+!
+
+withoutCRs
+    "return a new collection consisting of receivers elements
+     with all cr-characters replaced by \-characters"
+
+    |newCollection 
+     size "{ Class: SmallInteger }" |
+
+    newCollection := self copy.
+    size := self size.
+    1 to:size do:[:index|
+        ((self at:index) == Character cr) ifTrue:[
+            newCollection at:index put:$\
+        ]
+    ].
+    ^ newCollection
+! !
+
+!SequenceableCollection methodsFor:'adding & removing'!
+
+addFirst:anElement
+    "prepend the argument, anElement to the collection"
+
+    |newSize|
+
+    newSize := self size + 1.
+    self grow:newSize.
+    self replaceFrom:2 to:newSize with:self startingAt:1.
+    self at:1 put:anElement
+!
+
+add:anElement
+    "append the argument, anElement to the collection"
+
+    |newSize|
+
+    newSize := self size + 1.
+    self grow:newSize.
+    self at:newSize put:anElement
+!
+
+add:anElement beforeIndex:index
+    "insert the first argument, anObject into the collection before slot index"
+
+    |newSize|
+
+    newSize := self size + 1.
+    self grow:newSize.
+    self replaceFrom:index + 1 to:newSize with:self startingAt:index.
+    self at:index put:anElement
+!
+
+remove:anElement ifAbsent:aBlock
+    "search for anElement and, if present remove it; if not present
+     return the value of evaluating aBlock"
+
+    |any 
+     dstIndex "{ Class: SmallInteger }"
+     sz       "{ Class: SmallInteger }"|
+
+    dstIndex := 1.
+    any := false.
+    sz := self size.
+    1 to:sz do:[:srcIndex |
+        (anElement = (self at:srcIndex)) ifTrue:[
+            any := true
+        ] ifFalse:[
+            (dstIndex ~~ srcIndex) ifTrue:[
+                self at:dstIndex put:(self at:srcIndex)
+            ].
+            dstIndex := dstIndex + 1
+        ]
+    ].
+    any ifTrue:[
+        self grow:dstIndex - 1
+    ] ifFalse:[
+        aBlock value
+    ]
+!
+
+removeFromIndex:startIndex toIndex:endIndex
+    "remove the elements stored at indexes between startIndex and endIndex"
+
+    |newSize|
+
+    newSize := self size - endIndex + startIndex - 1.
+    self replaceFrom:startIndex to:newSize with:self startingAt:(endIndex + 1).
+    self grow:newSize
+!
+
+removeIndex:index
+    "remove the argument stored at index"
+
+    self removeFromIndex:index toIndex:index
+! !
+
+!SequenceableCollection methodsFor:'searching'!
+
+detect:aBlock ifNone:exceptionBlock
+    "find the first element, for which evaluation of the argument, aBlock
+     return true; if none does so, return the evaluation of exceptionBlock
+
+    reimplemented here for speed"
+
+    |index "{ Class: SmallInteger }"
+     stop  "{ Class: SmallInteger }"
+     element|
+
+    stop := self size.
+    index := 1.
+    [index <= stop] whileTrue:[
+        element := self at:index.
+        (aBlock value:element) ifTrue:[
+            ^ element
+        ].
+        index := index + 1
+    ].
+    ^ exceptionBlock value
+!
+
+indexOf:anElement
+    "search the collection for anElement;
+     if found, return the index otherwise return 0.
+     The comparison is done using = (i.e. equality test)."
+
+    ^ self indexOf:anElement startingAt:1
+!
+
+indexOf:anElement ifAbsent:exceptionBlock
+    "search the collection for anElement;
+     if found, return the index otherwise return the value of the
+     exceptionBlock.
+     The comparison is done using = (i.e. equality test)."
+
+    |index|
+
+    index := self indexOf:anElement startingAt:1.
+    (index == 0) ifTrue:[^ exceptionBlock value].
+    ^ index
+!
+
+indexOf:anElement startingAt:start
+    "search the collection for anElement staring search at index start;
+     if found, return the index otherwise return 0.
+     The comparison is done using = (i.e. equality test)."
+
+    |index "{ Class: SmallInteger }"
+     stop  "{ Class: SmallInteger }" |
+
+    index := start.
+    stop := self size.
+    [index <= stop] whileTrue:[
+        anElement = (self at:index) ifTrue:[^ index].
+        index := index + 1
+    ].
+    ^ 0
+!
+
+indexOf:anElement startingAt:start ifAbsent:exceptionBlock
+    "search the collection for anElement starting search at start;
+     if found, return the index otherwise return the value of the
+     exceptionBlock.
+     The comparison is done using = (i.e. equality test)."
+
+    |index|
+
+    index := self indexOf:anElement startingAt:start.
+    (index == 0) ifTrue:[^ exceptionBlock value].
+    ^ index
+!
+
+identityIndexOf:anElement
+    "search the collection for anElement using identity compare (i.e. ==);
+     if found, return the index otherwise return 0."
+
+    ^ self identityIndexOf:anElement startingAt:1
+!
+
+identityIndexOf:anElement ifAbsent:exceptionBlock
+    "search the collection for anElement using identity compare (i.e. ==);
+     if found, return the index otherwise return the value of the
+     exceptionBlock."
+
+    |index|
+
+    index := self identityIndexOf:anElement startingAt:1.
+    (index == 0) ifTrue:[^ exceptionBlock value].
+    ^ index
+!
+
+identityIndexOf:anElement startingAt:start
+    "search the collection for anElement staring search at index start
+     using identity compare  (i.e. ==);
+     if found, return the index otherwise return 0."
+
+    |index "{ Class: SmallInteger }"
+     stop  "{ Class: SmallInteger }" |
+
+    index := start.
+    stop := self size.
+    [index <= stop] whileTrue:[
+        anElement == (self at:index) ifTrue:[^ index].
+        index := index + 1
+    ].
+    ^ 0
+!
+
+identityIndexOf:anElement startingAt:start ifAbsent:exceptionBlock
+    "search the collection for anElement starting search at start;
+     if found, return the index otherwise return the value of the
+     exceptionBlock.
+     This one searches for identical objects (i.e. ==)."
+
+    |index|
+
+    index := self identityIndexOf:anElement startingAt:start.
+    (index == 0) ifTrue:[^ exceptionBlock value].
+    ^ index
+!
+
+findFirst:aBlock
+    "find the first element, for which evaluation of the argument, aBlock
+     return true; return its index or 0 if none detected."
+
+    |index "{ Class: SmallInteger }"
+     stop  "{ Class: SmallInteger }" |
+
+    stop := self size.
+    index := 1.
+    [index <= stop] whileTrue:[
+        (aBlock value:(self at:index)) ifTrue:[^ index].
+        index := index + 1
+    ].
+    ^ 0
+
+    "#(1 2 3 4 5 6) findFirst:[:x | (x > 3) and:[x even]]"
+!
+
+includes:anElement
+    "return true if the collection contains anElement; false otherwise.
+     Comparison is done using equality compare (i.e. =)."
+
+    ((self indexOf:anElement startingAt:1) == 0) ifTrue:[^ false].
+    ^ true
+! !
+
+!SequenceableCollection methodsFor:'sorting & reordering'!
+
+reverse
+    "reverse the order of the arguments inplace"
+
+    |lowIndex "{ Class: SmallInteger }"
+     hiIndex  "{ Class: SmallInteger }"
+     t|
+
+    hiIndex := self size.
+    lowIndex := 1.
+    [lowIndex < hiIndex] whileTrue:[
+        t := self at:lowIndex.
+        self at:lowIndex put:(self at:hiIndex). 
+        self at:hiIndex put:t.
+        lowIndex := lowIndex + 1.
+        hiIndex := hiIndex - 1
+    ]
+    "#(4 5 6 7 7) reverse"
+!
+
+quickSortFrom:begin to:end
+    "actual quicksort worker for sort-message"
+
+    |b "{ Class: SmallInteger }"
+     e "{ Class: SmallInteger }"
+     middleElement temp |
+
+    b := begin.
+    e := end.
+    middleElement := self at:((b + e) // 2).
+
+    [b < e] whileTrue:[
+        [b < end and:[(self at:b) < middleElement]] whileTrue:[b := b + 1].
+        [e > begin and:[middleElement < (self at:e)]] whileTrue:[e := e - 1].
+
+        (b <= e) ifTrue:[
+            (b == e) ifFalse:[
+                temp := self at:b.
+                self at:b put:(self at:e).
+                self at:e put:temp
+            ].
+            b := b + 1.
+            e := e - 1
+        ]
+    ].
+    (begin < e) ifTrue:[self quickSortFrom:begin to:e].
+    (b < end) ifTrue:[self quickSortFrom:b to:end]
+!
+
+quickSortFrom:begin to:end with:aCollection
+    "actual quicksort worker for sortWith-message"
+
+    |b "{ Class: SmallInteger }"
+     e "{ Class: SmallInteger }"
+     middleElement temp |
+
+    b := begin.
+    e := end.
+    middleElement := self at:((b + e) // 2).
+
+    [b < e] whileTrue:[
+        [b < end and:[(self at:b) < middleElement]] whileTrue:[b := b + 1].
+        [e > begin and:[middleElement < (self at:e)]] whileTrue:[e := e - 1].
+
+        (b <= e) ifTrue:[
+            (b == e) ifFalse:[
+                temp := self at:b.
+                self at:b put:(self at:e).
+                self at:e put:temp.
+                temp := aCollection at:b.
+                aCollection at:b put:(aCollection at:e).
+                aCollection at:e put:temp
+            ].
+            b := b + 1.
+            e := e - 1
+        ]
+    ].
+    (begin < e) ifTrue:[self quickSortFrom:begin to:e with:aCollection].
+    (b < end) ifTrue:[self quickSortFrom:b to:end with:aCollection]
+!
+
+quickSortFrom:begin to:end sortBlock:sortBlock
+    "actual quicksort worker for sort:-message"
+
+    |b "{ Class: SmallInteger }"
+     e "{ Class: SmallInteger }"
+     middleElement temp |
+
+    b := begin.
+    e := end.
+    middleElement := self at:((b + e) // 2).
+
+    [b < e] whileTrue:[
+        [b < end and:[sortBlock value:(self at:b) value:middleElement]] whileTrue:[b := b + 1].
+        [e > begin and:[sortBlock value:middleElement value:(self at:e)]] whileTrue:[e := e - 1].
+
+        (b <= e) ifTrue:[
+            (b == e) ifFalse:[
+                temp := self at:b.
+                self at:b put:(self at:e).
+                self at:e put:temp
+            ].
+            b := b + 1.
+            e := e - 1
+        ]
+    ].
+    (begin < e) ifTrue:[self quickSortFrom:begin to:e sortBlock:sortBlock].
+    (b < end) ifTrue:[self quickSortFrom:b to:end sortBlock:sortBlock]
+!
+
+quickSortFrom:begin to:end sortBlock:sortBlock with:aCollection
+    "actual quicksort worker for sort:with:-message"
+
+    |b "{ Class: SmallInteger }"
+     e "{ Class: SmallInteger }"
+     middleElement temp |
+
+    b := begin.
+    e := end.
+    middleElement := self at:((b + e) // 2).
+
+    [b < e] whileTrue:[
+        [b < end and:[sortBlock value:(self at:b) value:middleElement]] whileTrue:[b := b + 1].
+        [e > begin and:[sortBlock value:middleElement value:(self at:e)]] whileTrue:[e := e - 1].
+
+        (b <= e) ifTrue:[
+            (b == e) ifFalse:[
+                temp := self at:b.
+                self at:b put:(self at:e).
+                self at:e put:temp.
+                temp := aCollection at:b.
+                aCollection at:b put:(aCollection at:e).
+                aCollection at:e put:temp
+            ].
+            b := b + 1.
+            e := e - 1
+        ]
+    ].
+    (begin < e) ifTrue:[self quickSortFrom:begin to:e sortBlock:sortBlock with:aCollection].
+    (b < end) ifTrue:[self quickSortFrom:b to:end sortBlock:sortBlock with:aCollection]
+!
+
+bubbleSort
+    "sort the collection inplace using bubbleSort (sloooow)
+     - this one makes only sense to sort after inserting an element into
+       an already sorted collection (if at all)"
+
+    |index  "{ Class: SmallInteger }"
+     index2 "{ Class: SmallInteger }"
+     end    "{ Class: SmallInteger }"
+     smallest smallestIndex thisOne|
+
+    end := self size.
+    index := 1.
+    [index <= end] whileTrue:[
+        smallest := self at:index.
+        smallestIndex := index.
+        index2 := index + 1.
+        [index2 <= end] whileTrue:[
+            (self at:index2) < smallest ifTrue:[
+                smallestIndex := index2.
+                smallest := self at:index2
+            ].
+            index2 := index2 + 1
+        ].
+        (smallestIndex ~~ index) ifTrue:[
+            thisOne := self at:index.
+            self at:index put:smallest.
+            self at:smallestIndex put:thisOne
+        ].
+        index := index + 1
+    ]
+
+    "#(1 16 7 98 3 19 4 0) bubbleSort"
+!
+
+sort
+    "sort the collection inplace. The elements are compared using
+     > and < i.e. they should offer a magnitude-like protocol."
+    |sz|
+
+    sz := self size.
+    (sz > 1) ifTrue:[
+        self quickSortFrom:1 to:sz
+    ]
+
+    "#(1 16 7 98 3 19 4 0) sort"
+!
+
+sortWith:aCollection
+    "sort the receiver collection inplace, also sort aCollection with it.
+     Use, when you have a key collection to sort another collection with."
+
+    |sz|
+
+    sz := self size.
+    (sz > 1) ifTrue:[
+        self quickSortFrom:1 to:sz with:aCollection
+    ]
+
+    "|c1 c2|
+     c1 := #(1 16 7 9).
+     c2 := #('one' 'sixteen' 'seven' 'nine').
+     c1 sortWith:c2.
+     c1 printNewline.
+     c2 printNewline"
+!
+
+sort:sortBlock
+    "sort the collection inplace using the 2-arg block sortBlock
+     for comparison. This allows any sort criteria to be implemented."
+
+    |sz|
+
+    sz := self size.
+    (sz > 1) ifTrue:[
+        self quickSortFrom:1 to:sz sortBlock:sortBlock
+    ]
+
+    "#(1 16 7 98 3 19 4 0) sort:[:a :b | a < b]"
+    "#(1 16 7 98 3 19 4 0) sort:[:a :b | a > b]"
+!
+
+sort:sortBlock with:aCollection
+    "sort the collection inplace using the 2-arg block sortBlock
+     for comparison. Also reorder the elements in aCollection"
+
+    |sz|
+
+    sz := self size.
+    (sz > 1) ifTrue:[
+        self quickSortFrom:1 to:sz sortBlock:sortBlock with:aCollection
+    ]
+
+    "|c1 c2|
+     c1 := #(1 16 7 9).
+     c2 := #('one' 'sixteen' 'seven' 'nine').
+     c1 sort:[:a :b | a > b] with:c2.
+     c1 printNewline.
+     c2 printNewline"
+! !
+
+!SequenceableCollection methodsFor:'enumerating'!
+
+do:aBlock
+    "evaluate the argument, aBlock for every element in the collection."
+
+    |index  "{ Class:SmallInteger }"
+     length "{ Class:SmallInteger }"|
+
+    index := 1.
+    length := self size.
+    [index <= length] whileTrue:[
+        aBlock value:(self at:index).
+        index := index + 1
+    ]
+!
+
+from:index1 to:index2 do:aBlock
+    "evaluate the argument, aBlock for the elements with index index1 to
+     index2 in the collection"
+
+    |index "{ Class:SmallInteger }"
+     stop  "{ Class:SmallInteger }" |
+
+    index := index1.
+    stop := index2.
+    [index <= stop] whileTrue:[
+        aBlock value:(self at:index).
+        index := index + 1
+    ]
+!
+
+with:aCollection do:aBlock
+    "evaluate the argument, aBlock for successive elements from
+     each of the two collections self and aCollection.
+     aBlock must be a two-argument block"
+
+    |index "{ Class: SmallInteger }"
+     stop  "{ Class: SmallInteger }" |
+
+    index := 1.
+    stop := self size.
+    [index <= stop] whileTrue:[
+        aBlock value:(self at:index) value:(aCollection at:index).
+        index := index + 1
+    ]
+!
+
+reverseDo:aBlock
+    "evaluate the argument, aBlock for every element in the collection
+     in reverse order"
+
+    |index "{ Class:SmallInteger }" |
+
+    index := self size.
+    [index > 0] whileTrue:[
+        aBlock value:(self at:index).
+        index := index - 1
+    ]
+!
+
+collect:aBlock
+    "evaluate the argument, aBlock for every element in the collection
+     and return a collection of the results"
+
+    |newCollection
+     index  "{ Class:SmallInteger }"
+     length "{ Class:SmallInteger }" |
+
+    length := self size.
+    newCollection := self species new:length.
+    index := 1.
+    [index <= length] whileTrue:[
+        newCollection at:index put:(aBlock value:(self at:index)).
+        index := index + 1
+    ].
+    ^ newCollection
+!
+
+select:aBlock
+    "evaluate the argument, aBlock for every element in the collection
+     and return a collection of all elements for which the block return
+     true"
+
+    |element newColl
+     index  "{ Class:SmallInteger }"
+     length "{ Class:SmallInteger }" |
+
+    length := self size.
+    newColl := OrderedCollection new:length.
+    index := 1.
+    [index <= length] whileTrue:[
+        element := self at:index.
+        (aBlock value:element) ifTrue:[
+            newColl add:element
+        ].
+        index := index + 1
+    ].
+    ^ newColl
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SequenceableCollection.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,955 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Collection subclass:#SequenceableCollection
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Abstract'
+!
+
+SequenceableCollection comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+SequenceableCollections have ordered elements which can be accessed via
+an index. SequenceableCollection is an abstract class - there are no
+instances of it in the system.
+
+%W% %E%
+
+written spring 89 by claus
+'!
+
+!SequenceableCollection class methodsFor:'instance creation'!
+
+new:size withAll:element
+    "return a new Collection of size, where all elements are
+     initialized to element"
+
+    |newCollection|
+
+    newCollection := self new:size.
+    newCollection atAllPut:element.
+    ^ newCollection
+! !
+
+!SequenceableCollection methodsFor:'accessing'!
+
+first
+    "return the first element"
+
+    ^ self at:1
+!
+
+last
+    "return the last element"
+
+    ^ self at:(self size)
+! !
+
+!SequenceableCollection methodsFor:'comparing'!
+
+= aCollection
+    "return true if the receiver and aCollection represent collections
+     with equal contents."
+
+    |index "{ Class: SmallInteger }"
+     stop  "{ Class: SmallInteger }" |
+
+    (aCollection == self) ifTrue:[^true].
+    (aCollection isKindOf:SequenceableCollection) ifFalse:[^false].
+
+    stop := self size.
+    stop == (aCollection size) ifFalse:[^false].
+    index := 1.
+    [index <= stop] whileTrue:[
+        (self at:index) = (aCollection at:index) ifFalse:[^false].
+        index := index + 1
+    ].
+    ^ true
+!
+
+startsWith:aCollection
+    "return true, if the receivers first elements match those
+     of aCollection"
+
+    |index "{ Class: SmallInteger }"
+     stop  "{ Class: SmallInteger }" |
+
+    (aCollection == self) ifTrue:[^true].
+    (aCollection isKindOf:SequenceableCollection) ifFalse:[^false].
+
+    stop := aCollection size.
+    stop > self size ifTrue:[^false].
+
+    index := 1.
+    [index <= stop] whileTrue:[
+        (self at:index) = (aCollection at:index) ifFalse:[^false].
+        index := index + 1
+    ].
+    ^ true
+
+    "'abcde' startsWith:#($a $b $c)"
+    "#[1 2 3 4] startsWith:#(1 2 3)"
+    "#(1 2 3 4) asOrderedCollection startsWith:#(1 2 3)"
+!
+
+endsWith:aCollection
+    "return true, if the receivers last elements match those
+     of aCollection"
+
+    |index1 "{ Class: SmallInteger }"
+     index2 "{ Class: SmallInteger }" 
+     stop   "{ Class: SmallInteger }" |
+
+    (aCollection == self) ifTrue:[^true].
+    (aCollection isKindOf:SequenceableCollection) ifFalse:[^false].
+
+    stop := aCollection size.
+    stop > self size ifTrue:[^false].
+
+    index1 := self size.
+    index2 := aCollection size.
+    [index2 > 0] whileTrue:[
+        (self at:index1) = (aCollection at:index2) ifFalse:[^false].
+        index1 := index1 - 1.
+        index2 := index2 - 1
+    ].
+    ^ true
+
+    "'abcde' endsWith:#($d $e)"
+    "#[1 2 3 4] endsWith:#(3 4)"
+    "#(1 2 3 4) asOrderedCollection endsWith:#(3 4)"
+! !
+
+!SequenceableCollection methodsFor:'testing'!
+
+size
+    "return the number of elements in the collection.
+     concrete implementations must define this"
+
+    ^ self subclassResponsibility
+! !
+
+!SequenceableCollection methodsFor:'copying'!
+
+, aCollection
+    "return a new collection formed from concatenating the receiver with
+     the argument"
+
+    |newCollection 
+     mySize    "{ Class: SmallInteger }"
+     newSize   "{ Class: SmallInteger }"
+     otherSize "{ Class: SmallInteger }"
+     dstIndex  "{ Class: SmallInteger }"|
+
+    mySize := self size.
+    otherSize := aCollection size.
+    newSize := mySize + otherSize.
+    newCollection := self species new:newSize.
+
+    newCollection replaceFrom:1 to:mySize with:self startingAt:1.
+    dstIndex := mySize + 1.
+    (aCollection isKindOf:SequenceableCollection) ifTrue:[
+        "yes, aCollection has indexed elements"
+        newCollection replaceFrom:dstIndex to:newSize
+                             with:aCollection startingAt:1.
+        ^ newCollection
+    ] ifFalse:[
+        "no, enumerate aCollection"
+        aCollection do:[:element |
+            newCollection at:dstIndex put:element.
+            dstIndex := dstIndex + 1
+        ]
+    ].
+    ^ newCollection
+!
+
+copyWith:newElement
+    "return a new collection consisting of receivers elements
+     plus the argument"
+
+    |newCollection mySize newSize|
+
+    mySize := self size.
+    newSize := mySize + 1.
+    newCollection := self species new:newSize.
+    newCollection replaceFrom:1 to:mySize with:self startingAt:1.
+    newCollection at:newSize put:newElement.
+    ^newCollection
+!
+
+copyWithout:anElement
+    "return a new collection consisting of receivers elements
+     without anElement (if it was present)"
+
+    |newCollection skipIndex 
+     dstIndex "{ Class: SmallInteger }"
+     index    "{ Class: SmallInteger }"
+     stop     "{ Class: SmallInteger }" |
+
+    skipIndex := self indexOf:anElement startingAt:1.
+    (skipIndex == 0) ifTrue:[^ self copy].
+    stop := self size.
+    newCollection := self class new:(stop - 1).
+    dstIndex := 1.
+    index := 1.
+    [index <= stop] whileTrue:[
+        (index ~~ skipIndex) ifTrue:[
+            newCollection at:dstIndex put:(self at:index).
+            dstIndex := dstIndex + 1
+        ].
+        index := index + 1
+    ].
+    ^ newCollection
+!
+
+copyFrom:start to:stop
+    "return a new collection consisting of receivers elements
+     between start and stop"
+
+    |newCollection newSize|
+
+    newSize := stop - start + 1.
+    newCollection := self class new:newSize.
+    newCollection replaceFrom:1 to:newSize with:self startingAt:start.
+    ^ newCollection
+!
+
+copyFrom:start
+    "return a new collection consisting of receivers elements
+     from start to the end of the collection"
+
+    ^ self copyFrom:start to:(self size)
+!
+
+copyTo:stop
+    "return a new collection consisting of receivers elements
+     from 1 up to index stop"
+
+    ^ self copyFrom:1 to:stop
+!
+
+copyWithoutIndex:omitIndex
+    "return a new collection consisting of receivers elements
+     without the argument stored at omitIndex"
+
+    |copy|
+
+    copy := self class new:(self size - 1).
+    copy replaceFrom:1 to:(omitIndex - 1) with:self startingAt:1.
+    copy replaceFrom:omitIndex to:(copy size) 
+                with:self startingAt:(omitIndex + 1).
+    ^ copy
+! !
+
+!SequenceableCollection methodsFor:'filling and replacing'!
+
+from:index1 to:index2 put:anObject
+    "replace the elements from index1 to index2 of the collection
+     by the argument, anObject"
+
+    |index "{ Class: SmallInteger }"
+     end   "{ Class: SmallInteger }"|
+
+    index := index1.
+    end := index2.
+    [index <= end] whileTrue:[
+        self at:index put:anObject.
+        index := index + 1
+    ]
+!
+
+atAllPut:anObject
+    "replace all elements of the collection by the argument, anObject"
+
+    self from:1 to:(self size) put:anObject
+!
+
+atAll:indexCollection put:anObject
+    "put anObject into all indexes from indexCollection in the receiver"
+
+    indexCollection do:[:index | self at:index put:anObject]
+
+    "(Array new:10) atAll:(1 to:5) put:0"
+    "(Array new:10) atAll:#(1 5 6 9) put:0"
+!
+
+replaceAll:oldObject by:newObject
+    "replace all oldObjects by newObject in the receiver"
+
+    1 to:self size do:[:index |
+        (self at:index) = oldObject ifTrue:[
+            self at:index put:newObject
+        ]
+    ]
+!
+
+replaceFrom:start with:replacementCollection
+    "replace elements starting at start with elements
+     taken from replacementCollection (starting at 1)"
+
+    ^ self replaceFrom:start 
+                    to:(start + replacementCollection size - 1)
+                  with:replacementCollection
+            startingAt:1
+!
+
+replaceFrom:start to:stop with:replacementCollection
+    "replace elements between index start and stop with elements
+     taken from replacementCollection (starting at 1)"
+
+    ^ self replaceFrom:start
+                    to:stop
+                  with:replacementCollection
+            startingAt:1
+!
+
+replaceFrom:start to:stop with:replacementCollection startingAt:repStart
+    "replace elements between index start and stop with elements
+     taken from replacementCollection (starting at repStart)"
+
+    |srcIndex "{ Class: SmallInteger }"
+     dstIndex "{ Class: SmallInteger }"
+     end      "{ Class: SmallInteger }" |
+
+    (replacementCollection == self) ifTrue:[
+        (repStart < start) ifTrue:[
+            " must do reverse copy "
+            srcIndex := repStart + (stop - start).
+            dstIndex := stop.
+            end := start.
+            [dstIndex >= end] whileTrue:[
+                self at:dstIndex put:(replacementCollection at:srcIndex).
+                srcIndex := srcIndex - 1.
+                dstIndex := dstIndex - 1
+            ].
+            ^ self
+        ]
+    ].
+
+    srcIndex := repStart.
+    dstIndex := start.
+    end := stop.
+    [dstIndex <= end] whileTrue:[
+        self at:dstIndex put:(replacementCollection at:srcIndex).
+        srcIndex := srcIndex + 1.
+        dstIndex := dstIndex + 1
+    ]
+!
+
+withCRs
+    "return a new collection consisting of receivers elements
+     with all \-characters replaced by cr-characters"
+
+    |newCollection
+     size "{ Class: SmallInteger }" |
+
+    newCollection := self copy.
+    size := self size.
+    1 to:size do:[:index |
+        ((self at:index) == $\) ifTrue:[
+            newCollection at:index put:(Character cr)
+        ]
+    ].
+    ^ newCollection
+!
+
+withoutCRs
+    "return a new collection consisting of receivers elements
+     with all cr-characters replaced by \-characters"
+
+    |newCollection 
+     size "{ Class: SmallInteger }" |
+
+    newCollection := self copy.
+    size := self size.
+    1 to:size do:[:index|
+        ((self at:index) == Character cr) ifTrue:[
+            newCollection at:index put:$\
+        ]
+    ].
+    ^ newCollection
+! !
+
+!SequenceableCollection methodsFor:'adding & removing'!
+
+addFirst:anElement
+    "prepend the argument, anElement to the collection"
+
+    |newSize|
+
+    newSize := self size + 1.
+    self grow:newSize.
+    self replaceFrom:2 to:newSize with:self startingAt:1.
+    self at:1 put:anElement
+!
+
+add:anElement
+    "append the argument, anElement to the collection"
+
+    |newSize|
+
+    newSize := self size + 1.
+    self grow:newSize.
+    self at:newSize put:anElement
+!
+
+add:anElement beforeIndex:index
+    "insert the first argument, anObject into the collection before slot index"
+
+    |newSize|
+
+    newSize := self size + 1.
+    self grow:newSize.
+    self replaceFrom:index + 1 to:newSize with:self startingAt:index.
+    self at:index put:anElement
+!
+
+remove:anElement ifAbsent:aBlock
+    "search for anElement and, if present remove it; if not present
+     return the value of evaluating aBlock"
+
+    |any 
+     dstIndex "{ Class: SmallInteger }"
+     sz       "{ Class: SmallInteger }"|
+
+    dstIndex := 1.
+    any := false.
+    sz := self size.
+    1 to:sz do:[:srcIndex |
+        (anElement = (self at:srcIndex)) ifTrue:[
+            any := true
+        ] ifFalse:[
+            (dstIndex ~~ srcIndex) ifTrue:[
+                self at:dstIndex put:(self at:srcIndex)
+            ].
+            dstIndex := dstIndex + 1
+        ]
+    ].
+    any ifTrue:[
+        self grow:dstIndex - 1
+    ] ifFalse:[
+        aBlock value
+    ]
+!
+
+removeFromIndex:startIndex toIndex:endIndex
+    "remove the elements stored at indexes between startIndex and endIndex"
+
+    |newSize|
+
+    newSize := self size - endIndex + startIndex - 1.
+    self replaceFrom:startIndex to:newSize with:self startingAt:(endIndex + 1).
+    self grow:newSize
+!
+
+removeIndex:index
+    "remove the argument stored at index"
+
+    self removeFromIndex:index toIndex:index
+! !
+
+!SequenceableCollection methodsFor:'searching'!
+
+detect:aBlock ifNone:exceptionBlock
+    "find the first element, for which evaluation of the argument, aBlock
+     return true; if none does so, return the evaluation of exceptionBlock
+
+    reimplemented here for speed"
+
+    |index "{ Class: SmallInteger }"
+     stop  "{ Class: SmallInteger }"
+     element|
+
+    stop := self size.
+    index := 1.
+    [index <= stop] whileTrue:[
+        element := self at:index.
+        (aBlock value:element) ifTrue:[
+            ^ element
+        ].
+        index := index + 1
+    ].
+    ^ exceptionBlock value
+!
+
+indexOf:anElement
+    "search the collection for anElement;
+     if found, return the index otherwise return 0.
+     The comparison is done using = (i.e. equality test)."
+
+    ^ self indexOf:anElement startingAt:1
+!
+
+indexOf:anElement ifAbsent:exceptionBlock
+    "search the collection for anElement;
+     if found, return the index otherwise return the value of the
+     exceptionBlock.
+     The comparison is done using = (i.e. equality test)."
+
+    |index|
+
+    index := self indexOf:anElement startingAt:1.
+    (index == 0) ifTrue:[^ exceptionBlock value].
+    ^ index
+!
+
+indexOf:anElement startingAt:start
+    "search the collection for anElement staring search at index start;
+     if found, return the index otherwise return 0.
+     The comparison is done using = (i.e. equality test)."
+
+    |index "{ Class: SmallInteger }"
+     stop  "{ Class: SmallInteger }" |
+
+    index := start.
+    stop := self size.
+    [index <= stop] whileTrue:[
+        anElement = (self at:index) ifTrue:[^ index].
+        index := index + 1
+    ].
+    ^ 0
+!
+
+indexOf:anElement startingAt:start ifAbsent:exceptionBlock
+    "search the collection for anElement starting search at start;
+     if found, return the index otherwise return the value of the
+     exceptionBlock.
+     The comparison is done using = (i.e. equality test)."
+
+    |index|
+
+    index := self indexOf:anElement startingAt:start.
+    (index == 0) ifTrue:[^ exceptionBlock value].
+    ^ index
+!
+
+identityIndexOf:anElement
+    "search the collection for anElement using identity compare (i.e. ==);
+     if found, return the index otherwise return 0."
+
+    ^ self identityIndexOf:anElement startingAt:1
+!
+
+identityIndexOf:anElement ifAbsent:exceptionBlock
+    "search the collection for anElement using identity compare (i.e. ==);
+     if found, return the index otherwise return the value of the
+     exceptionBlock."
+
+    |index|
+
+    index := self identityIndexOf:anElement startingAt:1.
+    (index == 0) ifTrue:[^ exceptionBlock value].
+    ^ index
+!
+
+identityIndexOf:anElement startingAt:start
+    "search the collection for anElement staring search at index start
+     using identity compare  (i.e. ==);
+     if found, return the index otherwise return 0."
+
+    |index "{ Class: SmallInteger }"
+     stop  "{ Class: SmallInteger }" |
+
+    index := start.
+    stop := self size.
+    [index <= stop] whileTrue:[
+        anElement == (self at:index) ifTrue:[^ index].
+        index := index + 1
+    ].
+    ^ 0
+!
+
+identityIndexOf:anElement startingAt:start ifAbsent:exceptionBlock
+    "search the collection for anElement starting search at start;
+     if found, return the index otherwise return the value of the
+     exceptionBlock.
+     This one searches for identical objects (i.e. ==)."
+
+    |index|
+
+    index := self identityIndexOf:anElement startingAt:start.
+    (index == 0) ifTrue:[^ exceptionBlock value].
+    ^ index
+!
+
+findFirst:aBlock
+    "find the first element, for which evaluation of the argument, aBlock
+     return true; return its index or 0 if none detected."
+
+    |index "{ Class: SmallInteger }"
+     stop  "{ Class: SmallInteger }" |
+
+    stop := self size.
+    index := 1.
+    [index <= stop] whileTrue:[
+        (aBlock value:(self at:index)) ifTrue:[^ index].
+        index := index + 1
+    ].
+    ^ 0
+
+    "#(1 2 3 4 5 6) findFirst:[:x | (x > 3) and:[x even]]"
+!
+
+includes:anElement
+    "return true if the collection contains anElement; false otherwise.
+     Comparison is done using equality compare (i.e. =)."
+
+    ((self indexOf:anElement startingAt:1) == 0) ifTrue:[^ false].
+    ^ true
+! !
+
+!SequenceableCollection methodsFor:'sorting & reordering'!
+
+reverse
+    "reverse the order of the arguments inplace"
+
+    |lowIndex "{ Class: SmallInteger }"
+     hiIndex  "{ Class: SmallInteger }"
+     t|
+
+    hiIndex := self size.
+    lowIndex := 1.
+    [lowIndex < hiIndex] whileTrue:[
+        t := self at:lowIndex.
+        self at:lowIndex put:(self at:hiIndex). 
+        self at:hiIndex put:t.
+        lowIndex := lowIndex + 1.
+        hiIndex := hiIndex - 1
+    ]
+    "#(4 5 6 7 7) reverse"
+!
+
+quickSortFrom:begin to:end
+    "actual quicksort worker for sort-message"
+
+    |b "{ Class: SmallInteger }"
+     e "{ Class: SmallInteger }"
+     middleElement temp |
+
+    b := begin.
+    e := end.
+    middleElement := self at:((b + e) // 2).
+
+    [b < e] whileTrue:[
+        [b < end and:[(self at:b) < middleElement]] whileTrue:[b := b + 1].
+        [e > begin and:[middleElement < (self at:e)]] whileTrue:[e := e - 1].
+
+        (b <= e) ifTrue:[
+            (b == e) ifFalse:[
+                temp := self at:b.
+                self at:b put:(self at:e).
+                self at:e put:temp
+            ].
+            b := b + 1.
+            e := e - 1
+        ]
+    ].
+    (begin < e) ifTrue:[self quickSortFrom:begin to:e].
+    (b < end) ifTrue:[self quickSortFrom:b to:end]
+!
+
+quickSortFrom:begin to:end with:aCollection
+    "actual quicksort worker for sortWith-message"
+
+    |b "{ Class: SmallInteger }"
+     e "{ Class: SmallInteger }"
+     middleElement temp |
+
+    b := begin.
+    e := end.
+    middleElement := self at:((b + e) // 2).
+
+    [b < e] whileTrue:[
+        [b < end and:[(self at:b) < middleElement]] whileTrue:[b := b + 1].
+        [e > begin and:[middleElement < (self at:e)]] whileTrue:[e := e - 1].
+
+        (b <= e) ifTrue:[
+            (b == e) ifFalse:[
+                temp := self at:b.
+                self at:b put:(self at:e).
+                self at:e put:temp.
+                temp := aCollection at:b.
+                aCollection at:b put:(aCollection at:e).
+                aCollection at:e put:temp
+            ].
+            b := b + 1.
+            e := e - 1
+        ]
+    ].
+    (begin < e) ifTrue:[self quickSortFrom:begin to:e with:aCollection].
+    (b < end) ifTrue:[self quickSortFrom:b to:end with:aCollection]
+!
+
+quickSortFrom:begin to:end sortBlock:sortBlock
+    "actual quicksort worker for sort:-message"
+
+    |b "{ Class: SmallInteger }"
+     e "{ Class: SmallInteger }"
+     middleElement temp |
+
+    b := begin.
+    e := end.
+    middleElement := self at:((b + e) // 2).
+
+    [b < e] whileTrue:[
+        [b < end and:[sortBlock value:(self at:b) value:middleElement]] whileTrue:[b := b + 1].
+        [e > begin and:[sortBlock value:middleElement value:(self at:e)]] whileTrue:[e := e - 1].
+
+        (b <= e) ifTrue:[
+            (b == e) ifFalse:[
+                temp := self at:b.
+                self at:b put:(self at:e).
+                self at:e put:temp
+            ].
+            b := b + 1.
+            e := e - 1
+        ]
+    ].
+    (begin < e) ifTrue:[self quickSortFrom:begin to:e sortBlock:sortBlock].
+    (b < end) ifTrue:[self quickSortFrom:b to:end sortBlock:sortBlock]
+!
+
+quickSortFrom:begin to:end sortBlock:sortBlock with:aCollection
+    "actual quicksort worker for sort:with:-message"
+
+    |b "{ Class: SmallInteger }"
+     e "{ Class: SmallInteger }"
+     middleElement temp |
+
+    b := begin.
+    e := end.
+    middleElement := self at:((b + e) // 2).
+
+    [b < e] whileTrue:[
+        [b < end and:[sortBlock value:(self at:b) value:middleElement]] whileTrue:[b := b + 1].
+        [e > begin and:[sortBlock value:middleElement value:(self at:e)]] whileTrue:[e := e - 1].
+
+        (b <= e) ifTrue:[
+            (b == e) ifFalse:[
+                temp := self at:b.
+                self at:b put:(self at:e).
+                self at:e put:temp.
+                temp := aCollection at:b.
+                aCollection at:b put:(aCollection at:e).
+                aCollection at:e put:temp
+            ].
+            b := b + 1.
+            e := e - 1
+        ]
+    ].
+    (begin < e) ifTrue:[self quickSortFrom:begin to:e sortBlock:sortBlock with:aCollection].
+    (b < end) ifTrue:[self quickSortFrom:b to:end sortBlock:sortBlock with:aCollection]
+!
+
+bubbleSort
+    "sort the collection inplace using bubbleSort (sloooow)
+     - this one makes only sense to sort after inserting an element into
+       an already sorted collection (if at all)"
+
+    |index  "{ Class: SmallInteger }"
+     index2 "{ Class: SmallInteger }"
+     end    "{ Class: SmallInteger }"
+     smallest smallestIndex thisOne|
+
+    end := self size.
+    index := 1.
+    [index <= end] whileTrue:[
+        smallest := self at:index.
+        smallestIndex := index.
+        index2 := index + 1.
+        [index2 <= end] whileTrue:[
+            (self at:index2) < smallest ifTrue:[
+                smallestIndex := index2.
+                smallest := self at:index2
+            ].
+            index2 := index2 + 1
+        ].
+        (smallestIndex ~~ index) ifTrue:[
+            thisOne := self at:index.
+            self at:index put:smallest.
+            self at:smallestIndex put:thisOne
+        ].
+        index := index + 1
+    ]
+
+    "#(1 16 7 98 3 19 4 0) bubbleSort"
+!
+
+sort
+    "sort the collection inplace. The elements are compared using
+     > and < i.e. they should offer a magnitude-like protocol."
+    |sz|
+
+    sz := self size.
+    (sz > 1) ifTrue:[
+        self quickSortFrom:1 to:sz
+    ]
+
+    "#(1 16 7 98 3 19 4 0) sort"
+!
+
+sortWith:aCollection
+    "sort the receiver collection inplace, also sort aCollection with it.
+     Use, when you have a key collection to sort another collection with."
+
+    |sz|
+
+    sz := self size.
+    (sz > 1) ifTrue:[
+        self quickSortFrom:1 to:sz with:aCollection
+    ]
+
+    "|c1 c2|
+     c1 := #(1 16 7 9).
+     c2 := #('one' 'sixteen' 'seven' 'nine').
+     c1 sortWith:c2.
+     c1 printNewline.
+     c2 printNewline"
+!
+
+sort:sortBlock
+    "sort the collection inplace using the 2-arg block sortBlock
+     for comparison. This allows any sort criteria to be implemented."
+
+    |sz|
+
+    sz := self size.
+    (sz > 1) ifTrue:[
+        self quickSortFrom:1 to:sz sortBlock:sortBlock
+    ]
+
+    "#(1 16 7 98 3 19 4 0) sort:[:a :b | a < b]"
+    "#(1 16 7 98 3 19 4 0) sort:[:a :b | a > b]"
+!
+
+sort:sortBlock with:aCollection
+    "sort the collection inplace using the 2-arg block sortBlock
+     for comparison. Also reorder the elements in aCollection"
+
+    |sz|
+
+    sz := self size.
+    (sz > 1) ifTrue:[
+        self quickSortFrom:1 to:sz sortBlock:sortBlock with:aCollection
+    ]
+
+    "|c1 c2|
+     c1 := #(1 16 7 9).
+     c2 := #('one' 'sixteen' 'seven' 'nine').
+     c1 sort:[:a :b | a > b] with:c2.
+     c1 printNewline.
+     c2 printNewline"
+! !
+
+!SequenceableCollection methodsFor:'enumerating'!
+
+do:aBlock
+    "evaluate the argument, aBlock for every element in the collection."
+
+    |index  "{ Class:SmallInteger }"
+     length "{ Class:SmallInteger }"|
+
+    index := 1.
+    length := self size.
+    [index <= length] whileTrue:[
+        aBlock value:(self at:index).
+        index := index + 1
+    ]
+!
+
+from:index1 to:index2 do:aBlock
+    "evaluate the argument, aBlock for the elements with index index1 to
+     index2 in the collection"
+
+    |index "{ Class:SmallInteger }"
+     stop  "{ Class:SmallInteger }" |
+
+    index := index1.
+    stop := index2.
+    [index <= stop] whileTrue:[
+        aBlock value:(self at:index).
+        index := index + 1
+    ]
+!
+
+with:aCollection do:aBlock
+    "evaluate the argument, aBlock for successive elements from
+     each of the two collections self and aCollection.
+     aBlock must be a two-argument block"
+
+    |index "{ Class: SmallInteger }"
+     stop  "{ Class: SmallInteger }" |
+
+    index := 1.
+    stop := self size.
+    [index <= stop] whileTrue:[
+        aBlock value:(self at:index) value:(aCollection at:index).
+        index := index + 1
+    ]
+!
+
+reverseDo:aBlock
+    "evaluate the argument, aBlock for every element in the collection
+     in reverse order"
+
+    |index "{ Class:SmallInteger }" |
+
+    index := self size.
+    [index > 0] whileTrue:[
+        aBlock value:(self at:index).
+        index := index - 1
+    ]
+!
+
+collect:aBlock
+    "evaluate the argument, aBlock for every element in the collection
+     and return a collection of the results"
+
+    |newCollection
+     index  "{ Class:SmallInteger }"
+     length "{ Class:SmallInteger }" |
+
+    length := self size.
+    newCollection := self species new:length.
+    index := 1.
+    [index <= length] whileTrue:[
+        newCollection at:index put:(aBlock value:(self at:index)).
+        index := index + 1
+    ].
+    ^ newCollection
+!
+
+select:aBlock
+    "evaluate the argument, aBlock for every element in the collection
+     and return a collection of all elements for which the block return
+     true"
+
+    |element newColl
+     index  "{ Class:SmallInteger }"
+     length "{ Class:SmallInteger }" |
+
+    length := self size.
+    newColl := OrderedCollection new:length.
+    index := 1.
+    [index <= length] whileTrue:[
+        element := self at:index.
+        (aBlock value:element) ifTrue:[
+            newColl add:element
+        ].
+        index := index + 1
+    ].
+    ^ newColl
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Set.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,326 @@
+"
+ COPYRIGHT (c) 1991-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Collection subclass:#Set
+       instanceVariableNames:'tally contentsArray'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Unordered'
+!
+
+Set comment:'
+
+COPYRIGHT (c) 1991-93 by Claus Gittinger
+              All Rights Reserved
+
+a Set is a collection where each element occurs at most once.
+
+%W% %E%
+written jun 91 by claus
+jan 93 claus: changed to use hashing
+'!
+
+!Set class methodsFor:'instance creation'!
+
+new
+    "return a new empty Set"
+
+    ^ self new:7
+!
+
+new:anInteger
+    "return a new empty Set with space for anInteger elements"
+
+    ^ self basicNew setTally:anInteger
+! !
+
+!Set methodsFor:'private'!
+
+goodSizeFor:arg
+    "return a good array size for the given argument.
+     Returns the next prime after arg"
+
+    arg <= 7 ifTrue:[^ 7].
+    arg <= 16384 ifTrue:[
+           "2 4 8  16 32 64 128 256 512 1024 2048 4096 8192 16384"
+        ^ #(7 7 11 17 37 67 131 257 521 1031 2053 4099 8209 16411) at:(arg highBit)
+    ].
+    ^ arg bitOr:1
+!
+
+setTally:count
+    "initialize the contents array (for at least count slots) 
+     and set tally to zero.
+     The size is increased to the next prime for better hashing behavior."
+
+    contentsArray := Array new:(self goodSizeFor:count). 
+    tally := 0
+!
+
+find:key ifAbsent:aBlock
+    "Look for the key in the receiver.  If it is found, return
+     the index of the slot containing the key, otherwise
+     return the value of evaluating aBlock."
+
+    |index      "{ Class:SmallInteger }"
+     length     "{ Class:SmallInteger }"
+     startIndex "{ Class:SmallInteger }" 
+     probe|
+
+    length := contentsArray basicSize.
+    startIndex := key hash \\ length + 1.
+    index := startIndex.
+
+    [true] whileTrue:[
+        probe := (contentsArray basicAt:index).
+        key = probe ifTrue:[^ index].
+        probe isNil ifTrue:[^ aBlock value].
+
+        index == length ifTrue:[
+            index := 1
+        ] ifFalse:[
+            index := index + 1
+        ].
+        index == startIndex ifTrue:[^ aBlock value].
+    ]
+!
+
+findElementOrNil:key
+    "Look for the key in the receiver.  If it is found, return
+     the index of the slot containing the key, otherwise
+     return the index of the first unused slot. Grow the receiver,
+     if key was not found, and no unused slots where present"
+
+    |index      "{ Class:SmallInteger }"
+     length     "{ Class:SmallInteger }"
+     startIndex "{ Class:SmallInteger }"
+     probe|
+
+    length := contentsArray basicSize.
+    startIndex := key hash \\ length + 1.
+    index := startIndex.
+
+    [true] whileTrue:[
+        probe := contentsArray basicAt:index.
+        (probe isNil or: [key = probe]) ifTrue:[^ index].
+
+        index == length ifTrue:[
+            index := 1
+        ] ifFalse:[
+            index := index + 1
+        ].
+        index == startIndex ifTrue:[^ self grow findElementOrNil:key].
+    ]
+!
+
+findNil:key
+    "Look for the next slot usable for key.  This method assumes that
+     key is not already in the receiver - used only while growing/rehashing"
+
+    |index  "{ Class:SmallInteger }"
+     length "{ Class:SmallInteger }"|
+
+    length := contentsArray basicSize.
+    index := key hash \\ length + 1.
+
+    [(contentsArray basicAt:index) notNil] whileTrue:[
+        index == length ifTrue:[
+            index := 1
+        ] ifFalse:[
+            index := index + 1
+        ].
+        "notice: no check for no nil found - we must find one since
+         this is only called after growing"
+    ].
+    ^ index
+!
+
+grow
+    "change the number of element slots of the collection to a useful
+     new size"
+
+    self grow:(contentsArray basicSize * 2)
+!
+
+grow:newSize
+    "change the number of element slots of the collection - to do this,
+     we have to rehash (which is done by re-adding all elements to a new
+     empty set)."
+
+    |oldElements oldSize
+     srcIndex "{ Class:SmallInteger }"|
+
+    oldElements := contentsArray.
+    oldSize := tally.
+    self setTally:newSize.
+
+    srcIndex := 1.
+    oldElements do:[:elem |
+        elem notNil ifTrue:[
+            "cannot be already there"
+            contentsArray basicAt:(self findNil:elem) put:elem
+        ].
+        srcIndex := srcIndex + 1
+    ].
+    tally := oldSize
+!
+
+rehash
+    "rehash is done by re-adding all elements to a new empty set."
+
+    | oldArray |
+
+    oldArray := contentsArray.
+    contentsArray := Array new:(contentsArray size).
+    oldArray do:[:element |
+        element notNil ifTrue:[
+            "cannot be already there"
+            contentsArray basicAt:(self findNil:element) put:element
+        ].
+    ]
+!
+
+rehashFrom:startIndex
+    "rehash elements starting at index - after a remove"
+
+    |element i length
+     index "{ Class:SmallInteger }" |
+
+    length := contentsArray basicSize.
+    index := startIndex.
+    element := contentsArray basicAt:index.
+    [element notNil] whileTrue:[
+        i := self findNil:element.
+        i == index ifTrue:[
+            ^ self
+        ].
+        contentsArray basicAt:i put:element.
+        contentsArray basicAt:index put:nil.
+
+        index == length ifTrue:[
+            index := 1
+        ] ifFalse:[
+            index := index + 1.
+        ].
+        element := contentsArray basicAt:index.
+    ]
+! !
+
+!Set methodsFor:'accessing'!
+
+at:index
+    "report an error: at: is not allowed for Sets"
+
+    ^ self errorNotKeyed
+!
+
+at:index put:anObject
+    "report an error: at:put: is not allowed for Sets"
+
+    ^ self errorNotKeyed
+! !
+
+!Set methodsFor:'testing'!
+
+size
+    "return the number of set elements"
+
+    ^ tally
+!
+
+includes:anObject
+    "return true if the argument anObject is in the receiver"
+
+    ^ (self find:anObject ifAbsent:[0]) ~~ 0
+!
+
+isEmpty
+    "return true if the receiver is empty"
+
+    ^ tally == 0
+!
+
+occurrencesOf:anObject
+    "return the number of occurrences of anObject in the receiver"
+
+    (self find:anObject ifAbsent:[0]) == 0 ifTrue:[^ 0].
+    ^ 1
+!
+
+isFixedSize
+    "return true if the receiver cannot grow - this will vanish once
+     Arrays and Strings learn how to grow ..."
+
+    ^ false
+! !
+
+!Set methodsFor:'adding & removing'!
+
+add:anObject
+    "add the argument, anObject to the receiver"
+
+    |index|
+
+    anObject notNil ifTrue:[
+        index := self findElementOrNil:anObject.
+        (contentsArray basicAt:index) isNil ifTrue:[
+            contentsArray basicAt:index put:anObject.
+            tally := tally + 1.
+
+            "grow if filled more than 70% "
+            tally > (contentsArray basicSize * 7 // 10) ifTrue:[
+                self grow
+            ]
+        ]
+    ].
+    ^ anObject
+!
+
+remove:oldObject ifAbsent:exceptionBlock
+    "remove oldObject from the collection and return it.
+     If it was not in the collection return the value of exceptionBlock."
+
+    |index next|
+
+    index := self find:oldObject ifAbsent:[^ exceptionBlock value].
+    contentsArray basicAt:index put:nil.
+    tally := tally - 1.
+    tally == 0 ifTrue:[
+        contentsArray := Array new:(self goodSizeFor:0). 
+    ] ifFalse:[
+        index == contentsArray basicSize ifTrue:[
+            next := 1
+        ] ifFalse:[
+            next := index + 1.
+        ].
+        "this check is redundant - is also done in rehashFrom:,
+         however, since there is some probability that the next
+         element is nil, this saves a send sometimes
+        "
+        (contentsArray basicAt:next) notNil ifTrue:[
+            self rehashFrom:next.
+        ]
+    ].
+    ^ oldObject
+! !
+
+!Set methodsFor:'enumerating'!
+
+do:aBlock
+    "perform the block for all members in the collection."
+
+    contentsArray do:[:each |
+        each notNil ifTrue:[
+            aBlock value:each
+        ]
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Signal.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,184 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Signal
+         instanceVariableNames:'mayProceed notifierString nameClass message'
+         classVariableNames:'NoHandlerSignal'
+         poolDictionaries:''
+         category:'Kernel-Exceptions'
+!
+
+Signal comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+Signal and Exception provide a framework for exception handling.
+A Signal object is usually defined somewhere up in the calling chain
+and associated with some abnormal event. When the event is raised 
+(by Signal>>raise) the control will be either given to a debugger
+or - if a handler was defined - to the handler. The handler will 
+get a description of what (and where) happened in an Exception object.
+
+This Signal implementation has been modeled after what some PD
+programs seem to expect - it may not be perfect currently.
+
+See samples in doc/coding.
+
+%W% %E%
+'!
+
+!Signal class methodsFor:'initialization'!
+
+initialize
+    NoHandlerSignal := (Signal new).
+    NoHandlerSignal mayProceed:true.
+    NoHandlerSignal notifierString:'no Handler'
+! !
+
+!Signal class methodsFor:'instance creation'!
+
+new
+    "return a new signal"
+
+    ^ self basicNew notifierString:'signal'
+! !
+
+!Signal class methodsFor:'signal access'!
+
+noHandlerSignal
+    ^ NoHandlerSignal
+! !
+
+!Signal methodsFor:'instance creation'!
+
+newSignalMayProceed:aBoolean
+    "create a new signal, using the receiver as a prototype"
+
+    ^ (self copy) mayProceed:aBoolean
+!
+
+newSignal
+    "create a new signal, using the receiver as a prototype"
+
+    ^ (self copy)
+! !
+
+!Signal methodsFor:'accessing'!
+
+nameClass:aClass message:aSelector
+    "I dont know what that is used for (yet)"
+
+    nameClass := aClass.
+    message := aSelector
+!
+
+mayProceed:aBoolean
+    "set/clear the signals ability to proceed"
+
+    mayProceed := aBoolean
+!
+
+notifierString:aString
+    "set the notifier string"
+
+    notifierString := aString
+!
+
+notifierString
+    "return the notifier string"
+
+    ^ notifierString
+! !
+
+!Signal methodsFor:'save evaluation'!
+
+handle:handleBlock do:aBlock
+    "evaluate the argument, aBlock.
+     If the receiver-signal is raised during evaluation,
+     evaluate the handleBlock passing it an Exception argument.
+     The handler may decide how to react to the signal by sending
+     a corresponding message to the exception (see there).
+     If the signal is not raised, return the value of evaluating
+     aBlock."
+
+     ^ aBlock value  "the real logic is in raise/Exception"
+! !
+
+!Signal methodsFor:'raising'!
+
+raise
+    "raise a signal - create an Exception object
+     and call the handler with this as argument."
+
+    |ex|
+
+    ex := Exception new signal:self.
+    ex resumeBlock:[:value | ^ value].
+    self evaluateHandlerWith:ex.
+
+    "mmhh - no handler found (will eventually raise a noHandlerSignal)"
+    self error:('unhandled exception: ' , notifierString).
+    ^ nil
+!
+
+raiseRequestWith:aParameter
+    "raise a signal - create an Exception object with aParameter
+     and call the handler with this as argument."
+
+    |ex|
+
+    ex := Exception new signal:self.
+    ex parameter:aParameter.
+    ex resumeBlock:[:value | ^ value].
+    self evaluateHandlerWith:ex.
+
+    "mmhh - no handler found (will eventually raise a noHandlerSignal)"
+    self error:('unhandled exception: ' , notifierString).
+    ^ nil
+! !
+
+!Signal methodsFor:'private'!
+
+evaluateHandlerWith:anException
+    "search through the context-calling chain for a
+     handle:do: frame to the receiver or a SignalSet which includes
+     the receiver."
+
+    |con|
+
+    con := thisContext sender.
+    [con notNil] whileTrue:[
+        (con selector == #handle:do:) ifTrue:[
+            "
+             if this is the Signal>>handle:do: context
+             or a SignalSet>>handle:do: context with self in it,
+             call the handler
+            "
+            ((con receiver == self) 
+            or:[(con receiver isMemberOf:SignalSet) and:[con receiver includes:self]]) ifTrue:[
+                "call the handler"
+                anException handlerContext:con.
+                self doCallHandler:(con args at:1) with:anException.
+                "if the handler rejects or falls through we arrive here"
+                "continue search for another handler"
+            ].
+        ].
+        con := con sender
+    ]
+!
+
+doCallHandler:aHandler with:ex
+    ex rejectBlock:[^ self].
+    aHandler value:ex.
+    "handler return - is just like a reject"
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SignalSet.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,72 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+IdentitySet subclass:#SignalSet
+         instanceVariableNames:'special'
+         classVariableNames:''
+         poolDictionaries:''
+         category:'Kernel-Exceptions'
+!
+
+SignalSet comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+SignalSet allows catching of multiple signals. A SignalSet consists of
+a number of signals and also implements the handle:do: method just as
+signals do. However, any signal from the SignalSet will, if signalled, lead 
+into the handler.
+For more detail, see comment in Signal and examples in doc/coding.
+
+%W% %E%
+'!
+
+!SignalSet class methodsFor:'instance creation'!
+
+anySignal
+    "return a new signalSet catching any signal"
+
+    ^ self basicNew special:#any
+! !
+
+!SignalSet methodsFor:'save evaluation'!
+
+handle:handleBlock do:aBlock
+    "evaluate the argument, aBlock.
+     If any of the signals in the receiver is raised during evaluation,
+     evaluate the handleBlock passing it an Exception argument.
+     The handler may decide how to react to the signal by sending
+     a corresponding message to the exception (see there).
+     If the signal is not raised, return the value of evaluating
+     aBlock."
+
+     ^ aBlock value  "the real logic is in raise/Exception"
+! !
+
+!SignalSet methodsFor:'queries'!
+
+includes:aSignal
+    "return true, if the receiver contains the argument, aSignal.
+     The special any-Set includes every signal."
+
+    (special == #any) ifTrue:[
+        ^ aSignal isKindOf:Signal
+    ].
+    ^ super includes:aSignal
+! !
+
+!SignalSet methodsFor:'private'!
+
+special:how
+    special := how
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SmallInt.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,1362 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Integer subclass:#SmallInteger
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Magnitude-Numbers'
+!
+
+SmallInteger comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+%W% %E%
+
+SmallIntegers are Integers in the range of +/- 2^30 (i.e. 31 bits).
+These are no real objects - they have no instances (not even storage !)
+and cannot be subclassed (sorry)
+
+The reason is to save both storage and runtime by not collecting
+SmallIntegers in the system. SmallInts are marked by having the TAG_INT 
+bit set in contrast to Objects which have not. Since this knowledge is 
+hardwired into the system (an there is no class-field stored with
+SmallIntegers) there can be no subclass of SmallInteger (sorry).
+'!
+
+!SmallInteger class methodsFor:'instance creation'!
+
+basicNew
+    "catch instance creation
+     - SmallIntegers cannot be created with new"
+
+    self error:'instances of SmallInteger cannot be created with new'
+!
+
+basicNew:size
+    "catch instance creation
+     - SmallIntegers cannot be created with new"
+
+    self error:'instances of SmallInteger cannot be created with new'
+! !
+
+!SmallInteger class methodsFor:'constants'!
+
+maxBits
+    "return the number of bits in instances of me"
+
+%{  /* NOCONTEXT */
+    RETURN ( _MKSMALLINT(N_INT_BITS) );
+%}
+!
+
+maxBytes
+    "return the number of bytes in instances of me"
+
+%{  /* NOCONTEXT */
+    RETURN ( _MKSMALLINT(N_INT_BITS / 8 + 1) );
+%}
+!
+
+minVal
+    "return the smallest Integer representable as SmallInteger"
+
+%{  /* NOCONTEXT */
+    RETURN ( _MKSMALLINT(_MIN_INT) );
+%}
+!
+
+maxVal
+    "return the largest Integer representable as SmallInteger"
+
+%{  /* NOCONTEXT */
+    RETURN ( _MKSMALLINT(_MAX_INT) );
+%}
+! !
+
+!SmallInteger methodsFor:'error catching'!
+
+at:index
+    "catch indexed access - report an error
+     defined here since at: in Object ommits the SmallInteger check"
+
+    self notIndexed
+!
+
+basicAt:index
+    "catch indexed access - report an error
+     defined here since basicAt: in Object ommits the SmallInteger check"
+
+    self notIndexed
+!
+
+at:index put:anObject
+    "catch indexed access - report an error
+     defined here since at:put: in Object ommits the SmallInteger check"
+
+    self notIndexed
+!
+
+basicAt:index put:anObject
+    "catch indexed access - report an error
+     defined here since basicAt:put: in Object ommits the SmallInteger check"
+
+    self notIndexed
+!
+
+size
+    "return the number of indexed instvars - SmallIntegers have none
+     defined here since size in Object ommits the SmallInteger check"
+
+    ^ 0
+!
+
+basicSize
+    "return the number of indexed instvars - SmallIntegers have none
+     defined here since basicSize in Object ommits the SmallInteger check"
+
+    ^ 0
+! !
+
+!SmallInteger methodsFor:'copying'!
+
+shallowCopy
+    "return a shallow copy of myself
+     - reimplemented here since numbers are unique"
+
+    ^ self
+!
+
+deepCopy
+    "return a deep copy of myself
+     - reimplemented here since numbers are unique"
+
+    ^ self
+! !
+
+!SmallInteger methodsFor:'comparing'!
+
+= aNumber
+    "return true, if the arguments value is equal to mine"
+
+%{  /* NOCONTEXT */
+
+    if (aNumber == self) {
+        RETURN ( true );
+    }
+    if (! _isNonNilObject(aNumber)) {
+        RETURN ( false );
+    }
+
+    if (_qClass(aNumber) == Float) {
+        RETURN ( ((double)_intVal(self) == _floatVal(aNumber)) ? true : false );
+    }
+%}
+.
+    aNumber respondsToArithmetic ifFalse:[^ false].
+    ^ self retry:#= coercing:aNumber
+!
+
+~= aNumber
+    "return true, if the arguments value is not equal to mine"
+
+%{  /* NOCONTEXT */
+
+    if (aNumber == self) {
+        RETURN ( false );
+    }
+    if (! _isNonNilObject(aNumber)) {
+        RETURN ( true );
+    }
+
+    if (_qClass(aNumber) == Float) {
+        RETURN ( ((double)_intVal(self) == _floatVal(aNumber)) ? false : true );
+    }
+%}
+.
+    aNumber respondsToArithmetic ifFalse:[^ true].
+    ^ self retry:#~= coercing:aNumber
+!
+
+< aNumber
+    "return true, if the argument is greater than the receiver"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+        RETURN ( (_intVal(self) < _intVal(aNumber)) ? true : false );
+#else
+        /* tag bit does not change ordering */
+        RETURN ( ((INT)self < (INT)aNumber) ? true : false );
+#endif
+    }
+    if (_isFloat(aNumber)) {
+        RETURN ( ((double)_intVal(self) < _floatVal(aNumber)) ? true : false );
+    }
+%}
+.
+    ^ aNumber lessFromInteger:self
+    "^ self retry:#< coercing:aNumber"
+!
+
+> aNumber
+    "return true, if the argument is less than the receiver"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+        RETURN ( (_intVal(self) > _intVal(aNumber)) ? true : false );
+#else
+        /* tag bit does not change ordering */
+        RETURN ( ((INT)self > (INT)aNumber) ? true : false );
+#endif
+    }
+    if (_isFloat(aNumber)) {
+        RETURN ( ((double)_intVal(self) > _floatVal(aNumber)) ? true : false );
+    }
+%}
+.
+    ^ self retry:#> coercing:aNumber
+!
+
+>= aNumber
+    "return true, if the argument is less or equal"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+        RETURN ( (_intVal(self) >= _intVal(aNumber)) ? true : false );
+#else
+        /* tag bit does not change ordering */
+        RETURN ( ((INT)self >= (INT)aNumber) ? true : false );
+#endif
+    }
+    if (_isFloat(aNumber)) {
+        RETURN ( ((double)_intVal(self) >= _floatVal(aNumber)) ? true : false );
+    }
+%}
+.
+    ^ self retry:#>= coercing:aNumber
+!
+
+<= aNumber
+    "return true, if the argument is greater or equal"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+        RETURN ( (_intVal(self) <= _intVal(aNumber)) ? true : false );
+#else
+        /* tag bit does not change ordering */
+        RETURN ( ((INT)self <= (INT)aNumber) ? true : false );
+#endif
+    }
+    if (_isFloat(aNumber)) {
+        RETURN ( ((double)_intVal(self) <= _floatVal(aNumber)) ? true : false );
+    }
+%}
+.
+    ^ self retry:#<= coercing:aNumber
+!
+
+identityHash
+    "return an integer useful for hashing on identity"
+
+    self > 0 ifTrue:[
+        ^ self + 8192
+    ].
+    ^ self negated + 8192
+!
+
+min:aNumber
+    "return the receiver or the argument, whichever is smaller"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+        if (_intVal(self) < _intVal(aNumber)) {
+#else
+        /* tag bit does not change ordering */
+        if ((INT)(self) < (INT)(aNumber)) {
+#endif
+            RETURN ( self );
+        }
+        RETURN ( aNumber );
+    }
+    if (_isFloat(aNumber)) {
+        if ( (double)_intVal(self) < _floatVal(aNumber) ) {
+            RETURN ( self );
+        }
+        RETURN ( aNumber );
+    }
+%}
+.
+    (self < aNumber) ifTrue:[^ self].
+    ^ aNumber
+!
+
+max:aNumber
+    "return the receiver or the argument, whichever is greater"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+        if (_intVal(self) > _intVal(aNumber)) {
+#else
+        /* tag bit does not change ordering */
+        if ((INT)(self) > (INT)(aNumber)) {
+#endif
+            RETURN ( self );
+        }
+        RETURN ( aNumber );
+    }
+    if (_isFloat(aNumber)) {
+        if ( (double)_intVal(self) > _floatVal(aNumber) ) {
+            RETURN ( self );
+        }
+        RETURN ( aNumber );
+    }
+%}
+.
+    (self > aNumber) ifTrue:[^ self].
+    ^ aNumber
+! !
+
+!SmallInteger methodsFor:'testing'!
+
+negative
+    "return true, if the receiver is less than zero
+     reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+    RETURN ( (_intVal(self) < 0) ? true : false );
+#else
+    /* tag bit does not change sign */
+    RETURN ( ((INT)(self) < 0) ? true : false );
+#endif
+%}
+!
+
+positive
+    "return true, if the receiver is not negative
+     reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+    RETURN ( (_intVal(self) >= 0) ? true : false );
+#else
+    /* tag bit does not change sign */
+    RETURN ( ((INT)(self) >= 0) ? true : false );
+#endif
+%}
+!
+
+strictlyPositive
+    "return true, if the receiver is greater than zero
+     reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+    RETURN ( (_intVal(self) > 0) ? true : false );
+#else
+    /* tag bit does not change sign */
+    RETURN ( ((INT)(self) > 0) ? true : false );
+#endif
+%}
+!
+
+sign
+    "return the sign of the receiver
+     reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+    INT val = _intVal(self);
+
+    if (val < 0) {
+        RETURN ( _MKSMALLINT(-1) ); 
+    }
+    if (val > 0) {
+        RETURN ( _MKSMALLINT(1) );
+    }
+    RETURN ( _MKSMALLINT(0) );
+%}
+!
+
+between:min and:max
+    "return true if the receiver is less than or equal to the argument max
+     and greater than or equal to the argument min.
+     - reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(min) && _isSmallInteger(max)) {
+	REGISTER INT selfVal;
+
+	selfVal = _intVal(self);
+        if (selfVal < _intVal(min)) {
+             RETURN ( false );
+        }
+        if (selfVal > _intVal(max)) {
+             RETURN ( false );
+        }
+        RETURN ( true );
+    }
+%}
+.
+    (self < min) ifTrue:[^ false].
+    (self > max) ifTrue:[^ false].
+    ^ true
+!
+
+even
+    "return true, if the receiver is even"
+
+%{  /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+    RETURN ( ((INT)self & 1) ? false : true );
+#else    
+    RETURN ( ((INT)self & ((INT)_MKSMALLINT(1) & ~TAG_INT)) ? false : true );
+#endif
+%}
+!
+
+odd
+    "return true, if the receiver is odd"
+
+%{  /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+    RETURN ( ((INT)self & 1) ? true : false );
+#else    
+    RETURN ( ((INT)self & ((INT)_MKSMALLINT(1) & ~TAG_INT)) ? true : false );
+#endif
+%}
+! !
+
+!SmallInteger methodsFor:'arithmetic'!
+
++ aNumber
+    "return the sum of the receivers value and the arguments value"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+#ifdef _ADD_IO_IO
+	RETURN ( _ADD_IO_IO(self, aNumber) );
+#else
+        REGISTER INT sum;
+	extern OBJ _makeLarge();
+
+        sum =  _intVal(self) + _intVal(aNumber);
+        if ((sum >= _MIN_INT) && (sum <= _MAX_INT)) {
+            RETURN ( _MKSMALLINT(sum) );
+        }
+	RETURN ( _makeLarge(sum) );
+#endif
+    }
+    if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+        extern char *newNextPtr, *newEndPtr;
+        OBJ newFloat;
+	double val;
+
+	val = _floatVal(aNumber);
+        _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+        _InstPtr(newFloat)->o_class = Float;
+        _FloatInstPtr(newFloat)->f_floatvalue = (double)(_intVal(self)) + val;
+        RETURN ( newFloat );
+    }
+%}
+.
+    ^ aNumber sumFromInteger:self
+!
+
+- aNumber
+    "return the difference of the receivers value and the arguments value"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+#ifdef _SUB_IO_IO
+	RETURN ( _SUB_IO_IO(self, aNumber) );
+#else
+        REGISTER INT diff;
+	extern OBJ _makeLarge();
+
+        diff =  _intVal(self) - _intVal(aNumber);
+        if ((diff >= _MIN_INT) && (diff <= _MAX_INT)) {
+            RETURN ( _MKSMALLINT(diff) );
+        }
+	RETURN ( _makeLarge(diff) );
+#endif
+    }
+    if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+        extern char *newNextPtr, *newEndPtr;
+        OBJ newFloat;
+	double val;
+
+	val = _floatVal(aNumber);
+        _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+        _InstPtr(newFloat)->o_class = Float;
+        _FloatInstPtr(newFloat)->f_floatvalue = (double)(_intVal(self)) - val;
+        RETURN ( newFloat );
+    }
+%}
+.
+    ^ aNumber differenceFromInteger:self
+!
+
+* aNumber
+    "return the product of the receivers value and the arguments value"
+
+    |aLarge|
+
+%{  /* NOCONTEXT */
+
+    REGISTER INT myValue, otherValue;
+    unsigned INT pHH, pHL, pLH, pLL;
+
+    if (_isSmallInteger(aNumber)) {
+        /* this is too slow:
+         * since most machines can do 32*32 to 64 bit multiply,
+         * (or at least 32*32 with Overflow check)
+         * its better to do it this way .. - need an assembler (inline) function here 
+         */
+        myValue = _intVal(self);
+        if (myValue < 0) myValue = -myValue;
+        otherValue = _intVal(aNumber);
+        if (otherValue < 0) otherValue = -otherValue;
+#ifdef NOTDEF
+        if (! ((myValue & ~0x7FFF) || (otherValue & ~0x7FFF))) {
+#else
+        pHH = ((myValue >> 16) & 0xFFFF) * ((otherValue >> 16) & 0xFFFF);
+        pHL = ((myValue >> 16) & 0xFFFF) * (otherValue & 0xFFFF);
+        pLH = (myValue & 0xFFFF) * ((otherValue >> 16) & 0xFFFF);
+        pLL = (myValue & 0xFFFF) * (otherValue & 0xFFFF);
+        if (! (pHH || (pHL & 0xFFFFc000) || (pLH & 0xFFFFc000) || (pLL & 0xc0000000))) {
+#endif
+            RETURN ( _MKSMALLINT(_intVal(self) * _intVal(aNumber)) );
+        }
+    } else if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+        extern char *newNextPtr, *newEndPtr;
+        OBJ newFloat;
+	double val;
+
+	val = _floatVal(aNumber);
+        _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+        _InstPtr(newFloat)->o_class = Float;
+        _FloatInstPtr(newFloat)->f_floatvalue = (double)(_intVal(self)) * val;
+        RETURN ( newFloat );
+    }
+%}
+.
+%{
+    extern OBJ LargeInteger, __mu, _value_;
+    static struct inlineCache val = _ILC1;
+    static struct inlineCache mu = _ILC1;
+
+    if (_isSmallInteger(aNumber)) {
+        /*
+         * non overflow case has already been checked
+         */
+#ifdef PASS_ARG_REF
+        aLarge = (*val.ilc_func)(LargeInteger, _value_, CON_COMMA nil, &val, &self);
+        RETURN ( (*mu.ilc_func)(aLarge, __mu, CON_COMMA nil, &mu, &aNumber) );
+#else
+        aLarge = (*val.ilc_func)(LargeInteger, _value_, CON_COMMA nil, &val, self);
+        RETURN ( (*mu.ilc_func)(aLarge, __mu, CON_COMMA nil, &mu, aNumber) );
+#endif
+    }
+%}
+.
+    ^ aNumber productFromInteger:self
+!
+
+/ aNumber
+    "return the quotient of the receivers value and the arguments value"
+
+%{  /* NOCONTEXT */
+
+    INT me, t, val;
+    double dval;
+
+    if (_isSmallInteger(aNumber)) {
+        val = _intVal(aNumber);
+        if (val != 0) {
+            me = _intVal(self);
+            t = me / val;
+#ifdef GOOD_OPTIMIZER
+            if (me % val) {
+#else
+            /* this is stupid - all I want is to look for a remainder ... 
+               but most compilers are too stupid and generate an extra mod instr.
+               for "if (me % val)" even if most div instructions also compute
+               the remainder.
+               therefore I use a multiplication which is faster than a modulu
+               on most machines
+            */
+            if ((t * val) == me) {
+#endif
+                RETURN ( _MKSMALLINT(t) );
+            }
+/*
+ * now disabled - Fractions work
+ *
+            RETURN ( _MKFLOAT((double)_intVal(self) / (double)val, __context) );
+*/
+        }
+    } else {
+        if (_isFloat(aNumber)) {
+            dval = _floatVal(aNumber);
+            if (dval != 0.0) {
+                me = _intVal(self);
+                RETURN ( _MKFLOAT((double)me / dval COMMA_CON) );
+            }
+        }
+    }
+%}
+.
+    aNumber isInteger ifTrue:[
+        aNumber = 0 ifTrue:[
+            DivisionByZeroSignal raise.
+            ^ self
+        ].
+        ^ Fraction numerator:self denominator:aNumber
+    ].
+    ^ aNumber quotientFromInteger:self
+!
+
+// anInteger
+    "return the integer part of the quotient of the receivers value
+     and the arguments value"
+
+%{  /* NOCONTEXT */
+    INT val;
+
+    if (_isSmallInteger(anInteger)) {
+        val = _intVal(anInteger);
+        if (val != 0) {
+            RETURN ( _MKSMALLINT(_intVal(self) / val) );
+        }
+    }
+%}
+.
+    (anInteger = 0) ifTrue:[
+        DivisionByZeroSignal raise.
+        ^ self
+    ].
+    ^ self retry:#// coercing:anInteger
+!
+
+\\ anInteger
+    "return the integer rest of the receivers value
+     divided by the arguments value"
+
+%{  /* NOCONTEXT */
+    INT mySelf, val;
+
+    if (_isSmallInteger(anInteger)) {
+        mySelf = _intVal(self);
+        if (mySelf < 0) mySelf = -mySelf;
+        val = _intVal(anInteger);
+        if (val != 0) {
+            if (val < 0) {
+                RETURN ( _MKSMALLINT(-(mySelf % -val)) );
+            }
+            RETURN ( _MKSMALLINT(mySelf % val) );
+        }
+    }
+%}
+.
+    (anInteger = 0) ifTrue:[
+        DivisionByZeroSignal raise.
+        ^ self
+    ].
+    ^ self retry:#\\ coercing:anInteger
+!
+
+abs
+    "return the absolute value of the receiver
+     reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+    INT val = _intVal(self);
+
+    if (val != _MIN_INT) {
+        RETURN ( (val < 0) ? _MKSMALLINT(-val) : self );
+    }
+%}
+.
+    "only reached for minVal"
+    ^ self negated
+!
+
+negated
+    "return the negative value of the receiver
+     reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+    INT val = _intVal(self);
+
+    if (val != _MIN_INT) {
+        RETURN ( _MKSMALLINT(- val) );
+    }
+%}
+.
+    ^ (LargeInteger value:(SmallInteger maxVal)) + 1
+! !
+
+!SmallInteger methodsFor:'modulu arithmetic'!
+
+times:aNumber
+    "return the product of the receiver and the argument as SmallInteger. 
+     If the result overflows integer range the value modulu the SmallInteger 
+     range is returned.
+     This is of course not always correct, but some code does a modulu anyway
+     and can therefore speed things up by not going through LargeIntegers."
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+        RETURN ( _MKSMALLINT((_intVal(self) * _intVal(aNumber)) & 0x7FFFFFFF) );
+    }
+%}
+.
+    self primitiveFailed
+!
+
+plus:aNumber
+    "return the sum of the receiver and the argument as SmallInteger.
+     If the result overflows integer range, the value modulu the SmallInteger
+     range is returned.
+     This is of course not always correct, but some code does a modulu anyway
+     and can therefore speed things up by not going through LargeIntegers."
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+        RETURN ( _MKSMALLINT((_intVal(self) + _intVal(aNumber)) & 0x7FFFFFFF) );
+    }
+%}
+.
+    self primitiveFailed
+! !
+
+!SmallInteger class methodsFor:'bit mask constants'!
+
+bitMaskFor:index
+    "return a bitmask for the index's bit (index starts at 1)"
+
+    (index between:1 and:SmallInteger maxBits) ifFalse:[
+        ^ self error:'index out of bounds'
+    ].
+    ^ 1 bitShift:(index - 1)
+! !
+
+!SmallInteger methodsFor:'bit operators'!
+
+bitAt:index
+    "return the value of the index's bit (index starts at 1)"
+
+    |mask|
+
+    (index between:1 and:SmallInteger maxBits) ifFalse:[
+        ^ self error:'index out of bounds'
+    ].
+    mask := 1 bitShift:(index - 1).
+    ((self bitAnd:mask) == 0) ifTrue:[^ 0].
+    ^ 1
+!
+
+allMask:anInteger
+    "True if all bits in anInteger are 1 in the receiver"
+
+    ^(self bitAnd:anInteger) == anInteger
+!
+
+anyMask:anInteger
+    "True if any 1 bits in anInteger are 1 in the receiver"
+
+    ^(self bitAnd:anInteger) ~~ 0
+!
+
+noMask:anInteger
+    "True if no 1 bits in anInteger are 1 in the receiver"
+
+    ^(self bitAnd:anInteger) == 0
+!
+
+highBit
+    "return the bitIndex of the highest bit set"
+
+%{  /* NOCONTEXT */
+
+    INT mask, index, bits;
+
+    bits = _intVal(self);
+    if (bits == 0) {
+        RETURN ( _MKSMALLINT(-1) );
+    }
+#ifdef alpha
+    mask = 0x2000000000000000;
+    index = 62;
+#else
+    mask = 0x20000000;
+    index = 30;
+#endif
+    while (index) {
+        if (bits & mask) break;
+        mask = mask >> 1;
+        index--;
+    }
+    RETURN ( _MKSMALLINT(index) );
+%}
+!
+
+lowBit
+    "return the bitIndex of the lowest bit set"
+%{  /* NOCONTEXT */
+
+    INT mask, index, bits;
+
+    bits = _intVal(self);
+    if (bits == 0) {
+        RETURN ( _MKSMALLINT(-1) );
+    }
+    mask = 1;
+    index = 1;
+#ifdef alpha
+    while (index != 63) {
+#else
+    while (index != 31) {
+#endif
+        if (bits & mask) {
+            RETURN ( _MKSMALLINT(index) );
+        }
+        mask = mask << 1;
+        index++;
+    }
+    RETURN ( _MKSMALLINT(-1) );
+    /* notreached */
+%}
+!
+
+bitShift:shiftCount
+    "return the value of the receiver shifted by shiftCount bits;
+     leftShift if shiftCount > 0; rightShift otherwise"
+
+%{  /* NOCONTEXT */
+
+    INT bits, count;
+
+    if (_isSmallInteger(shiftCount)) {
+        count = _intVal(shiftCount);
+        bits = _intVal(self);
+        if (count > 0) {
+            RETURN ( _MKSMALLINT(bits << count) );
+        }
+        if (count < 0) {
+            RETURN ( _MKSMALLINT(bits >> -count) );
+        }
+        RETURN (self );
+    }
+%}
+.
+    ^ self bitShift:(shiftCount coerce:1)
+!
+
+bitOr:anInteger
+    "return the bit-or of the receiver and the argument, anInteger"
+
+%{  /* NOCONTEXT */
+
+    /* oring the tags doesn't change it */
+    if (_isSmallInteger(anInteger)) {
+        RETURN ( ((OBJ) ((INT)self | (INT)anInteger)) );
+    }
+%}
+.
+    ^ self retry:#bitOr coercing:anInteger
+!
+
+bitAnd:anInteger
+    "return the bit-and of the receiver and the argument, anInteger"
+
+%{  /* NOCONTEXT */
+
+    /* anding the tags doesn't change it */
+    if (_isSmallInteger(anInteger)) {
+        RETURN ( ((OBJ) ((INT)self & (INT)anInteger)) );
+    }
+%}
+.
+    ^ self retry:#bitAnd coercing:anInteger
+!
+
+bitXor:anInteger
+    "return the bit-exclusive-or of the receiver and the argument, anInteger"
+
+%{  /* NOCONTEXT */
+
+    /* xoring the tags turns it off - or it in again */
+    if (_isSmallInteger(anInteger)) {
+        RETURN ( (OBJ)( ((INT)self ^ (INT)anInteger) | TAG_INT) );
+    }
+%}
+.
+    ^ self retry:#bitXor coercing:anInteger
+!
+
+bitInvert
+    "return the value of the receiver with all bits inverted"
+
+%{  /* NOCONTEXT */
+
+    /* invert anything except tag bits */
+    RETURN ( ((OBJ) ((INT)self ^ ~TAG_MASK)) );
+%}
+!
+
+bitTest:aMask
+    "return true, if any bit from aMask is set in the receiver"
+
+%{  /* NOCONTEXT */
+
+    /* and all bits except tag */
+    if (_isSmallInteger(aMask)) {
+        RETURN ( ((INT)self & ((INT)aMask & ~TAG_MASK)) ? true : false );
+    }
+%}
+.
+    ^ self retry:#bitTest coercing:aMask
+! !
+
+!SmallInteger methodsFor:'byte access'!
+
+digitLength
+    "return the number bytes used by this Integer"
+
+    ^ self abs highBit - 1 // 8 + 1
+!
+
+digitAt:index
+    "return 8 bits of value, starting at byte index"
+
+%{  /* NOCONTEXT */
+
+    INT val;
+
+    if (_isSmallInteger(index)) {
+        val = _intVal(self);
+        if (val < 0)
+            val = -val;
+        switch (_intVal(index)) {
+            case 1:
+                RETURN ( _MKSMALLINT( val & 0xFF) );
+            case 2:
+                RETURN ( _MKSMALLINT( (val >> 8) & 0xFF) );
+            case 3:
+                RETURN ( _MKSMALLINT( (val >> 16) & 0xFF) );
+            case 4:
+                RETURN ( _MKSMALLINT( (val >> 24) & 0xFF) );
+#ifdef alpha
+            case 5:
+                RETURN ( _MKSMALLINT( (val >> 32) & 0xFF) );
+            case 6:
+                RETURN ( _MKSMALLINT( (val >> 40) & 0xFF) );
+            case 7:
+                RETURN ( _MKSMALLINT( (val >> 48) & 0xFF) );
+            case 8:
+                RETURN ( _MKSMALLINT( (val >> 56) & 0xFF) );
+#endif
+        }
+    }
+%}
+.
+    self primitiveFailed
+! !
+
+!SmallInteger methodsFor:'misc math functions'!
+
+gcd:anInteger
+    "return the greatest common divisor (Euclid's algorithm).
+     This has been redefined here for more speed since due to the
+     use of gcd in Fraction code, it has become time-critical for
+     some code. (thanx to MessageTally)"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(anInteger)) {
+        INT orgArg, ttt, selfInt, temp;
+
+        ttt = orgArg = _intVal(anInteger);
+	if (ttt) {
+            selfInt = _intVal(self);
+            while (ttt != 0) {
+                temp = selfInt % ttt;
+                selfInt = ttt;
+                ttt = temp;
+            }
+	    /*
+	     * since its not defined in what the sign of
+	     * a modulu result is when the arg is negative,
+	     * change it explicitely here ...
+	     */
+	    if (orgArg < 0) {
+		/* result should be negative */
+                if (selfInt > 0) selfInt = -selfInt;
+	    } else {
+		/* result should be positive */
+		if (selfInt < 0) selfInt = -selfInt;
+	    }
+            RETURN ( _MKSMALLINT(selfInt) );
+        }
+    }
+%}
+.
+    ^ super gcd:anInteger
+!
+
+intlog10
+    "return the truncation of log10 of the receiver -
+     stupid implementation; used to find out the number of digits needed
+     to print a number/and for conversion to a LargeInteger"
+
+    self <= 0 ifTrue:[
+        self error:'logarithm of negative integer'
+    ].
+    self < 10 ifTrue:[^ 1].
+    self < 100 ifTrue:[^ 2].
+    self < 1000 ifTrue:[^ 3].
+    self < 10000 ifTrue:[^ 4].
+    self < 100000 ifTrue:[^ 5].
+    self < 1000000 ifTrue:[^ 6].
+    self < 10000000 ifTrue:[^ 7].
+    self < 100000000 ifTrue:[^ 8].
+    self < 1000000000 ifTrue:[^ 9].
+    ^ 10
+! !
+
+!SmallInteger methodsFor:'coercing and converting'!
+
+coerce:aNumber
+    ^ aNumber asInteger
+!
+
+generality
+    ^ 20
+!
+
+asFloat
+    "return a Float with same value as receiver"
+
+%{  /* NOCONTEXT */
+
+    OBJ newFloat;
+
+    _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+    _InstPtr(newFloat)->o_class = Float;
+    _FloatInstPtr(newFloat)->f_floatvalue = _intVal(self);
+    RETURN ( newFloat );
+%}
+!
+
+asLargeInteger
+    "return a LargeInteger with same value as receiver"
+
+    ^ LargeInteger value:self
+!
+
+asCharacter
+    "Return self as an ascii character"
+
+    ^ Character value:self
+! !
+
+!SmallInteger methodsFor:'iterators'!
+
+timesRepeat:aBlock
+    "evaluate the argument, aBlock self times"
+
+    |count "{ Class: SmallInteger }" |
+
+    count := self.
+    [count > 0] whileTrue:[
+        aBlock value.
+        count := count - 1
+    ]
+!
+
+to:stop do:aBlock
+    "reimplemented for speed"
+
+    |home index|
+%{
+    REGISTER INT tmp;
+    INT final;
+    REGISTER OBJFUNC code;
+    extern OBJ Block, _value_;
+    static struct inlineCache blockVal = _ILC1;
+#ifdef UPDATE_WHOLE_STACK
+    REGISTER OBJ rHome;
+#   undef home
+#   define home rHome
+#endif
+
+    if (_isSmallInteger(stop)) {
+        tmp = _intVal(self);
+        final = _intVal(stop);
+        if (_isBlock(aBlock)
+         && ((code = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
+         && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
+            /*
+	     * arg is a compiled block - 
+             * directly call it without going through "Block-value"
+             */
+            home = _BlockInstPtr(aBlock)->b_home;
+            while (tmp <= final) {
+                if (InterruptPending != nil) interrupt(CONARG);
+
+                index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+                (*code)(home, CON_COMMA &index);
+#else
+                (*code)(home, CON_COMMA index);
+#endif
+                tmp++;
+            }
+        } else {
+            /*
+	     * arg is something else - call it with Block-value"
+             */
+            while (tmp <= final) {
+                if (InterruptPending != nil) interrupt(CONARG);
+
+                index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+                (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, &index);
+#else
+                (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, index);
+#endif
+                tmp++;
+            }
+        }
+        RETURN ( self );
+    }
+%}
+.
+    ^super to:stop do:aBlock
+!
+
+to:stop by:incr do:aBlock
+    "reimplemented for speed"
+
+    |home index|
+%{
+    REGISTER INT tmp, step;
+    REGISTER INT final;
+    REGISTER OBJFUNC code;
+    extern OBJ Block, _value_;
+    static struct inlineCache blockVal = _ILC1;
+#ifdef UPDATE_WHOLE_STACK
+    REGISTER OBJ rHome;
+#   undef home
+#   define home rHome
+#endif
+
+    if (_isSmallInteger(incr)
+     && _isSmallInteger(stop)) {
+        tmp = _intVal(self);
+        final = _intVal(stop);
+        step = _intVal(incr);
+        if (_isBlock(aBlock)
+         && ((code = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
+         && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
+            /*
+	     * arg is a compiled block - 
+             * directly call it without going through "Block-value"
+             */
+            home = _BlockInstPtr(aBlock)->b_home;
+	    if (step < 0) {
+                while (tmp >= final) {
+                    if (InterruptPending != nil) interrupt(CONARG);
+
+                    index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+                    (*code)(home, CON_COMMA &index);
+#else
+                    (*code)(home, CON_COMMA index);
+#endif
+                    tmp += step;
+                }
+	    } else {
+                while (tmp <= final) {
+                    if (InterruptPending != nil) interrupt(CONARG);
+
+                    index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+                    (*code)(home, CON_COMMA &index);
+#else
+                    (*code)(home, CON_COMMA index);
+#endif
+                    tmp += step;
+                }
+            }
+        } else {
+            /*
+	     * arg is something else - call it with Block-value"
+             */
+	    if (step < 0) {
+                while (tmp >= final) {
+                    if (InterruptPending != nil) interrupt(CONARG);
+
+                    index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+                    (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, &index);
+#else
+                    (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, index);
+#endif
+                    tmp += step;
+                }
+	    } else {
+                while (tmp <= final) {
+                    if (InterruptPending != nil) interrupt(CONARG);
+
+                    index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+                    (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, &index);
+#else
+                    (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, index);
+#endif
+                    tmp += step;
+                }
+	    }
+        }
+        RETURN ( self );
+    }
+%}
+.
+    ^super to:stop do:aBlock
+! !
+
+!SmallInteger methodsFor:'printing & storing'!
+
+printString
+    "return my printstring (base 10)"
+
+%{  /* NOCONTEXT */
+
+    extern char *newNextPtr, *newEndPtr;
+    char buffer[30];
+    OBJ newString;
+#ifdef THIS_CONTEXT
+    OBJ sav = __thisContext;
+#endif
+
+    sprintf(buffer, "%d", _intVal(self));
+#ifdef THIS_CONTEXT
+    __thisContext = sav;
+#endif
+    _qNew(newString, sizeof(struct stringheader) + strlen(buffer) + 1, SENDER);
+    _InstPtr(newString)->o_class = String;
+    strcpy(_stringVal(newString), buffer);
+    RETURN (newString);
+%}
+!
+
+printStringRadix:radix
+    "return my printstring (base 10)"
+
+%{  /* NOCONTEXT */
+
+    extern char *newNextPtr, *newEndPtr;
+    char *format = (char *)0;
+    char buffer[30];
+    OBJ newString;
+
+    if (_isSmallInteger(radix)) {
+        switch (_intVal(radix)) {
+	    case 10:
+		format = "%d";
+		break;
+	    case 16:
+		format = "%x";
+		break;
+	    case 8:
+		format = "%o";
+		break;
+	}
+    }
+
+    if (format) {
+#ifdef THIS_CONTEXT
+        OBJ sav = __thisContext;
+#endif
+        sprintf(buffer, format, _intVal(self));
+#ifdef THIS_CONTEXT
+        __thisContext = sav;
+#endif
+        _qNew(newString, sizeof(struct stringheader) + strlen(buffer) + 1, SENDER);
+        _InstPtr(newString)->o_class = String;
+        strcpy(_stringVal(newString), buffer);
+        RETURN (newString);
+    }
+%}
+.
+    ^ super printStringRadix:radix
+!
+
+printfPrintString:formatString
+    "non-portable, but sometimes useful.
+     return a printed representation of the receiver
+     as specified by formatString, which is defined by printf.
+     No checking for string overrun - must be shorter than 256 chars or else ..."
+
+%{  /* NOCONTEXT */
+
+    char buffer[256];
+
+    if (_isString(formatString)) {
+#ifdef THIS_CONTEXT
+        OBJ sav = __thisContext;
+#endif
+        sprintf(buffer, _stringVal(formatString), _intVal(self));
+#ifdef THIS_CONTEXT
+        __thisContext = sav;
+#endif
+        RETURN ( _MKSTRING(buffer COMMA_SND) );
+    }
+%}
+.
+    self primitiveFailed
+
+    "123 printfPrintString:'%%d -> %d'"
+    "123 printfPrintString:'%%6d -> %6d'"
+    "123 printfPrintString:'%%x -> %x'"
+    "123 printfPrintString:'%%4x -> %4x'"
+    "123 printfPrintString:'%%04x -> %04x'"
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SmallInteger.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,1362 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Integer subclass:#SmallInteger
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Magnitude-Numbers'
+!
+
+SmallInteger comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+%W% %E%
+
+SmallIntegers are Integers in the range of +/- 2^30 (i.e. 31 bits).
+These are no real objects - they have no instances (not even storage !)
+and cannot be subclassed (sorry)
+
+The reason is to save both storage and runtime by not collecting
+SmallIntegers in the system. SmallInts are marked by having the TAG_INT 
+bit set in contrast to Objects which have not. Since this knowledge is 
+hardwired into the system (an there is no class-field stored with
+SmallIntegers) there can be no subclass of SmallInteger (sorry).
+'!
+
+!SmallInteger class methodsFor:'instance creation'!
+
+basicNew
+    "catch instance creation
+     - SmallIntegers cannot be created with new"
+
+    self error:'instances of SmallInteger cannot be created with new'
+!
+
+basicNew:size
+    "catch instance creation
+     - SmallIntegers cannot be created with new"
+
+    self error:'instances of SmallInteger cannot be created with new'
+! !
+
+!SmallInteger class methodsFor:'constants'!
+
+maxBits
+    "return the number of bits in instances of me"
+
+%{  /* NOCONTEXT */
+    RETURN ( _MKSMALLINT(N_INT_BITS) );
+%}
+!
+
+maxBytes
+    "return the number of bytes in instances of me"
+
+%{  /* NOCONTEXT */
+    RETURN ( _MKSMALLINT(N_INT_BITS / 8 + 1) );
+%}
+!
+
+minVal
+    "return the smallest Integer representable as SmallInteger"
+
+%{  /* NOCONTEXT */
+    RETURN ( _MKSMALLINT(_MIN_INT) );
+%}
+!
+
+maxVal
+    "return the largest Integer representable as SmallInteger"
+
+%{  /* NOCONTEXT */
+    RETURN ( _MKSMALLINT(_MAX_INT) );
+%}
+! !
+
+!SmallInteger methodsFor:'error catching'!
+
+at:index
+    "catch indexed access - report an error
+     defined here since at: in Object ommits the SmallInteger check"
+
+    self notIndexed
+!
+
+basicAt:index
+    "catch indexed access - report an error
+     defined here since basicAt: in Object ommits the SmallInteger check"
+
+    self notIndexed
+!
+
+at:index put:anObject
+    "catch indexed access - report an error
+     defined here since at:put: in Object ommits the SmallInteger check"
+
+    self notIndexed
+!
+
+basicAt:index put:anObject
+    "catch indexed access - report an error
+     defined here since basicAt:put: in Object ommits the SmallInteger check"
+
+    self notIndexed
+!
+
+size
+    "return the number of indexed instvars - SmallIntegers have none
+     defined here since size in Object ommits the SmallInteger check"
+
+    ^ 0
+!
+
+basicSize
+    "return the number of indexed instvars - SmallIntegers have none
+     defined here since basicSize in Object ommits the SmallInteger check"
+
+    ^ 0
+! !
+
+!SmallInteger methodsFor:'copying'!
+
+shallowCopy
+    "return a shallow copy of myself
+     - reimplemented here since numbers are unique"
+
+    ^ self
+!
+
+deepCopy
+    "return a deep copy of myself
+     - reimplemented here since numbers are unique"
+
+    ^ self
+! !
+
+!SmallInteger methodsFor:'comparing'!
+
+= aNumber
+    "return true, if the arguments value is equal to mine"
+
+%{  /* NOCONTEXT */
+
+    if (aNumber == self) {
+        RETURN ( true );
+    }
+    if (! _isNonNilObject(aNumber)) {
+        RETURN ( false );
+    }
+
+    if (_qClass(aNumber) == Float) {
+        RETURN ( ((double)_intVal(self) == _floatVal(aNumber)) ? true : false );
+    }
+%}
+.
+    aNumber respondsToArithmetic ifFalse:[^ false].
+    ^ self retry:#= coercing:aNumber
+!
+
+~= aNumber
+    "return true, if the arguments value is not equal to mine"
+
+%{  /* NOCONTEXT */
+
+    if (aNumber == self) {
+        RETURN ( false );
+    }
+    if (! _isNonNilObject(aNumber)) {
+        RETURN ( true );
+    }
+
+    if (_qClass(aNumber) == Float) {
+        RETURN ( ((double)_intVal(self) == _floatVal(aNumber)) ? false : true );
+    }
+%}
+.
+    aNumber respondsToArithmetic ifFalse:[^ true].
+    ^ self retry:#~= coercing:aNumber
+!
+
+< aNumber
+    "return true, if the argument is greater than the receiver"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+        RETURN ( (_intVal(self) < _intVal(aNumber)) ? true : false );
+#else
+        /* tag bit does not change ordering */
+        RETURN ( ((INT)self < (INT)aNumber) ? true : false );
+#endif
+    }
+    if (_isFloat(aNumber)) {
+        RETURN ( ((double)_intVal(self) < _floatVal(aNumber)) ? true : false );
+    }
+%}
+.
+    ^ aNumber lessFromInteger:self
+    "^ self retry:#< coercing:aNumber"
+!
+
+> aNumber
+    "return true, if the argument is less than the receiver"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+        RETURN ( (_intVal(self) > _intVal(aNumber)) ? true : false );
+#else
+        /* tag bit does not change ordering */
+        RETURN ( ((INT)self > (INT)aNumber) ? true : false );
+#endif
+    }
+    if (_isFloat(aNumber)) {
+        RETURN ( ((double)_intVal(self) > _floatVal(aNumber)) ? true : false );
+    }
+%}
+.
+    ^ self retry:#> coercing:aNumber
+!
+
+>= aNumber
+    "return true, if the argument is less or equal"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+        RETURN ( (_intVal(self) >= _intVal(aNumber)) ? true : false );
+#else
+        /* tag bit does not change ordering */
+        RETURN ( ((INT)self >= (INT)aNumber) ? true : false );
+#endif
+    }
+    if (_isFloat(aNumber)) {
+        RETURN ( ((double)_intVal(self) >= _floatVal(aNumber)) ? true : false );
+    }
+%}
+.
+    ^ self retry:#>= coercing:aNumber
+!
+
+<= aNumber
+    "return true, if the argument is greater or equal"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+        RETURN ( (_intVal(self) <= _intVal(aNumber)) ? true : false );
+#else
+        /* tag bit does not change ordering */
+        RETURN ( ((INT)self <= (INT)aNumber) ? true : false );
+#endif
+    }
+    if (_isFloat(aNumber)) {
+        RETURN ( ((double)_intVal(self) <= _floatVal(aNumber)) ? true : false );
+    }
+%}
+.
+    ^ self retry:#<= coercing:aNumber
+!
+
+identityHash
+    "return an integer useful for hashing on identity"
+
+    self > 0 ifTrue:[
+        ^ self + 8192
+    ].
+    ^ self negated + 8192
+!
+
+min:aNumber
+    "return the receiver or the argument, whichever is smaller"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+        if (_intVal(self) < _intVal(aNumber)) {
+#else
+        /* tag bit does not change ordering */
+        if ((INT)(self) < (INT)(aNumber)) {
+#endif
+            RETURN ( self );
+        }
+        RETURN ( aNumber );
+    }
+    if (_isFloat(aNumber)) {
+        if ( (double)_intVal(self) < _floatVal(aNumber) ) {
+            RETURN ( self );
+        }
+        RETURN ( aNumber );
+    }
+%}
+.
+    (self < aNumber) ifTrue:[^ self].
+    ^ aNumber
+!
+
+max:aNumber
+    "return the receiver or the argument, whichever is greater"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+#ifdef POSITIVE_ADDRESSES
+        if (_intVal(self) > _intVal(aNumber)) {
+#else
+        /* tag bit does not change ordering */
+        if ((INT)(self) > (INT)(aNumber)) {
+#endif
+            RETURN ( self );
+        }
+        RETURN ( aNumber );
+    }
+    if (_isFloat(aNumber)) {
+        if ( (double)_intVal(self) > _floatVal(aNumber) ) {
+            RETURN ( self );
+        }
+        RETURN ( aNumber );
+    }
+%}
+.
+    (self > aNumber) ifTrue:[^ self].
+    ^ aNumber
+! !
+
+!SmallInteger methodsFor:'testing'!
+
+negative
+    "return true, if the receiver is less than zero
+     reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+    RETURN ( (_intVal(self) < 0) ? true : false );
+#else
+    /* tag bit does not change sign */
+    RETURN ( ((INT)(self) < 0) ? true : false );
+#endif
+%}
+!
+
+positive
+    "return true, if the receiver is not negative
+     reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+    RETURN ( (_intVal(self) >= 0) ? true : false );
+#else
+    /* tag bit does not change sign */
+    RETURN ( ((INT)(self) >= 0) ? true : false );
+#endif
+%}
+!
+
+strictlyPositive
+    "return true, if the receiver is greater than zero
+     reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+    RETURN ( (_intVal(self) > 0) ? true : false );
+#else
+    /* tag bit does not change sign */
+    RETURN ( ((INT)(self) > 0) ? true : false );
+#endif
+%}
+!
+
+sign
+    "return the sign of the receiver
+     reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+    INT val = _intVal(self);
+
+    if (val < 0) {
+        RETURN ( _MKSMALLINT(-1) ); 
+    }
+    if (val > 0) {
+        RETURN ( _MKSMALLINT(1) );
+    }
+    RETURN ( _MKSMALLINT(0) );
+%}
+!
+
+between:min and:max
+    "return true if the receiver is less than or equal to the argument max
+     and greater than or equal to the argument min.
+     - reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(min) && _isSmallInteger(max)) {
+	REGISTER INT selfVal;
+
+	selfVal = _intVal(self);
+        if (selfVal < _intVal(min)) {
+             RETURN ( false );
+        }
+        if (selfVal > _intVal(max)) {
+             RETURN ( false );
+        }
+        RETURN ( true );
+    }
+%}
+.
+    (self < min) ifTrue:[^ false].
+    (self > max) ifTrue:[^ false].
+    ^ true
+!
+
+even
+    "return true, if the receiver is even"
+
+%{  /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+    RETURN ( ((INT)self & 1) ? false : true );
+#else    
+    RETURN ( ((INT)self & ((INT)_MKSMALLINT(1) & ~TAG_INT)) ? false : true );
+#endif
+%}
+!
+
+odd
+    "return true, if the receiver is odd"
+
+%{  /* NOCONTEXT */
+
+#ifdef POSITIVE_ADDRESSES
+    RETURN ( ((INT)self & 1) ? true : false );
+#else    
+    RETURN ( ((INT)self & ((INT)_MKSMALLINT(1) & ~TAG_INT)) ? true : false );
+#endif
+%}
+! !
+
+!SmallInteger methodsFor:'arithmetic'!
+
++ aNumber
+    "return the sum of the receivers value and the arguments value"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+#ifdef _ADD_IO_IO
+	RETURN ( _ADD_IO_IO(self, aNumber) );
+#else
+        REGISTER INT sum;
+	extern OBJ _makeLarge();
+
+        sum =  _intVal(self) + _intVal(aNumber);
+        if ((sum >= _MIN_INT) && (sum <= _MAX_INT)) {
+            RETURN ( _MKSMALLINT(sum) );
+        }
+	RETURN ( _makeLarge(sum) );
+#endif
+    }
+    if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+        extern char *newNextPtr, *newEndPtr;
+        OBJ newFloat;
+	double val;
+
+	val = _floatVal(aNumber);
+        _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+        _InstPtr(newFloat)->o_class = Float;
+        _FloatInstPtr(newFloat)->f_floatvalue = (double)(_intVal(self)) + val;
+        RETURN ( newFloat );
+    }
+%}
+.
+    ^ aNumber sumFromInteger:self
+!
+
+- aNumber
+    "return the difference of the receivers value and the arguments value"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+#ifdef _SUB_IO_IO
+	RETURN ( _SUB_IO_IO(self, aNumber) );
+#else
+        REGISTER INT diff;
+	extern OBJ _makeLarge();
+
+        diff =  _intVal(self) - _intVal(aNumber);
+        if ((diff >= _MIN_INT) && (diff <= _MAX_INT)) {
+            RETURN ( _MKSMALLINT(diff) );
+        }
+	RETURN ( _makeLarge(diff) );
+#endif
+    }
+    if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+        extern char *newNextPtr, *newEndPtr;
+        OBJ newFloat;
+	double val;
+
+	val = _floatVal(aNumber);
+        _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+        _InstPtr(newFloat)->o_class = Float;
+        _FloatInstPtr(newFloat)->f_floatvalue = (double)(_intVal(self)) - val;
+        RETURN ( newFloat );
+    }
+%}
+.
+    ^ aNumber differenceFromInteger:self
+!
+
+* aNumber
+    "return the product of the receivers value and the arguments value"
+
+    |aLarge|
+
+%{  /* NOCONTEXT */
+
+    REGISTER INT myValue, otherValue;
+    unsigned INT pHH, pHL, pLH, pLL;
+
+    if (_isSmallInteger(aNumber)) {
+        /* this is too slow:
+         * since most machines can do 32*32 to 64 bit multiply,
+         * (or at least 32*32 with Overflow check)
+         * its better to do it this way .. - need an assembler (inline) function here 
+         */
+        myValue = _intVal(self);
+        if (myValue < 0) myValue = -myValue;
+        otherValue = _intVal(aNumber);
+        if (otherValue < 0) otherValue = -otherValue;
+#ifdef NOTDEF
+        if (! ((myValue & ~0x7FFF) || (otherValue & ~0x7FFF))) {
+#else
+        pHH = ((myValue >> 16) & 0xFFFF) * ((otherValue >> 16) & 0xFFFF);
+        pHL = ((myValue >> 16) & 0xFFFF) * (otherValue & 0xFFFF);
+        pLH = (myValue & 0xFFFF) * ((otherValue >> 16) & 0xFFFF);
+        pLL = (myValue & 0xFFFF) * (otherValue & 0xFFFF);
+        if (! (pHH || (pHL & 0xFFFFc000) || (pLH & 0xFFFFc000) || (pLL & 0xc0000000))) {
+#endif
+            RETURN ( _MKSMALLINT(_intVal(self) * _intVal(aNumber)) );
+        }
+    } else if ((aNumber != nil) && (_qClass(aNumber) == Float)) {
+        extern char *newNextPtr, *newEndPtr;
+        OBJ newFloat;
+	double val;
+
+	val = _floatVal(aNumber);
+        _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+        _InstPtr(newFloat)->o_class = Float;
+        _FloatInstPtr(newFloat)->f_floatvalue = (double)(_intVal(self)) * val;
+        RETURN ( newFloat );
+    }
+%}
+.
+%{
+    extern OBJ LargeInteger, __mu, _value_;
+    static struct inlineCache val = _ILC1;
+    static struct inlineCache mu = _ILC1;
+
+    if (_isSmallInteger(aNumber)) {
+        /*
+         * non overflow case has already been checked
+         */
+#ifdef PASS_ARG_REF
+        aLarge = (*val.ilc_func)(LargeInteger, _value_, CON_COMMA nil, &val, &self);
+        RETURN ( (*mu.ilc_func)(aLarge, __mu, CON_COMMA nil, &mu, &aNumber) );
+#else
+        aLarge = (*val.ilc_func)(LargeInteger, _value_, CON_COMMA nil, &val, self);
+        RETURN ( (*mu.ilc_func)(aLarge, __mu, CON_COMMA nil, &mu, aNumber) );
+#endif
+    }
+%}
+.
+    ^ aNumber productFromInteger:self
+!
+
+/ aNumber
+    "return the quotient of the receivers value and the arguments value"
+
+%{  /* NOCONTEXT */
+
+    INT me, t, val;
+    double dval;
+
+    if (_isSmallInteger(aNumber)) {
+        val = _intVal(aNumber);
+        if (val != 0) {
+            me = _intVal(self);
+            t = me / val;
+#ifdef GOOD_OPTIMIZER
+            if (me % val) {
+#else
+            /* this is stupid - all I want is to look for a remainder ... 
+               but most compilers are too stupid and generate an extra mod instr.
+               for "if (me % val)" even if most div instructions also compute
+               the remainder.
+               therefore I use a multiplication which is faster than a modulu
+               on most machines
+            */
+            if ((t * val) == me) {
+#endif
+                RETURN ( _MKSMALLINT(t) );
+            }
+/*
+ * now disabled - Fractions work
+ *
+            RETURN ( _MKFLOAT((double)_intVal(self) / (double)val, __context) );
+*/
+        }
+    } else {
+        if (_isFloat(aNumber)) {
+            dval = _floatVal(aNumber);
+            if (dval != 0.0) {
+                me = _intVal(self);
+                RETURN ( _MKFLOAT((double)me / dval COMMA_CON) );
+            }
+        }
+    }
+%}
+.
+    aNumber isInteger ifTrue:[
+        aNumber = 0 ifTrue:[
+            DivisionByZeroSignal raise.
+            ^ self
+        ].
+        ^ Fraction numerator:self denominator:aNumber
+    ].
+    ^ aNumber quotientFromInteger:self
+!
+
+// anInteger
+    "return the integer part of the quotient of the receivers value
+     and the arguments value"
+
+%{  /* NOCONTEXT */
+    INT val;
+
+    if (_isSmallInteger(anInteger)) {
+        val = _intVal(anInteger);
+        if (val != 0) {
+            RETURN ( _MKSMALLINT(_intVal(self) / val) );
+        }
+    }
+%}
+.
+    (anInteger = 0) ifTrue:[
+        DivisionByZeroSignal raise.
+        ^ self
+    ].
+    ^ self retry:#// coercing:anInteger
+!
+
+\\ anInteger
+    "return the integer rest of the receivers value
+     divided by the arguments value"
+
+%{  /* NOCONTEXT */
+    INT mySelf, val;
+
+    if (_isSmallInteger(anInteger)) {
+        mySelf = _intVal(self);
+        if (mySelf < 0) mySelf = -mySelf;
+        val = _intVal(anInteger);
+        if (val != 0) {
+            if (val < 0) {
+                RETURN ( _MKSMALLINT(-(mySelf % -val)) );
+            }
+            RETURN ( _MKSMALLINT(mySelf % val) );
+        }
+    }
+%}
+.
+    (anInteger = 0) ifTrue:[
+        DivisionByZeroSignal raise.
+        ^ self
+    ].
+    ^ self retry:#\\ coercing:anInteger
+!
+
+abs
+    "return the absolute value of the receiver
+     reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+    INT val = _intVal(self);
+
+    if (val != _MIN_INT) {
+        RETURN ( (val < 0) ? _MKSMALLINT(-val) : self );
+    }
+%}
+.
+    "only reached for minVal"
+    ^ self negated
+!
+
+negated
+    "return the negative value of the receiver
+     reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+    INT val = _intVal(self);
+
+    if (val != _MIN_INT) {
+        RETURN ( _MKSMALLINT(- val) );
+    }
+%}
+.
+    ^ (LargeInteger value:(SmallInteger maxVal)) + 1
+! !
+
+!SmallInteger methodsFor:'modulu arithmetic'!
+
+times:aNumber
+    "return the product of the receiver and the argument as SmallInteger. 
+     If the result overflows integer range the value modulu the SmallInteger 
+     range is returned.
+     This is of course not always correct, but some code does a modulu anyway
+     and can therefore speed things up by not going through LargeIntegers."
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+        RETURN ( _MKSMALLINT((_intVal(self) * _intVal(aNumber)) & 0x7FFFFFFF) );
+    }
+%}
+.
+    self primitiveFailed
+!
+
+plus:aNumber
+    "return the sum of the receiver and the argument as SmallInteger.
+     If the result overflows integer range, the value modulu the SmallInteger
+     range is returned.
+     This is of course not always correct, but some code does a modulu anyway
+     and can therefore speed things up by not going through LargeIntegers."
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(aNumber)) {
+        RETURN ( _MKSMALLINT((_intVal(self) + _intVal(aNumber)) & 0x7FFFFFFF) );
+    }
+%}
+.
+    self primitiveFailed
+! !
+
+!SmallInteger class methodsFor:'bit mask constants'!
+
+bitMaskFor:index
+    "return a bitmask for the index's bit (index starts at 1)"
+
+    (index between:1 and:SmallInteger maxBits) ifFalse:[
+        ^ self error:'index out of bounds'
+    ].
+    ^ 1 bitShift:(index - 1)
+! !
+
+!SmallInteger methodsFor:'bit operators'!
+
+bitAt:index
+    "return the value of the index's bit (index starts at 1)"
+
+    |mask|
+
+    (index between:1 and:SmallInteger maxBits) ifFalse:[
+        ^ self error:'index out of bounds'
+    ].
+    mask := 1 bitShift:(index - 1).
+    ((self bitAnd:mask) == 0) ifTrue:[^ 0].
+    ^ 1
+!
+
+allMask:anInteger
+    "True if all bits in anInteger are 1 in the receiver"
+
+    ^(self bitAnd:anInteger) == anInteger
+!
+
+anyMask:anInteger
+    "True if any 1 bits in anInteger are 1 in the receiver"
+
+    ^(self bitAnd:anInteger) ~~ 0
+!
+
+noMask:anInteger
+    "True if no 1 bits in anInteger are 1 in the receiver"
+
+    ^(self bitAnd:anInteger) == 0
+!
+
+highBit
+    "return the bitIndex of the highest bit set"
+
+%{  /* NOCONTEXT */
+
+    INT mask, index, bits;
+
+    bits = _intVal(self);
+    if (bits == 0) {
+        RETURN ( _MKSMALLINT(-1) );
+    }
+#ifdef alpha
+    mask = 0x2000000000000000;
+    index = 62;
+#else
+    mask = 0x20000000;
+    index = 30;
+#endif
+    while (index) {
+        if (bits & mask) break;
+        mask = mask >> 1;
+        index--;
+    }
+    RETURN ( _MKSMALLINT(index) );
+%}
+!
+
+lowBit
+    "return the bitIndex of the lowest bit set"
+%{  /* NOCONTEXT */
+
+    INT mask, index, bits;
+
+    bits = _intVal(self);
+    if (bits == 0) {
+        RETURN ( _MKSMALLINT(-1) );
+    }
+    mask = 1;
+    index = 1;
+#ifdef alpha
+    while (index != 63) {
+#else
+    while (index != 31) {
+#endif
+        if (bits & mask) {
+            RETURN ( _MKSMALLINT(index) );
+        }
+        mask = mask << 1;
+        index++;
+    }
+    RETURN ( _MKSMALLINT(-1) );
+    /* notreached */
+%}
+!
+
+bitShift:shiftCount
+    "return the value of the receiver shifted by shiftCount bits;
+     leftShift if shiftCount > 0; rightShift otherwise"
+
+%{  /* NOCONTEXT */
+
+    INT bits, count;
+
+    if (_isSmallInteger(shiftCount)) {
+        count = _intVal(shiftCount);
+        bits = _intVal(self);
+        if (count > 0) {
+            RETURN ( _MKSMALLINT(bits << count) );
+        }
+        if (count < 0) {
+            RETURN ( _MKSMALLINT(bits >> -count) );
+        }
+        RETURN (self );
+    }
+%}
+.
+    ^ self bitShift:(shiftCount coerce:1)
+!
+
+bitOr:anInteger
+    "return the bit-or of the receiver and the argument, anInteger"
+
+%{  /* NOCONTEXT */
+
+    /* oring the tags doesn't change it */
+    if (_isSmallInteger(anInteger)) {
+        RETURN ( ((OBJ) ((INT)self | (INT)anInteger)) );
+    }
+%}
+.
+    ^ self retry:#bitOr coercing:anInteger
+!
+
+bitAnd:anInteger
+    "return the bit-and of the receiver and the argument, anInteger"
+
+%{  /* NOCONTEXT */
+
+    /* anding the tags doesn't change it */
+    if (_isSmallInteger(anInteger)) {
+        RETURN ( ((OBJ) ((INT)self & (INT)anInteger)) );
+    }
+%}
+.
+    ^ self retry:#bitAnd coercing:anInteger
+!
+
+bitXor:anInteger
+    "return the bit-exclusive-or of the receiver and the argument, anInteger"
+
+%{  /* NOCONTEXT */
+
+    /* xoring the tags turns it off - or it in again */
+    if (_isSmallInteger(anInteger)) {
+        RETURN ( (OBJ)( ((INT)self ^ (INT)anInteger) | TAG_INT) );
+    }
+%}
+.
+    ^ self retry:#bitXor coercing:anInteger
+!
+
+bitInvert
+    "return the value of the receiver with all bits inverted"
+
+%{  /* NOCONTEXT */
+
+    /* invert anything except tag bits */
+    RETURN ( ((OBJ) ((INT)self ^ ~TAG_MASK)) );
+%}
+!
+
+bitTest:aMask
+    "return true, if any bit from aMask is set in the receiver"
+
+%{  /* NOCONTEXT */
+
+    /* and all bits except tag */
+    if (_isSmallInteger(aMask)) {
+        RETURN ( ((INT)self & ((INT)aMask & ~TAG_MASK)) ? true : false );
+    }
+%}
+.
+    ^ self retry:#bitTest coercing:aMask
+! !
+
+!SmallInteger methodsFor:'byte access'!
+
+digitLength
+    "return the number bytes used by this Integer"
+
+    ^ self abs highBit - 1 // 8 + 1
+!
+
+digitAt:index
+    "return 8 bits of value, starting at byte index"
+
+%{  /* NOCONTEXT */
+
+    INT val;
+
+    if (_isSmallInteger(index)) {
+        val = _intVal(self);
+        if (val < 0)
+            val = -val;
+        switch (_intVal(index)) {
+            case 1:
+                RETURN ( _MKSMALLINT( val & 0xFF) );
+            case 2:
+                RETURN ( _MKSMALLINT( (val >> 8) & 0xFF) );
+            case 3:
+                RETURN ( _MKSMALLINT( (val >> 16) & 0xFF) );
+            case 4:
+                RETURN ( _MKSMALLINT( (val >> 24) & 0xFF) );
+#ifdef alpha
+            case 5:
+                RETURN ( _MKSMALLINT( (val >> 32) & 0xFF) );
+            case 6:
+                RETURN ( _MKSMALLINT( (val >> 40) & 0xFF) );
+            case 7:
+                RETURN ( _MKSMALLINT( (val >> 48) & 0xFF) );
+            case 8:
+                RETURN ( _MKSMALLINT( (val >> 56) & 0xFF) );
+#endif
+        }
+    }
+%}
+.
+    self primitiveFailed
+! !
+
+!SmallInteger methodsFor:'misc math functions'!
+
+gcd:anInteger
+    "return the greatest common divisor (Euclid's algorithm).
+     This has been redefined here for more speed since due to the
+     use of gcd in Fraction code, it has become time-critical for
+     some code. (thanx to MessageTally)"
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(anInteger)) {
+        INT orgArg, ttt, selfInt, temp;
+
+        ttt = orgArg = _intVal(anInteger);
+	if (ttt) {
+            selfInt = _intVal(self);
+            while (ttt != 0) {
+                temp = selfInt % ttt;
+                selfInt = ttt;
+                ttt = temp;
+            }
+	    /*
+	     * since its not defined in what the sign of
+	     * a modulu result is when the arg is negative,
+	     * change it explicitely here ...
+	     */
+	    if (orgArg < 0) {
+		/* result should be negative */
+                if (selfInt > 0) selfInt = -selfInt;
+	    } else {
+		/* result should be positive */
+		if (selfInt < 0) selfInt = -selfInt;
+	    }
+            RETURN ( _MKSMALLINT(selfInt) );
+        }
+    }
+%}
+.
+    ^ super gcd:anInteger
+!
+
+intlog10
+    "return the truncation of log10 of the receiver -
+     stupid implementation; used to find out the number of digits needed
+     to print a number/and for conversion to a LargeInteger"
+
+    self <= 0 ifTrue:[
+        self error:'logarithm of negative integer'
+    ].
+    self < 10 ifTrue:[^ 1].
+    self < 100 ifTrue:[^ 2].
+    self < 1000 ifTrue:[^ 3].
+    self < 10000 ifTrue:[^ 4].
+    self < 100000 ifTrue:[^ 5].
+    self < 1000000 ifTrue:[^ 6].
+    self < 10000000 ifTrue:[^ 7].
+    self < 100000000 ifTrue:[^ 8].
+    self < 1000000000 ifTrue:[^ 9].
+    ^ 10
+! !
+
+!SmallInteger methodsFor:'coercing and converting'!
+
+coerce:aNumber
+    ^ aNumber asInteger
+!
+
+generality
+    ^ 20
+!
+
+asFloat
+    "return a Float with same value as receiver"
+
+%{  /* NOCONTEXT */
+
+    OBJ newFloat;
+
+    _qAlignedNew(newFloat, sizeof(struct floatstruct), SENDER);
+    _InstPtr(newFloat)->o_class = Float;
+    _FloatInstPtr(newFloat)->f_floatvalue = _intVal(self);
+    RETURN ( newFloat );
+%}
+!
+
+asLargeInteger
+    "return a LargeInteger with same value as receiver"
+
+    ^ LargeInteger value:self
+!
+
+asCharacter
+    "Return self as an ascii character"
+
+    ^ Character value:self
+! !
+
+!SmallInteger methodsFor:'iterators'!
+
+timesRepeat:aBlock
+    "evaluate the argument, aBlock self times"
+
+    |count "{ Class: SmallInteger }" |
+
+    count := self.
+    [count > 0] whileTrue:[
+        aBlock value.
+        count := count - 1
+    ]
+!
+
+to:stop do:aBlock
+    "reimplemented for speed"
+
+    |home index|
+%{
+    REGISTER INT tmp;
+    INT final;
+    REGISTER OBJFUNC code;
+    extern OBJ Block, _value_;
+    static struct inlineCache blockVal = _ILC1;
+#ifdef UPDATE_WHOLE_STACK
+    REGISTER OBJ rHome;
+#   undef home
+#   define home rHome
+#endif
+
+    if (_isSmallInteger(stop)) {
+        tmp = _intVal(self);
+        final = _intVal(stop);
+        if (_isBlock(aBlock)
+         && ((code = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
+         && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
+            /*
+	     * arg is a compiled block - 
+             * directly call it without going through "Block-value"
+             */
+            home = _BlockInstPtr(aBlock)->b_home;
+            while (tmp <= final) {
+                if (InterruptPending != nil) interrupt(CONARG);
+
+                index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+                (*code)(home, CON_COMMA &index);
+#else
+                (*code)(home, CON_COMMA index);
+#endif
+                tmp++;
+            }
+        } else {
+            /*
+	     * arg is something else - call it with Block-value"
+             */
+            while (tmp <= final) {
+                if (InterruptPending != nil) interrupt(CONARG);
+
+                index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+                (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, &index);
+#else
+                (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, index);
+#endif
+                tmp++;
+            }
+        }
+        RETURN ( self );
+    }
+%}
+.
+    ^super to:stop do:aBlock
+!
+
+to:stop by:incr do:aBlock
+    "reimplemented for speed"
+
+    |home index|
+%{
+    REGISTER INT tmp, step;
+    REGISTER INT final;
+    REGISTER OBJFUNC code;
+    extern OBJ Block, _value_;
+    static struct inlineCache blockVal = _ILC1;
+#ifdef UPDATE_WHOLE_STACK
+    REGISTER OBJ rHome;
+#   undef home
+#   define home rHome
+#endif
+
+    if (_isSmallInteger(incr)
+     && _isSmallInteger(stop)) {
+        tmp = _intVal(self);
+        final = _intVal(stop);
+        step = _intVal(incr);
+        if (_isBlock(aBlock)
+         && ((code = _BlockInstPtr(aBlock)->b_code) != (OBJFUNC)nil)
+         && (_BlockInstPtr(aBlock)->b_nargs == _MKSMALLINT(1))) {
+            /*
+	     * arg is a compiled block - 
+             * directly call it without going through "Block-value"
+             */
+            home = _BlockInstPtr(aBlock)->b_home;
+	    if (step < 0) {
+                while (tmp >= final) {
+                    if (InterruptPending != nil) interrupt(CONARG);
+
+                    index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+                    (*code)(home, CON_COMMA &index);
+#else
+                    (*code)(home, CON_COMMA index);
+#endif
+                    tmp += step;
+                }
+	    } else {
+                while (tmp <= final) {
+                    if (InterruptPending != nil) interrupt(CONARG);
+
+                    index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+                    (*code)(home, CON_COMMA &index);
+#else
+                    (*code)(home, CON_COMMA index);
+#endif
+                    tmp += step;
+                }
+            }
+        } else {
+            /*
+	     * arg is something else - call it with Block-value"
+             */
+	    if (step < 0) {
+                while (tmp >= final) {
+                    if (InterruptPending != nil) interrupt(CONARG);
+
+                    index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+                    (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, &index);
+#else
+                    (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, index);
+#endif
+                    tmp += step;
+                }
+	    } else {
+                while (tmp <= final) {
+                    if (InterruptPending != nil) interrupt(CONARG);
+
+                    index = _MKSMALLINT(tmp);
+#ifdef PASS_ARG_REF
+                    (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, &index);
+#else
+                    (*blockVal.ilc_func)(aBlock, _value_, CON_COMMA nil, &blockVal, index);
+#endif
+                    tmp += step;
+                }
+	    }
+        }
+        RETURN ( self );
+    }
+%}
+.
+    ^super to:stop do:aBlock
+! !
+
+!SmallInteger methodsFor:'printing & storing'!
+
+printString
+    "return my printstring (base 10)"
+
+%{  /* NOCONTEXT */
+
+    extern char *newNextPtr, *newEndPtr;
+    char buffer[30];
+    OBJ newString;
+#ifdef THIS_CONTEXT
+    OBJ sav = __thisContext;
+#endif
+
+    sprintf(buffer, "%d", _intVal(self));
+#ifdef THIS_CONTEXT
+    __thisContext = sav;
+#endif
+    _qNew(newString, sizeof(struct stringheader) + strlen(buffer) + 1, SENDER);
+    _InstPtr(newString)->o_class = String;
+    strcpy(_stringVal(newString), buffer);
+    RETURN (newString);
+%}
+!
+
+printStringRadix:radix
+    "return my printstring (base 10)"
+
+%{  /* NOCONTEXT */
+
+    extern char *newNextPtr, *newEndPtr;
+    char *format = (char *)0;
+    char buffer[30];
+    OBJ newString;
+
+    if (_isSmallInteger(radix)) {
+        switch (_intVal(radix)) {
+	    case 10:
+		format = "%d";
+		break;
+	    case 16:
+		format = "%x";
+		break;
+	    case 8:
+		format = "%o";
+		break;
+	}
+    }
+
+    if (format) {
+#ifdef THIS_CONTEXT
+        OBJ sav = __thisContext;
+#endif
+        sprintf(buffer, format, _intVal(self));
+#ifdef THIS_CONTEXT
+        __thisContext = sav;
+#endif
+        _qNew(newString, sizeof(struct stringheader) + strlen(buffer) + 1, SENDER);
+        _InstPtr(newString)->o_class = String;
+        strcpy(_stringVal(newString), buffer);
+        RETURN (newString);
+    }
+%}
+.
+    ^ super printStringRadix:radix
+!
+
+printfPrintString:formatString
+    "non-portable, but sometimes useful.
+     return a printed representation of the receiver
+     as specified by formatString, which is defined by printf.
+     No checking for string overrun - must be shorter than 256 chars or else ..."
+
+%{  /* NOCONTEXT */
+
+    char buffer[256];
+
+    if (_isString(formatString)) {
+#ifdef THIS_CONTEXT
+        OBJ sav = __thisContext;
+#endif
+        sprintf(buffer, _stringVal(formatString), _intVal(self));
+#ifdef THIS_CONTEXT
+        __thisContext = sav;
+#endif
+        RETURN ( _MKSTRING(buffer COMMA_SND) );
+    }
+%}
+.
+    self primitiveFailed
+
+    "123 printfPrintString:'%%d -> %d'"
+    "123 printfPrintString:'%%6d -> %6d'"
+    "123 printfPrintString:'%%x -> %x'"
+    "123 printfPrintString:'%%4x -> %4x'"
+    "123 printfPrintString:'%%04x -> %04x'"
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Smalltalk.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,1123 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Smalltalk
+       instanceVariableNames:''
+       classVariableNames:'exitBlocks CachedClasses'
+       poolDictionaries:''
+       category:'System-Support'
+!
+
+Smalltalk comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+             All Rights Reserved
+
+This is one of the central classes in the system;
+it provides all system-startup, shutdown and maintenance support.
+Also global variables are kept here.
+
+As you will notice, this is NOT a Dictionary
+ - my implementation of globals is totally different
+   (due to the need to be able to access globals from c-code as well).
+
+%W% %E%
+'!
+
+Smalltalk at:#ErrorNumber put:nil!
+Smalltalk at:#ErrorString put:nil!
+Smalltalk at:#Language put:#english!
+Smalltalk at:#LanguageTerritory put:#usa!
+Smalltalk at:#Initializing put:false!
+Smalltalk at:#SilentLoading put:false!
+Smalltalk at:#RecursionLimit put:nil!
+Smalltalk at:#MemoryLimit put:nil!
+Smalltalk at:#SystemPath put:nil!
+Smalltalk at:#StartupClass put:nil!
+Smalltalk at:#StartupSelector put:nil!
+Smalltalk at:#SignalCatchBlock put:nil!
+
+!Smalltalk class methodsFor:'time-versions'!
+
+majorVersion
+    "return the major version number"
+
+    ^ 2
+
+    "Smalltalk majorVersion"
+!
+
+minorVersion
+    "return the minor version number"
+
+    ^ 7
+
+    "Smalltalk minorVersion"
+!
+
+revision
+    "return the revision number"
+
+    ^ 1
+
+    "Smalltalk revision"
+!
+
+version
+    "return the version string"
+
+    ^ (self majorVersion printString ,
+       '.',
+       self minorVersion printString ,
+       '.',
+       self revision printString)
+
+    "Smalltalk version"
+!
+
+versionDate
+    "return the version date"
+
+    ^ '9-Jul-1993'
+
+    "Smalltalk versionDate"
+!
+
+copyright
+    "return a copyright string"
+
+    ^ 'Copyright (c) 1988-93 by Claus Gittinger'
+
+    "Smalltalk copyright"
+!
+
+hello
+    "return a greeting string"
+
+    (Language == #german) ifTrue:[
+        ^ 'Willkommen bei Smalltalk/X version '
+          , self version , ' vom ' , self versionDate
+    ].
+    ^ 'Hello World - here is Smalltalk/X version '
+      , self version , ' of ' , self versionDate
+
+    "Smalltalk hello"
+!
+
+timeStamp
+    "return a string useful for timestamping a file"
+
+    ^ '''From Smalltalk/X, Version:' , (Smalltalk version) , ' on '
+      , Date today printString , ' at ' , Time now printString
+      , ''''
+! !
+
+!Smalltalk class methodsFor:'initialization'!
+
+initialize
+    "this one is called from init - initialize all other classes"
+
+    self initGlobalsFromEnvironment.
+
+    "sorry - there are some, which MUST be initialized before ..
+     reason: if any error happens during init, we need Stdout to be there"
+
+    Object initialize.
+
+    ExternalStream initialize.
+    self initStandardStreams.
+
+    "sorry, path must be set before ...
+     reason: some classes need it during initialize"
+
+    self initSystemPath.
+
+    "must init display here - some classes (Color) need it during
+     initialize"
+
+    Workstation notNil ifTrue:[
+        Workstation initialize
+    ].
+
+    Inspector := MiniInspector.
+    Debugger := MiniDebugger.
+    Compiler := ByteCodeCompiler.
+    Compiler isNil ifTrue:[
+        "this allows at least immediate evaluations"
+        Compiler := Parser
+    ].
+
+    self allClassesDo:[:aClass |
+        "aviod never-ending story ..."
+        (aClass ~~ Smalltalk) ifTrue:[
+            aClass initialize
+        ]
+    ].
+    self initStandardTools.
+    self initInterrupts
+
+    "Smalltalk initialize"
+!
+
+initGlobalsFromEnvironment
+    "setup globals from the shell-environment"
+
+    |envString firstChar i langString terrString|
+
+    "extract Language and LanguageTerritory from LANG variable.
+     the language and territory must not be abbreviated,
+     valid is for example: english_usa
+                           english
+                           german
+                           german_austria"
+
+    envString := OperatingSystem getEnvironment:'LANG'.
+    envString notNil ifTrue:[
+        i := envString indexOf:$_.
+        (i == 0) ifTrue:[
+            langString := envString.
+            terrString := envString
+        ] ifFalse:[
+            langString := envString copyFrom:1 to:(i - 1).
+            terrString := envString copyFrom:(i + 1)
+        ].
+        Language := langString asSymbol.
+        LanguageTerritory := terrString asSymbol
+    ].
+
+    envString := OperatingSystem getEnvironment:'VIEW3D'.
+    envString notNil ifTrue:[
+        firstChar := (envString at:1) asLowercase.
+        (firstChar == $t) ifTrue:[
+            Smalltalk at:#View3D put:true
+        ] ifFalse: [
+            Smalltalk at:#View3D put:false
+        ]
+    ]
+    "Smalltalk initGlobalsFromEnvironment"
+!
+
+initStandardTools
+    "predefine some tools we will need later
+     - if the view-classes exist,
+       they will redefine Inspector and Debugger for graphical interfaces"
+
+    "redefine debug-tools, if view-classes exist"
+
+    (Smalltalk at:#Display) notNil ifTrue:[
+        (Smalltalk at:#InspectorView) notNil ifTrue:[
+            Inspector := Smalltalk at:#InspectorView
+        ].
+        (Smalltalk at:#DebugView) notNil ifTrue:[
+            Debugger := Smalltalk at:#DebugView
+        ].
+        Display initialize
+    ]
+    "Smalltalk initStandardTools"
+!
+
+initStandardStreams
+    "initialize some well-known streams"
+
+    Stdout := NonPositionableExternalStream forStdout.
+    Stderr := NonPositionableExternalStream forStderr.
+    Stdin := NonPositionableExternalStream forStdin.
+    Printer := PrinterStream.
+    Transcript := Stderr
+
+    "Smalltalk initStandardStreams"
+!
+
+initInterrupts
+    "initialize interrupts"
+
+    UserInterruptHandler := self.
+    ErrorInterruptHandler := self.
+    MemoryInterruptHandler := self.
+    SignalInterruptHandler := self.
+    ExceptionInterruptHandler := self.
+    OperatingSystem enableUserInterrupts.
+    OperatingSystem enableSignalInterrupts.
+    OperatingSystem enableFpExceptionInterrupts
+
+    "Smalltalk initInterrupts"
+!
+
+initSystemPath
+    "setup path to search for system files"
+
+    |p|
+
+    "the path is set to search files first locally
+     - this allows private stuff to override global stuff"
+
+    SystemPath := OrderedCollection new.
+    SystemPath add:'.'.
+    SystemPath add:'..'.
+    SystemPath add:(OperatingSystem getHomeDirectory).
+    (OperatingSystem isDirectory:(OperatingSystem getHomeDirectory , '/.smalltalk')) ifTrue:[
+        SystemPath add:(OperatingSystem getHomeDirectory , '/.smalltalk')
+    ].
+    p := OperatingSystem getEnvironment:'SMALLTALK_LIBDIR'.
+    p notNil ifTrue:[
+        SystemPath add:p
+    ].
+    (OperatingSystem isDirectory:'/usr/local/lib/smalltalk') ifTrue:[
+        SystemPath add:'/usr/local/lib/smalltalk'
+    ].
+    (OperatingSystem isDirectory:'/usr/lib/smalltalk') ifTrue:[
+        SystemPath add:'/usr/lib/smalltalk'
+    ].
+
+    "Smalltalk initSystemPath"
+    "SystemPath"
+!
+
+start
+    "main startup, if there is a Display, initialize it
+     and start dispatching; otherwise go into a read-eval-print loop"
+
+    Initializing := true.
+
+    "read patches- and rc-file, do not add things into change-file"
+
+    Class updateChanges:false.
+    [
+        self fileIn:'patches'.
+
+        (self fileIn:((Arguments at:1) , '.rc')) ifFalse:[
+            "no .rc file where executable is; try default smalltalk.rc"
+            self fileIn:'smalltalk.rc'
+        ]
+    ] valueNowOrOnUnwindDo:[Class updateChanges:true].
+
+    SilentLoading ifFalse:[
+        Transcript showCr:(self hello).
+        Transcript showCr:(self copyright).
+        Transcript cr
+    ].
+
+    Initializing := false.
+    DemoMode ifTrue:[
+        Transcript showCr:'Unlicensed demo mode with limitations.'
+    ].
+
+    [self saveMainLoop] whileTrue:[ ].
+
+    "done"
+
+    self exit
+!
+
+restart
+    "startup after an image has been loaded 
+     "
+    |deb insp|
+
+    Initializing := true.
+
+    "temporary switch back to dumb interface"
+
+    insp := Inspector.
+    deb := Debugger.
+    Inspector := MiniInspector.
+    Debugger := MiniDebugger.
+
+    ObjectMemory changed:#restarted.
+
+    "
+     some must be reinitialized before ...
+     - sorry, but order is important
+    "
+
+    Workstation reinitialize.
+    View reinitialize.
+
+    ObjectMemory changed:#returnFromSnapshot.
+
+    OperatingSystem enableUserInterrupts.
+    OperatingSystem enableSignalInterrupts.
+
+    Inspector := insp.
+    Debugger := deb.
+
+    Initializing := false.
+
+
+    "
+     if there is no Transcript, go to stderr
+    "
+    Transcript isNil ifTrue:[
+        self initStandardStreams.
+        Transcript := Stderr
+    ].
+
+    Transcript cr.
+    Transcript showCr:('Smalltalk restarted from:' , ImageName).
+    DemoMode ifTrue:[
+        Transcript showCr:'Unlicensed demo mode with limitations.'
+    ].
+
+    "this allows firing an application by defining
+     these two globals during snapshot ..."
+
+    StartupClass notNil ifTrue:[
+        StartupSelector notNil ifTrue:[
+
+            "allow customization by reading an image specific rc-file"
+            ImageName notNil ifTrue:[
+                (ImageName endsWith:'.img') ifTrue:[
+                    self fileIn:((ImageName copyFrom:1 to:(ImageName size - 4)), '.rc')
+                ] ifFalse:[
+                    self fileIn:(ImageName , '.rc')
+                ]
+            ].
+            StartupClass perform:StartupSelector
+        ]
+    ].
+
+    Display notNil ifTrue:[
+        Display dispatch
+    ] ifFalse:[
+        self readEvalPrint
+    ].
+
+    self exit
+!
+
+saveMainLoop
+    "main dispatching loop - exits with true for a bad exit (to restart),
+     false for real exit"
+
+    Smalltalk at:#SignalCatchBlock put:[^ true].
+
+    "if view-classes exist, start dispatching;
+     otherwise go into a read-eval-print loop"
+
+    Display notNil ifTrue:[
+        Display dispatch
+    ] ifFalse:[
+        self readEvalPrint
+    ].
+    ^ false
+!
+
+readEvalPrint
+    "simple read-eval-print loop for non-graphical Tinytalk"
+
+    |text|
+
+    'ST- ' print.
+    Stdin skipSeparators.
+    text := Stdin nextChunk.
+    [text notNil] whileTrue:[
+        (Compiler evaluate:text) printNewline.
+        'ST- ' print.
+        text := Stdin nextChunk
+    ].
+    '' printNewline
+! !
+
+!Smalltalk class methodsFor:'accessing'!
+
+at:aKey
+    "retrieve the value stored under aKey, a symbol"
+
+%{  /* NOCONTEXT */
+    extern OBJ _GETGLOBAL();
+
+    RETURN ( _GETGLOBAL(aKey) );
+%}
+!
+
+at:aKey ifAbsent:aBlock
+    "retrieve the value stored under aKey.
+     If there is none stored this key, return the value of
+     the evaluation of aBlock"
+
+    (self includesKey:aKey) ifTrue:[
+        ^ self at:aKey
+    ].
+    ^ aBlock value
+!
+
+at:aKey put:aValue
+    "store the argument aValue under aKey, a symbol"
+
+    CachedClasses := nil.
+
+%{  /* NOCONTEXT */
+    extern OBJ _SETGLOBAL();
+
+    RETURN ( _SETGLOBAL(aKey, aValue, (OBJ *)0) );
+%}
+!
+
+removeKey:aKey
+    "remove the argument from the globals dictionary"
+
+    CachedClasses := nil.
+
+%{  /* NOCONTEXT */
+    extern OBJ _REMOVEGLOBAL();
+
+    RETURN ( _REMOVEGLOBAL(aKey) );
+%}
+!
+
+includesKey:aKey
+    "return true, if the key is known"
+
+%{  /* NOCONTEXT */
+    extern OBJ _KEYKNOWN();
+
+    RETURN ( _KEYKNOWN(aKey) );
+%}
+!
+
+keyAtValue:anObject
+    "return the symbol under which anObject is stored - or nil"
+
+    self allKeysDo:[:aKey |
+        (self at:aKey) == anObject ifTrue:[^ aKey]
+    ]
+
+    "Smalltalk keyAtValue:Object"
+!
+
+keys
+    "return a collection with all keys in the Smalltalk dictionary"
+
+    |keys|
+
+    keys := OrderedCollection new.
+    self allKeysDo:[:k | keys add:k].
+    ^ keys
+! !
+
+!Smalltalk class methodsFor:'copying'!
+
+shallowCopy
+    "redefine copy - there is only one Smalltalk dictionary"
+
+    ^ self
+!
+
+deepCopy
+    "redefine copy - there is only one Smalltalk dictionary"
+
+    ^ self
+! !
+
+!Smalltalk class methodsFor:'inspecting'!
+
+inspect
+    "redefined to launch a DictionaryInspector on the receiver
+     (instead of the default InspectorView)."
+
+    DictionaryInspectorView isNil ifTrue:[
+        super inspect
+    ] ifFalse:[
+        DictionaryInspectorView openOn:self
+    ]
+! !
+
+!Smalltalk class methodsFor:'misc stuff'!
+
+addExitBlock:aBlock
+    "add a block to be executed when Smalltalk finishes"
+
+    exitBlocks isNil ifTrue:[
+        exitBlocks := Array with:aBlock
+    ] ifFalse:[
+        exitBlocks add:aBlock
+    ]
+!
+
+exit
+    "finish Smalltalk system"
+
+    exitBlocks notNil ifTrue:[
+        exitBlocks do:[:aBlock |
+            aBlock value
+        ]
+    ].
+%{
+    mainExit(0);
+%}
+.
+    OperatingSystem exit
+
+    "Smalltalk exit"
+!
+
+sleep:aDelay
+    "wait for aDelay seconds"
+
+    OperatingSystem sleep:aDelay
+! !
+
+!Smalltalk class methodsFor:'debugging'!
+
+printStackBacktrace
+    "print a stack backtrace"
+
+%{
+    printStack(__context);
+%}
+!
+
+fatalAbort
+    "abort program and dump core"
+%{
+    fatal0(__context, "abort");
+%}
+!
+
+statistic
+    "print some statistic data"
+%{
+    statistic();
+%}
+!
+
+debugOn
+    "temporary"
+
+    "LookupTrace := true.   "
+    MessageTrace := true.
+    "AllocTrace := true.     "
+    ObjectMemory flushInlineCaches
+!
+
+debugOff
+    "temporary"
+
+    LookupTrace := nil.    
+    MessageTrace := nil
+    ". AllocTrace := nil     "
+!
+
+allocDebugOn
+    "temporary"
+
+    AllocTrace := true
+!
+
+allocDebugOff
+    "temporary"
+
+    AllocTrace := nil
+!
+
+executionDebugOn
+    "temporary"
+
+    ExecutionTrace := true
+!
+
+executionDebugOff
+    "temporary"
+
+    ExecutionTrace := nil
+! !
+
+!Smalltalk class methodsFor:'looping'!
+
+do:aBlock
+    "evaluate the argument, aBlock for all values in the Smalltalk dictionary"
+%{
+    __allGlobalsDo(&aBlock COMMA_CON);
+%}
+!
+
+allKeysDo:aBlock
+    "evaluate the argument, aBlock for all keys in the Smalltalk dictionary"
+%{
+    __allKeysDo(&aBlock COMMA_CON);
+%}
+!
+
+allClassesDo:aBlock
+    "evaluate the argument, aBlock for all classes in the system"
+
+    self allClasses do:aBlock
+!
+
+associationsDo:aBlock
+    "evaluate the argument, aBlock for all key/value pairs 
+     in the Smalltalk dictionary"
+
+    self allKeysDo:[:aKey |
+        aBlock value:(aKey -> (self at:aKey))
+    ]
+
+    "Smalltalk associationsDo:[:assoc | assoc printNewline]"
+! !
+
+!Smalltalk class methodsFor:'queries'!
+
+numberOfGlobals
+    "return the number of global variables in the system"
+
+    |tally|
+
+    tally := 0.
+    self do:[:obj | tally := tally + 1].
+    ^ tally
+
+    "Smalltalk numberOfGlobals"
+!
+
+cellAt:aName
+    "return the address of a global cell
+     - used internally for compiler only"
+
+%{  /* NOCONTEXT */
+    extern OBJ _GETGLOBALCELL();
+
+    RETURN ( _GETGLOBALCELL(aName) );
+%}
+!
+
+references:anObject
+    "return true, if I refer to the argument, anObject
+     must be reimplemented since Smalltalk is no real collection"
+
+    self do:[:o |
+        (o == anObject) ifTrue:[^ true]
+    ].
+    ^ false
+!
+
+allClasses
+    "return a collection of all classes in the system"
+
+    CachedClasses isNil ifTrue:[
+        CachedClasses := IdentitySet new:400.
+        self do:[:anObject |
+            anObject notNil ifTrue:[
+                (anObject isBehavior) ifTrue:[
+                    CachedClasses add:anObject
+                ]
+            ]
+        ]
+    ].
+    ^ CachedClasses
+
+    "Smalltalk allClasses"
+!
+
+classNames
+    "return a collection of all classNames in the system"
+
+
+    ^ self allClasses collect:[:aClass | aClass name]
+! !
+
+!Smalltalk class methodsFor:'system management'!
+
+removeClass:aClass
+    "remove the argument, aClass from the smalltalk dictionary;
+     we have to flush the caches since these methods are now void"
+
+    |sym|
+
+    sym := aClass name asSymbol.
+    ((self at:sym) == aClass) ifTrue:[
+        self at:sym put:nil. "nil it out for compiled accesses"
+        " self removeKey:sym. "
+"
+        actually could get along with less flushing
+        (entries for aClass and subclasses only)
+
+        ObjectMemory flushInlineCachesForClass:aClass.
+        ObjectMemory flushMethodCacheFor:aClass
+"
+        ObjectMemory flushInlineCaches.
+        ObjectMemory flushMethodCache
+    ]
+!
+
+browseChanges
+    "startup a changes browser"
+
+    (self at:#ChangesBrowser) notNil ifTrue:[
+        ChangesBrowser start
+    ] ifFalse:[
+        self error:'no ChangesBrowser'
+    ]
+
+    "Smalltalk browseChanges "
+!
+
+browseAllSelect:aBlock
+    "startup a browser for all methods for which aBlock returns true"
+
+    SystemBrowser browseAllSelect:aBlock
+
+    " Smalltalk browseAllSelect:[:m | m literals isNil] "
+!
+
+browseImplementorsOf:aSelectorSymbol
+    "startup a browser for all methods implementing a particular message"
+
+    SystemBrowser browseImplementorsOf:aSelectorSymbol
+
+    " Smalltalk browseImplementorsOf:#at:put: "
+!
+
+browseAllCallsOn:aSelectorSymbol
+    "startup a browser for all methods sending a particular message"
+
+    SystemBrowser browseAllCallsOn:aSelectorSymbol
+
+    " Smalltalk browseAllCallsOn:#at:put: "
+!
+
+createSourceFilesIn:aFileDirectory
+    "create a new set of sources in aFileDirectory"
+
+    |aStream|
+
+    aStream := FileStream newFileNamed:'List.proto' in:aFileDirectory.
+    aStream isNil ifTrue:[
+        ^ self error:'cannot create prototype fileList:List.proto'
+    ]. 
+    self allClassesDo:[:aClass |
+        (aClass isMeta) ifFalse:[
+            Transcript show:('creating source for:' , aClass name , '...').
+
+            aStream nextPutAll:(aClass name , '.o').
+            aStream cr.
+
+            aClass fileOutIn:aFileDirectory.
+
+            Transcript cr
+        ]
+    ].
+    aStream close
+!
+
+createMakefileIn:aFileDirectory
+    "create a new Makefile in aFileDirectory"
+
+    |aStream classes fileNames superIndex count onum first
+     numClasses "{ Class: SmallInteger }" |
+
+    classes := VariableArray new:200.
+    classes grow:0.
+    fileNames := VariableArray new:200.
+    fileNames grow:0.
+
+    Transcript show:'building class tree ...'.
+
+    classes add:Object.
+    fileNames add:'Object'.
+    Object allSubclassesInOrderDo:[:aClass |
+        ((classes identityIndexOf:aClass startingAt:1) == 0) ifTrue:[
+            classes add:aClass.
+            fileNames add:(Smalltalk fileNameForClass:aClass name)
+        ]
+    ].
+    Transcript cr.
+    numClasses := classes size.
+
+    aStream := FileStream newFileNamed:'Makefile' in:aFileDirectory.
+    aStream isNil ifTrue:[
+        ^ self error:'cannot create Makefile'
+    ].
+
+    aStream nextPutAll:'LIBTOP=/usr/local/lib/smalltalk'. aStream cr.
+    aStream nextPutAll:'#LIBTOP=../..'. aStream cr.
+
+    aStream nextPutAll:'INCL=include'. aStream cr.
+    aStream nextPutAll:'#INCL2=../../include'. aStream cr.
+    aStream nextPutAll:'INCL2=/usr/include/smalltalk'. aStream cr.
+
+    aStream nextPutAll:'STC=/usr/local/bin/stc'. aStream cr.
+    aStream nextPutAll:'#STC=../../stc/stc'. aStream cr.
+
+    aStream nextPutAll:'#CFLAGS=-O'. aStream cr.
+    aStream nextPutAll:'STCOPT=+optinline +optspace'. aStream cr.
+    aStream nextPutAll:'STCFLAGS=-H$(INCL) -I$(INCL) -I$(INCL2)'. aStream cr.
+
+    aStream cr.
+    aStream nextPutAll:'smalltalk: $(INCLUDE) objs main.o'. aStream cr.
+    aStream cr.
+
+    aStream nextPutAll:'main.o: $(LIBTOP)/librun/main.c'. aStream cr.
+    aStream cr.
+
+    aStream nextPutAll:'$(INCLUDE):'. aStream cr.
+    aStream tab. aStream nextPutAll:'mkdir $(INCLUDE)'. aStream cr.
+    aStream cr.
+
+    aStream nextPutAll:'.SUFFIXES: .st .o'. aStream cr.
+    aStream nextPutAll:'.st.o:'. aStream cr.
+    aStream tab. aStream nextPutAll:'$(STC) $(STCFLAGS) $(CFLAGS) -c $*.st'.
+    aStream cr.
+    aStream cr.
+
+    aStream nextPutAll:'.SUFFIXES: .st .c'. aStream cr.
+    aStream nextPutAll:'.st.c:'. aStream cr.
+    aStream tab. aStream nextPutAll:'$(STC) $(STCFLAGS) $(CFLAGS) -C $*.st'.
+    aStream cr.
+    aStream cr.
+
+
+    onum := 1.
+    count := 0.
+
+    Transcript show:'appending o-file entries ...'.
+    1 to:numClasses do:[:index |
+        (count == 0) ifTrue:[
+            aStream nextPutAll:'objs'.
+            aStream nextPutAll:(onum printString).
+            aStream nextPutAll:':'.
+            first := true
+        ].
+        first ifFalse:[
+            aStream nextPutAll:' \'. aStream cr
+        ] ifTrue:[
+            first := false
+        ].
+        aStream tab.
+        aStream nextPutAll:((fileNames at:index) , '.o').
+        count := count + 1.
+        (count == 10) ifTrue:[
+            aStream cr.
+            count := 0.
+            onum := onum + 1
+        ]
+    ].
+    aStream cr.
+    aStream cr.
+
+    aStream nextPutAll:'objs:'.
+    first := true.
+    1 to:onum do:[:i |
+        first ifFalse:[
+            aStream nextPutAll:' \'. aStream cr
+        ] ifTrue:[
+            first := false
+        ].
+
+        aStream tab.
+        aStream nextPutAll:'objs'.
+        aStream nextPutAll:(i printString)
+    ].
+    aStream cr.
+    aStream cr.
+
+    Transcript cr.
+
+    "create dependency info"
+    Transcript show:'append dependency entries ...'.
+
+    1 to:numClasses do:[:index |
+        aStream nextPutAll:((fileNames at:index) , '.o:').
+        aStream tab.
+        aStream nextPutAll:((fileNames at:index) , '.st').
+        first := true.
+        (classes at:index) allSuperclassesDo:[:superClass |
+            first ifFalse:[
+                aStream nextPutAll:' \'. aStream cr
+            ] ifTrue:[
+                first := false
+            ].
+
+            superIndex := classes indexOf:superClass.
+            aStream tab.
+            aStream nextPutAll:'$(INCLUDE)/'.
+            aStream nextPutAll:((fileNames at:superIndex) , '.H')
+        ].
+        aStream cr.
+        aStream cr
+    ].
+
+    Transcript cr.
+    aStream close.
+
+    "create abbreviation file"
+    aStream := FileStream newFileNamed:'abbrev.stc' in:aFileDirectory.
+    aStream isNil ifTrue:[
+        ^ self error:'cannot create abbrev.stc'
+    ].
+    1 to:numClasses do:[:index |
+        ((classes at:index) name ~= (fileNames at:index)) ifTrue:[
+            aStream nextPutAll:(classes at:index) name.
+            aStream tab.
+            aStream nextPutAll:(fileNames at:index).
+            aStream cr
+        ]
+    ].
+    aStream close.
+
+    "create classlist file"
+    aStream := FileStream newFileNamed:'classList.stc' in:aFileDirectory.
+    aStream isNil ifTrue:[
+        ^ self error:'cannot create classList.stc'
+    ].
+    classes do:[:aClass |
+        aStream nextPutAll:aClass name.
+        aStream cr
+    ].
+    aStream close
+
+    " Smalltalk createMakefileIn:(FileDirectory directoryNamed:'source2.6') "
+!
+
+createNewSources
+    "create a new source directory, and fileOut all classes into this"
+
+    |nextVersion dirName here fileDirectory|
+
+    nextVersion := self minorVersion + 1.
+    dirName := 'source' 
+               , self majorVersion printString
+               , '.'
+               , nextVersion printString.
+    here := FileDirectory currentDirectory.
+    (here createDirectory:dirName) ifFalse:[
+        self error:'cannot create new source directory'
+    ].
+    Transcript showCr:('creating sources in ' , dirName); endEntry.
+
+    fileDirectory := FileDirectory directoryNamed:dirName in:here.
+    self createSourceFilesIn:fileDirectory.
+    self createMakefileIn:fileDirectory
+
+    " Smalltalk createNewSources "
+!
+
+systemFileStreamFor:aFileName
+    "search aFileName in some standard places;
+     return a fileStream or nil if not found"
+
+    |aStream|
+
+    SystemPath do:[:dirName |
+        aStream := FileStream readonlyFileNamed:(dirName , '/' , aFileName).
+        aStream notNil ifTrue:[^ aStream]
+    ].
+    ^ nil
+!
+
+fileNameForClass:aClassName
+    "return a good filename for aClassName -
+     using abbreviation file if there is one"
+
+    |fileName aStream abbrev line thisName index|
+
+    fileName := aClassName.
+
+    fileName size < 10 ifTrue:[^ fileName].
+
+    "too bad - look for abbreviation"
+
+    aStream := self systemFileStreamFor:'abbrev.stc'.
+    aStream notNil ifTrue:[
+        [aStream atEnd] whileFalse:[
+            line := aStream nextLine.
+            line notNil ifTrue:[
+                (line countWords == 2) ifTrue:[
+                    index := line indexOfSeparatorStartingAt:1.
+                    (index ~~ 0) ifTrue:[
+                        thisName := line copyFrom:1 to:(index - 1).
+                        (thisName = fileName) ifTrue:[
+                            abbrev := (line copyFrom:index) withoutSeparators.
+                            aStream close.
+                            ^ abbrev
+                        ]
+                    ]
+                ]
+            ]
+        ].
+        aStream close
+    ].
+
+    "no file found"
+    OperatingSystem maxFileNameLength >= (fileName size + 3) ifTrue:[
+        " self warn:'filename ' , fileName , ' will not work on sys5 machines' "
+    ] ifFalse:[
+        self error:'cant find short for ' , fileName , ' in abbreviation file'
+    ].
+    ^ fileName
+!
+
+fileInClassObject:aClassName from:aFileName
+    "read in the named object file - look for it in some standard places;
+     return true if ok, false if failed"
+
+    |aStream|
+
+    aStream := self systemFileStreamFor:aFileName.
+    aStream isNil ifTrue:[^ false].
+    aStream close.
+
+    (ObjectFileLoader loadClass:aClassName fromObjectFile:aFileName) isNil ifTrue:[^ false].
+    ^ true
+
+    " Smalltalk fileInClassObject:'AbstractPath' from:'../goodies/Paths/AbstrPath.o' "
+!
+
+fileIn:aFileName
+    "read in the named file - look for it in some standard places;
+     return true if ok, false if failed"
+
+    |aStream|
+
+    aStream := self systemFileStreamFor:aFileName.
+    aStream isNil ifTrue:[^ false].
+
+    [aStream fileIn] valueNowOrOnUnwindDo:[aStream close].
+    ^ true
+
+    " Smalltalk fileIn:'games/TicTacToe.st' "
+!
+
+fileInChanges
+    "read in the last changes file - bringing the system to the state it
+     had when left the last time"
+
+    |upd|
+
+    "tell Class to NOT update the changes file now ..."
+    upd := Class updateChanges:false.
+    [self fileIn:'changes'] valueNowOrOnUnwindDo:[Class updateChanges:upd]
+
+    "Smalltalk fileInChanges "
+!
+
+fileInClass:aClassName
+    "find a source/object file for aClassName and -if found - load it"
+
+    |fName newClass upd|
+
+    fName := self fileNameForClass:aClassName.
+    fName notNil ifTrue:[
+        upd := Class updateChanges:false.
+        [
+            (self fileIn:('fileIn/' , fName , '.ld')) ifFalse:[
+                (self fileInClassObject:aClassName from:('binary/' , fName, '.so')) ifFalse:[
+                    (self fileInClassObject:aClassName from:('binary/' , fName, '.o')) ifFalse:[
+                        self fileIn:(fName , '.st')
+                    ]
+                ]
+            ]
+        ] valueNowOrOnUnwindDo:[Class updateChanges:upd].
+        newClass := self at:(aClassName asSymbol).
+        (newClass notNil
+         and:[newClass implements:#initialize]) ifTrue:[newClass initialize]
+    ]
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SortColl.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,234 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+OrderedCollection subclass:#SortedCollection
+         instanceVariableNames:'sortBlock'
+         classVariableNames:'DefaultSortBlock'
+         poolDictionaries:''
+         category:'Collections-Ordered'
+!
+
+SortedCollection comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+I keep my elements sorted. The sort order is defined by a sortblock,
+which is a two-argument block which, when given two elements of the
+collection, returns true if the element given as first arg has to
+come before the element given as second arg.
+Thus a sortBlock of [:a :b | a < b] defines ascending sort-order,
+while [:a :b | a > b] defines descening order.
+
+%W% %E%
+'!
+
+!SortedCollection class methodsFor:'initialization'!
+
+initialize
+    DefaultSortBlock := [:a :b | a < b ]
+
+    "SortedCollection initialize"
+! !
+
+!SortedCollection class methodsFor:'instance creation'!
+
+new
+    ^ super new setSortBlock:DefaultSortBlock
+!
+
+new:size
+    ^ (super new:size) setSortBlock:DefaultSortBlock
+!
+
+sortBlock:aBlock
+    ^ super new setSortBlock:aBlock
+! !
+
+!SortedCollection methodsFor:'adding & removing'!
+
+addFirst:anObject
+    self shouldNotImplement
+!
+
+addLast:anObject
+    self shouldNotImplement
+!
+
+at:index put:anObject
+    self shouldNotImplement
+!
+
+add:newObject after:oldObject
+    self shouldNotImplement
+!
+
+add:newObject before:oldObject
+    self shouldNotImplement
+!
+
+addAll:aCollection
+    "add all elements of the argument, aCollection to the
+     receiver"
+
+    |mySize    "{ Class: SmallInteger }"
+     otherSize "{ Class: SmallInteger }"
+     dstIndex  "{ Class: SmallInteger }"
+     newSize newContents|
+
+    "if aCollection is bigger than a threshhold, its faster
+     to add all and resort - question: what is a good treshhold ?"
+
+    mySize := self size.
+    otherSize := aCollection size.
+    ((mySize == 0) or:[otherSize > 5]) ifTrue:[
+        newSize := mySize + otherSize.
+        newContents := Array new:newSize.
+        newContents replaceFrom:1 to:mySize with:contentsArray startingAt:1.
+        (aCollection isKindOf:SequenceableCollection) ifTrue:[
+            "maybe we can do it in one big move"
+            newContents replaceFrom:(mySize + 1) to:newSize with:aCollection startingAt:1.
+        ] ifFalse:[
+            dstIndex := mySize + 1.
+            aCollection do:[:element |
+                newContents at:dstIndex put:element.
+                dstIndex := dstIndex + 1
+            ]
+        ].
+        firstIndex := 1.
+        lastIndex := newSize.
+        contentsArray := newContents.
+        contentsArray sort:sortBlock.
+        ^ self
+    ].
+    super addAll:aCollection
+
+    "#(7 3 9 10 99) asSortedCollection addAll:#(77 0 1 16 5)"
+!
+
+add:anObject
+    "add the argument, anObject at the proper place in the
+     receiver"
+
+    |sz index|
+
+    sz := self size.
+    sz == 0 ifTrue:[
+        super addLast:anObject
+    ] ifFalse:[
+        index := self findIndexFor:anObject. 
+        self makeRoomAtIndex:index.
+        contentsArray at:index put:anObject
+    ].
+    ^ anObject
+! !
+
+!SortedCollection methodsFor:'testing'!
+
+includes:anObject
+    "return true, if the argument, anObject is in the collection.
+     (can reduce the number of compares since we are sorted)"
+
+    |index|
+
+    index := self findIndexFor:anObject.
+    ^ (index <= self size) and:[(contentsArray at:index) = anObject]
+!
+
+occurrencesOf:anObject
+    "return how many times the argument, anObject is in the collection.
+     (can reduce the number of compares since we are sorted)"
+
+    |index      "{ Class: SmallInteger }"
+     mySize     "{ Class: SmallInteger }"
+     tally      "{ Class: SmallInteger }" |
+
+    index := self findIndexFor:anObject.
+    mySize := self size.
+    index > mySize ifTrue:[^ 0].
+    tally := 0.
+    [(index <= mySize) and:[(contentsArray at:index) = anObject]] whileTrue:[
+        tally := tally + 1.
+        index := index + 1
+    ].
+    ^ tally
+! !
+
+!SortedCollection methodsFor:'instance protocol'!
+
+sortBlock
+    "return the block used for sorting"
+
+    ^ sortBlock
+!
+
+sortBlock:aSortBlock
+    "change the sort criteria for a sorted collection, resort the elements of 
+    the collection, and return the receiver. The argument, aSortBlock must
+    be a two-argument block which returns true if its arg1 has to come before
+    its arg2 in the collection."
+
+    sortBlock := aSortBlock.
+    lastIndex > firstIndex ifTrue:[
+        contentsArray quickSortFrom:firstIndex to:lastIndex sortBlock:aSortBlock
+    ]
+
+    "#(9 8 7 6 5 4 3) asSortedCollection"
+    "#(9 8 7 6 5 4 3) asSortedCollection sortBlock:[:a : b | a > b]"
+    "#($f $G $z $Y $o $H) asSortedCollectionSortedCollection"
+    "#($f $G $z $Y $o $H) asSortedCollection sortBlock:[:a : b | a asUppercase < b asUppercase]"
+! !
+
+!SortedCollection methodsFor:'enumerating'!
+
+collect: aBlock
+    | newOrderedCollection |
+    newOrderedCollection := OrderedCollection new.
+    self do: [ :element | newOrderedCollection add: (aBlock value: element) ].
+    ^newOrderedCollection
+! !
+
+!SortedCollection methodsFor:'private'!
+
+setSortBlock: aSortBlock
+    "set the sortblock without resorting - private only"
+
+    sortBlock := aSortBlock
+!
+
+findIndexFor:anObject
+    "search the index at which to insert anObject. Can also be used
+     to search for an existing element by checking if the element at
+     the returned index is the one we look for."
+
+    |low    "{ Class: SmallInteger}"
+     high   "{ Class: SmallInteger}"
+     middle "{ Class: SmallInteger}"
+     element|
+
+    low := firstIndex.
+    high := lastIndex.
+    [low <= high] whileTrue:[
+        middle := (low + high) // 2.
+        element := super at:middle.
+        (sortBlock value:element value:anObject) ifTrue:[
+            "middleelement is smaller than object"
+            low := middle + 1
+        ] ifFalse:[
+            high := middle - 1
+        ]
+    ].
+    ^ low
+
+    "#(1 2 3 4 7 99 1313 981989 898989898) asSortedCollection"
+
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SortedCollection.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,234 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+OrderedCollection subclass:#SortedCollection
+         instanceVariableNames:'sortBlock'
+         classVariableNames:'DefaultSortBlock'
+         poolDictionaries:''
+         category:'Collections-Ordered'
+!
+
+SortedCollection comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+I keep my elements sorted. The sort order is defined by a sortblock,
+which is a two-argument block which, when given two elements of the
+collection, returns true if the element given as first arg has to
+come before the element given as second arg.
+Thus a sortBlock of [:a :b | a < b] defines ascending sort-order,
+while [:a :b | a > b] defines descening order.
+
+%W% %E%
+'!
+
+!SortedCollection class methodsFor:'initialization'!
+
+initialize
+    DefaultSortBlock := [:a :b | a < b ]
+
+    "SortedCollection initialize"
+! !
+
+!SortedCollection class methodsFor:'instance creation'!
+
+new
+    ^ super new setSortBlock:DefaultSortBlock
+!
+
+new:size
+    ^ (super new:size) setSortBlock:DefaultSortBlock
+!
+
+sortBlock:aBlock
+    ^ super new setSortBlock:aBlock
+! !
+
+!SortedCollection methodsFor:'adding & removing'!
+
+addFirst:anObject
+    self shouldNotImplement
+!
+
+addLast:anObject
+    self shouldNotImplement
+!
+
+at:index put:anObject
+    self shouldNotImplement
+!
+
+add:newObject after:oldObject
+    self shouldNotImplement
+!
+
+add:newObject before:oldObject
+    self shouldNotImplement
+!
+
+addAll:aCollection
+    "add all elements of the argument, aCollection to the
+     receiver"
+
+    |mySize    "{ Class: SmallInteger }"
+     otherSize "{ Class: SmallInteger }"
+     dstIndex  "{ Class: SmallInteger }"
+     newSize newContents|
+
+    "if aCollection is bigger than a threshhold, its faster
+     to add all and resort - question: what is a good treshhold ?"
+
+    mySize := self size.
+    otherSize := aCollection size.
+    ((mySize == 0) or:[otherSize > 5]) ifTrue:[
+        newSize := mySize + otherSize.
+        newContents := Array new:newSize.
+        newContents replaceFrom:1 to:mySize with:contentsArray startingAt:1.
+        (aCollection isKindOf:SequenceableCollection) ifTrue:[
+            "maybe we can do it in one big move"
+            newContents replaceFrom:(mySize + 1) to:newSize with:aCollection startingAt:1.
+        ] ifFalse:[
+            dstIndex := mySize + 1.
+            aCollection do:[:element |
+                newContents at:dstIndex put:element.
+                dstIndex := dstIndex + 1
+            ]
+        ].
+        firstIndex := 1.
+        lastIndex := newSize.
+        contentsArray := newContents.
+        contentsArray sort:sortBlock.
+        ^ self
+    ].
+    super addAll:aCollection
+
+    "#(7 3 9 10 99) asSortedCollection addAll:#(77 0 1 16 5)"
+!
+
+add:anObject
+    "add the argument, anObject at the proper place in the
+     receiver"
+
+    |sz index|
+
+    sz := self size.
+    sz == 0 ifTrue:[
+        super addLast:anObject
+    ] ifFalse:[
+        index := self findIndexFor:anObject. 
+        self makeRoomAtIndex:index.
+        contentsArray at:index put:anObject
+    ].
+    ^ anObject
+! !
+
+!SortedCollection methodsFor:'testing'!
+
+includes:anObject
+    "return true, if the argument, anObject is in the collection.
+     (can reduce the number of compares since we are sorted)"
+
+    |index|
+
+    index := self findIndexFor:anObject.
+    ^ (index <= self size) and:[(contentsArray at:index) = anObject]
+!
+
+occurrencesOf:anObject
+    "return how many times the argument, anObject is in the collection.
+     (can reduce the number of compares since we are sorted)"
+
+    |index      "{ Class: SmallInteger }"
+     mySize     "{ Class: SmallInteger }"
+     tally      "{ Class: SmallInteger }" |
+
+    index := self findIndexFor:anObject.
+    mySize := self size.
+    index > mySize ifTrue:[^ 0].
+    tally := 0.
+    [(index <= mySize) and:[(contentsArray at:index) = anObject]] whileTrue:[
+        tally := tally + 1.
+        index := index + 1
+    ].
+    ^ tally
+! !
+
+!SortedCollection methodsFor:'instance protocol'!
+
+sortBlock
+    "return the block used for sorting"
+
+    ^ sortBlock
+!
+
+sortBlock:aSortBlock
+    "change the sort criteria for a sorted collection, resort the elements of 
+    the collection, and return the receiver. The argument, aSortBlock must
+    be a two-argument block which returns true if its arg1 has to come before
+    its arg2 in the collection."
+
+    sortBlock := aSortBlock.
+    lastIndex > firstIndex ifTrue:[
+        contentsArray quickSortFrom:firstIndex to:lastIndex sortBlock:aSortBlock
+    ]
+
+    "#(9 8 7 6 5 4 3) asSortedCollection"
+    "#(9 8 7 6 5 4 3) asSortedCollection sortBlock:[:a : b | a > b]"
+    "#($f $G $z $Y $o $H) asSortedCollectionSortedCollection"
+    "#($f $G $z $Y $o $H) asSortedCollection sortBlock:[:a : b | a asUppercase < b asUppercase]"
+! !
+
+!SortedCollection methodsFor:'enumerating'!
+
+collect: aBlock
+    | newOrderedCollection |
+    newOrderedCollection := OrderedCollection new.
+    self do: [ :element | newOrderedCollection add: (aBlock value: element) ].
+    ^newOrderedCollection
+! !
+
+!SortedCollection methodsFor:'private'!
+
+setSortBlock: aSortBlock
+    "set the sortblock without resorting - private only"
+
+    sortBlock := aSortBlock
+!
+
+findIndexFor:anObject
+    "search the index at which to insert anObject. Can also be used
+     to search for an existing element by checking if the element at
+     the returned index is the one we look for."
+
+    |low    "{ Class: SmallInteger}"
+     high   "{ Class: SmallInteger}"
+     middle "{ Class: SmallInteger}"
+     element|
+
+    low := firstIndex.
+    high := lastIndex.
+    [low <= high] whileTrue:[
+        middle := (low + high) // 2.
+        element := super at:middle.
+        (sortBlock value:element value:anObject) ifTrue:[
+            "middleelement is smaller than object"
+            low := middle + 1
+        ] ifFalse:[
+            high := middle - 1
+        ]
+    ].
+    ^ low
+
+    "#(1 2 3 4 7 99 1313 981989 898989898) asSortedCollection"
+
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Stream.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,180 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#Stream
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Streams'
+!
+
+Stream comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+%W% %E%
+'!
+
+!Stream class methodsFor:'instance creation'!
+
+new
+    "report an error - Streams are created using on:-messages"
+
+    ^ self error:'Streams cannot be created with new'
+! !
+
+!Stream methodsFor:'accessing'!
+
+contents
+    "return the contents of the stream
+     - we do not know here how to do it, it must be redefined in subclass"
+
+    ^ self subclassResponsibility
+! !
+
+!Stream methodsFor:'accessing-reading'!
+
+next
+    "return the next element of the stream
+     - we do not know here how to do it, it must be redefined in subclass"
+
+    ^ self subclassResponsibility
+!
+
+next:count
+    "return the next count elements of the stream as a Collection"
+
+    |array|
+
+    array := Array new:count.
+    1 to:count do: [:index |
+        array at:index put:(self next)
+    ].
+    ^ array
+!
+
+nextPeek
+    "advance to next element and return the peeked element"
+
+    self next.
+    ^ self peek
+!
+
+nextMatchFor:anObject
+    "read from the receiver, searching for the argument, anObject.
+     if the end is reached, return nil; 
+     otherwise return the argument, anObject"
+
+    |nextElement|
+
+    [self atEnd] whileFalse:[
+        nextElement := self next.
+        (nextElement == anObject) ifTrue: [ ^ nextElement ]
+    ].
+    ^ nil
+! !
+
+!Stream methodsFor:'accessing-writing'!
+
+nextPut:anObject
+    "put the argument, anObject onto the receiver
+     - we do not know here how to do it, it must be redefined in subclass"
+
+    ^ self subclassResponsibility
+!
+
+next:count put:anObject
+    "put the argument, anObject count times onto the receiver"
+
+    count timesRepeat:[self nextPut:anObject].
+    ^ anObject
+!
+
+nextPutAll:aCollection
+    "put all elements of the argument, aCollection onto the receiver"
+
+    aCollection do:[:element |
+        self nextPut:element
+    ].
+    ^ aCollection
+!
+
+nextPutAll:aCollection startingAt:first to:last
+    "put the elements with index from first to last
+     of the argument, aCollection onto the receiver"
+
+    aCollection from:first to:last do:[:element |
+        self nextPut:element
+    ].
+    ^ aCollection
+!
+
+show:aString
+    "put all elements of the argument, aString onto the receiver;
+     this makes streams somewhat compatible to TextCollectors and
+     allows you to say: Smalltalk at:#Transcript put:Stdout"
+
+    ^ self nextPutAll:aString printString
+!
+
+showCr:aString
+    "put all elements of the argument, aString onto the receiver;
+     and append a newline.
+     for compatibility with TextCollectors"
+
+    self show:aString.
+    self cr
+! !
+
+!Stream methodsFor:'closing'!
+
+close
+    "close the stream - nothing done here"
+
+    ^ self
+! !
+
+!Stream methodsFor:'testing'!
+
+atEnd
+    "return true if the end of the stream has been reached;
+     - we do not know here how to do it, it must be redefined in subclass"
+
+    ^ self subclassResponsibility
+! !
+
+!Stream methodsFor:'enumerating'!
+
+do:aBlock
+    "evaluate the argument, aBlock for every element up to the end of the
+     stream"
+
+    [self atEnd] whileFalse:[
+        aBlock value:(self next)
+    ]
+! !
+
+!Stream methodsFor:'queries'!
+
+isStream
+    "return true, if the receiver is some kind of Stream."
+
+    ^ true
+!
+
+lineLength
+    "this is just a layout hint for prettyprinting functions
+     - for compatibility with TextCollectors"
+
+    ^ 80
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/String.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,1761 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+ByteArray subclass:#String
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Text'
+!
+
+String comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+             All Rights Reserved
+
+%W% %E%
+'!
+
+%{
+#include <stdio.h>
+#include <ctype.h>
+%}
+
+!String class methodsFor:'documentation'!
+
+documentation
+"
+Strings are ByteArrays storing Characters.
+
+Strings are kind of kludgy: to allow for easy handling by c-functions,
+there is always one 0-byte added at the end, which is not counted
+in size. also, the at:put: method does not allow for storing 0-bytes.
+(to do this, the basicAt:put: and basicNew: methods are redefined)
+
+You cannot add any instvars to String, since the code (also in the run time
+system & compiler) knows that strings have no named instvars. If you need
+to, you have to create a subclass.
+"
+! !
+
+!String class methodsFor:'instance creation'!
+
+basicNew:anInteger
+    "return a new empty string with anInteger characters"
+
+%{  /* NOCONTEXT */
+
+    OBJ newString;
+    REGISTER int len;
+    REGISTER unsigned char *cp;
+    REGISTER OBJ *op;
+    int nInstVars, instsize;
+    extern OBJ new();
+
+    if (_isSmallInteger(anInteger)) {
+        len = _intVal(anInteger);
+        if (len >= 0) {
+            nInstVars = _intVal(_ClassInstPtr(self)->c_ninstvars);
+            instsize = OHDR_SIZE + (nInstVars * sizeof(OBJ)) + len + 1;
+            PROTECT(self);
+            _qNew(newString, instsize, SENDER);
+            UNPROTECT(self);
+            _InstPtr(newString)->o_class = self;
+
+            if (nInstVars) {
+#if defined(memset4)
+                memset4(_InstPtr(newString)->i_instvars, nil, nInstVars);
+#else
+# if defined(FAST_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
+                /*
+                 * knowing that nil is 0
+                 */
+                memset(_InstPtr(newString)->i_instvars, 0, instsize - OHDR_SIZE);
+# else
+                op = _InstPtr(newString)->i_instvars;
+                do {
+                    *op++ = nil;
+                } while (--nInstVars);
+# endif
+#endif
+                cp = _stringVal(newString) + (nInstVars * sizeof(OBJ));
+            } else
+                cp = _stringVal(newString);
+
+#ifdef FAST_MEMSET
+            memset(cp, ' ', len);
+            *(cp + len) = '\0';
+#else
+            while (len--)
+                *cp++ = ' ';
+            *cp = '\0';
+#endif
+            RETURN (newString);
+        }
+    }
+%}
+.
+    ^ (super basicNew:anInteger) atAllPut:(Character space)
+!
+
+new:anInteger
+    "same as basicNew - to avoid another send"
+
+    ^ self basicNew:anInteger
+!
+
+basicNew
+    "return a new empty string"
+
+    ^ self basicNew:0
+!
+
+new
+    "return a new empty string"
+
+    ^ self basicNew:0
+!
+
+unititializedNew:anInteger
+    "redefine it back - strings must have a 0-byte at the end"
+
+    ^ self basicNew:anInteger
+!
+
+fromString:aString
+    "return a copy of the argument, aString"
+
+    ^ aString copyFrom:1 to:(aString size)
+! !
+
+!String methodsFor:'accessing'!
+
+basicSize
+    "return the number of characters in myself"
+
+%{  /* NOCONTEXT */
+
+    if ((_qClass(self) == String) || (_qClass(self) == Symbol)) {
+        RETURN ( _MKSMALLINT(_stringSize(self)) );
+    }
+%}
+.
+    ^ super basicSize - 1
+!
+
+size
+    "return the number of characters in myself
+     - reimplemented here to avoid double send (size -> basicSize)"
+
+%{  /* NOCONTEXT */
+
+    if ((_qClass(self) == String) || (_qClass(self) == Symbol)) {
+        RETURN ( _MKSMALLINT(_stringSize(self)) );
+    }
+%}
+.
+    ^ super basicSize - 1
+!
+
+basicAt:index
+    "return the character at position index, an Integer
+     - reimplemented here since we return characters"
+
+%{  /* NOCONTEXT */
+
+    REGISTER int indx;
+    int len;
+
+    if (_isSmallInteger(index)) {
+        indx = _intVal(index);
+        if (_qClass(self) != String)
+            indx += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+        len = _stringSize(self);
+        if ((indx > 0) && (indx <= len)) {
+            RETURN ( _MKCHARACTER(_stringVal(self)[indx-1] & 0xFF) );
+        }
+    }
+%}
+.
+    self subscriptBoundsError:index
+!
+
+basicAt:index put:aCharacter
+    "store the argument, aCharacter at position index, an Integer
+     - reimplemented here since we store characters"
+
+%{  /* NOCONTEXT */
+
+    REGISTER int value, indx;
+    int len;
+
+    if (_isCharacter(aCharacter)) {
+        value = _intVal(_characterVal(aCharacter));
+        if (value && _isSmallInteger(index)) {
+            indx = _intVal(index);
+            if (_qClass(self) != String)
+                indx += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+            len = _stringSize(self);
+            if ((indx > 0) && (indx <= len)) {
+                _stringVal(self)[indx-1] = value;
+                RETURN ( aCharacter );
+            }
+        }
+    }
+%}
+.
+    (aCharacter isMemberOf:Character) ifFalse:[
+        self elementNotCharacter
+    ] ifTrue:[
+        (aCharacter asciiValue == 0) ifTrue:[
+            self error:'0-character not allowed in strings'
+        ] ifFalse:[
+            self subscriptBoundsError:index
+        ]
+    ]
+! !
+
+!String methodsFor:'converting'!
+
+asUppercase
+    "return a copy of myself in uppercase letters"
+
+    |newStr
+     mySize "{ Class: SmallInteger }" |
+
+    mySize := self size.
+    newStr := self species new:mySize.
+    1 to:mySize do:[:i |
+        newStr at:i put:(self at:i) asUppercase
+    ].
+    ^newStr
+!
+
+asLowercase
+    "return a copy of myself in lowercase letters"
+
+    |newStr
+     mySize "{ Class: SmallInteger }" |
+
+    mySize := self size.
+    newStr := self species new:mySize.
+    1 to:mySize do:[:i |
+        newStr at:i put:(self at:i) asLowercase
+    ].
+    ^newStr
+!
+
+asString
+    "return myself - I am a string"
+
+    ^ self
+!
+
+asSymbol
+    "return a unique symbol with name taken from myself.
+     The argument must be a String, subclass instances are not allowed."
+%{
+    if (_qClass(self) == String) {
+        RETURN ( _MKSYMBOL(_stringVal(self), (OBJ *)0, __context) );
+    }
+%}
+.
+    self primitiveFailed
+!
+
+asText
+    "return a Text-object (collection of lines) from myself"
+
+    ^ Text from:self
+!
+
+asNumber
+    "read a number from the receiver"
+
+    ^ Number readFromString:self
+
+    "'123' asNumber"
+    "'123.567' asNumber"
+    "'(5/6)' asNumber"
+!
+
+asFilename
+    "return a Filename with pathname taken from the receiver"
+
+    ^ Filename named:self
+! !
+
+!String methodsFor:'printing & storing'!
+
+printOn:aStream
+    "print the receiver on aStream"
+
+    aStream nextPutAll:self
+!
+
+printString
+    "return a string for printing - thats myself"
+
+    ^ self
+!
+
+print
+    "print the receiver on standard output - for debugging only"
+
+%{  /* NOCONTEXT */
+
+    if (_qClass(self) == String) {
+        printf("%s", _stringVal(self));
+        RETURN (self);
+    }
+%}
+.
+    super print
+!
+
+printfPrintString:formatString
+    "non-portable but sometimes useful.
+     return a printed representation of the receiver
+     as specified by formatString, which is defined by printf.
+     No checking on overrunning the buffer - the result must be shorter than 8k chars"
+
+%{  /* NOCONTEXT */
+
+    char buffer[8192];
+    char *cp;
+
+    if (_isString(formatString)) {
+#ifdef THIS_CONTEXT
+        /* mhmh - sprintf seems to destroy thisContext (if its in a register) */
+        OBJ sav = __thisContext;
+#endif
+        cp = (char *)_stringVal(self);
+        if (_qClass(self) != String)
+            cp += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+
+        sprintf(buffer, (char *)_stringVal(formatString), cp);
+#ifdef THIS_CONTEXT
+        __thisContext = sav;
+#endif
+        RETURN ( _MKSTRING(buffer COMMA_SND) );
+    }
+%}
+.
+    self primitiveFailed
+
+    "'hello' printfPrintString:'%%s -> %s'"
+    "'hello' printfPrintString:'%%10s -> %10s'"
+    "'hello' printfPrintString:'%%-10s -> %-10s'"
+!
+
+displayString
+    "return a string to display the receiver - use storeString to have
+     quotes around"
+
+    ^ self storeString
+!
+
+storeString
+    "return a String for storing myself"
+
+    |s|
+
+    (self includes:$') ifTrue:[
+        s := ''''.
+        self do:[:thisChar |
+            (thisChar == $') ifTrue:[s := s , ''''].
+            s := s copyWith:thisChar
+        ].
+        s := s , ''''.
+        ^ s
+    ].
+    ^ '''' asString , self , '''' asString
+!
+
+storeOn:aStream
+    "put the storeString of myself on aStream"
+
+    aStream nextPut: $'.
+    (self includes:$') ifTrue:[
+        self do:[:thisChar |
+            (thisChar == $') ifTrue:[aStream nextPut:thisChar].
+            aStream nextPut:thisChar
+        ]
+    ] ifFalse:[
+        aStream nextPutAll:self
+    ].
+    aStream nextPut:$'
+! !
+
+!String methodsFor:'comparing'!
+
+hash
+    "return an integer useful as a hash-key"
+
+%{  /* NOCONTEXT */
+
+    REGISTER int g, val;
+    REGISTER unsigned char *cp, *cp0;
+    int l;
+
+    cp = _stringVal(self);
+    l = _stringSize(self);
+    if (_qClass(self) != String) {
+        int n = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+
+        cp += n;
+        l -= n;
+    }
+
+    /*
+     * this is the dragon-book algorithm with a funny start
+     * value (to give short strings a number above 8192)
+     */
+    val = 12345;
+    for (cp0 = cp, cp += l - 1; cp >= cp0; cp--) {
+        val = (val << 5) + (*cp & 0x1F);
+        if (g = (val & 0x3E000000))
+            val ^= g >> 25 /* 23 */ /* 25 */;
+        val &= 0x3FFFFFFF;
+    }
+
+    if (l) {
+        l |= 1; 
+        val = (val * l) & 0x3FFFFFFF;
+    }
+
+    RETURN ( _MKSMALLINT(val) );
+%}
+!
+
+<= something
+    "Compare the receiver with the argument and return true if the
+     receiver is less than or equal to the argument. Otherwise return false."
+
+    ^ (self > something) not
+!
+
+< something
+    "Compare the receiver with the argument and return true if the
+     receiver is less than the argument. Otherwise return false."
+
+    ^ (something > self)
+!
+
+>= something
+    "Compare the receiver with the argument and return true if the
+     receiver is greater than or equal to the argument.
+     Otherwise return false."
+
+    ^ (something > self) not
+!
+
+> aString
+    "Compare the receiver with the argument and return true if the
+     receiver is greater than the argument. Otherwise return false."
+
+%{  /* NOCONTEXT */
+
+    int len1, len2, cmp;
+    REGISTER OBJ s = aString;
+    char *cp1, *cp2;
+
+    if (_isNonNilObject(s)
+     && ((_qClass(s) == String) || (_qClass(s) == Symbol) || (_qClass(s) == _qClass(self)))) {
+        cp1 = (char *) _stringVal(self);
+        len1 = _stringSize(self);
+        if (_qClass(self) != String) {
+            int n = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+
+            cp1 += n;
+            len1 -= n;
+        }
+
+        cp2 = (char *) _stringVal(s);
+        len2 = _stringSize(s);
+        if (_qClass(s) != String) {
+            int n = _intVal(_ClassInstPtr(_qClass(s))->c_ninstvars) * sizeof(OBJ);
+
+            cp2 += n;
+            len2 -= n;
+        }
+
+        if (len1 <= len2)
+            cmp = strncmp(cp1, cp2, len1);
+        else
+            cmp = strncmp(cp1, cp2, len2);
+
+        if (cmp > 0) {
+            RETURN ( true );
+        }
+        if ((cmp == 0) && (len1 > len2)) {
+            RETURN ( true );
+        }
+        RETURN ( false );
+    }
+%}
+.
+    ^ super > aString
+!
+
+= aString
+    "Compare the receiver with the argument and return true if the
+     receiver is equal to the argument. Otherwise return false."
+
+%{  /* NOCONTEXT */
+
+    int l1, l2;
+    REGISTER OBJ s = aString;
+    char *cp1, *cp2;
+
+    if (s == self) {
+        RETURN ( true );
+    }
+    if (! _isNonNilObject(s)) {
+        RETURN ( false );
+    }
+
+    if ((_qClass(s) == String) || (_qClass(s) == Symbol) || (_qClass(s) == _qClass(self))) {
+        cp1 = (char *) _stringVal(self);
+        l1 = _stringSize(self);
+        if (_qClass(self) != String) {
+            int n = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+
+            cp1 += n;
+            l1 -= n;
+        }
+
+        cp2 = (char *) _stringVal(s);
+        l2 = _stringSize(s);
+        if (_qClass(s) != String) {
+            int n = _intVal(_ClassInstPtr(_qClass(s))->c_ninstvars) * sizeof(OBJ);
+
+            cp2 += n;
+            l2 -= n;
+        }
+
+        if (l1 != l2) {
+            RETURN ( false );
+        }
+        RETURN ( (strncmp(cp1, cp2, l1) == 0) ? true : false );
+    }
+%}
+.
+    ^ super = aString
+!
+
+~= aString
+    "Compare the receiver with the argument and return true if the
+     receiver is not equal to the argument. Otherwise return false."
+
+%{  /* NOCONTEXT */
+
+    int l1, l2;
+    REGISTER OBJ s = aString;
+    char *cp1, *cp2;
+
+    if (s == self) {
+        RETURN ( false );
+    }
+    if (! _isNonNilObject(s)) {
+        RETURN ( true );
+    }
+    if ((_qClass(s) == String) || (_qClass(s) == Symbol) || (_qClass(s) == _qClass(self))) {
+        cp1 = (char *) _stringVal(self);
+        l1 = _stringSize(self);
+        if (_qClass(self) != String) {
+            int n = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+
+            cp1 += n;
+            l1 -= n;
+        }
+
+        cp2 = (char *) _stringVal(s);
+        l2 = _stringSize(s);
+        if (_qClass(s) != String) {
+            int n = _intVal(_ClassInstPtr(_qClass(s))->c_ninstvars) * sizeof(OBJ);
+
+            cp2 += n;
+            l2 -= n;
+        }
+
+        if (l1 != l2) {
+            RETURN ( true );
+        }
+        RETURN ( (strncmp(cp1, cp2, l1) == 0) ? false : true );
+    }
+%}
+.
+    ^ super ~= aString
+! !
+
+!String methodsFor:'testing'!
+
+occurrencesOf:aCharacter
+    "count the occurrences of the argument, aCharacter in myself
+      - reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+    REGISTER unsigned char *cp;
+    REGISTER int byteValue;
+    REGISTER int count;
+
+    if (_isCharacter(aCharacter)) {
+        count = 0;
+        byteValue = _intVal(_characterVal(aCharacter));
+        cp = _stringVal(self);
+        if (_qClass(self) != String)
+            cp += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+        while (*cp) {
+            if (*cp++ == byteValue) count++;
+        }
+        RETURN ( _MKSMALLINT(count) );
+    }
+%}
+.
+    ^ 0
+!
+
+includes:aCharacter
+    "return true if the argument, aCharacter is included in the receiver
+     - reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+    REGISTER unsigned char *cp;
+    REGISTER int byteValue;
+    extern char *strchr();
+
+    if (_isCharacter(aCharacter)) {
+        byteValue = _intVal(_characterVal(aCharacter));
+        cp = _stringVal(self);
+        if (_qClass(self) != String)
+            cp += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+#ifdef FAST_STRCHR
+        cp = (unsigned char *) strchr(cp, _intVal(_characterVal(aCharacter)));
+        if (cp) {
+            RETURN ( true );
+        }
+#else
+        while (*cp) {
+            if (*cp == byteValue) {
+                RETURN ( true );
+            }
+            cp++;
+        }
+#endif
+    }
+%}
+.
+    ^ false
+!
+
+indexOf:aCharacter
+    "return the index of the first occurrences of the argument, aCharacter
+     in the receiver or 0 if not found - reimplemented here for speed."
+
+%{  /* NOCONTEXT */
+
+    REGISTER unsigned char *cp;
+#ifdef FAST_STRCHR
+    char *strchr();
+#else
+    REGISTER int byteValue;
+    REGISTER int index;
+#endif
+
+    if (_isCharacter(aCharacter)) {
+        cp = _stringVal(self);
+        if (_qClass(self) != String)
+            cp += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+#ifdef FAST_STRCHR
+        cp = (unsigned char *) strchr(cp, _intVal(_characterVal(aCharacter)));
+        if (cp) {
+            RETURN ( _MKSMALLINT(cp - _stringVal(self) + 1) );
+        }
+#else
+        byteValue = _intVal(_characterVal(aCharacter));
+        index = 1;
+        while (*cp) {
+            if (*cp++ == byteValue) {
+                RETURN ( _MKSMALLINT(index) );
+            }
+            index++;
+        }
+#endif
+    }
+%}
+.
+    ^ 0
+!
+
+indexOf:aCharacter startingAt:start
+    "return the index of the first occurrence of the argument, aCharacter
+     in myself starting at start, anInteger or 0 if not found;
+     - reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+    REGISTER unsigned char *cp;
+    REGISTER int index, byteValue;
+#ifdef FAST_STRCHR
+    char *strchr();
+#endif
+    int len;
+
+    if (_isSmallInteger(start)) {
+        if (_isCharacter(aCharacter)) {
+            byteValue = _intVal(_characterVal(aCharacter));
+            index = _intVal(start);
+            if (index <= 0)
+                index = 1;
+            if (_qClass(self) != String)
+                index += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+            len = _stringSize(self);
+            if (index <= len) {
+                cp = _stringVal(self) + index - 1;
+#ifdef FAST_STRCHR
+                cp = (unsigned char *) strchr(cp, byteValue);
+                if (cp) {
+                    RETURN ( _MKSMALLINT(cp - _stringVal(self) + 1) );
+                }
+#else
+                while (*cp) {
+                    if (*cp++ == byteValue) {
+                        RETURN ( _MKSMALLINT(index) );
+                    }
+                    index++;
+                }
+#endif
+            }
+        }
+        RETURN ( _MKSMALLINT(0) );
+    }
+%}
+.
+    ^ super indexOf:aCharacter startingAt:start
+!
+
+indexOfSeparatorStartingAt:start
+    "return the index of the next separator character"
+
+%{  /* NOCONTEXT */
+
+    REGISTER unsigned char *cp;
+    REGISTER char c;
+    int len, index;
+
+    index = _intVal(start);
+    if (index <= 0) {
+        index = 1;
+    }
+    if (_qClass(self) != String)
+        index += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+    len = _stringSize(self);
+    if (index > len) {
+        RETURN ( _MKSMALLINT(0) );
+    }
+    cp = _stringVal(self) + index - 1;
+    while (c = *cp++) {
+#ifdef ASCII
+        if (c <= ' ')
+#endif
+        if ((c == ' ') || (c == '\t') || (c == '\n')
+         || (c == '\r') || (c == '\f')) {
+            RETURN ( _MKSMALLINT(cp - _stringVal(self)) );
+        }
+    }
+%}
+.
+    ^ 0
+!
+
+includesMatchCharacters
+    "return true if the receiver includes any meta characters (i.e. $* or $#) 
+     for match operations; false if not"
+
+    (self includes:$*) ifTrue:[^ true].
+    ^ (self includes:$#)
+!
+
+from:matchStart to:matchStop match:aString from:start to:stop
+    "helper for match; return true if the characters from start to stop in
+     aString are matching the receivers characters from matchStart to matchStop.
+     The receiver may contain meta-match characters $* (to match any string) 
+     or $# (to match any character)."
+
+    |matchChar mStart mStop sStart sStop mSize sSize index cont matchLast|
+
+    mStart := matchStart.
+    mStop := matchStop.
+    sStart := start.
+    sStop := stop.
+
+    [true] whileTrue:[
+        mSize := mStop - mStart + 1.
+        sSize := sStop - sStart + 1.
+
+        "empty strings match"
+        (mSize == 0) ifTrue:[^ (sSize == 0)].
+
+        matchChar := self at:mStart.
+
+        (matchChar == $#) ifTrue:[
+            "testString empty -> no match"
+            (sSize == 0) ifTrue:[^ false].
+            "# matches single character"
+            ((sSize == 1) and:[mSize == 1]) ifTrue:[^ true].
+            "cut off 1st chars and continue"
+            mStart := mStart + 1.
+            sStart := sStart + 1
+        ] ifFalse:[
+            (matchChar == $*) ifTrue:[
+                "testString empty -> we have a match"
+                (sSize == 0) ifTrue:[^ true].
+                "* matches anything"
+                (mSize == 1) ifTrue:[^ true].
+
+                "try to avoid some of the recursion by checking last
+                 character and continue with shortened strings if possible"
+                cont := false.
+                (mStop >= mStart) ifTrue:[
+                    matchLast := self at:mStop.
+                    (matchLast ~~ $*) ifTrue:[
+                        (matchLast == $#) ifTrue:[
+                            cont := true
+                        ] ifFalse:[
+                            (matchLast == (aString at:sStop)) ifTrue:[
+                                cont := true
+                            ]
+                        ]
+                    ]
+                ].
+                cont ifFalse:[
+                    index := sStart.
+                    [index <= sStop] whileTrue:[
+                        (self from:(mStart + 1) to:mStop match:aString 
+                              from:index to:sStop) ifTrue:[
+                            ^ true
+                        ].
+                        index := index + 1
+                    ].
+                    ^ false
+                ].
+                mStop := mStop - 1.
+                sStop := sStop - 1
+            ] ifFalse:[
+
+                "testString empty ?"
+                (sSize == 0) ifTrue:[^ false].
+
+                "first characters equal ?"
+                ((aString at:sStart) ~~ matchChar) ifTrue:[^ false].
+
+                "avoid recursion if possible"
+                ((sSize == mSize) and:[self = aString]) ifTrue:[^ true].
+
+                "cut off 1st chars and continue"
+                mStart := mStart + 1.
+                sStart := sStart + 1
+            ]
+        ]
+    ]
+!
+
+match:aString
+    "return true if aString matches self, where self may contain meta-match 
+     characters $* (to match any string) or $# (to match any character)."
+
+    ^ self from:1 to:(self size) match:aString from:1 to:(aString size)
+
+    " '*ute*' match:'computer' "
+    " '*uter' match:'computer' "
+    " 'uter*' match:'computer' "
+!
+
+startsWith:aString
+    "return true, if the receiver starts with something, aString"
+
+    (aString isKindOf:String) ifFalse: [
+        (aString isMemberOf:Character) ifTrue:[
+            self isEmpty ifTrue:[^ false].
+            ^ (self at:1) == aString
+        ].
+        ^ super startsWith:aString
+    ].
+%{
+    int len1, len2;
+    REGISTER unsigned char *src1, *src2;
+    REGISTER OBJ s = aString;
+
+    len1 = _qSize(self);
+    src1 = _stringVal(self);
+    if (_qClass(self) != String) {
+        int n = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+        len1 -= n;
+        src1 += n;
+    }
+    len2 = _qSize(s);
+    src2 = _stringVal(s);
+    if (_qClass(s) != String) {
+        int n = _intVal(_ClassInstPtr(_qClass(s))->c_ninstvars) * sizeof(OBJ);
+        len2 -= n;
+        src2 += n;
+    }
+    if (len1 < len2) {
+        RETURN ( false );
+    }
+    while (*src2)
+        if (*src2++ != *src1++) {
+            RETURN ( false );
+        }
+%}
+.
+    ^ true
+
+    "'hello world' startsWith:'hello'"
+    "'hello world' startsWith:'hi'"
+!
+
+endsWith:aString
+    "return true, if the receiver end with something, aString"
+
+    (aString isKindOf:String) ifFalse: [
+        (aString isMemberOf:Character) ifTrue:[
+            self isEmpty ifTrue:[^ false].
+            ^ (self at:(self size)) == aString
+        ].
+        ^ super endsWith:aString
+    ].
+%{
+    int len1, len2;
+    REGISTER unsigned char *src1, *src2;
+    REGISTER OBJ s = aString;
+
+    len1 = _qSize(self);
+    src1 = _stringVal(self);
+    if (_qClass(self) != String) {
+        int n = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+        len1 -= n;
+        src1 += n;
+    }
+    len2 = _qSize(s);
+    src2 = _stringVal(s);
+    if (_qClass(s) != String) {
+        int n = _intVal(_ClassInstPtr(_qClass(s))->c_ninstvars) * sizeof(OBJ);
+        len2 -= n;
+        src2 += n;
+    }
+    if (len1 < len2) {
+        RETURN ( false );
+    }
+    src1 = _stringVal(self) + len1 - len2;
+    src2 = _stringVal(aString);
+    while (*src2)
+        if (*src2++ != *src1++) {
+            RETURN ( false );
+        }
+%}
+.
+    ^ true
+
+    "'hello world' endsWith:'world'"
+    "'hello world' endsWith:'earth'"
+!
+
+isBlank
+    "return true, if the receiver contains spaces only"
+
+%{  /* NOCONTEXT */
+
+    REGISTER unsigned char *src;
+
+    src = _stringVal(self);
+    if (_qClass(self) != String)
+        src += _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+
+    while (*src)
+        if (*src++ != ' ') {
+            RETURN ( false );
+        }
+%}
+.
+    ^ true
+!
+
+countWords
+    "return the number of words, which are separated by separators"
+
+    |tally start stop mySize|
+
+    tally := 0.
+    start := 1.
+    mySize := self size.
+    [start <= mySize] whileTrue:[
+         (self at:start) isSeparator ifTrue:[
+             start := start + 1
+         ] ifFalse:[
+             stop := self indexOfSeparatorStartingAt:start.
+             (stop == 0) ifTrue:[
+                 stop := mySize + 1
+             ].
+             tally := tally + 1.
+             start := stop
+         ]
+    ].
+    ^ tally
+!
+
+asCollectionOfWords
+    "return a collection containing the words (separated by whitespace) of the receiver"
+
+    |words start stop mySize|
+
+    words := OrderedCollection new.
+    start := 1.
+    mySize := self size.
+    [start <= mySize] whileTrue:[
+        (self at:start) isSeparator ifTrue:[
+            start := start + 1
+        ] ifFalse:[
+            stop := self indexOfSeparatorStartingAt:start.
+            stop == 0 ifTrue:[
+                words add:(self copyFrom:start to:mySize).
+                ^ words
+            ].
+            words add:(self copyFrom:start to:(stop - 1)).
+            start := stop
+        ]
+    ].
+    ^ words
+!
+
+levenshteinTo:aString
+    "return the levenshtein distance to the argument, aString;
+     this value corrensponds to the number of replacements that have to be
+     made to get aString from the receiver.
+     see IEEE transactions on Computers 1976 Pg 172 ff."
+
+    ^ self levenshteinTo:aString s:4 c:1 i:2 d:6
+!
+
+levenshteinTo:aString s:substWeight c:caseWeight i:insrtWeight d:deleteWeight
+    "parametrized levenshtein. arguments are the costs for
+     substitution, case-change, insertion and deletion of a character."
+
+    |d  "delta matrix"
+     len1 len2 dim prevRow row col dimPlus1
+     min pp c1 c2|
+
+%{  /* NOCONTEXT */
+
+    /* 
+     * this is very heavy used when correcting errors 
+     * (all symbols are searched for best match) - therefore it must be fast
+     */
+{
+    unsigned short *data;
+    int l1, l2;
+    REGISTER int sz;
+    unsigned char *s1, *s2;
+    int v1, v2, v3, m;
+    REGISTER unsigned short *dp;
+    REGISTER int delta;
+    REGISTER int j;
+    int i;
+    int iW, cW, sW, dW;
+#   define FASTSIZE 30
+    short fastData[(FASTSIZE + 1) * (FASTSIZE + 1)];
+
+    if ((_isString(self) || _isSymbol(self))
+     && (_isString(aString) || _isSymbol(aString))
+     && _isSmallInteger(insrtWeight) && _isSmallInteger(caseWeight)
+     && _isSmallInteger(substWeight) && _isSmallInteger(deleteWeight)) {
+        iW = _intVal(insrtWeight);
+        cW = _intVal(caseWeight);
+        sW = _intVal(substWeight);
+        dW = _intVal(deleteWeight);
+        s1 = _stringVal(self);
+        s2 = _stringVal(aString);
+        l1 = strlen(s1);
+        l2 = strlen(s2);
+
+        sz = (l1 < l2) ? l2 : l1;
+        delta = sz + 1;
+        if (sz <= FASTSIZE) {
+            data = fastData;
+        } else {
+            /* add ifdef ALLOCA here ... */
+            data = (unsigned short *)malloc(delta * delta * sizeof(short));
+        }
+        
+        data[0] = 0;
+        dp = data+1;
+        for (j=1, dp=data+1; j<=sz; j++, dp++)
+            *dp = *(dp-1) + iW;
+        
+        for (i=1, dp=data+delta; i<=sz; i++, dp+=delta)
+            *dp = *(dp-delta) + dW;
+
+        for (i=1; i<=l1; i++) {
+            for (j=1; j<=l2; j++) {
+                dp = data + (i*delta) + j;
+                if (s1[i] != s2[j]) {
+                    if (tolower(s1[i]) == tolower(s2[j])) {
+                        m = cW;
+                    } else {
+                        m = sW;
+                    }
+                } else
+                    m = 0;
+
+                v2 = *(dp - 1) + iW;
+                v3 = *(dp - delta) + dW;
+                v1 = *(dp - delta - 1) + m;
+                if (v1 < v2)
+                    if (v1 < v3)
+                        m = v1;
+                    else
+                        m = v3;
+                else
+                    if (v2 < v3)
+                        m = v2;
+                    else
+                        m = v3;
+                *dp = m;
+            }
+        }
+        m = data[l1 * delta + l2];
+        if (sz > FASTSIZE) 
+            free(data);
+        RETURN ( _MKSMALLINT(m) );
+    }
+}
+%}
+.
+    len1 := self size.
+    len2 := aString size.
+
+    "create the help-matrix"
+
+    dim := len1 max:len2.
+    dimPlus1 := dim + 1.
+
+    d := Array new:dimPlus1.
+    1 to:dimPlus1 do:[:i |
+        d at:i put:(Array new:dimPlus1)
+    ].
+
+    "init help-matrix"
+
+    (d at:1) at:1 put:0.
+    row := d at:1.
+    1 to:dim do:[:j |
+        row at:(j + 1) put:( (row at:j) + insrtWeight )
+    ].
+
+    1 to:dim do:[:i |
+         (d at:(i + 1)) at:1 put:(  ((d at:i) at:1) + deleteWeight )
+    ].
+
+    1 to:len1 do:[:i |
+        c1 := self at:i.
+        1 to:len2 do:[:j |
+            c2 := aString at:j.
+            (c1 == c2) ifTrue:[
+                pp := 0
+            ] ifFalse:[
+                (c1 asLowercase == c2 asLowercase) ifTrue:[
+                    pp := caseWeight
+                ] ifFalse:[
+                    pp := substWeight
+                ]
+            ].
+            prevRow := d at:i.
+            row := d at:(i + 1).
+            col := j + 1.
+            min := (prevRow at:j) + pp.
+            min := min min:( (row at:j) + insrtWeight).
+            min := min min:( (prevRow at:col) + deleteWeight).
+            row at:col put: min
+        ]
+    ].
+
+    ^ (d at:(len1 + 1)) at:(len2 + 1)
+
+    "'ocmprt' levenshteinTo:'computer'
+     'computer' levenshteinTo:'computer'
+     'ocmputer' levenshteinTo:'computer'
+     'cmputer' levenshteinTo:'computer'
+     'Computer' levenshteinTo:'computer'"
+! !
+
+!String methodsFor:'copying'!
+
+shallowCopy
+    "return a copy of the receiver
+     - redefined for more speed"
+
+    ^ self copyFrom:1
+!
+
+deepCopy
+    "return a copy of the receiver
+     - redefined for speed"
+
+    ^ self copyFrom:1
+!
+
+, aString
+    "return the concatenation of myself and the argument, aString
+     - reimplemented here for speed"
+
+    |newString|
+%{
+    int l1, l2;
+    char *cp1, *cp2;
+    REGISTER unsigned char *dstp;
+    REGISTER OBJ s = aString;
+    OBJ new();
+
+    if ((_qClass(s) == String) || (_qClass(s) == Symbol) || (_qClass(s) == _qClass(self))) {
+        cp1 = (char *) _stringVal(self);
+        l1 = _stringSize(self);
+        if (_qClass(self) != String) {
+            int n = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+
+            cp1 += n;
+            l1 -= n;
+        }
+
+        cp2 = (char *) _stringVal(s);
+        l2 = _stringSize(s);
+        if (_qClass(s) != String) {
+            int n = _intVal(_ClassInstPtr(_qClass(s))->c_ninstvars) * sizeof(OBJ);
+
+            cp2 += n;
+            l2 -= n;
+        }
+
+        _qNew(newString, OHDR_SIZE + l1 + l2 + 1, __context);
+        _InstPtr(newString)->o_class = String;
+        dstp = _stringVal(newString);
+#ifdef FAST_MEMCPY
+        bcopy(cp1, dstp, l1);
+        bcopy(cp2, dstp + l1, l2+1);
+#else
+# ifdef FAST_STRCPY
+        strcpy(dstp, cp1);
+        strcpy(dstp + l1, cp2);
+# else
+        while ((*dstp++ = *cp1++) != '\0') ;
+        dstp--;
+        while ((*dstp++ = *cp2++) != '\0') ;
+# endif
+#endif
+        RETURN ( newString );
+    }
+%}
+.
+    ^ super , aString
+!
+
+concatenate:string1 and:string2
+    "return the concatenation of myself and the arguments, string1 and string2.
+     This is equivalent to self , string1 , string2
+     - generated by compiler when such a construct is detected"
+
+    |newString|
+%{
+    int len1, len2, len3;
+#if !defined(FAST_MEMCPY) && !defined(FAST_STRCPY)
+    REGISTER unsigned char *srcp;
+#endif
+    REGISTER unsigned char *dstp;
+    OBJ new();
+
+    if ((_isString(self) || _isSymbol(self))
+     && (_isString(string1) || _isSymbol(string1))
+     && (_isString(string2) || _isSymbol(string2))) {
+        len1 = _stringSize(self);
+        len2 = _stringSize(string1);
+        len3 = _stringSize(string2);
+        _qNew(newString, OHDR_SIZE + len1 + len2 + len3 + 1, __context);
+        _InstPtr(newString)->o_class = String;
+        dstp = _stringVal(newString);
+#ifdef FAST_MEMCPY
+        bcopy(_stringVal(self), dstp, len1);
+        bcopy(_stringVal(string1), dstp + len1, len2);
+        bcopy(_stringVal(string2), dstp + len1 + len2, len3+1);
+#else
+# ifdef FAST_STRCPY
+        strcpy(dstp, _stringVal(self));
+        strcpy(dstp + len1, _stringVal(string1));
+        strcpy(dstp + len1 + len2, _stringVal(string2));
+# else
+        srcp = _stringVal(self);
+        while ((*dstp++ = *srcp++) != '\0') ;
+        dstp--;
+        srcp = _stringVal(string1);
+        while ((*dstp++ = *srcp++) != '\0') ;
+        dstp--;
+        srcp = _stringVal(string2);
+        while ((*dstp++ = *srcp++) != '\0') ;
+# endif
+#endif
+        RETURN ( newString );
+    }
+%}
+.
+    ^ super , string1 , string2
+!
+
+concatenate:string1 and:string2 and:string3
+    "return the concatenation of myself and the string arguments.
+     This is equivalent to self , string1 , string2 , string3
+     - generated by compiler when such a construct is detected"
+
+    |newString|
+%{
+    int len1, len2, len3, len4;
+#if !defined(FAST_MEMCPY) && !defined(FAST_STRCPY)
+    REGISTER unsigned char *srcp;
+#endif
+    REGISTER unsigned char *dstp;
+    OBJ new();
+
+    if ((_isString(self) || _isSymbol(self))
+     && (_isString(string1) || _isSymbol(string1))
+     && (_isString(string2) || _isSymbol(string2))
+     && (_isString(string3) || _isSymbol(string3))) {
+        len1 = _stringSize(self);
+        len2 = _stringSize(string1);
+        len3 = _stringSize(string2);
+        len4 = _stringSize(string3);
+        _qNew(newString, OHDR_SIZE + len1 + len2 + len3 + len4 + 1, __context);
+        _InstPtr(newString)->o_class = String;
+        dstp = _stringVal(newString);
+#ifdef FAST_MEMCPY
+        bcopy(_stringVal(self), dstp, len1);
+        bcopy(_stringVal(string1), dstp + len1, len2);
+        bcopy(_stringVal(string2), dstp + len1 + len2, len3);
+        bcopy(_stringVal(string3), dstp + len1 + len2 + len3, len4+1);
+#else
+# ifdef FAST_STRCPY
+        strcpy(dstp, _stringVal(self));
+        strcpy(dstp + len1, _stringVal(string1));
+        strcpy(dstp + len1 + len2, _stringVal(string2));
+        strcpy(dstp + len1 + len2 + len3, _stringVal(string3));
+# else
+        srcp = _stringVal(self);
+        while ((*dstp++ = *srcp++) != '\0') ;
+        dstp--;
+        srcp = _stringVal(string1);
+        while ((*dstp++ = *srcp++) != '\0') ;
+        dstp--;
+        srcp = _stringVal(string2);
+        while ((*dstp++ = *srcp++) != '\0') ;
+        dstp--;
+        srcp = _stringVal(string3);
+        while ((*dstp++ = *srcp++) != '\0') ;
+# endif
+#endif
+        RETURN ( newString );
+    }
+%}
+.
+    ^ super , string1 , string2 , string3
+!
+
+copyWith:aCharacter
+    "return the concatenation of myself and the argument, aCharacter
+     - reimplemented here for speed"
+
+    |newString|
+
+    (aCharacter isMemberOf:Character) ifFalse:[
+        ^ super copyWith:aCharacter
+    ].
+%{
+    OBJ new();
+    int sz;
+    REGISTER unsigned char *dstp;
+    int offs;
+
+    sz = _qSize(self) + 1;
+    if (_qClass(self) != String) {
+        offs = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+        sz -= offs;
+    } else
+        offs = 0;
+
+    _qNew(newString, sz, __context);
+    _InstPtr(newString)->o_class = String;
+    dstp = _stringVal(newString);
+#ifdef FAST_MEMCPY
+    sz = sz - OHDR_SIZE - 1 - 1;
+    bcopy(_stringVal(self) + offs, dstp, sz);
+    dstp += sz;
+#else
+# ifdef FAST_STRCPY
+    strcpy(dstp, _stringVal(self) + offs);
+    dstp += sz - OHDR_SIZE - 1 - 1;
+# else
+    {
+        REGISTER unsigned char *srcp;
+
+        srcp = _stringVal(self) + offs;
+        while ((*dstp = *srcp++) != '\0')
+            dstp++;
+    }
+# endif
+#endif
+    *dstp++ = _intVal(_characterVal(aCharacter));
+    *dstp = '\0';
+%}
+.
+    ^ newString
+!
+
+copyFrom:start to:stop
+    "return the substring starting at index start, anInteger and ending
+     at stop, anInteger.
+     - reimplemented here for speed"
+
+    |newString|
+%{
+    OBJ new();
+#if !defined(FAST_MEMCPY)
+    REGISTER unsigned char *srcp;
+#endif
+    REGISTER unsigned char *dstp;
+    REGISTER int count;
+    int len, index1, index2;
+
+    if (_isSmallInteger(start) && _isSmallInteger(stop)) {
+        len = _stringSize(self);
+        index1 = _intVal(start);
+        index2 = _intVal(stop);
+
+        if ((index1 <= index2) && (index1 > 0)) {
+            if (_qClass(self) != String) {
+                int n = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+
+                index1 += n;
+                index2 += n;
+            }
+            if (index2 <= len) {
+                count = index2 - index1 + 1;
+                _qNew(newString, OHDR_SIZE+count+1, __context);
+                _InstPtr(newString)->o_class = String;
+                dstp = _stringVal(newString);
+#ifdef FAST_MEMCPY
+                bcopy(_stringVal(self) + index1 - 1, dstp, count);
+                dstp[count] = '\0';
+#else
+                srcp = _stringVal(self) + index1 - 1;
+                while (count--) {
+                    *dstp++ = *srcp++;
+                }
+                *dstp = '\0';
+#endif
+                RETURN ( newString );
+            }
+        }
+    }
+%}
+.
+    ^ super copyFrom:start to:stop
+!
+
+copyFrom:start
+    "return the substring from start, anInteger to the end
+     - reimplemented here for speed"
+
+    |newString|
+%{
+    OBJ new();
+#if !defined(FAST_MEMCPY)
+    REGISTER unsigned char *srcp;
+#endif
+    REGISTER unsigned char *dstp;
+    REGISTER int count;
+    int len, index1;
+
+    if (_isSmallInteger(start)) {
+        len = _stringSize(self);
+        index1 = _intVal(start);
+        if (index1 > 0) {
+            if (_qClass(self) != String) {
+                int n = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+
+                index1 += n;
+            }
+            if (index1 <= len) {
+                count = len - index1 + 1;
+                _qNew(newString, OHDR_SIZE+count+1, __context);
+                _InstPtr(newString)->o_class = String;
+                dstp = _stringVal(newString);
+#ifdef FAST_MEMCPY
+                bcopy(_stringVal(self) + index1 - 1, dstp, count);
+                dstp[count] = '\0';
+#else
+                srcp = _stringVal(self) + index1 - 1;
+                while (count--) {
+                    *dstp++ = *srcp++;
+                }
+                *dstp = '\0';
+#endif
+                RETURN ( newString );
+            }
+        }
+    }
+%}
+.
+    ^ super copyFrom:start
+! !
+
+!String methodsFor:'filling and replacing'!
+
+replaceFrom:start to:stop with:aString startingAt:repStart
+    "replace the characters starting at index start, anInteger and ending
+     at stop, anInteger with characters from aString starting at repStart.
+
+     - reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+    REGISTER unsigned char *srcp, *dstp;
+    REGISTER int count;
+    int len, index1, index2;
+    int repLen, repIndex;
+
+    if ((_isString(aString) || _isSymbol(aString))
+     && _isString(self)
+     && _isSmallInteger(start)
+     && _isSmallInteger(stop)) {
+        len = _stringSize(self);
+        index1 = _intVal(start);
+        index2 = _intVal(stop);
+        count = index2 - index1 + 1;
+        if (count <= 0) {
+             RETURN (self);
+        }
+        if ((index2 <= len) && (index1 > 0)) {
+            repLen = _stringSize(aString);
+            repIndex = _intVal(repStart);
+            if ((repIndex > 0) && ((repIndex + count - 1) <= repLen)) {
+                srcp = _stringVal(aString) + repIndex - 1;
+                dstp = _stringVal(self) + index1 - 1;
+                if (aString == self) {
+                    /* take care of overlapping copy */
+                    if (srcp < dstp) {
+                        /* must do a reverse copy */
+                        srcp += count;
+                        dstp += count;
+                        while (count-- > 0) {
+                            *--dstp = *--srcp;
+                        }
+                        RETURN (self);
+                    }
+                }
+#ifdef FAST_MEMCPY
+                bcopy(srcp, dstp, count);
+#else
+                while (count-- > 0) {
+                    *dstp++ = *srcp++;
+                }
+#endif
+                RETURN (self);
+            }
+        }
+    }
+%}
+.
+    ^ super replaceFrom:start to:stop with:aString startingAt:repStart
+!
+
+replaceAll:oldCharacter by:newCharacter
+    "replace all oldCharacters by newCharacter in the receiver"
+
+%{  /* NOCONTEXT */
+
+    REGISTER unsigned char *srcp;
+    REGISTER unsigned oldVal, newVal;
+
+    if (_isCharacter(oldCharacter)
+     && _isCharacter(newCharacter)
+     && _isString(self)) {
+        srcp = (unsigned char *)_stringVal(self);
+        oldVal = _intVal(_characterVal(oldCharacter));
+        newVal = _intVal(_characterVal(newCharacter));
+        while (*srcp) {
+            if (*srcp == oldVal)
+                *srcp = newVal;
+            srcp++;
+        }
+        RETURN ( self );
+    }
+%}
+.
+    ^ super replaceAll:oldCharacter by:newCharacter
+!
+
+reverse                                                                         
+    "in-place reverse the characters of the string"
+
+%{  /* NOCONTEXT */
+
+    REGISTER char c;
+    REGISTER unsigned char *hip, *lowp;
+
+    if (_isString(self)) {
+        lowp = _stringVal(self);
+        hip = lowp + _stringSize(self) - 1;
+        while (lowp < hip) {
+            c = *lowp;
+            *lowp = *hip;
+            *hip = c;
+            lowp++;
+            hip--;
+        }
+        RETURN ( self );
+    }
+%}
+.
+    ^ super reverse
+!
+
+withCRs
+    "return a copy of the receiver, where
+     all \-characters are replaced by newline characters
+     - reimplemented here for speed"
+
+    |newString|
+%{
+    OBJ new();
+    REGISTER char c;
+    REGISTER unsigned char *srcp, *dstp;
+    int len, offs;
+
+    len = _qSize(self);
+    if (_qClass(self) != String) {
+        offs = _intVal(_ClassInstPtr(_qClass(self))->c_ninstvars) * sizeof(OBJ);
+        len -= offs;
+    } else
+        offs = 0;
+
+    _qNew(newString, len, __context);
+    _InstPtr(newString)->o_class = String;
+    srcp = _stringVal(self) + offs;
+    dstp = _stringVal(newString);
+    while (c = *srcp++)
+        if (c == '\\')
+            *dstp++ = '\n';
+        else
+            *dstp++ = c;
+    *dstp++ = '\0';
+    RETURN ( newString );
+%}
+!
+
+atAllPut:aCharacter
+    "replace all characters with aCharacter
+     - reimplemented here for speed"
+
+%{  /* NOCONTEXT */
+
+    REGISTER int  byteValue;
+#ifndef FAST_MEMSET
+    REGISTER unsigned char *dst;
+#endif
+
+    if (_isCharacter(aCharacter) && _isString(self)) {
+        byteValue = _intVal(_characterVal(aCharacter));
+#ifdef FAST_MEMSET
+        memset(_stringVal(self), byteValue, _qSize(self) - OHDR_SIZE - 1);
+#else
+        dst = _stringVal(self);
+        while (*dst != '\0')
+            *dst++ = byteValue;
+#endif
+        RETURN ( self );
+    }
+%}
+.
+    ^ super atAllPut:aCharacter
+!
+
+withoutSpaces
+    "return a copy of myself without leading and trailing spaces"
+
+    |startIndex endIndex blank|
+
+    startIndex := 1.
+    endIndex := self size.
+    blank := Character space.
+    [(startIndex < endIndex) and:[(self at:startIndex) == blank]] whileTrue:[
+        startIndex := startIndex + 1
+    ].
+    [(endIndex > 1) and:[(self at:endIndex) == blank]] whileTrue:[
+        endIndex := endIndex - 1
+    ].
+    startIndex > endIndex ifTrue:[
+        ^ ''
+    ].
+    ((startIndex == 1) and:[endIndex == self size]) ifTrue:[
+        ^ self
+    ].
+    ^ self copyFrom:startIndex to:endIndex
+!
+
+withoutSeparators
+    "return a copy of myself without leading and trailing whitespace"
+
+    |startIndex endIndex|
+
+    startIndex := 1.
+    endIndex := self size.
+    [(startIndex < endIndex) and:[(self at:startIndex) isSeparator]] whileTrue:[
+        startIndex := startIndex + 1
+    ].
+    [(endIndex > 1) and:[(self at:endIndex) isSeparator]] whileTrue:[
+        endIndex := endIndex - 1
+    ].
+    startIndex > endIndex ifTrue:[
+        ^ ''
+    ].
+    ((startIndex == 1) and:[endIndex == self size]) ifTrue:[
+        ^ self
+    ].
+    ^ self copyFrom:startIndex to:endIndex
+! !
+
+!String methodsFor:'queries'!
+
+encoding
+    "assume iso8859 encoding"
+
+    ^ #iso8859
+!
+
+knownAsSymbol
+    "return true, if there is a symbol with same characters in the
+     system - use to check for existance of a symbol without creating one"
+
+%{  /* NOCONTEXT */
+    extern OBJ _KNOWNASSYMBOL();
+
+    RETURN ( _KNOWNASSYMBOL(_stringVal(self)) );
+%}
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StringCollection.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,162 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+VariableArray subclass:#Text
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Text'
+!
+
+Text comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+text is an array of lines which are strings.
+this is just temporary - may change in the future.
+
+%W% %E%
+'!
+
+!Text class methodsFor:'instance creation'!
+
+from:aString
+    "return a new text object with lines taken from the argument, aString"
+
+    ^ (self new:1) from:aString
+!
+
+fromArray:anArray
+    "return a new text object with lines taken from the argument, an array
+     of strings"
+
+    |newText
+     size "{ Class: SmallInteger }" |
+
+    size := anArray size.
+    newText := self new:size.
+    1 to:size do:[:line |
+        newText at:line put:(anArray at:line)
+    ].
+    ^ newText
+! !
+
+!Text methodsFor:'growing'!
+
+grow:newSize
+    "grow to newsize - new elements are initialized with empty strings -
+     not nil"
+
+    |oldSize "{ Class:SmallInteger }"|
+
+    oldSize := tally.
+    super grow:newSize.
+    (oldSize < newSize) ifTrue:[
+        contentsArray from:(oldSize + 1) to:newSize put:''
+    ]
+!
+
+add:aString
+    "append the argument, aString to myself -
+     we increase physical size by 50 to avoid lots of copying around"
+
+    |oldSize "{ Class:SmallInteger }"|
+
+    oldSize := tally.
+    super grow:(oldSize + 50).
+    tally := oldSize + 1.
+    contentsArray at:tally put:aString
+! !
+
+!Text methodsFor:'converting'!
+
+asString
+    "return myself as a string with embedded cr's"
+
+    |totalLength "{ Class:SmallInteger }"
+     pos         "{ Class:SmallInteger }"
+     newString |
+
+    totalLength := 0.
+    self do:[:lineString |
+        lineString isNil ifTrue:[
+            totalLength := totalLength + 1
+        ] ifFalse: [
+            totalLength := totalLength + lineString size + 1
+        ].
+        0
+    ].
+    newString := String new:totalLength.
+    pos := 1.
+    self do:[:lineString |
+        lineString isNil ifFalse:[
+            newString replaceFrom:pos with:lineString.
+            pos := pos + (lineString size)
+        ].
+        newString at:pos put:(Character cr).
+        pos := pos + 1
+    ].
+    ^ newString
+!
+
+from:aString
+    "setup my contents from the argument, aString"
+
+    |numberOfLines "{ Class:SmallInteger }"
+     start         "{ Class:SmallInteger }"
+     stop          "{ Class:SmallInteger }" |
+
+    numberOfLines := aString occurrencesOf:(Character cr).
+    numberOfLines := numberOfLines + 1.
+    self grow:numberOfLines.
+    start := 1.
+    1 to:numberOfLines do:[:lineNr |
+        stop := aString indexOf:(Character cr)
+                     startingAt:start
+                       ifAbsent:[aString size + 1].
+        stop := stop - 1.
+        (stop < start) ifTrue: [
+            self at:lineNr put:(String new:0)
+        ] ifFalse: [
+            self at:lineNr put:(aString copyFrom:start to:stop)
+        ].
+        start := stop + 2
+    ]
+!
+
+asText
+    ^ self
+! !
+
+!Text methodsFor:'printing'!
+
+printString
+    ^ self asString
+! !
+
+!Text methodsFor:'searching'!
+
+indexOfLineStartingWith:aString
+    "return the index of the first line starting with the argument, aString"
+
+    |index "{ Class:SmallInteger }" |
+
+    index := 1.
+    [index <= self size] whileTrue:[
+        ((self at:index) startsWith:aString) ifTrue:[
+            ^ index
+        ].
+        index := index + 1
+    ].
+    ^ 0
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Symbol.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,159 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+String subclass:#Symbol
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Text'
+!
+
+Symbol comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+Symbols represent unique strings - every symbol with same printString
+exists exactly once in the system; Symbols are used for selectors, global
+variable-keys etc.
+
+%W% %E%
+'!
+
+!Symbol class methodsFor:'instance creation'!
+
+basicNew:size
+    "catch instance creation - symbols are not created with new"
+
+    self error:'symbols may not be created with new:'
+! 
+
+intern:aString
+    "return a unique symbol with printname taken from the String-argument"
+
+%{
+    if (_isString(aString)) {
+        RETURN ( _MKSYMBOL(_stringVal(aString), (OBJ *)0, __context) );
+    }
+%}
+.
+    ^ self mustBeString
+!
+
+internCharacter:aCharacter
+    "return a unique symbol with printname taken from the Character-argument"
+
+    ^ self intern:(aCharacter asString)
+! !
+
+!Symbol class methodsFor:'queries'!
+
+hasInterned:aString ifTrue:trueBlock
+    "for ST-80 compatibility - if the argument, aString is known
+     as Symbol, evaluate the block with the corresponding symbol
+     as argument and return true; otherwise return false"
+
+    aString knownAsSymbol ifTrue:[
+        trueBlock value:(aString asSymbol).
+        ^ true
+    ].
+    ^ false
+! !
+
+!Symbol methodsFor:'accessing'!
+
+basicAt:index put:something
+    "report an error - symbols may not be changed"
+
+    self error:'symbols may not be changed'
+!
+
+at:index put:something
+    "report an error - symbols may not be changed"
+
+    self error:'symbols may not be changed'
+! !
+
+!Symbol methodsFor:'copying'!
+
+deepCopy
+    "return a copy of myself
+     - reimplemented here since symbols are unique"
+
+     ^ self
+! !
+
+!Symbol methodsFor:'converting'!
+
+asString
+    "return a string with printname taken from mine"
+
+    ^ self printString
+!
+
+asSymbol
+    "I am a symbol - just return myself"
+
+    ^ self
+! !
+
+!Symbol methodsFor:'misc'!
+
+species
+    ^ String
+!
+
+nArgsIfSelector
+    "if symbol is used as a selector, how many arguments would it take"
+
+    |binopChars|
+
+    (self size > 2) ifFalse:[
+        binopChars := '|&-+=*/\<>~@,'.
+        (self size == 1) ifTrue:[
+            ((binopChars occurrencesOf:(self at:1)) == 0) ifTrue:[^ 0].
+            ^ 1
+        ].
+        ((binopChars occurrencesOf:(self at:1)) == 0) ifFalse:[
+            ((binopChars occurrencesOf:(self at:2)) == 0) ifFalse:[^ 1]
+        ]
+    ].
+    ^ self occurrencesOf:$:
+! !
+
+!Symbol methodsFor:'printing & storing'!
+
+printString
+%{
+    RETURN ( _MKSTRING(_stringVal(self) COMMA_CON) );
+%}
+!
+
+printOn:aStream
+    "aStream nextPut:$#."
+    aStream nextPutAll:(self printString)
+!
+ 
+displayString
+    ^ self storeString
+!
+
+storeString
+    "return a String for storing myself"
+
+    ^ $# asString , self
+!
+
+storeOn:aStream
+    aStream nextPut:$#.
+    aStream nextPutAll:(self printString)
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Time.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,219 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Magnitude subclass:#Time
+       instanceVariableNames:'secondsLow secondsHi'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Magnitude-General'
+!
+
+Time comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+time represents a particular second in a day; since we depend on
+unix, the seconds are counted from 1. Jan 1970 NOT as in Smalltalk-80
+from 1. Jan 1901; since unix-time is 32 bit which does not fit into
+a SmallInteger, we keep low and hi 16bit of the time separately.
+
+%W% %E%
+'!
+
+!Time class methodsFor:'instance creation'!
+
+secondClock
+    "return seconds of now - for GNU-ST compatibility"
+
+    ^ OperatingSystem getTime
+!
+
+fromUnixTimeLow:low and:hi
+    ^ self basicNew setSecondsLow:low and:hi
+!
+
+now
+    "return an instance of Time representing this moment"
+
+    ^ self basicNew setSecondsLow:(OperatingSystem getTimeLow)
+                              and:(OperatingSystem getTimeHi)
+!
+
+millisecondClockValue
+    "return the millisecond clock - since this one overruns
+     regularly, use only for short timing deltas"
+
+    ^ OperatingSystem getMillisecondTime.
+! !
+
+!Time class methodsFor:'timing evaluations'!
+
+secondsToRun:aBlock
+    "evaluate the argument, aBlock; return the number of seconds it took"
+
+    |startTime endTime|
+
+    startTime := self now.
+    aBlock value.
+    endTime := self now.
+    ^ endTime - startTime
+!
+
+millisecondsToRun:aBlock
+    "evaluate the argument, aBlock; return the number of milliseconds it took"
+
+    |startTime endTime|
+
+    startTime := OperatingSystem getMillisecondTime.
+    aBlock value.
+    endTime := OperatingSystem getMillisecondTime.
+    ^ endTime - startTime
+! !
+
+!Time class methodsFor:'ST-80 compatibility'!
+
+totalSeconds
+    ^ self secondClock
+! !
+
+!Time methodsFor:'accessing'!
+
+hourInDay
+    "return the hour-part"
+
+    |hr|
+
+    OperatingSystem computeTimePartsOf:secondsLow and:secondsHi for:[
+        :hours :minutes :secs |
+
+        hr := hours
+    ].
+    ^ hr
+!
+
+minuteInDay
+    "return the minute-part"
+
+    |m|
+
+    OperatingSystem computeTimePartsOf:secondsLow and:secondsHi for:[
+        :hours :minutes :secs |
+
+        m := minutes
+    ].
+    ^ m
+!
+
+secondInDay
+    "return the second-part"
+
+    |s|
+
+    OperatingSystem computeTimePartsOf:secondsLow and:secondsHi for:[
+        :hours :minutes :secs |
+
+        s := secs
+    ].
+    ^ s
+! !
+
+!Time methodsFor:'comparing'!
+
+> aTime
+    (aTime isMemberOf:Time) ifTrue:[
+        (secondsHi > aTime secondsHi) ifTrue:[^ true].
+        (secondsHi < aTime secondsHi) ifTrue:[^ false].
+        (secondsLow > aTime secondsLow) ifTrue:[^ true].
+        ^ false
+    ].
+    ^ self getSeconds > aTime getSeconds
+!
+
+< aTime
+    (aTime isMemberOf:Time) ifTrue:[
+        (secondsHi < aTime secondsHi) ifTrue:[^ true].
+        (secondsHi > aTime secondsHi) ifTrue:[^ false].
+        (secondsLow < aTime secondsLow) ifTrue:[^ true].
+        ^ false
+    ].
+    ^ self getSeconds < aTime getSeconds
+!
+
+= aTime
+    (aTime isMemberOf:Time) ifTrue:[
+        ^ ((secondsLow == aTime secondsLow) and:[secondsHi == aTime secondsHi])
+    ].
+    ^ self getSeconds = aTime getSeconds
+! !
+
+!Time methodsFor:'arithmetic'!
+
+- aTime
+    "return delta in seconds between 2 times"
+
+    ^ self getSeconds - (aTime getSeconds)
+!
+
+addTime:timeAmount
+    "return a new Time timeAmount seconds from myself"
+
+    ^Time new setSeconds:(self getSeconds + timeAmount)
+!
+
+subtractTime:timeAmount
+    "return a new Time timeAmount seconds before myself"
+
+    ^Time new setSeconds:(self getSeconds - timeAmount)
+! !
+
+!Time methodsFor:'printing'!
+
+printString
+    |aString|
+
+    OperatingSystem computeTimePartsOf:secondsLow and:secondsHi for:[
+        :hours :minutes :secs |
+
+        aString := hours printString , ':' .
+        (minutes < 10) ifTrue:[aString := aString , '0'].
+        aString := aString , minutes printString.
+        aString := aString , ':'.
+        (secs < 10) ifTrue:[aString := aString , '0'].
+        aString := aString , secs printString
+    ].
+    ^ aString
+! !
+
+!Time methodsFor:'private'!
+
+secondsLow
+    ^ secondsLow
+!
+
+secondsHi
+    ^ secondsHi
+!
+
+getSeconds
+    ^ (secondsHi * 16r10000) + secondsLow
+!
+
+setSeconds:secs
+    secondsHi := secs // 16r10000.
+    secondsLow := secs \\ 16r10000
+!
+
+setSecondsLow:secsLow and:secsHi
+    secondsHi := secsHi.
+    secondsLow := secsLow
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/True.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,120 @@
+"
+ COPYRIGHT (c) 1988-92 by Claus Gittinger
+	      All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Boolean subclass:#True
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Kernel-Objects'
+!
+
+True comment:'
+
+COPYRIGHT (c) 1988-92 by Claus Gittinger
+	      All Rights Reserved
+
+Class True has only one instance, true, representing logical truth.
+
+%W% %E%
+'!
+
+!True methodsFor: 'logical operations'!
+
+& aBoolean
+   "return true if both the receiver and the argument are true
+    (since the receiver is true, return the argument, aBoolen)"
+
+    ^ aBoolean
+!
+
+| aBoolean
+   "return true if either the receiver or the argument are true
+    (since the receiver is true, return true)"
+
+    ^ true
+!
+
+not
+   "return true if the receiver is false, false otherwise
+    (since the receiver is true, return false)"
+
+    ^ false
+!
+
+eqv:aBoolean
+   "return true if both the receiver and the argument are the same truth value
+    (since the receiver is true, return true if the argument is also)"
+
+    ^ aBoolean
+!
+
+xor:aBoolean
+   "return true if the receiver and the argument are different truth values
+    (since the receiver is true, return true, if the argument is not)"
+
+    ^ aBoolean not
+! !
+
+!True methodsFor: 'conditional evaluation'!
+
+and:aBlock
+    "evaluate aBlock if the receiver is true.
+     since the receiver is known to be true,
+     return the value of evaluating the block.
+     - open coded by compiler"
+
+    ^ aBlock value
+!
+
+or:aBlock
+    "evaluate aBlock if the receiver is false.
+     since the receiver is known to be true simply return true.
+     - open coded by compiler"
+
+    ^ self
+!
+
+ifTrue:aBlock
+    "return the value of evaluating aBlock (since the receiver is true)
+     - open coded by compiler"
+
+    ^ aBlock value
+!
+
+ifFalse:aBlock
+    "return the true alternative, nil (since the receiver is true)
+     - open coded by compiler"
+
+    ^ nil
+!
+
+ifTrue:trueBlock ifFalse:falseBlock
+    "return the value of evaluating trueBlock (since the receiver is true)
+     - open coded by compiler"
+
+    ^ trueBlock value
+!
+
+ifFalse:falseBlock ifTrue:trueBlock
+    "return the value of evaluating trueBlock (since the receiver is true)
+     - open coded by compiler"
+
+    ^ trueBlock value
+! !
+
+!True methodsFor: 'printing'!
+
+printString
+    "return a Character sequence representing the receiver"
+
+    ^ 'true'
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TwoByteString.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,61 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+WordArray subclass:#TwoByteString
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Collections-Text'
+!
+
+TwoByteString comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+             All Rights Reserved
+
+TwoByteStrings are like strings, but storing 16bits per character.
+The integration of them into the system is not completed ....
+
+%W% %E%
+'!
+
+!TwoByteString class methodsFor:'instance creation'!
+
+basicNew:anInteger
+    "return a new empty string with anInteger characters"
+
+    ^ super basicNew:anInteger atAllPut:(Character space)
+! !
+
+!TwoByteString methodsFor:'accessing'!
+
+basicAt:index
+    "return the character at position index, an Integer
+     - reimplemented here since we return characters"
+
+    ^ Character value:(super basicAt:index)
+!
+
+basicAt:index put:aCharacter
+    "store the argument, aCharacter at position index, an Integer
+     - reimplemented here since we store characters"
+
+    super basicAt:index put:(aCharacter asciiValue)
+! !
+
+!TwoByteString methodsFor:'converting'!
+
+asString
+    "return myself - I am a string"
+
+    ^ self
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UIBytes.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,63 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+ByteArray subclass:#UninterpretedBytes
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'System-Support'
+!
+
+UninterpretedBytes comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+this class has been added for ST-80 compatibility.
+
+%W% %E%
+'!
+
+!UninterpretedBytes class methodsFor:'queries'!
+
+isBigEndian
+    "return true, if words/shorts store the most-significant
+     byte last. I.e. true if LSB-first (vax, intel), 
+     false if MSB-first (m68k, sun)."
+
+%{  /* NOCONTEXT */
+
+    /*
+     * I dont like ifdefs - you always forget some ...
+     * therefore we look into a structure at run-time
+     */
+    union {
+        unsigned int   u_l;
+        char           u_c[sizeof(int)];
+    } u;
+
+    u.u_l = 0x87654321;
+    if (u.u_c[0] == 0x21) RETURN (true);
+    RETURN (false );
+%}
+    "UninterpretedBytes isBigEndian"
+! !
+
+!UninterpretedBytes methodsFor:'accessing'!
+
+byteAt:index
+    ^ self basicAt:index
+!
+
+byteAt:index put:value
+    ^ self basicAt:index put:value
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UndefObj.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,142 @@
+"
+ COPYRIGHT (c) 1988-92 by Claus Gittinger
+	      All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#UndefinedObject
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Kernel-Objects'
+!
+
+UndefinedObject comment:'
+
+COPYRIGHT (c) 1988-92 by Claus Gittinger
+	      All Rights Reserved
+
+there is only one instance of this class: nil
+
+%W% %E%
+'!
+
+!UndefinedObject class methodsFor:'instance creation'!
+
+basicNew
+    "catch new - there MUST be only one nil in the system"
+
+    ^ nil
+!
+
+basicNew:size
+    "catch new - there MUST be only one nil in the system"
+
+    ^ nil
+! !
+
+!UndefinedObject methodsFor:'error catching'!
+
+basicAt:index
+    "catch array access - its illegal
+     defined here since basicAt: in Object ommits the nil-check"
+
+    ^ self notIndexed
+!
+
+at:index
+    "catch array access - its illegal
+     defined here since at: in Object ommits the nil-check"
+
+    ^ self notIndexed
+!
+
+basicAt:index put:anObject
+    "catch array access - its illegal
+     defined here since basicAt:put: in Object ommits the nil-check"
+
+    ^ self notIndexed
+!
+
+at:index put:anObject
+    "catch array access - its illegal
+     defined here since at:put: in Object ommits the nil-check"
+
+    ^ self notIndexed
+! !
+
+!UndefinedObject methodsFor:'testing'!
+
+isNil
+    "return true if I am nil - since I am, return true"
+
+    ^ true
+!
+
+notNil
+    "return true if I am not nil - since I am nil, return false"
+
+    ^ false
+!
+
+size
+    "return the number of indexed instvars
+     defined here since size in Object ommits the nil-check"
+ 
+    ^ 0
+!
+
+basicSize
+    "return the number of indexed instvars
+     defined here since size in Object ommits the nil-check"
+
+    ^ 0
+!
+
+hash
+    "return an integer useful as a hash key"
+
+    ^ 0
+!
+
+identityHash
+    "return an integer useful as a hash key"
+
+    ^ 0
+! !
+
+!UndefinedObject methodsFor:'copying'!
+
+shallowCopy
+    "return a shallow copy of myself
+     - since there is only one nil in the system return self"
+
+    ^ self
+!
+
+deepCopy
+    "return a deep copy of myself
+     - since there is only one nil in the system return self"
+
+    ^ self
+! !
+
+!UndefinedObject methodsFor:'printing & storing'!
+
+printString
+    "return a string for printing myself"
+
+    ^ 'nil'
+!
+
+storeString
+    "return a string for storing myself"
+
+    ^ 'nil'
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UndefinedObject.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,142 @@
+"
+ COPYRIGHT (c) 1988-92 by Claus Gittinger
+	      All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#UndefinedObject
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Kernel-Objects'
+!
+
+UndefinedObject comment:'
+
+COPYRIGHT (c) 1988-92 by Claus Gittinger
+	      All Rights Reserved
+
+there is only one instance of this class: nil
+
+%W% %E%
+'!
+
+!UndefinedObject class methodsFor:'instance creation'!
+
+basicNew
+    "catch new - there MUST be only one nil in the system"
+
+    ^ nil
+!
+
+basicNew:size
+    "catch new - there MUST be only one nil in the system"
+
+    ^ nil
+! !
+
+!UndefinedObject methodsFor:'error catching'!
+
+basicAt:index
+    "catch array access - its illegal
+     defined here since basicAt: in Object ommits the nil-check"
+
+    ^ self notIndexed
+!
+
+at:index
+    "catch array access - its illegal
+     defined here since at: in Object ommits the nil-check"
+
+    ^ self notIndexed
+!
+
+basicAt:index put:anObject
+    "catch array access - its illegal
+     defined here since basicAt:put: in Object ommits the nil-check"
+
+    ^ self notIndexed
+!
+
+at:index put:anObject
+    "catch array access - its illegal
+     defined here since at:put: in Object ommits the nil-check"
+
+    ^ self notIndexed
+! !
+
+!UndefinedObject methodsFor:'testing'!
+
+isNil
+    "return true if I am nil - since I am, return true"
+
+    ^ true
+!
+
+notNil
+    "return true if I am not nil - since I am nil, return false"
+
+    ^ false
+!
+
+size
+    "return the number of indexed instvars
+     defined here since size in Object ommits the nil-check"
+ 
+    ^ 0
+!
+
+basicSize
+    "return the number of indexed instvars
+     defined here since size in Object ommits the nil-check"
+
+    ^ 0
+!
+
+hash
+    "return an integer useful as a hash key"
+
+    ^ 0
+!
+
+identityHash
+    "return an integer useful as a hash key"
+
+    ^ 0
+! !
+
+!UndefinedObject methodsFor:'copying'!
+
+shallowCopy
+    "return a shallow copy of myself
+     - since there is only one nil in the system return self"
+
+    ^ self
+!
+
+deepCopy
+    "return a deep copy of myself
+     - since there is only one nil in the system return self"
+
+    ^ self
+! !
+
+!UndefinedObject methodsFor:'printing & storing'!
+
+printString
+    "return a string for printing myself"
+
+    ^ 'nil'
+!
+
+storeString
+    "return a string for storing myself"
+
+    ^ 'nil'
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UninterpretedBytes.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,63 @@
+"
+ COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+ByteArray subclass:#UninterpretedBytes
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'System-Support'
+!
+
+UninterpretedBytes comment:'
+
+COPYRIGHT (c) 1993 by Claus Gittinger
+              All Rights Reserved
+
+this class has been added for ST-80 compatibility.
+
+%W% %E%
+'!
+
+!UninterpretedBytes class methodsFor:'queries'!
+
+isBigEndian
+    "return true, if words/shorts store the most-significant
+     byte last. I.e. true if LSB-first (vax, intel), 
+     false if MSB-first (m68k, sun)."
+
+%{  /* NOCONTEXT */
+
+    /*
+     * I dont like ifdefs - you always forget some ...
+     * therefore we look into a structure at run-time
+     */
+    union {
+        unsigned int   u_l;
+        char           u_c[sizeof(int)];
+    } u;
+
+    u.u_l = 0x87654321;
+    if (u.u_c[0] == 0x21) RETURN (true);
+    RETURN (false );
+%}
+    "UninterpretedBytes isBigEndian"
+! !
+
+!UninterpretedBytes methodsFor:'accessing'!
+
+byteAt:index
+    ^ self basicAt:index
+!
+
+byteAt:index put:value
+    ^ self basicAt:index put:value
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Unix.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,1542 @@
+"
+ COPYRIGHT (c) 1988-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+Object subclass:#OperatingSystem
+       instanceVariableNames:''
+       classVariableNames:''
+       poolDictionaries:''
+       category:'System-Support'
+!
+
+OperatingSystem comment:'
+
+COPYRIGHT (c) 1988-93 by Claus Gittinger
+             All Rights Reserved
+
+this class gives access to some operating system services;
+some of it is very specific for unix. 
+
+%W% %E%
+
+written 1988 by claus
+'!
+
+%{
+
+#ifdef transputer
+# define unlink(f)      ((remove(f) == 0) ? 0 : -1)
+#else
+# include <signal.h>
+
+# ifdef SYSV
+#  include <sys/types.h>
+#  include <sys/param.h>
+#  include <sys/times.h>
+#  if ! defined(sco3_2)
+#   include <unistd.h>
+#  endif
+#  if defined(isc3_2) || defined(sco3_2)
+#   include <sys/time.h>
+#  endif
+#  if !defined(isc3_2)
+#   if defined(PCS) && defined(mips)
+#    include "/usr/include/bsd/sys/time.h"
+#    include "/usr/include/sys/time.h"
+#   else
+#    include <time.h>
+#   endif
+#  endif
+#  if defined(isc3_2)
+#   include <sys/bsdtypes.h>
+#  endif
+#  ifdef FAST_TIMER
+#   include <ft.h>
+    static int timer_fd = -1;
+#  endif
+# else /* not SYSV */
+#  include <sys/time.h>
+#  include <sys/types.h>
+# endif
+# include <pwd.h>
+# include <grp.h>
+
+# include <sys/stat.h>
+# ifndef S_IXUSR
+#  define S_IXUSR S_IEXEC
+#  define S_IXGRP (S_IEXEC>>3)
+#  define S_IXOTH (S_IEXEC>>6)
+# endif
+
+#endif
+
+#ifndef errno
+ extern errno;
+#endif
+
+#include <stdio.h>
+#ifndef R_OK
+# define R_OK    4       /* Test for Read permission */ 
+# define W_OK    2       /* Test for Write permission */
+# define X_OK    1       /* Test for eXecute permission */
+# define F_OK    0       /* Test for existence of File */
+#endif
+%}
+
+!OperatingSystem class methodsFor:'misc'!
+
+exit
+    "shutdown smalltalk immediately"
+
+%{  /* NOCONTEXT */
+    mainExit(0);
+%}
+    "OperatingSystem exit - dont evaluate this"
+!
+
+exit:exitCode
+    "shutdown smalltalk immediately returning an exit-code"
+
+%{  /* NOCONTEXT */
+    if (! _isSmallInteger(exitCode))
+        exit(1);
+    mainExit(_intVal(exitCode));
+%}
+    "OperatingSystem exit:1 - dont evaluate this"
+!
+
+getEnvironment:aString
+    "get an environment string"
+
+%{  /* NOCONTEXT */
+
+    char *env;
+
+    if (_isString(aString)) {
+        env =  (char *)getenv(_stringVal(aString));
+        if (env) {
+            RETURN ( _MKSTRING(env COMMA_CON) );
+        }
+    }
+%}
+.
+    ^ nil
+
+    "OperatingSystem getEnvironment:'LANG'"
+!
+
+getLoginName
+    "return a string with the users name"
+
+%{  /* NOCONTEXT */
+
+    char *name = "you";
+#ifndef transputer
+    name = (char *)getlogin();
+    if (! name) {
+        name = (char *)getenv("LOGNAME");
+    }
+#endif
+    RETURN ( _MKSTRING(name COMMA_CON) );
+%}
+    "OperatingSystem getLogin"
+!
+
+getUserNameFromID:aNumber
+    "return the user-name-string for a given numeric user-id"
+
+%{  /* NOCONTEXT */
+
+#ifndef transputer
+    struct passwd *p;
+
+    if (_isSmallInteger(aNumber)) {
+        p = getpwuid(_intVal(aNumber));
+        if (p) {
+            RETURN ( _MKSTRING(p->pw_name COMMA_CON) );
+        }
+    }
+#endif
+%}
+.
+    ^ '???'
+
+    "OperatingSystem getUserNameFromID:0"
+!
+
+getGroupNameFromID:aNumber
+    "return the group-name-string for a given numeric group-id"
+
+%{  /* NOCONTEXT */
+
+#ifndef transputer
+    struct group *g;
+
+    if (_isSmallInteger(aNumber)) {
+        g = getgrgid(_intVal(aNumber));
+        if (g) {
+            RETURN ( _MKSTRING(g->gr_name COMMA_CON) );
+        }
+    }
+#endif
+%}
+.
+    ^ '???'
+
+    "OperatingSystem getGroupNameFromID:0"
+!
+
+getHomeDirectory
+    "return the name of the home directory"
+
+    ^ OperatingSystem getEnvironment:'HOME'
+
+    "OperatingSystem getHomeDirectory"
+!
+
+getProcessId
+    "return the processId"
+
+%{  /* NOCONTEXT */
+
+    int pid = 0;
+#ifndef transputer
+    pid = getpid();
+#endif
+    RETURN ( _MKSMALLINT(pid) );
+%}
+    "OperatingSystem getProcessId"
+!
+
+getCharacter
+    "read a character from keyboard"
+
+%{  /* NOCONTEXT */
+
+    RETURN ( _MKSMALLINT(getchar()) );
+%}
+!
+
+getCPUType
+    "return a string giving the type of machine we're running on"
+
+    |cpu|
+    
+    cpu := 'unknown'.
+
+%{  /* NOCONTEXT */
+#ifdef vax
+    cpu = _MKSTRING("vax" COMMA_CON);
+#endif
+#ifdef mips
+    cpu = _MKSTRING("mips" COMMA_CON);
+#endif
+#ifdef i386
+    cpu = _MKSTRING("i386" COMMA_CON);
+#endif
+#ifdef ns32k
+    cpu = _MKSTRING("ns32k" COMMA_CON);
+#endif
+#ifdef mc68k
+    cpu = _MKSTRING("mc68k" COMMA_CON);
+#endif
+#ifdef sparc
+    cpu = _MKSTRING("sparc" COMMA_CON);
+#endif
+#ifdef snake
+    cpu = _MKSTRING("snake" COMMA_CON);
+#endif
+#ifdef rs6000
+    cpu = _MKSTRING("rs6000" COMMA_CON);
+#endif
+#ifdef alpha
+    cpu = _MKSTRING("alpha" COMMA_CON);
+#endif
+#ifdef transputer
+    cpu = _MKSTRING("transputer" COMMA_CON);
+#endif
+%}
+.
+    ^ cpu
+
+    "OperatingSystem getCPUType"
+!
+
+getOSType
+    "return a string giving the type of os we're running on"
+
+    |os|
+
+    os := 'unknown'.
+
+%{  /* NOCONTEXT */
+#ifdef sinix
+  os = _MKSTRING("sinix" COMMA_CON);
+#endif
+
+#ifdef ultrix
+  os = _MKSTRING("ultrix" COMMA_CON);
+#endif
+
+#ifdef sco
+  os = _MKSTRING("sco" COMMA_CON);
+#endif
+
+#ifdef LINUX
+  os = _MKSTRING("linux" COMMA_CON);
+#endif
+
+#ifdef BSD
+# ifdef MACH
+  os = _MKSTRING("mach" COMMA_CON);
+# endif
+
+# ifdef sunos
+  os = _MKSTRING("sunos" COMMA_CON);
+# endif
+
+# ifdef IRIS
+  os = _MKSTRING("irix" COMMA_CON);
+# endif
+
+  if (os == nil) os = _MKSTRING("bsd" COMMA_CON);
+#endif
+
+#ifdef SYSV
+# ifdef SYSV3
+  os = _MKSTRING("sys5.3" COMMA_CON);
+# else
+#  ifdef SYSV4
+    os = _MKSTRING("sys5.4" COMMA_CON);
+#  else
+    os = _MKSTRING("sys5" COMMA_CON);
+#  endif
+# endif
+#endif
+%}
+.
+    ^ os
+
+    "OperatingSystem getOSType"
+!
+
+getSystemType
+    "return a string giving the type of system we're running on"
+
+    |sys|
+
+    sys := 'unknown'.
+
+%{  /* NOCONTEXT */
+#ifdef sinix
+  sys = _MKSTRING("sinix" COMMA_CON);
+#endif
+
+#ifdef ultrix
+  sys = _MKSTRING("ultrix" COMMA_CON);
+#endif
+
+#ifdef sco
+  sys = _MKSTRING("sco" COMMA_CON);
+#endif
+
+#ifdef sunos
+  sys = _MKSTRING("sunos" COMMA_CON);
+#endif
+
+#ifdef solaris
+  sys = _MKSTRING("solaris" COMMA_CON);
+#endif
+
+#ifdef NEXT
+  sys = _MKSTRING("next" COMMA_CON);
+#endif
+
+#ifdef IRIS
+  sys = _MKSTRING("iris" COMMA_CON);
+#endif
+
+#ifdef LINUX
+  sys = _MKSTRING("linux" COMMA_CON);
+#endif
+
+#ifdef BSD
+# ifdef MACH
+  if (sys == nil) sys = _MKSTRING("mach" COMMA_CON);
+# endif
+  if (sys == nil) sys = _MKSTRING("bsd" COMMA_CON);
+#endif
+
+#ifdef SYSV
+# ifdef SYSV3
+  if (sys == nil) sys = _MKSTRING("sys5.3" COMMA_CON);
+# else
+#  ifdef SYSV4
+    if (sys == nil) sys = _MKSTRING("sys5.4" COMMA_CON);
+#  else
+    if (sys == nil) sys = _MKSTRING("sys5" COMMA_CON);
+#  endif
+# endif
+#endif
+%}
+.
+    ^ sys
+
+    "OperatingSystem getSystemType"
+!
+
+isBSDlike
+    "return true, if the OS we're running on is a real unix."
+
+%{  /* NOCONTEXT */
+
+#ifdef BSD
+    RETURN ( true );
+#endif
+#ifdef SYSV4
+    RETURN ( true );
+#endif
+%}
+.
+    ^ false
+!
+
+maxFileNameLength
+    "return the max number of characters in a filename."
+
+%{  /* NOCONTEXT */
+#if defined(BSD) || defined(SYSV4) || defined(LINUX)
+    RETURN ( _MKSMALLINT(255) );
+#else
+# ifdef SYSV
+    RETURN ( _MKSMALLINT(14) );
+# endif
+# ifdef MSDOS
+    RETURN ( _MKSMALLINT(9) );
+# endif
+#endif
+%}
+.
+    ^ 14
+! !
+
+!OperatingSystem class methodsFor:'error messages'!
+
+errorTextForNumber:errNr
+    "return a message string from a unix errorNumber 
+     (as returned by a system call). Should be replaced by
+     a resource lookup."
+
+    |msg messages|
+
+    (Language == #german) ifTrue:[
+        messages := #('keine superuser Berechtigung'
+                      'ungueltiger Datei- oder VerzeichnisName'
+                      nil "'ungueltige Prozessnummer'  "
+                      nil "'unterbrochener systemcall' "
+                      'E/A Fehler'
+                      nil "'Geraet existiert nicht' "
+                      'zu viele Argumente'
+                      'nicht ausfuehrbar'
+                      nil "'falscher FileDescriptor'"
+                      nil "'kein Kindprozess'       "
+                      'zu viele Prozesse oder zu wenig Speicher'
+                      'zu wenig Speicher'
+                      'keine ZugriffsBerechtigung'
+                      nil "'falsche Adresse'        "
+                      nil "'kein Blockgeraet'       "
+                      nil "'Platte noch im Zugriff' "
+                      'Datei existiert bereits'
+                      nil "'Link ueber Plattengrenzen hinweg' "
+                      'Geraet existiert nicht'
+                      'ist kein Verzeichnis'
+                      'ist ein Verzeichnis'
+                      nil "'ungueltiges Argument' "
+                      'zu viele Dateien offen'
+                      'zu viele Dateien offen'
+                      nil "'kein Terminalgeraet'  "
+                      'Datei wird gerade ausgefuehrt'
+                      'Datei zu gross'
+                      'Platte ist voll'
+                      'ungueltige Positionierung'
+                      'Platte ist schreibgeschuetzt'
+                      'zu viele Links'
+                      'Pipe unterbrochen'
+                      'argument nicht im gueltigen Bereich'
+                      'Ergebnis nicht im gueltigen Bereich')
+    ] ifFalse:[
+        messages := #('Not super-user'
+                      'No such file or directory'
+                      nil "'No such process'   "
+                      nil "'interrupted system call' "
+                      'I/O error'
+                      nil "'No such device or address' "
+                      'Arg list too long'
+                      'Exec format error'
+                      nil "'Bad file number'"
+                      nil "'No children'       "
+                      'No more processes'
+                      'Not enough core'
+                      'Permission denied'
+                      nil "'Bad address'        "
+                      nil "'Block device required'       "
+                      nil "'Mount device busy' "
+                      'File exists'
+                      nil "'Cross-device link' "
+                      'No such device'
+                      'Not a directory'
+                      'Is a directory'
+                      nil 'Invalid argument'
+                      'File table overflow'
+                      'Too many open files'
+                      nil "'Not a typewriter' "
+                      'Text file busy'
+                      'File too large'
+                      'No space left on device'
+                      'Illegal seek'
+                      'Read only file system'
+                      'Too many links'
+                      'Broken pipe'
+                      'Math arg out of domain of func'
+                      'Math result not representable')
+    ].
+
+    (errNr between:1 and:messages size) ifTrue:[
+        msg := messages at:errNr
+    ].
+    msg isNil ifTrue:[
+        ^ ('ErrorNr: ' , errNr printString)
+    ].
+    ^ msg
+! !
+
+!OperatingSystem class methodsFor:'interrupts'!
+
+enableUserInterrupts
+    "enable userInterrupt (^C) handling;
+     after enabling, ^C will send the message 'userInterrupt'
+     to the UserInterruptHandler object."
+
+%{  /* NOCONTEXT */
+    extern void userInterrupt(), exceptionInterrupt();
+
+    signal(SIGINT, userInterrupt);
+    /* signal(SIGQUIT, userInterrupt); */
+%}
+!
+
+enableFpExceptionInterrupts
+    "enable floating point exception interrupts (if the
+     architecture supports it).
+     after enabling, fpu-exceptions will send the message 
+     'fpuExceptionInterrupt' to the FPUExceptionInterruptHandler object."
+
+%{  /* NOCONTEXT */
+    extern void fpExceptionInterrupt();
+
+    signal(SIGFPE, fpExceptionInterrupt);
+%}
+!
+
+enableSignalInterrupts
+    "enable signal exception interrupts (trap, buserror & segm. violation).
+     after enabling, these exceptions will send the message 
+     'signalInterrupt' to the SignalInterruptHandler object."
+
+%{  /* NOCONTEXT */
+    extern void signalPIPEInterrupt();
+    extern void signalBUSInterrupt();
+    extern void signalSEGVInterrupt();
+
+    signal(SIGPIPE, signalPIPEInterrupt);
+#ifdef SIGBUS
+    signal(SIGBUS,  signalBUSInterrupt);
+#endif
+    signal(SIGSEGV, signalSEGVInterrupt);
+%}
+!
+
+enableIOInterrupts
+    "enable IO availability interrupts 
+     (SIGPOLL/SIGIO, if the architecture supports it).
+     after enabling, these signals will send the message 
+     'ioInterrupt' to the IOInterruptHandler object."
+
+%{  /* NOCONTEXT */
+    extern void ioInterrupt();
+
+#ifdef SIGPOLL
+    signal(SIGPOLL, ioInterrupt);
+#endif
+#ifdef SIGIO
+    signal(SIGIO,  ioInterrupt);
+#endif
+%}
+!
+
+startSpyTimer
+    "trigger a spyInterrupt, to be signalled after some (short) time.
+     This is used by MessageTally for profiling."
+
+%{  /* NOCONTEXT */
+
+    extern void spyInterrupt();
+#ifdef FAST_TIMER
+    /*
+     * using PD ft-driver for tick-resolution signals
+     */
+    if (timer_fd < 0) {
+        timer_fd = open("/dev/ft0", 0);
+    }
+    if (timer_fd > 0) {
+        ioctl(timer_fd, FTIOCSET, (2<<16) | SIGALRM);
+    }
+#else
+# if defined(BSD) || defined(isc3_2) || defined(SYSV4) || defined(LINUX)
+    struct itimerval dt;
+
+    dt.it_interval.tv_sec = 0;
+    dt.it_interval.tv_usec = 0;
+    dt.it_value.tv_sec = 0;
+    dt.it_value.tv_usec = 10000;   /* 100 Hz */
+    setitimer(ITIMER_VIRTUAL, &dt, 0);
+#  ifdef BSD
+#   ifndef SYSV4
+    sigsetmask(0);
+#   endif
+#  endif
+# endif
+#endif
+
+#ifdef SIGVTALRM
+    signal(SIGVTALRM, spyInterrupt);
+#else
+    signal(SIGALRM, spyInterrupt);
+#endif
+%}
+!
+
+stopSpyTimer
+    "stop spy timing"
+
+%{  /* NOCONTEXT */
+
+#ifdef FAST_TIMER
+    if (timer_fd > 0) {
+        ioctl(timer_fd, FTIOCANCEL, 0);
+        close(timer_fd);
+        timer_fd = -1;
+    }
+#else
+# if defined(BSD) || defined(isc3_2) || defined(SYSV4) || defined(LINUX)
+    struct itimerval dt;
+
+    dt.it_interval.tv_sec = 0;
+    dt.it_interval.tv_usec = 0;
+    dt.it_value.tv_sec = 0;
+    dt.it_value.tv_usec = 0;
+    setitimer(ITIMER_VIRTUAL, &dt, 0);
+# endif
+#endif
+%}
+! !
+
+!OperatingSystem class methodsFor:'time and date'!
+
+getTimeLow
+    "return low 16 bits of current time. 
+     Obsolete: Dont use this method, use getTimeParts below.
+     This method will not always return the correct time 
+     if used together with getTimeHi.
+     (a wrap between the two getTimeXXX calls could occur)"
+
+%{  /* NOCONTEXT */
+
+    RETURN ( _MKSMALLINT(time(0) & 0xFFFF) );
+%}
+.
+    self primitiveFailed
+
+    "OperatingSystem getTimeLow"
+!
+
+getTimeHi
+    "return hi 16 bits of current time. 
+     Obsolete: Dont use this method, use getTimeParts below. 
+     This method will NOT always return the correct time
+     if used together with getTimeHi.
+     (a wrap between the two getTimeXXX calls could occur)"
+
+%{  /* NOCONTEXT */
+
+    RETURN ( _MKSMALLINT((time(0) >> 16) & 0xFFFF) );
+%}
+.
+    self primitiveFailed
+
+    "OperatingSystem getTimeHi"
+!
+
+getTimeInto:aBlock
+    "evaluate the argument aBlock, passing the time-parts of
+     the current time as arguments."
+
+    |low hi|
+%{ 
+    int now;
+
+    now = time(0);
+    hi  = _MKSMALLINT((now >> 16) & 0xFFFF);
+    low = _MKSMALLINT(now & 0xFFFF);
+%}
+.
+    aBlock value:low value:hi
+
+    "OperatingSystem getTimeTimeInto:[:low :hi | low printNewline. hi printNewline]"
+!
+
+getTime
+    "return current Time (in seconds since 1970).
+     This might return a LargeInteger some time."
+
+    ^ self getTimeHi * 16r10000 + self getTimeLow
+
+    "OperatingSystem getTime"
+!
+
+computeDatePartsOf:timeLow and:timeHi for:aBlock
+    "compute year, month and day from the time-parts timeLow and
+     timeHi and evaluate the argument, a 3-arg block with these.
+     This method was added to avoid LargeInteger arithmetic; the time-parts
+     are those returned by getTimeLow and getTimeHi."
+
+    |year month day|
+
+    ((timeLow isMemberOf:SmallInteger) and:[timeHi isMemberOf:SmallInteger])
+    ifFalse:[
+        ^ self primitiveFailed
+    ].
+%{
+    struct tm *tmPtr;
+    long t;
+
+    t = (_intVal(timeHi) << 16) | _intVal(timeLow);
+    tmPtr = localtime(&t);
+    year = _MKSMALLINT(tmPtr->tm_year + 1900);
+    month = _MKSMALLINT(tmPtr->tm_mon + 1);
+    day = _MKSMALLINT(tmPtr->tm_mday);
+%}
+.
+    aBlock value:year value:month value:day
+!
+
+computeTimePartsOf:timeLow and:timeHi for:aBlock
+    "compute hours, minutes and seconds from the time-parts timeLow and
+     timeHi and evaluate the argument, a 3-arg block with these."
+
+    |hours minutes seconds|
+
+    ((timeLow isMemberOf:SmallInteger) and:[timeHi isMemberOf:SmallInteger])
+    ifFalse:[
+        ^ self primitiveFailed
+    ].
+%{
+    struct tm *tmPtr;
+    long t;
+
+    t = (_intVal(timeHi) << 16) | _intVal(timeLow);
+    tmPtr = localtime(&t);
+    hours = _MKSMALLINT(tmPtr->tm_hour);
+    minutes = _MKSMALLINT(tmPtr->tm_min);
+    seconds = _MKSMALLINT(tmPtr->tm_sec);
+%}
+.
+    aBlock value:hours value:minutes value:seconds
+!
+
+getMillisecondTime
+    "since range is limited to 0..1ffffff and value is wrapping around
+     at 1fffffff, this can only be used for relative time deltas.
+     Use methods below to compare and add time deltas (should move to Time)"
+
+%{  /* NOCONTEXT */
+
+    long t;
+#ifdef SYSV
+# ifdef HZ
+    /* sys5 time */
+    long ticks;
+    struct tms tb;
+
+    ticks = times(&tb);
+    t = (ticks * 1000) / HZ;
+# endif
+#else
+    /* bsd time */
+    struct timeval tb;
+    struct timezone tzb;
+
+    gettimeofday(&tb, &tzb);
+    t = tb.tv_sec*1000 + tb.tv_usec/1000;
+#endif
+    RETURN ( _MKSMALLINT(t & 0x0FFFFFFF) );
+%}
+.
+    self error:'time not available'
+!
+
+millisecondTimeDeltaBetween:msTime1 and:msTime2
+    "subtract two millisecond times (such as returned getMillisecondTime).
+     The returned value is msTime1 - msTime2 where a wrap occurs at:16r0FFFFFFF."
+
+    (msTime1 > msTime2) ifTrue:[
+        ^ msTime1 - msTime2
+    ].
+    ^ msTime1 + 16r10000000 - msTime2
+!
+
+millisecondTime:msTime1 isAfter:msTime2
+    "return true if msTime1 is after msTime2, false if not.
+     handling wrap at 16r0FFFFFFF. The two arguments are
+     millisecond times (such as returned getMillisecondTime)."
+
+    (msTime1 > msTime2) ifTrue:[
+        ((msTime1 - msTime2) > 16r08000000) ifTrue:[
+            ^ false
+        ].
+        ^ true
+    ].
+    ((msTime2 - msTime1) > 16r08000000) ifTrue:[
+        ^ true
+    ].
+    ^ false
+!
+
+millisecondTimeAdd:msTime1 and:msTime2
+    "add two millisecond times (such as returned getMillisecondTime).
+     The returned value is msTime1 + msTime2 where a wrap occurs at:16r0FFFFFFF."
+
+    |sum|
+
+    sum := msTime1 + msTime2.
+    (sum > 16r0FFFFFFF) ifTrue:[^ sum - 16r10000000].
+    (sum < 0) ifTrue:[^ sum + 16r10000000].
+    ^ sum
+!
+
+millisecondDelay:millis
+    "delay execution for millis milliseconds."
+
+    self selectOn:nil withTimeOut:(millis * 0.001)
+
+    "OperatingSystem millisecondDelay:1000"
+!
+
+sleep:numberOfSeconds
+    "cease any action for some time. 
+     Not really useful since not even low-prio processes and interrupt
+     handling will run during the sleep - use millisecondDelay:."
+
+%{  /* NOCONTEXT */
+
+    if (_isSmallInteger(numberOfSeconds)) {
+        sleep(_intVal(numberOfSeconds));
+        RETURN ( self );
+    }
+%}
+.
+    self primitiveFailed
+!
+
+selectOn:aFileDescriptor withTimeOut:seconds
+    "wait for aFileDesriptor to become ready; timeout after t seconds.
+     Return true, if i/o ok, false if timed-out or interrupted.
+     With 0 as timeout argument, this can be used to check for availability
+     of read-data.
+     Experimental."
+
+    |millis|
+
+    millis := (seconds * 1000) rounded.
+%{
+    fd_set rset, wset, eset;
+    int t, fd, i, lX, bX;
+    struct timeval wt;
+
+    if ((aFileDescriptor == nil) || _isSmallInteger(aFileDescriptor)) {
+        if (_isSmallInteger(millis)) {
+            FD_ZERO(&rset);
+            FD_ZERO(&wset);
+            FD_ZERO(&eset);
+            if (aFileDescriptor != nil) {
+                fd = _intVal(aFileDescriptor);
+                if ((fd >= 0) && (fd < FD_SETSIZE))
+                    FD_SET(fd, &rset);
+            } else
+                fd = 0;
+            t = _intVal(millis);
+            wt.tv_sec = t / 1000;
+            wt.tv_usec = (t % 1000) * 1000;
+            RETURN ( (select(fd+1, &rset, &wset, &eset, &wt) == 0) ? false
+                                                                   : true );
+        }
+    }
+%}
+.
+    self primitiveFailed
+!
+
+selectOn:fd1 and:fd2 withTimeOut:seconds
+    "wait for any fd to become ready; timeout after t seconds.
+     Return fd if i/o ok, nil if timed-out or interrupted.
+     Experimental."
+
+    |millis|
+
+    millis := (seconds * 1000) rounded.
+%{
+    fd_set rset, wset, eset;
+    int t, f1, f2, i, lX, bX;
+    struct timeval wt;
+    OBJ retFd;
+
+    if (((fd1 == nil) || _isSmallInteger(fd1)) 
+     && ((fd2 == nil) || _isSmallInteger(fd2)))  {
+        if (_isSmallInteger(millis)) {
+            FD_ZERO(&rset);
+            FD_ZERO(&wset);
+            FD_ZERO(&eset);
+            if (fd1 != nil) {
+                f1 = _intVal(fd1);
+                if ((f1 >= 0) && (f1 < FD_SETSIZE))
+                    FD_SET(f1, &rset);
+            } else
+                f1 = 0;
+            if (fd2 != nil) {
+                f2 = _intVal(fd2);
+                if ((f2 >= 0) && (f2 < FD_SETSIZE))
+                    FD_SET(f2, &rset);
+            } else
+                f2 = 0;
+            if (f2 > f1)
+                f1 = f2;
+            t = _intVal(millis);
+            wt.tv_sec = t / 1000;
+            wt.tv_usec = (t % 1000) * 1000;
+            if (select(f1+1, &rset, &wset, &eset, &wt)) {
+                if (FD_ISSET(f1, &rset)) retFd = fd1;
+                else if (FD_ISSET(f1, &wset)) retFd = fd1;
+                else if (FD_ISSET(f1, &eset)) retFd = fd1;
+                else if (FD_ISSET(f2, &rset)) retFd = fd2;
+                else if (FD_ISSET(f2, &wset)) retFd = fd2;
+                else if (FD_ISSET(f2, &eset)) retFd = fd2;
+                RETURN ( retFd );
+            }
+            RETURN ( nil );
+        }
+    }
+%}
+.
+    self primitiveFailed
+!
+
+selectOnAnyReadable:fdArray withTimeOut:seconds
+    "wait for any fd in fdArray (an Array of integers) to become ready;
+     timeout after t seconds. An empty set will always wait.
+     Return first ready fd if i/o ok, nil if timed-out or interrupted.
+     Experimental."
+
+    |millis count|
+
+    millis := (seconds * 1000) rounded asInteger.
+    (fdArray class == Array) ifFalse:[
+        ^ self error:'argument must be an Array'
+    ].
+    count := fdArray size.
+%{
+    fd_set rset, wset, eset;
+    int t, f, maxF, i, lX, bX;
+    struct timeval wt;
+    OBJ fd, retFd;
+
+    if (_isSmallInteger(millis)) {
+        FD_ZERO(&rset);
+        FD_ZERO(&wset);
+        FD_ZERO(&eset);
+
+        maxF = 0;
+        for (i=0; i<_intVal(count);i++) {
+            fd = _ArrayInstPtr(fdArray)->a_element[i];
+            if (fd != nil) {
+                f = _intVal(fd);
+                if ((f >= 0) && (f < FD_SETSIZE)) {
+                    FD_SET(f, &rset);
+/*                   FD_SET(f, &wset);       */
+/*                   FD_SET(f, &eset);       */
+                    if (f > maxF) maxF = f;
+                }
+            }
+        }
+        t = _intVal(millis);
+        wt.tv_sec = t / 1000;
+        wt.tv_usec = (t % 1000) * 1000;
+        if (select(maxF+1, &rset, &wset, &eset, &wt)) {
+            for (i=0; i <= maxF; i++) {
+                if (FD_ISSET(i, &rset)
+/*                 || FD_ISSET(i, &wset)
+                   || FD_ISSET(i, &eset) */ ) {
+                    RETURN ( _MKSMALLINT(i) );
+                }
+            }
+        }
+        RETURN ( nil );
+    }
+%}
+.
+    self primitiveFailed
+! !
+
+!OperatingSystem class methodsFor:'executing commands'!
+
+fork
+    "fork a new process"
+
+%{  /* NOCONTEXT */
+
+    int pid;
+
+    pid = fork();
+    RETURN ( _MKSMALLINT(pid) );
+%}
+.
+    self primitiveFailed
+!
+
+exec:aPath withArguments:argArray
+    "execute the unix command specified by the argument, aPath, with
+     arguments in argArray.
+     If successful, this method does not return and smalltalk is gone.
+     If not sucessfull, false is returned. Normal use is with fork."
+
+%{
+    char *argv[64];
+    int nargs, i;
+    OBJ arg;
+
+    if (_isString(aPath) && _isArray(argArray)) {
+	nargs = _arraySize(argArray);
+        for (i=0; i < nargs; i++) {
+            arg = _ArrayInstPtr(argArray)->a_element[i];
+	    if (_isString(arg)) {
+                argv[i] = (char *) _stringVal(arg);
+	    }
+        }
+        argv[i] = NULL;
+        execv(_stringVal(aPath), argv);
+        /* should not be reached */
+        RETURN ( false );
+    }
+%}
+.
+    self primitiveFailed
+!
+
+executeCommand:aCommandString
+    "execute the unix command specified by the argument, aCommandString.
+     Return true if successful, false otherwise. Smalltalk is suspended,
+     while the command is executing."
+
+%{  /* NOCONTEXT */
+
+    int status;
+    extern OBJ ErrorNumber;
+
+    if (_isString(aCommandString)) {
+        status = system((char *) _stringVal(aCommandString));
+        if (status == 0) {
+            RETURN ( true );
+        }
+        ErrorNumber = _MKSMALLINT(errno);
+        RETURN ( false );
+    }
+%}
+.
+    self primitiveFailed
+
+    "OperatingSystem executeCommand:'pwd'"
+    "OperatingSystem executeCommand:'ls -l'"
+    "OperatingSystem executeCommand:'invalidCommand'"
+! !
+
+!OperatingSystem class methodsFor:'file access'!
+
+baseNameOf:aPath
+    "return the baseName of the argument, aPath
+     - thats the file/directory name without leading parent-dirs
+     (i.e. OperatingSystem baseNameOf:'/usr/lib/st/file' -> 'file'
+       and OperatingSystem baseNameOf:'/usr/lib' -> lib).
+     This method does not check if the path is valid (i.e. if these directories
+     really exist)."
+
+    |prev index|
+
+    (aPath = '/') ifTrue:[^ aPath].
+    prev := 1.
+    [true] whileTrue:[
+        index := aPath indexOf:$/
+                    startingAt:prev
+                      ifAbsent:[^ aPath copyFrom:prev].
+        prev := index + 1
+    ]
+!
+
+directoryNameOf:aPath
+    "return the directoryName of the argument, aPath
+     - thats the name of the directory where aPath is
+     (i.e. OperatingSystem directoryNameOf:'/usr/lib/st/file' -> '/usr/lib/st'
+       and OperatingSystem directoryNameOf:'/usr/lib' -> /usr').
+     This method does not check if the path is valid (i.e. if these directories
+     really exist)."
+
+    |last|
+
+    (aPath = '/') ifTrue:[^ aPath].
+    (aPath startsWith:'/') ifFalse:[
+        (aPath endsWith:'/') ifTrue:[
+            ^ aPath copyFrom:1 to:(aPath size - 1)
+        ].
+    ].
+    last := 1.
+    [true] whileTrue:[
+        last := aPath indexOf:$/
+                   startingAt:(last + 1)
+                     ifAbsent:[(last == 1) ifTrue:[^ '/'].
+                               ^ aPath copyFrom:1 to:(last - 1)
+                              ]
+    ]
+
+    "OperatingSystem directoryNameOf:'/fee/foo/bar'"
+    "OperatingSystem directoryNameOf:'foo/bar'"
+    "OperatingSystem directoryNameOf:'../../foo/bar'"
+!
+
+isValidPath:aPathName
+    "return true, if 'aPathName' is a valid path name
+     (i.e. the file or directory exists)"
+
+%{  /* NOCONTEXT */
+
+    struct stat buf;
+
+    if (_isString(aPathName)) {
+        RETURN ( (stat((char *) _stringVal(aPathName), &buf) < 0) ? false : true );
+    }
+%}
+.
+    self primitiveFailed
+!
+
+isDirectory:aPathName
+    "return true, if 'aPathName' is a valid directory path name.
+     (i.e. exists and is a directory)"
+
+%{  /* NOCONTEXT */
+
+    struct stat buf;
+
+    if (_isString(aPathName)) {
+        if ((stat((char *) _stringVal(aPathName), &buf) < 0)
+         || ((buf.st_mode & S_IFMT) != S_IFDIR)) {
+            RETURN ( false );
+        }
+        RETURN ( true );
+    }
+%}
+.
+    self primitiveFailed
+!
+
+isReadable:aPathName
+    "return true, if the file/dir 'aPathName' is readable."
+
+%{  /* NOCONTEXT */
+
+    extern OBJ ErrorNumber;
+
+    if (_isString(aPathName)) {
+        if (access(_stringVal(aPathName), R_OK) == 0) {
+            RETURN ( true );
+        }
+        ErrorNumber = _MKSMALLINT(errno);
+        RETURN ( false );
+    }
+%}
+.
+    self primitiveFailed
+!
+
+isWritable:aPathName
+    "return true, if the given file is writable"
+
+%{  /* NOCONTEXT */
+
+    extern OBJ ErrorNumber;
+
+    if (_isString(aPathName)) {
+        if (access(_stringVal(aPathName), W_OK) == 0) {
+            RETURN ( true );
+        }
+        ErrorNumber = _MKSMALLINT(errno);
+        RETURN ( false );
+    }
+%}
+.
+    self primitiveFailed
+!
+
+isExecutable:aPathName
+    "return true, if the given file is executable"
+
+%{  /* NOCONTEXT */
+
+    extern OBJ ErrorNumber;
+
+    if (_isString(aPathName)) {
+        if (access(_stringVal(aPathName), X_OK) == 0) {
+            RETURN ( true );
+        }
+        ErrorNumber = _MKSMALLINT(errno);
+        RETURN ( false );
+    }
+%}
+.
+    self primitiveFailed
+!
+
+infoOf:aPathName
+    "return an dictionary filled with info for the file 'aPathName';
+     info is: (type->t mode->n uid->u gid->g size->s id->ino).
+     return nil if such a file does not exist. A dictionary is returned,
+     since we might need to add more info in the future without affecting
+     existing applications."
+
+    |info type mode uid gid size id|
+
+    "{ Symbol: directory }"
+    "{ Symbol: regular }"
+    "{ Symbol: characterSpecial }"
+    "{ Symbol: blockSpecial }"
+    "{ Symbol: fifo }"
+    "{ Symbol: socket }"
+    "{ Symbol: symbolicLink }"
+    "{ Symbol: unknown }"
+
+%{
+    struct stat buf;
+
+    if (_isString(aPathName)) {
+        if (stat((char *) _stringVal(aPathName), &buf) < 0) {
+            RETURN ( nil );
+        }
+        switch (buf.st_mode & S_IFMT) {
+            case S_IFDIR:
+                type = _directory;
+                break;
+            case S_IFCHR:
+                type = _characterSpecial;
+                break;
+            case S_IFBLK:
+                type = _blockSpecial;
+                break;
+            case S_IFREG:
+                type = _regular;
+                break;
+#ifdef S_IFLNK
+            case S_IFLNK:
+                type = _symbolicLink;
+                break;
+#endif
+#ifdef S_IFSOCK
+            case S_IFSOCK:
+                type = _socket;
+                break;
+#endif
+#ifdef S_IFIFO
+            case S_IFIFO:
+                type = _fifo;
+                break;
+#endif
+            default:
+                type = _unknown;
+                break;
+        }
+        mode = _MKSMALLINT(buf.st_mode & 0777);
+        uid = _MKSMALLINT(buf.st_uid);
+        gid = _MKSMALLINT(buf.st_gid);
+        size = _MKSMALLINT(buf.st_size);
+        id = _MKSMALLINT(buf.st_ino);
+    }
+%}
+.
+    mode notNil ifTrue:[
+        info := IdentityDictionary new.
+        info at:#type put:type.
+        info at:#mode put:mode.
+        info at:#uid put:uid.
+        info at:#gid put:gid.
+        info at:#size put:size.
+        info at:#id put:id.
+        ^ info
+   ].
+   self primitiveFailed
+
+   "OperatingSystem infoOf:'/'"
+   "(OperatingSystem infoOf:'/') at:#uid"
+!
+
+accessModeOf:aPathName
+    "return a number representing access rights rwxrwxrwx for owner,
+     group and others. return nil if such a file does not exist."
+
+    "
+     this could have been implemented as:
+        (self infoOf:aPathName) at:#mode
+     but for huge directory searches the code below is faster
+    "
+
+%{  /* NOCONTEXT */
+
+    struct stat buf;
+
+    if (_isString(aPathName)) {
+        if (stat((char *) _stringVal(aPathName), &buf) < 0) {
+            RETURN ( nil );
+        }
+        RETURN ( _MKSMALLINT(buf.st_mode & 0777) );
+    }
+%}
+.
+   self primitiveFailed
+
+   "(OperatingSystem accessModeOf:'/') printStringRadix:8"
+!
+
+changeAccessModeOf:aPathName to:modeBits
+    "change the access rights rwxrwxrwx for owner,
+     group and others of aPathName. return true if changed, false
+     if such a file does not exist or change was not allowd."
+
+%{  /* NOCONTEXT */
+
+    if (_isString(aPathName) && _isSmallInteger(modeBits)) {
+        RETURN ( (chmod((char *) _stringVal(aPathName), _intVal(modeBits) ) < 0) ?
+                                false : true );
+    }
+%}
+.
+   self primitiveFailed
+!
+
+timeOfLastChange:aPathName
+    "return the timeStamp of a file"
+
+    |timeLow timeHi|
+%{
+    struct stat buf;
+    time_t mtime;
+
+    if (_isString(aPathName)) {
+        if (stat((char *) _stringVal(aPathName), &buf) >= 0) {
+            timeLow = _MKSMALLINT(buf.st_mtime & 0xFFFF);
+            timeHi = _MKSMALLINT((buf.st_mtime >> 16) & 0xFFFF);
+        }
+    }
+%}
+.
+    timeLow notNil ifTrue:[^ Time fromUnixTimeLow:timeLow and:timeHi].
+    self primitiveFailed
+
+    "OperatingSystem timeOfLastChange:'/'"
+!
+
+idOf:aPathName
+    "return the fileNumber (i.e. inode number) of a file"
+
+    "
+     this could have been implemented as:
+        (self infoOf:aPathName) at:#id 
+     but for huge directory searches the code below is faster
+    "
+
+%{  /* NOCONTEXT */
+
+    struct stat buf;
+
+    if (_isString(aPathName)) {
+        if (stat((char *) _stringVal(aPathName), &buf) >= 0) {
+            RETURN (_MKSMALLINT(buf.st_ino));
+        }
+    }
+%}
+.
+    self primitiveFailed
+
+    "OperatingSystem idOf:'/'"
+!
+
+typeOf:aPathName
+    "return the type of a file as a symbol"
+
+    "
+     this could have been implemented as:
+        (self infoOf:aPathName) at:#type 
+     but for huge directory searches the code below is faster
+    "
+
+%{  /* NOCONTEXT */
+
+    struct stat buf;
+
+    if (_isString(aPathName)) {
+        if (stat((char *) _stringVal(aPathName), &buf) < 0) {
+            RETURN ( nil );
+        }
+        switch (buf.st_mode & S_IFMT) {
+            case S_IFDIR:
+                RETURN ( _directory );
+            case S_IFCHR:
+                RETURN ( _characterSpecial );
+            case S_IFBLK:
+                RETURN ( _blockSpecial );
+            case S_IFREG:
+                RETURN ( _regular );
+#ifdef S_IFLNK
+            case S_IFLNK:
+                RETURN ( _symbolicLink );
+#endif
+#ifdef S_IFSOCK
+            case S_IFSOCK:
+                RETURN ( _socket );
+#endif
+#ifdef S_IFIFO
+            case S_IFIFO:
+                RETURN ( _fifo );
+#endif
+            default:
+                RETURN ( _unknown );
+        }
+    }
+%}
+.
+    self primitiveFailed
+
+    "OperatingSystem typeOf:'/'"
+!
+
+createDirectory:newPathName
+    "create a new directory with name 'newPathName'.
+     Return true if successful, false if failed."
+
+    "since createDirectory is not used too often,
+     you'll forgive me using mkdir ..."
+
+    ^ self executeCommand:('mkdir ' , newPathName)
+
+    "OperatingSystem createDirectory:'foo'"
+!
+
+recursiveCreateDirectory:dirName
+    "create a directory - with all parent dirs if needed.
+     Return true if successful, false otherwise. If false
+     is returned, a partial created tree may be left,
+     which is not cleaned-up here."
+
+    self createDirectory:dirName.
+    (self isDirectory:dirName) ifFalse:[
+        (self recursiveCreateDirectory:(self directoryNameOf:dirName)) ifFalse:[^ false].
+        ^ self createDirectory:dirName
+    ].
+    ^ (self isDirectory:dirName)
+
+    "OperatingSystem recursiveCreateDirectory:'foo/bar/baz'"
+!
+
+removeFile:fullPathName
+    "remove the file named 'fullPathName'; return true if successful"
+
+%{  /* NOCONTEXT */
+
+    if (_isString(fullPathName)) {
+        RETURN ( (unlink((char *) _stringVal(fullPathName)) >= 0) ? true : false );
+    }
+%}
+.
+    self primitiveFailed
+!
+
+removeDirectory:fullPathName
+    "remove the directory named 'fullPathName'.
+     Return true if successful, false if directory is not empty or no permission"
+
+%{  /* NOCONTEXT */
+
+    if (_isString(fullPathName)) {
+        RETURN ( (rmdir((char *) _stringVal(fullPathName)) >= 0) ? true : false );
+    }
+%}
+.
+    self primitiveFailed
+!
+
+link:oldPath to:newPath
+    "link the file 'oldPath' to 'newPath'. The link will be a hard link.
+     Return true if successful, false if not."
+
+%{  /* NOCONTEXT */
+
+    if (_isString(oldPath) && _isString(newPath)) {
+        RETURN ( (link((char *) _stringVal(oldPath), (char *) _stringVal(newPath)) >= 0) ?
+                                true : false );
+    }
+%}
+.
+    self primitiveFailed
+
+    "OperatingSystem link:'foo' to:'bar'"
+!
+
+rename:oldPath to:newPath
+    "rename the file 'oldPath' to 'newPath'. 
+     Return true if sucessfull, false if not"
+
+%{  /* NOCONTEXT */
+
+    if (_isString(oldPath) && _isString(newPath)) {
+#if defined(BSD)
+        if (rename((char *) _stringVal(oldPath), (char *) _stringVal(newPath)) >= 0) {
+            RETURN ( true );
+        }
+#else
+        if (link((char *) _stringVal(oldPath), (char *) _stringVal(newPath)) >= 0) {
+            if (unlink((char *) _stringVal(oldPath)) >= 0) {
+                RETURN ( true );
+            }
+            unlink((char *) _stringVal(newPath));
+        }
+#endif
+        RETURN ( false );
+    }
+%}
+.
+    self primitiveFailed
+
+    "OperatingSystem rename:'foo' to:'bar'"
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WriteStr.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,199 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+PositionableStream subclass:#WriteStream
+       instanceVariableNames:'writeLimit'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Streams'
+!
+
+WriteStream comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+Streams for writing into - this implementation currently DOES change the identity if the
+streamed-upon collection if it cannot grow. Thus its slightly incompatible to ST-80 since
+aStream contents does not always return the original collection. This will change soon.
+
+%W% %E%
+'!
+
+!WriteStream class methodsFor:'instance creation'!
+
+on:aCollection from:start to:last
+    "create and return a new stream for writing onto aCollection, where
+     writing is limited to the elements in the range start to last."
+
+    |newStream|
+    newStream := super on:aCollection from:start to:last.
+    newStream writeLimit:last.
+    ^ newStream
+! !
+
+!WriteStream methodsFor:'private'!
+
+on:aCollection
+    "create and return a new stream for writing onto aCollection"
+
+    super on:aCollection.
+    writeLimit := readLimit
+! !
+
+!WriteStream methodsFor:'accessing'!
+
+contents
+    "return the current contents (a collection) of the stream"
+
+    collection size == (position - 1) ifFalse:[
+        collection isFixedSize ifTrue:[
+            collection := collection copyFrom:1 to:(position - 1)
+        ] ifFalse:[
+            collection grow:(position - 1)
+        ]
+    ].
+    ^ collection
+!
+
+writeLimit:aNumber
+    "set the writeLimit, thats the position after which writing is
+     prohibited."
+
+    (aNumber < collection size) ifTrue:[writeLimit := aNumber]
+! !
+
+!WriteStream methodsFor:'reading/writing'!
+
+next
+    "catch read access to write stream"
+
+    ^ self error:'WriteStreams cannot read'
+!
+
+peek
+    "catch read access to write stream"
+
+    ^ self error:'WriteStreams cannot read'
+!
+
+nextPut:anObject
+    "append the argument, anObject to the stream"
+
+    (position > collection size) ifTrue:[self growCollection].
+    collection at:position put:anObject.
+    (position > readLimit) ifTrue:[readLimit := position].
+    position := position + 1.
+    ^anObject
+!
+
+nextPutAll:aCollection
+    "append all elements of the argument, aCollection to the stream"
+
+    |nMore final|
+
+    nMore := aCollection size.
+    final := position + nMore - 1.
+    (final > collection size) ifTrue:[
+        self growCollection:final
+    ].
+    collection replaceFrom:position
+                        to:final
+                      with:aCollection 
+                startingAt:1.
+
+    position := position + nMore.
+    (position > readLimit) ifTrue:[readLimit := position - 1].
+    ^ aCollection
+!
+
+cr
+    "append a carriage-return to the stream"
+
+    self nextPut:(Character cr)
+!
+
+tab
+    "append a tab-character to the stream"
+
+    self nextPut:(Character tab)
+!
+
+crTab
+    "append a carriage-return followed by a tab to the stream"
+
+    self nextPut:(Character cr).
+    self nextPut:(Character tab)
+!
+
+space
+    "append a space character to the receiver-stream"
+
+    self nextPut:(Character space)
+!
+
+spaces:count
+    "append count space-characters to the receiver-stream"
+
+    1 to:count do:[:dummy |
+        self nextPut:(Character space)
+    ]
+!
+
+ff
+    "append a form-feed (new-pagee) to the receiver-stream"
+
+    self nextPut:(Character ff)
+! !
+
+!WriteStream methodsFor:'private'!
+
+growCollection
+    |oldSize newSize newColl|
+
+    oldSize := collection size.
+    (oldSize == 0) ifTrue:[
+        newSize := 10
+    ] ifFalse:[
+        newSize := oldSize * 2
+    ].
+    collection isFixedSize ifTrue:[
+        newColl := collection species new:newSize.
+        newColl replaceFrom:1 to:oldSize with:collection startingAt:1.
+        collection := newColl
+    ] ifFalse:[
+        collection grow:newSize.
+    ].
+    writeLimit := newSize
+!
+
+growCollection:minNewSize
+    |oldSize newSize newColl|
+
+    oldSize := collection size.
+    (oldSize == 0) ifTrue:[
+        newSize := minNewSize
+    ] ifFalse:[
+        newSize := oldSize * 2.
+        [newSize < minNewSize] whileTrue:[
+            newSize := newSize * 2
+        ]
+    ].
+    collection isFixedSize ifTrue:[
+        newColl := collection species new:newSize.
+        newColl replaceFrom:1 to:oldSize with:collection startingAt:1.
+        collection := newColl
+    ] ifFalse:[
+        collection grow:newSize
+    ].
+    writeLimit := newSize
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WriteStream.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,199 @@
+"
+ COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+
+PositionableStream subclass:#WriteStream
+       instanceVariableNames:'writeLimit'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Streams'
+!
+
+WriteStream comment:'
+
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+Streams for writing into - this implementation currently DOES change the identity if the
+streamed-upon collection if it cannot grow. Thus its slightly incompatible to ST-80 since
+aStream contents does not always return the original collection. This will change soon.
+
+%W% %E%
+'!
+
+!WriteStream class methodsFor:'instance creation'!
+
+on:aCollection from:start to:last
+    "create and return a new stream for writing onto aCollection, where
+     writing is limited to the elements in the range start to last."
+
+    |newStream|
+    newStream := super on:aCollection from:start to:last.
+    newStream writeLimit:last.
+    ^ newStream
+! !
+
+!WriteStream methodsFor:'private'!
+
+on:aCollection
+    "create and return a new stream for writing onto aCollection"
+
+    super on:aCollection.
+    writeLimit := readLimit
+! !
+
+!WriteStream methodsFor:'accessing'!
+
+contents
+    "return the current contents (a collection) of the stream"
+
+    collection size == (position - 1) ifFalse:[
+        collection isFixedSize ifTrue:[
+            collection := collection copyFrom:1 to:(position - 1)
+        ] ifFalse:[
+            collection grow:(position - 1)
+        ]
+    ].
+    ^ collection
+!
+
+writeLimit:aNumber
+    "set the writeLimit, thats the position after which writing is
+     prohibited."
+
+    (aNumber < collection size) ifTrue:[writeLimit := aNumber]
+! !
+
+!WriteStream methodsFor:'reading/writing'!
+
+next
+    "catch read access to write stream"
+
+    ^ self error:'WriteStreams cannot read'
+!
+
+peek
+    "catch read access to write stream"
+
+    ^ self error:'WriteStreams cannot read'
+!
+
+nextPut:anObject
+    "append the argument, anObject to the stream"
+
+    (position > collection size) ifTrue:[self growCollection].
+    collection at:position put:anObject.
+    (position > readLimit) ifTrue:[readLimit := position].
+    position := position + 1.
+    ^anObject
+!
+
+nextPutAll:aCollection
+    "append all elements of the argument, aCollection to the stream"
+
+    |nMore final|
+
+    nMore := aCollection size.
+    final := position + nMore - 1.
+    (final > collection size) ifTrue:[
+        self growCollection:final
+    ].
+    collection replaceFrom:position
+                        to:final
+                      with:aCollection 
+                startingAt:1.
+
+    position := position + nMore.
+    (position > readLimit) ifTrue:[readLimit := position - 1].
+    ^ aCollection
+!
+
+cr
+    "append a carriage-return to the stream"
+
+    self nextPut:(Character cr)
+!
+
+tab
+    "append a tab-character to the stream"
+
+    self nextPut:(Character tab)
+!
+
+crTab
+    "append a carriage-return followed by a tab to the stream"
+
+    self nextPut:(Character cr).
+    self nextPut:(Character tab)
+!
+
+space
+    "append a space character to the receiver-stream"
+
+    self nextPut:(Character space)
+!
+
+spaces:count
+    "append count space-characters to the receiver-stream"
+
+    1 to:count do:[:dummy |
+        self nextPut:(Character space)
+    ]
+!
+
+ff
+    "append a form-feed (new-pagee) to the receiver-stream"
+
+    self nextPut:(Character ff)
+! !
+
+!WriteStream methodsFor:'private'!
+
+growCollection
+    |oldSize newSize newColl|
+
+    oldSize := collection size.
+    (oldSize == 0) ifTrue:[
+        newSize := 10
+    ] ifFalse:[
+        newSize := oldSize * 2
+    ].
+    collection isFixedSize ifTrue:[
+        newColl := collection species new:newSize.
+        newColl replaceFrom:1 to:oldSize with:collection startingAt:1.
+        collection := newColl
+    ] ifFalse:[
+        collection grow:newSize.
+    ].
+    writeLimit := newSize
+!
+
+growCollection:minNewSize
+    |oldSize newSize newColl|
+
+    oldSize := collection size.
+    (oldSize == 0) ifTrue:[
+        newSize := minNewSize
+    ] ifFalse:[
+        newSize := oldSize * 2.
+        [newSize < minNewSize] whileTrue:[
+            newSize := newSize * 2
+        ]
+    ].
+    collection isFixedSize ifTrue:[
+        newColl := collection species new:newSize.
+        newColl replaceFrom:1 to:oldSize with:collection startingAt:1.
+        collection := newColl
+    ] ifFalse:[
+        collection grow:newSize
+    ].
+    writeLimit := newSize
+! !