ILFilePool declare: Noop as: 2; declare: RequestBits as: 0176000; declare: BrokenConnection as: 12; declare: BadLeafHandle as: 1011.
phylum name: ’Smalltalk-User’ password: ’Smalltalk’.
’From Smalltalk 5.5k XM November 15 on 15 November 1980 at 9:58:05 am.’
̃
AltoFileDirectory asFollows̃
Dictionary
nextEntry: file | s elen [
dirFileffnil [
(file name compare: dirname) = 2 [
"return system directory file. known serialNumber and leader"
file serialNumber: 0100000, 0144; leader: 010000.
ffifile]
self error: ’directory not open’]
"return the next file entry, ignore deleted entries,
and leave dirFile positioned before next entry"
whileç (s ← dirFile nextword) doç [
elen ← s land: dfmask-1.
s allmask: dfmask [
file readFrom: dirFile.
dirFile skip: elen*2 - (file fileSize + 2).
ffifile]
"deleted entry, again"
dirFile skipwords: elen-1].
ffifalse]
open | f s a page len elen type [
nil ̄dirFile []
"assume some defaults in case DSHAPE is not in SysDir leader page.
these should only be needed if the disk is old (and not scavenged).
they will not work if a 14 sector system is missing DSHAPE (unlikely) since addresses of first page of directory and of DiskDescriptor might be computed incorrectly.
in a Smalltalk-76 system, nSectors, diskPages had better eventually match:
| a. a ← Vmem specialLocs‘13. mem‘(a+5), (mem‘(a+6))
"
nSectors ← 12.
diskPages ← 812*nSectors.
totalPages ← 2*diskPages.
"read SysDir leader page to find out file system configuration. see AltoFileSys.D"
f ← self find: dirname.
"to prevent address of page 1 from being stored"
f pageAddresses: false.
"length of property list, in words"
page ← f read: 0.
len ← page‘494.
[len ̄210 []
"scan file properties for DSHAPE"
s ← page asStream.
s skipwords: page‘493.
whileç len > 0 doç [
type ← s next.
type = 0 [
"0 terminates list. property not found. try to read if from DiskDescriptor"
len ← 0]
elen ← s next.
type = 1 andç elen = 5 [
"DSHAPE. read property"
self configure: s.
"set flags so configure and loop are not done again"
s ← false. len ← 0]
"skip over other property"
len ← len - elen.
s skipwords: elen-1]].
"now, with the correct (or default) file system configuration,
store the virtual address of next page (1), and create a FileStream on SysDir"
a ← AltoFileAddressTable new.
a‘1 ← page header: nextp.
f pageAddresses: a.
(dirFile ← f asStream) readonly.
(bitsFile ← self oldFile: ’DiskDescriptor’) readwrite.
[s [
"configuration not read from SysDir. this will work for 12 sector systems.
14 sector systems should have had the DSHAPE property"
self configure: bitsFile]].
super open.
]
FileDirectory
realToVirtual: adr ["see virtualToReal:.
Alto address format is
bits
0-3sector number (0 - 015, i.e. 12 or 14 sectors)
4-12cylinder number (0 - 0312, Model 31; 0-0625, Model 44)
13head number (0-1)
14disk number(0-1)
15restore bit.
in a system with two separable disks, addresses on disk 1 have a 0 disk bit, which is complemented by the disk primitive"
ffi("sector: field" adr lshift: fl12) +
("cylinder and head: field*" nSectors * ((adr land: 07774) lshift: fl2)) +
("disk: field*pages per disk" [(adr land: 2) = 2 [diskPages] 0]
"diskPages*(adr land: 2)/2")
"vadr < 0 orç vadr ffl totalPages [
self error: ’illegal disk address’]"]
virtualToReal: vadr | t2 d ["inverse of realToVirtual:"
"vadr < 0 orç vadr ffl totalPages [
self error: ’illegal virtual address’]"
"faster to do /\ for normal Integers"
"t ← vadr intdiv: diskPages.
sec ← t‘2 intdiv: nSectors"
[vadr < diskPages [
d ← 0.
t2 ← vadr]
d ← 2.
t2 ← vadr \ diskPages].
ffi("sector" (t2 \ nSectors) lshift: 12) +
("cylinder & head" (t2 / nSectors) lshift: 2) +
("disk" d "(vadr / diskPages) lshift: 1")]
Alto
allocate: nextPage after: address | index stop ch m vadr [
index ← false.
whileç true doç [
"go around bittable from address to end, and beginning to address.
we start over again if the table appears full or bitsFile is out of sync"
[index andç stop ffl totalPages [
"wrap around to where we started"
stop ← address.
index ← 0]
[index ff false ["first time or bitsFile out of sync"]
"disk probabbly full"
user quitThen:
’// YOUR DISK IS FULL - Please make some space available.
// Then resume Smalltalk and interrupt or continue as desired...’].
self open.
"index by bits rather than bytes? close enough for now"
index ← address land: 0177770.
stop ← totalPages].
bitsFile position ← index/8 + boffset.
whileç (index andç (index ← index+8) ̌stop) doç [
(ch ← bitsFile next) = 0377 ["8 full"]
"check that bitsFile position is correct --
possibly out of sync with index if growSmalltalkBy: occurred?"
bitsFile position ̄(index/8 + boffset) [index ← false]
m ← 0200.
forç vadr from: index-8 to: index-1 doç [
[(ch land: "nomask:" m) = 0 [
"page appears free. first update DiskDescriptor"
bitsFile skip: fl1; next ← ch ← ch lor: m.
"then check if page is really free"
vadr=0 ["O.S. boot"]
([nextPage init; freePage;
address: (self virtualToReal: vadr);
doCommand: CCR error: false]) [ffivadr]
"page not really free"]
"page not free according to bit"].
m ← m lshift: fl1].
].
]]
deallocate: page | index ch m [
[dirFileffnil [self open]].
index ← self realToVirtual: page address.
"character position"
bitsFile position ← index/8 + boffset.
ch ← bitsFile next.
"bit position"
m ← 0200 lshift: 0-(index land: 7).
"make page free by turning off bit in DiskDescriptor"
(ch land: m) = m [bitsFile skip: fl1; next← ch - m]
user cr; show: ’page already free (dealloc:)’]
growSmalltalkBy: n | zfpt i file page a zlen ["dp0 growSmalltalkBy: 100."
"find and read last page of small.boot, then extend file"
i ← 1. zlen← 96.
zfpt ← CoreLocs new base: (Vmem specialLocs‘7) length: zlen*2.
untilç zfpt‘(i+zlen) = 0 doç [i ← i+1].
a ← (zfpt‘(i+zlen-1)) + (zfpt‘i) - (zfpt‘(i-1)) - 1.
self open.
file ← self makeEntry: ’small.boot.’.
page ← file newPage.
page address: (self virtualToReal: a);
doCommand: CRR error: ’cannot read last page. growSmalltalkBy:’.
"bypass reading file and creating random access table, just extend it"
page lastPage [
file serialNumber: page serialNumber;
lastPage: page pageNumber;
pageAddresses: false "Read:, Write: check this";
Get: (page pageNumber: page pageNumber+n).
user space; print: self freePages; show: ’ pages left.’]
self error: ’growSmalltalkBy:. last page not last or 2 successive user grows’]
stampBoot | a file page ["dp0 stampBoot."
"update the time stamps in leader page of current boot file"
"find SafeId for current boot file"
a ← Vmem specialLocs‘13.
file ← self makeEntry: ’’.
file serialNumber: mem‘a, (mem‘(a+1)).
"read page one of the boot file to find out the leader address"
page ← file makeEntry: 1.
page address: mem‘(a+4).
"then set leader address and dirty flag, and close file
thereby updating create/write/read dates, but not name"
file doCommand: CCR page: page error: ’cannot read page 1 of boot file’;
leader: (page header: backp);
type: write;
close]
̃
AltoFile asFollows̃
File
Read: page | pn p palen [
pn ← page pageNumber.
[pageAddresses [palen ← pageAddresses length]
pn = 0 [palen ← 0]
ffifalse].
forç p from: (palen min: pn) to: pn doç [
"set up page for checking"
page
"zeroed by machine code
header: nextp ← [p < palen [pageAddresses‘(p+1)] 0];
header: backp ← [p=0 [0]; =1[leader] pageAddresses‘(p-1)];
length: [p < palen [page dataLength] 0];"
pageNumber: p;
address: [p=0 [leader] pageAddresses‘p];
doCommand: CCR error: ’readPage:’.
page lastPage [(lastpn ← p) < pn [ffifalse]]
p ffl palen andç pageAddresses[pageAddresses‘(p+1) ← page header: nextp]
"no need to store if already known or no page table"].
ffipage]
Alto
updateLeader: page | s time lastwrite [
"see <Alto>AltoFileSys.D, (p.3 leader page) for further info"
time ← user timewords.
s ← page asStream.
[type anymask: write [
"set creation/write/read date and file name"
directory flush.
lastwrite ← time.
s append: time; append: time; append: time.
name empty []
s nextString← name]
"remember creation, skip write, update read date"
lastwrite ← s next: 4.
s skip: 4; append: time].
self Write: page.
ffilastwrite]
̃
ILFile asFollows̃
File
close: e | p ["close file, possibly ignoring errors"
"for next open"
type ← read.
"shorten header block to first 2 words: command&length, file handle"
p ← self newPage.
p length: fl6.
self doCommand: Close page: p error: e]
release [self close: false]
close ["ignore errors if file was readonly" self close: [type = read [false] ’close’]]
classInit | i sym names ["ILFile classInit."
ILFilePool declare: NotFound as: ’207’.
names ← (
1011 (BadLeafHandle)
02000 (AnswerBit)
0176000 (RequestBits)
0260 (LeafType)
"5-bit operations for left field command block word"
0 (Error Open Close Delete)
6 (Read Write Reset)
"read/write modes"
0140 (SetEof "no holes, set eof")
0200 (NoExtend "don’t extend file on read or write")
0100 (NoHoles "for writing past end")
"open file modes"
0163400 (WriteOld "read, write, extend, any explicit, highest")
0103400 (ReadOld "read, any explicit, highest")
0167600 (CreateNew "read, write, extend, create, any explicit, next")
"control codes for left half of pupID1 field"
0 (Data Ack Noop)
5 (OpenConnection "if Reset")
9 (DestroyConnection Dally Quit BrokenConnection)) asStream.
forç i from: names doç [
forç sym from: names next doç [
ILFilePool declare: sym as: i.
i ← i+1]]]
errorString: errorCode | ef ename errorString notfound dollar cr [
[errorCode is: String [
errorString ← errorCode.
errorCode ← [(errorString‘1) isdigit [errorString asInteger] 0]]
errorString ← errorCode asString].
ename ← ’<System>Ifs.Errors’.
(self name compare: ename) = 2 [
"recursion" ffierrorString + ’ (cannot access Ifs.Errors !!!)’]
notfound ← errorString + ’(error code not found)’.
errorCode ̌0 [ffinotfound]
dollar ← ’$’‘1. cr ← 015.
ef ← directory oldFile: ename.
ef readonly.
errorString ← false.
"scan through the errors file looking for lines of the form:
$$nnsome message
"
untilç errorString doç [
ef skipTo: dollar [
ef next = dollar [
"valid line"
ef integerScan
> errorCode ["since errors are ordered" errorString ← notfound];
= errorCode [
errorString ← (String new: 200) asStream.
errorString print: errorCode.
untilç (ef peek = dollar orç ef peek = cr) doç [
errorString append: (ef upto: cr); space].
errorString ← errorString contents]
]]
"end of file"
errorString ← notfound].
ef close.
ffierrorString]
doCommand: com page: page error: e | in ecode [
page command: com.
whileç true doç [
"make sure connection is open"
directory open.
error ← nullString.
[in ← directory socket sendPacket: page packet [
in‘11 = BrokenConnection [ecode ← fl1]
"turn packet into a ILFilePage"
in ← [(self entryClass new) dictionary: self; page: in].
"check if answer is of same type as request"
((page header: 1) land: RequestBits) + AnswerBit =
((in header: 1) land: RequestBits) [ffiin]
ecode ← in header: 2]
"no response?"
ecode ← false].
"some kind of problem"
com = Quit ["ignore" ffifalse]
[ecode ff false orç ecode = fl1 ["make new connection" directory release]].
ecode = fl1 orç ecode = 1011 [
"try again after some reinitializing"
"reopen file"
self reopen.
"init page with new handle only -- don’t lose mode, length, etc."
page serialNumber: serialNumber]
error ← [ecode [e [self errorString: ecode] ecode asString]
directory directory + ’ not responding’].
e [self error: e "proceeding tries again"]
ffifalse]]
̃
ILFileDirectory asFollows̃
IFS
openFile: file mode: m | page [
"open bit modes
read
write
extend
multiple (0)
create name
explicit version in name (2b)
no, old, next or old, any
default (if no version specified) (2b)
no, lowest, highest, next
unused (7b)"
self open.
page ← file newPage.
"treat packet in page as a parameter block"
(ILParameterBlock new)
packet ← page packet;
nextword ← 0; "file handle"
nextword ← m; "modes"
nextString ← self userName;
nextString ← self userPassword;
nextString ← ’’; "connect name"
nextString ← ’’; "connect password"
nextString ← self checkName: file name.
"answer bits (in page header: 5)
same (5b)
version (4b)
bad, default lowest, default highest, default next, !*, !L, !H, !N, explicit old, explicit lowest, explicit highest, explicit next, explicit new, explicit-less, explicit between, explicit greater"
page ← file doCommand: Open page: page error: false [
file serialNumber: page serialNumber.
"open returns file length"
file lastPage: (page pageNumber - [
((page header: 4) land: 0777) =0 ["full last page" 1] 0] max: 1)]
file error = NotFound [ffifalse]
ffifile error: ’open ’ + (file errorString: file error)
]
̃
Integer asFollows̃
Arithmetic
< arg | t
[t ← arg asInteger.
t isLarge[t neg ff false]
ffiself < t]
̌arg | t
[t ← arg asInteger.
t isLarge[ffit neg ff false]
ffiself ̌t]
> arg | t
[t ← arg asInteger.
t isLarge[ffit neg]
ffiself > t]
ffl arg | t
[t ← arg asInteger.
t isLarge[ffit neg]
ffiself ffl t]
̃
Paragraph asFollows̃
Press printing
presson: press in: r [ffiself presson: press in: r style: self textStyle]
presson: press in: r style: style | char pos s3 y chop [
"Output paragraph inside rectangle (page coordinates)"
"probably ParagraphScanner should handle this"
text length > 0 andç text‘1 = 014 [
"formfeed --> page break"
ffiself copy: 2 to: text length]
y ← r corner y."We change corner y later"
s3 ← ParagraphScanner new of: self to: press style: style.
s3 init in: r.
pos ← s3 position.
chop ← [alignment=1 [0] alignment].
whileç (y andç (char ← s3 scan)) doç [
char = 011 [s3 tab]
char = 040 orç char = 015 [
"carriage return or exceeded max width and backed up to blank"
y ← s3 printfrom: pos aligned: [char=040[alignment] chop] skip: 1
[r corner y ← y. s3 init in: r. pos ← s3 position]]
char ff true [
"exceeded max width with no blanks in line"
s3 backup.
y ← s3 printfrom: pos aligned: 0 skip: 0
[r corner y ← y. s3 init in: r. pos ← s3 position]]
"user notify: ’unimplemented control char’"].
"Put out trailing text if any"
y andç ((pos=s3 position) orç (y ← s3 printfrom: pos aligned: chop skip: 0)) [
press append: text.
ffiy]
press append: text‘(1 to: pos).
ffiself copy: pos + 1 to: text length]
̃
ListPane asFollows̃
Pane protocol
hardcopy: pf | t cr first last lasty lineNum parag left right lineheight [
window hardcopy: pf thickness: 1.
[paraffnil [self makeParagraph]].
parag ← para asParagraph.
t ← para asStream.
last ← 0.
cr ← 015.
left ← frame minX.
right ← window maxX.
lasty ← frame minY.
lineheight ← self lineheight.
forç lineNum from: firstShown to: lastShown doç [
first ← last.
[(t skipTo: cr) orç lineNum = lastShown [last ← t position]
user notify: ’not enough lines’].
[lineNum = selection andç selection > 0 [
"outline selection; complementing doesn’t look good"
(self selectionRect-(01̆) inset: 01̆) hardcopy: pf thickness: 1]].
(parag copy: first+1 to: last-1) presson: pf in:
(pf transrect: (left ̆lasty rect: right ̆(lasty+lineheight+4))) style: style.
lasty ← lasty + lineheight.
]]
̃
Textframe asFollows̃
Printing
hardcopy: pf | r first last lasty len parag left right top bottom [
[paraffnil [self makeParagraph]].
parag ← para asParagraph.
frame=window [parag presson: pf in: (pf transrect: window) style: style]
left ← frame minX max: window minX.
right ← window maxX min: frame maxX.
bottom ← window maxY min: frame maxY.
top ← window minY max: frame minY.
lasty ← top + 4. "slop for char finding and making print rect larger"
first ← self charofpt: left+1 ̆lasty.
len ← parag length.
frame minX ffl left andç frame maxX ̌right [
"paragraph is inset and may be scrolled"
(parag copy: first to: len) presson: pf in: (
pf transrect: (left ̆top rect: right ̆(bottom+4))) style: style]
"yuk, frame extends left or right so do it a line at a time for clipping"
whileç (first < len andç lasty < bottom) doç [
last ← (self charofpt: right-1 ̆lasty) min: len.
r ← self rectofchar: last.
lasty ← lasty + r height.
(parag copy: first to: last) presson: pf in:
(pf transrect: (left ̆(r minY) rect: right ̆lasty)) style: style.
first ← last+1]]
̃
Textframe asFollows̃
Image
style [ffistyle]
Initialization
style: style
̃
PanedWindow asFollows̃
Window protocol
hardcopyTitle: pf [
"refresh title (since it’s a class var)"
self showtitle.
"draw title rectangle"
titleframe window hardcopy: pf.
"print title text (make frame larger)"
titleframe para presson: pf in: (pf transrect: (
titleframe frame origin rect: titleframe frame corner + (999 ̆2)))
style: titleframe style]
hardcopy: pf | pane [
self hardcopyTitle: pf.
"print frame rectangle"
frame hardcopy: pf.
"print all panes"
forç pane from: panes doç [pane hardcopy: pf].
"print cursor if it’s inside"
frame has: user mp [user currentCursor hardcopy: pf]]
̃
FileStream asFollows̃
Stream
end [
self reopen.
position < limit [ffifalse]
self read: page pageNumber+1 [ffi"page empty" position = limit]
ffitrue]
into: s endError: err | charsRead len t [
len ← s length.
[len > 80 [
charsRead ← len - (self readString: s from: 1 to: len)]
"in line: super into: s endError: err"
charsRead ← 0.
"read until count or stream is exhausted"
whileç (charsRead < len andç (t ← self next)) doç [s‘(charsRead ← charsRead+1) ←t]].
err [
charsRead = len [ffis]
user notify: ’only read first ’ + charsRead asString]
fficharsRead]
̃
Stream asFollows̃
Sequential reading and writing
nextString | len [
ffiself into: (String new: [
(len ← self next)
<192[len]"up to 191 chars (BCPL compat)"
len-192*256 + self next]) endError: true]"up to 16383 chars"
next: n [ffiself into: (array species new: n) endError: true]
̃
UserView asFollows̃
Misc System Stuff
systemworkspace1"for system releasers only!!!
this has been partitioned into three workspaces for editing convenience:
systemworkspace1
steps 0-4: general comments, handling Sources, creating a release.
systemworkspace2
steps 5-7: doing a vmem write or surgery, storing finished files on Phylum
systemworkspace3
step 8: after a release, e.g. updating press files
0. This boot file should be named small.boot for vmem writing, surgery, and command file purposes. If you made changes to the Sources disk be sure to update the current versions of (Xm)Smalltalk.Run, (Xm)Smalltalk.Syms, and (Xm)Byterp.mb on [Ivy]<Smalltalk>. This procedure works best on a Dorado for speed and disk space reasons, and it also can be done on an Alto (double disk O.S. required for vmem write). Microcode changes (including making non-xm versions) must(?) be done on an Alto. Step 5 (vmem writing) assumes enough space for another boot file. To turn off display during execution, hold down left shift key while selecting ’doit’.
For those who want to vmem write their own versions, do not execute steps 4, 7 or 8 without further editing of file and directory names. Underlined items are typical values and normally must be edited to be useful.
1. to create an xm version: filin changes and selected goodies. Undeclared must be empty for release to work (step 4). copy the categories of classes which have changed to systemworkspace3 (for later printing) and recompile it.
dp0 filin: (’changes.st’).
phylum filin: (’<Small-goodies>xx.st’).
Undeclared contents inVector, user changedCategories
2. update version number/letter and comments in UserView version
3. the Sources file will be ordinarily be created in step 4. if only a few changes are involved, it may be somewhat faster to copy the old sources file to the new sources file (this step). then step 4 will only append changes.
phylum store: Sources reset as: ’Smalltalk.Sources.’ + user versionName.
4. checks Undeclared, writes all or appends changed messages to Sources file, updates ChangedMessages, inits Changes, puts up greeting, and sets the default user name & password. note: this is only to be executed for releasing the Smalltalk system itself (supply the proper password!!). if you plan to do a vmem write next, better to do this as first line of step 5.
user releaseExternalViews.
phylum name: ’Smalltalk’ password: ’password’.
user release.
phylum name: ’Smalltalk-User’ password: ’Smalltalk’.
to write out the sources for a private version, specify which directory to use (don’t leave Smalltalk as the default) and which categories and/or classes are to be included.
| c classes. user releaseExternalViews. classes ← (Vector new: 50) asStream.
phylum name: ’name’ password: ’password’.
forç c from: (’category1’ class1) doç [
c is: String [classes append: (SystemOrganization category: c)]
classes next ← c].
user file: (phylum file: ’<ddd>xx.Sources.’ + user versionName)
classes: classes contents changesOnly: false.
"
systemworkspace2"for system releasers only!!!
5. if no surgery or vmem write involved, skip to step 7. start here with an xm version to make a non-xm version. specify option below:
1 vmem write (includes xm surgery)
2 xm surgery
3 non-xm surgery
to make things totally automatic, edit in your valid Maxc name and password, otherwise Ftp will ask you later (some of the file transfers can be edited out (i.e. if files are already present; also Ramload is superfluous on Dorado). in the case of surgery only, at the end you will have to hit a key after safing.
| option prefix dir file.
option ← 1.
dir ← phylum asFtpDirectory.
dir directoryName: ’Smalltalk-76’.
prefix ← [option=3 [’’] ’Xm’].
forç file from: (’Smalltalk.Run’ ’Smalltalk.Syms’ ’Byterp.Mb’) doç [
dir retrieve: prefix + file as: file].
dir closeThen: ([option=1 [’delete oldsmall.boot;
copy newsmall.boot ← small.boot; ’] ’’]) +
’ftp maxc Login/c yourname yourpassword directory/c alto retrieve/c packmu.run ramload.run;
Resume small.boot;
Ramload/N Byterp.mb/F 1000/A;
Smalltalk.run’.
option=1 [(VirtualMemory new) giveBirth3. user quit]
Vmem ramwrite: (dp0 oldFile: ’byterp.mb’).
Vmem surgery: (dp0 oldFile: ’Smalltalk.run’).
6. after a successful vmem write or surgery, execute this (selecting here is tricky or type in a Dispframe)
user systemStartup.
7. edit lastversion (and Smalltalk password) and execute the following, then close this window (clean up screen for non-xm?), and quit. it then renames old versions of files, stores new versions of files, e.g. remote XmSmall.Boot becomes XmSmall.Boot.5.5g and local Small.Boot becomes remote XmSmall.Boot
| lastversion dir file remotefile.
lastversion ← ’5.5j’.
dir ← phylum asFtpDirectory.
dir login: ’Smalltalk’ password: ’password’.
forç file from: (’Small.Boot’ ’Smalltalk.Syms’) doç [
remotefile ← ([user hasXM [’Xm’] ’’]) + file.
dir rename: remotefile newName: remotefile + ’.’ + lastversion;
store: file as: remotefile].
(dp0 file: ’rem.cm’) append: dir commands; cr; close.
user releaseMessage.
"
systemworkspace3"for system releasers only!!!
8. to update press files for system categories or cross reference listing directly on Phylum, browse or spawn this window. edit pf to specify a list of system categories to print, usually from step 1, e.g. user changedCategories: (’Basic Data Structures’ ...) or SystemOrganization categories (for all); delete toPrinter if you don’t want the press files printed. edit xref to be user classNames if you want to generate a cross reference listing.
| pf xref cat.
pf ← (’Text Objects’ ’Kernel Classes’ ’Press File Support’ ’IFS File System’ ’Alto File System’ ’Panes and Menus’ ’Files’ ’Juniper’ ’Windows’ ’Graphical Objects’ ’Numbers’ ’Basic Data Structures’ ).
xref ← ().
user releaseExternalViews.
phylum name: ’Smalltalk’ password: ’password’.
forç cat from: pf doç [
((phylum file: (cat + ’.Press’) asFileName) asPressPrinter) stamp;
printclass: (SystemOrganization category: cat); close; toPrinter].
xref empty []
user printCrossReference: xref on: (phylum file: ’CrossReference.Press’).
"
workspace
[user notify: ’Not meant to be executed’]
"
XEROX - Learning Research Group
user screenextent: 6405̆80 tab: 05̆0.
NotifyFlag ← true.
Changes init.
user changedMessages
user changedClasses
user changedCategories
Undeclared contents
to set the default printer
PrinterName←’Menlo’.
PrinterName←(PressFile new) selectPrinter: PrinterName.
to change phylum to access your account
user releaseExternalViews. phylum name: ’name’ password: ’password’.
dp0 filin: (’Changes.st’).
(dp0 file: ’changes.st’) filout.
(dp0 file: ’xxx’) edit.
dp0 pressfilin: (’xxx.press’).
(dp0 filesMatching: ’*.st’) sort
dp0 list. dp0 freePages
dp0 delete: ’old’
dp0 rename: ’old’ newName: ’new’
for reinitializing Sources and phylum
Sources release. phylum release. Sources reopen.
to make Smalltalk Sources local
| s. s ← ’Smalltalk.Sources.’.
(phylum asFtpDirectory) retrieve: ’<Smalltalk>’ + s + user versionName as: s; close.
Sources on: (dp0 file: s).
to switch back to remote Sources
Sources close; on: (phylum file: ’<Smalltalk>Smalltalk.Sources.’ + user versionName).
to filin a remote Smalltalk file
phylum filin: (’<Small-goodies>NotifyChange.st’).
to print a remote/local press file
(phylum pressfile: ’<Smalltalk>xxx.press’) toPrinter.
(dp0 pressfile: ’xxx.press’) toPrinter: ’Lilac’.
File noChanges.
BitRect new fromuser; edit.
user schedule: (defaultBitRectEditor newframe).
DocumentEditor new defaultdocument: ’test’.
DocumentEditor new init: (Document new fromPress: ’test.document’).
user releaseExternalViews.
E sleep. E kill. E ← nil.
E ← Etherworld new. E broadcastFilter: true. E wakeup.
Sources reopen.
for primary Smalltalk access to file servers and printers at other sites.
substitute yourserver for phylum above, compile this workspace
PrinterName ← ’name-of-your-printer’.
Smalltalk declare: yourserver.
yourserver ← ILFileDirectory new directory: ’name-of-your-server’.
yourserver name: ’Smalltalk-User’ password: ’Smalltalk’.
Sources on: (yourserver file: ’<Smalltalk>Smalltalk.Sources.’ + user versionName).
Changes init.
user Swat.
"
version [ffi’Smalltalk 5.5k ’ + [user hasXM [’XM ’] ’’] + ’November 20?’]
"user version
low level disk address calculations are more general (necessary for 14-sector Dorado/Dolphin file systems)
better error recovery for broken and timed out Leaf connections
AltoFileDirectory disk page allocation/deallocation bugs fixed
miscellaneous printing fixes
Juniper fixes (2)
goodie: again-del-forget.st
Phylum account changes
default Leaf connection is logged in to <Smalltalk-User>
system release uses [Phylum]<Smalltalk-76> instead of [Ivy]<Smalltalk>
see UserView workspace for logging into your account on Phylum, changing default printer
September 3, 5.5j
duplicate packet fix
fixes to ether (routing table, name lookup, phylum, Int32), printer names,
files, UserView time messages, context simulation,
replace in BitBlt & Paragraph, NotifyWindow cleanup,
Class code: always decompiles with left shift key, window printing fixes,
SystemOrganization globalComment contains no nulls
the following changes files were included:
[phylum]<small-goodies>
5.5i.changes.st, notifychange.st, window-print-changes.st
[phylum]<findit>5.5i.more.changes.st
[maxc]<dolbec>int32change.st
[maxc>ingalls>fixes.st
[ivy]<kaehler>context-simulation.st
[ivy]<borning>context-changes.st
May 1, 5.5i
obscure file bugs eliminated; version features added (goody: File-version.st).
Ifs multiple connections fixed; Ifs error numbers looked up in Ifs.Errors.
duplicate packets eliminated at lowest level.
Int32 primitive fix. Juniper retransmit parameters increased
Integer compare: LargeInteger now works
CodePane/FilePane ’print’ (within a CodeWindow) now prints entire Paragraph
rather than only part within window
ScrollBars hide during CodePane again & cancel. cancel saves your old text, so
an immediate undo will replace the current selection with your previous text.
April 11, 5.5h
Alto file names limited to 39 characters (’somestring’ asFileName will fix
name, truncating if necessary). other misc. file, ether, simulator fixes.
BitBlt fixed so that BitRects don’t lose their bits
BitBlt used to speedup reading&writing files, sending Press files to printers
ParagraphScanner puts underlining into Press files
printer names updated (PressFile classInit). hashing-changes.st included.
after font cataclysm, get new version of Fonts.Widths before printing
system release procedure modified
March 6, 5.5g
ether, file, vmem writing fixes. cursor clipping on screen boundary.
BitBlt used for String growing, copying, replacing
goodies included: display-off-after-notify.st, CodePane-doit.st,
context-simfix2.st, ILchanges.st, string-changes.st
see [Phylum]<Smalltalk> for the following files. () surround an optional prefix or suffix.
Document.Press
mini-guide to Smalltalk system and user interface
VersionHistory
information about versions up to 5.5g
ChangedMessages
a list of messages which have changed
xxx.Press
press file for CrossReference or for system category ’xxx’ in current version
to save paper, consider consulting the LRG alcove copies
(Xm)Small.Boot(.version)
(Xm)Smalltalk.Syms(.version)
older versions of .Boot and .Syms are explicitly named.
Smalltalk.Sources.version
all Smalltalk.Sources (including the current one) are explicitly named
[Phylum]<Small-Goodies> contains miscellaneous bug fixes and new features (and even some documentation: goodies.bravo, .press) offered by the community of Smalltalk Users.
"
System quit/resume
overlay: fileid | t [
dp0 stampBoot.
self releaseExternalViews.
"put the ethernet to sleep"
[Effnil [] E sleep].
"turn off display during quit/resume"
t ← mem‘0420. mem‘0420 ← 0.
self InLd: fileid.
"we start here after a resume"
mem‘0420 ← t.
whileç user keyset>0 doç [user show: ’The keyset is stuck’; cr]]
̃
’From Smalltalk 5.5j+ XM September 18 on 20 October 1980 at 1:03:32 pm.’
̃
"HalfToner"
Class new title: ’HalfToner’
subclassof: Object
fields: ’lines pixelsPerLine black white errorString rect vect inpix outpix nlines npix strm inset’
declare: ’’;
asFollows̃
This class converts ais image files to screen bits
AIS to Bits
decode: str using: s | i j k x cascadeRight cascadeDiag val error r msk masks
["Change 8-bit grey from str filling s"
masks← (128 64 32 16 8 4 2 1).
cascadeRight←0.
cascadeDiag←errorString‘1.
i←msk←j←k←1. x←0-outpix.
s‘1←0.
forç i to: pixelsPerLine doç
[whileç x<0 doç
[val←(str‘i)-black.
[(error←cascadeRight-val)ffl0
["print Black" s‘j←masks‘msk+(s‘j). (error>white)[error←white] ]
"print White" (error←error+white)<0[error←0] ].
error←error/2.
val←error/2.
errorString‘k←cascadeDiag+val.
cascadeRight←errorString‘(k+1)+error.
cascadeDiag←val.
[(msk←msk+1)>8[msk←1. j←j+1. s‘j←0] ].
x←x+inpix. k←k+1].
x←x-outpix].
ffis] primitive: 109
doFile | str i s2 r y skipsum
[str←String new: pixelsPerLine.
r←00̆ rect: (pixelsPerLine*outpix/inpix)1̆. r moveto: rect origin copy.
s2←String new: 1+((pixelsPerLine*outpix)/(8*inpix)).
vect←Vector new: lines. strm reset; position←2048+(inset y*npix). "crop top"
i←1. y←0-outpix. skipsum←0.
whileç iľines doç
[skipsum←skipsum+inset x. "inset left"
strm skip: skipsum. skipsum←0. "do all tallied skips prior to next read"
strm into: str endError: true.
r bitsFromString: (self decode: str using: s2).
skipsum←skipsum+npix-(pixelsPerLine+inset x).
r origin y←r origin y+1. r corner y←r corner y+1.
[(y←y+inpix)ffl0 "next line?"
[i←i+1. y←y-outpix.
whileç (yffl0 andç iľines) doç [i←i+1. y←y-outpix. skipsum←skipsum+npix] ]
skipsum←skipsum-npix] ]. "not next line"
strm close]
intoPress: p file: f | outrect "Creates an external file reference"
[outrect←p transrect: rect.
p setp: (outrect origin); dotsç
[p setcoding: 8 "byte samples" dots: npix lines: nlines;
setmode: 3 "to right and to bottom of page";
setwindowwidth: pixelsPerLine height: lines
skipdots: (inset x) skiplines: (inset y);
setsizewidth: (outrect width) height: (outrect height);
dotsfromAIS: f] ]
"
|p. p←dp0 pressfile: ’pix.press’.
p pictureinit. (HalfToner new test) intoPress: p file: ’Rolfup.AIS’. p close.
"
Init/Access
nlines [ffinlines]
npix [ffinpix]
rect [ffirect]
rect←rect
setup: strm | inrect croprect
[strm readonly.
(strm word: 2)1̄024 orç (strm word: 9)8̄[user notify: ’bad file’]
nlines←lines←strm word: 4.
npix←pixelsPerLine←strm nextword.
black←0. white←255.
inrect←00̆ rect: pixelsPerLinel̆ines.
inrect moveto: rect origin.
inrect usermove; comp. "show whole"
croprect←rect copy.
croprect moveto: inrect origin copy.
croprect maxstretch: inrect.
croprect userstretch: inrect.
inrect comp.
inset←croprect origin-inrect origin.
pixelsPerLine←croprect width.
lines←pixelsPerLine*rect height/rect width.
[rect width>pixelsPerLine
["blowup" inpix←32. outpix←(32*rect width/pixelsPerLine)]
"shrink" outpix←32. inpix←(32*pixelsPerLine/rect width)].
errorString←String new: pixelsPerLine*outpix/inpix+2.
errorString all←0]
strm [ffistrm]
test | files
[files←(dp0 filesMatching: ’*.ais.’) sort.
files empty[user notify: ’no .ais files on disk’]
strm←dp0 file: (files‘(Menu new stringFromVector: files) zbug). strm readonly.
rect←Rectangle new usersize. self setup: strm; doFile]
"
HalfToner new test.
"
̃
SystemOrganization classify: HalfToner under: ’Graphical Objects’.̃
Class new title: ’PressFile’
subclassof: Object
fields: ’DL "<File> stores data list"
EL "<Set> accumulates entity list"
parts "<Set> accumulates part directory"
DLstart "<Integer> position of current entity in DL"
ELstart "<Integer> word position of current entity in EL"
Pstart "<Integer> record position of current page in DL"
eorigin "<Point>"
scale "<Integer> micas per Alto screen dot"
boundbox "<Rectangle> bounding box for current page"
fontcodes "<Vector> of run codes corresponding to current fonts"
fontdefs "<Vector of WidthTables> corresponding to fontcodes"
estate "<Vector> some entity state to avoid setting duplicate values"
FL "<Set> accumulates strings for Ext. File part" ’
declare: ’prevstyle SMentity recordsize printers printerMenu ’;
asFollows̃
Initialization
of: DL [
EL ← Set new string: 200.
FL ← Set new string: 40.
parts ← Set new string: 40.
fontcodes ← Vector new: 0.
fontdefs ← Vector new: 0.
estate ← Vector new: 3 "font, spacex, spacey, ...".
prevstyle← nil.
self scale: PressScale;
startPage]
Entity/Page/File Commands
close | p i font [
DL writingfffalse [DL close]
self closePage.
partsfffalse orç parts empty []
"if present, include the external file part --- added Sept 80"
[FL empty[]
self partç [DL append: FL] code: 2.
FL reset.
self padpage].
"put font names and descriptions into font directory (part)"
self partç [
forç i to: fontdefs length doç [
font ← fontdefs ‘ i.
DL nextword← 16; nextword← i-1;
next ← font min; next ← font max.
self Bcpl: font name pad: 20.
DL next ← font face; next ← font min;
nextword ← font pointsize; nextword← 0]]
code: 1.
"write part directory. Pstart is current page position"
DL append: parts asReadStream.
self padpage.
p ← self recordnum.
"document directory"
DL nextword← 27183; "press password"
nextword← p + 1 "number of records";
nextword← parts position / 8 "number of parts";
nextword← Pstart; "part dir and length"
nextword← p - Pstart;
nextword← fl1; "backpointer to obsolete doc dir"
append: user timewords; "2 time words"
nextword← 1; "first and last copies"
nextword← 1;
nextword← fl1; "first and last pages"
nextword ← fl1;
nextword ← ’S’‘1 "solid color (looked at by color printers)";
next: 2*(0177-12) ← 0377.
p ← user now.
self Bcpl: self name pad: 52;
Bcpl: [currentProfileffnil [dp0 diskID‘1] currentProfile printedBy] pad: 32;
Bcpl: [((String new: 40) asStream) print: p‘1; space; print: p‘2; contents] pad: 40;
padpage.
DL close.
parts reset]
Bitmaps/Dots
AIS: file width: w height: h croprect: r at: pt scale: s
[self setp: (self transpt: pt); dotsç
[self setcoding: 8 "byte samples" dots: w lines: h;
setmode: 3 "to right and to bottom of page";
setsizewidth: (s*r width*scale) asInteger
height: (s*r height*scale) asInteger;
setwindowwidth: r width height: r height
skipdots: r minX skiplines: r minY;
dotsfromAIS: file]]
"
(dp0 pressfile: ’pix.press’) somefont; AIS: ’girl.ais’ width: 512 height: 512 croprect: (505̆0 rect: 5005̆00) at: 368̆0 scale: 0.65; close.
"
dotsfromAIS: file | f
[f←file length inString+file+[file length even[’ ’]’’]. "BCPLize"
DL nextword ← 4; nextword ← 4; append: f. FL append: f]
setwindowwidth: w height: h [
self setwindowwidth: w height: h skipdots: 0 skiplines: 0]
setwindowwidth: w height: h skipdots: sd skiplines: sl
[DL nextword ← 1;
nextword ← sd; nextword ← w;
nextword ← sl; nextword ← h]
Private
classInit | a p ["PressFile classInit."
Smalltalk declare: PressScale as: 32.
recordsize ← 512.
SMentity ← 5.
a ← (String new: 250) asStream.
"from [Maxc1]<Altodocs>NetTopology.Press, October 1980. in order of net number"
printers ← (
"net #""printer names"
" 1"’Navajo’"HENRIETTA"
" 3"’Menlo’ ’Clover’ ’Lilac’ "PARC: BLDG 35, FLOOR 2"
" 5"’Kanji’"PARC: BLDG 34"
" 6"’Wonder’ ’Quake’"PARC: BLDG 35, FLOOR 1&3"
"10"’Puff’"A&E"
"12"’White’ ’Colorado’"PASADENA"
"14"’Niagara’ ’Tioga’"WEBSTER"
"20"’Yoda’"PARC: BLDG 32"
"21"’Lily’ "SPG"
"23"’Ranger’"DALLAS"
"26"’Windfall’"DC"
"27"’Genesee’"WEBSTER"
"33"’Amarok’"TORONTO"
"34"’Yankee’"STAMFORD"
"36"’Cyclops’"LEESBURG"
"54"’Rover’"A&E"
"55"’SPGEng’ ’Emperor’"A&E"
"56"’Thud’"A&E"
"60"’Adelie’ ’Daisy’ ’RockHopper’ "BAYHILL"
"62"’Bud’"?"
).
forç p from: printers doç [a append: p; cr].
a append: ’same printer’; cr; append: ’no printer’.
printerMenu ← Menu new string: a contents]
̃
SystemOrganization classify: PressFile under: ’Press File Support’.̃
PressFile classInit̃
Rectangle asFollows̃
Altering
maxstretch: bound | bx by boundr selfr
[bx←(bound corner-origin) x. by←(bound corner-origin) y.
boundr←bx asFloat/by. selfr←self width asFloat/self height.
selfr>boundr[self extent←(bx(̆bx asFloat/selfr) asInteger)]
self extent←((by asFloat*selfr) asIntegerb̆y)]
usermove
[self usermove: user screenrect]
usermove: bound | m lim
[lim←bound corner-self extent. self bordercomp. m←user mp.
whileç true doç
[[user redbug
[self bordercomp; moveto: (bound origin max: ((m←user mp) min: lim)); bordercomp]].
whileç (user anybug andç m=user mp) doç [].
[user bluebug[user waitnobug. ffiself bordercomp]]]]
usersize
[self usersize: user screenrect]
usersize: bound | m lim
[[self originffnil[origin←user mp. self extent←16]].
self bordercomp. m←user mp.
whileç true doç
[lim←bound corner-self extent.
[user redbug
[self bordercomp; moveto: (bound origin max: ((m←user mp) min: lim)); bordercomp]].
[user yellowbug[self bordercomp.
corner←m←(user mp min: bound corner) max: origin. self bordercomp]].
whileç (user anybug andç m=user mp) doç [].
[user bluebug[user waitnobug. ffiself bordercomp]]]]
̃
FtpDirectory asFollows̃
FileDirectory
store: s | t [
s is: Vector [forç t from: s doç [self store: t]]
command append: ’ Store/C ’; append: (self checkName: s)]
retrieve: s | t [
s is: Vector [forç t from: s doç [self retrieve: t]]
command append: ’ Retrieve/C ’; append: (self checkName: s)]
̃
dpj name: ’Smalltalk2’ password: ’LRG’.
’From Smalltalk 5.5k XM November 15 on 18 November 1980 at 12:30:40 pm.’
̃
JuniperInterface asFollows̃
FILE DIRECTORY (restricted)
Insert: pFile
"...issues a ’create file’ request to the Juniper file server and sets the returned long file handle and name in pFile (a JuniperFileController)."
| tRequest tResult
[
tRequest ← self newRequestParameterBlock. "1"
tRequest longInteger: 2 ← 0 "user rawtotalsecs". "1.5"
tRequest nextDataBlockString ← (self checkDirectory: (pFile name)). "2"
tRequest nextDataBlockString ← ’’. "3"
tResult ← self doAction: sCreateFile requestPrs: tRequest. "4"
pFile
longFileHandle: tResult nextDataBlockString;
name: tResult nextDataBlockString. "5"
]
"
1. Create a new request parameter block.
1.5 Set creation date (to default (current) -- later some specific date&time?)
2. Get the file name from pFile, add the default directory name to it if necessary, and write it in the request parameter block.
3. Blank the file server field of the request parameter block.
4. Issue a ’create file’ request and get the result parameter block.
5. Set the long file handle and name (from the result parameter block) in pFile.
"
MISC (internal)
login: pName password: pPassword
"...sets the necessary request parameters and invokes doLogin to issue a login request. pName (a String) specifies the name of an account on the Juniper file system. pPassword (a String) specifies the password of the account."
| tRequest
[
tRequest ← self newRequestParameterBlock. "1"
tRequest
leader: 3 ← sLogin; "2"
leader: 4 ← 512; "3"
leader: 5 ← 7; "4"
leader: 6 ← (self hash: pPassword); "5"
nextDataBlockString ← pName; "6"
pupType ← sCustodian. "7"
ffi (self doLogin: tRequest) "8"
]
"
1. Create a new request parameter block.
2. Set the command to Login.
3. Set the number of bytes per page to 512.
4. Set the Juniper version number to 7 (Nov 80).
5. Set the hashed account password to pPassword hashed.
6. Set the account name to pName.
7. Set the packet pup type to custodian.
8. Issue the request and return the result.
"
̃
ParagraphEditor asFollows̃
Editing
again | many
[many← user leftShiftKey.
[self fintype [Scrap ← Scrap text. self select]].
many[whileç self againOnce doç []]
self againOnce[] frame flash]
againOnce | t
[t ← para findString: Deletion startingAt: c2.
t=0 [ffifalse]
self unselect.
c1 ← t.
c2 ← c1 + Deletion length.
self replace: Scrap; selectAndScroll]
Public Messages
kbd | more char "key struck on the keyboard"
[c1<c2 andç self checklooks[ffiself show complement]
more ← Set new string: 16.
[begintypein[] Deletion ← self selection. begintypein ← c1].
whileç (char ← user kbdnext) doç [
char
=bs ["backspace"
more empty [begintypein ← begintypein min: (c1 ← 1 max: c1-1)]
more skip: fl1];
=cut [self fintype. [c1=c2[c2← c1+1 min: para length+1]].
self replace: nullString; complement. Scrap ← Deletion. ffiself];
=paste [ffiself paste];
=ctlw ["ctl-w for backspace word"
[more empty [] self replace: more. more reset. c1 ← c2].
c1 ← 1 max: c1-1.
whileç [c1>1 andç (para‘(c1-1)) tokenish] doç [c1 ← c1-1].
begintypein ← begintypein min: c1];
=esc ["select previous type-in"
[more empty[self unselect]
self replace: more. c1 ← c2].
self fintype.
c1 ← c2-Scrap length.
ffiself complement]
"just a normal character"
more next← char].
self replace: more.
c1 ← c2.
self selectAndScroll]
̃
’From Smalltalk 5.5i XM May 1 on 18 November 1980 at 12:37:46 pm.’
̃
Class asFollows̃
Message access
derstands: selector | c"overstands? undersits? - forget it"
[selector is: Vector[forç c from: selector doç [self derstands: c]]
(messagedict has: selector)fffalse[]
messagedict ← messagedict delete: selector.
self organization delete: selector.
lastClass ← lastSelector ← lastParagraph ← nil.
[Changes has: (c←title+’ ’+selector) [Changes delete: c]].
Changes insert: (c←’~’+c).
ffic]
install: name method: method literals: literals
code: code backpointers: backpointers | c
[messagedict ← messagedict insert: name method: method
literals: literals code: code makeBoldPattern backpointers: backpointers.
lastClass ← self.
lastSelector ← name.
lastParagraph ← code.
Changes insert: (c←title+’ ’+name).
Changes has: (c←’~’+c)[Changes delete: c]]
Filin and Filout
noChanges | s t
[t← title+’ *’.
forç s from: Changes contents doç
[(s‘1=126 "~" andç (t match: s‘(2 to: s length)))
orç (t match: s)
[Changes delete: s]]]
̃
ParagraphPrinter asFollows̃
Class stuff
printchanges: lis | selector class heading old mes s delFlg
"prints Changes format: (’class message’ ’class message’ ...)
or alternate format: (class (message ...) class () ...) or both
If an element appears in the list of the form ’~class message’, this puts out a
line causing the system to forget that method. These come after any additons,
owing to the sort on Changes"
[lis empty [ffilis]
user displayoffwhileç [
lis ← lis asStream.
old ← mes ← false.
whileç class doç [
"get next class, selector pair"
[delFlg← false.
mes andç (selector ← mes next) ["more of alternate form"]
s ← lis next [
s is: UniqueString [
class ← Smalltalk lookup: s.
mes ← lis next asStream.
selector ← mes next]
"Changes format"
s ← s asStream.
[s peek=126 "~"[s next. "take it off stream" delFlg← true]].
class ← Smalltalk‘(s upto: 040) unique.
selector ← s upto: 040.]
class ← false].
delFlg[self printForget: selector class: class]
"same, different or no class"
[old ff class []
[old [old endCategoryOn: self; endChangesOn: self]].
class ff false ["finished"]
user cr; show: class title.
old ← class.
class startChangesOn: self.
heading ← ’As yet unclassified’].
classfffalse []
user space; show: selector.
s ← class organization invert: (selector ← selector unique).
s[[s ̄heading[class startCategory: (heading ← s) on: self]].
class printMethod: selector on: self]]]]
printForget: selector class: class
"Print a line that causes a message to be forgotten"
[user cr; show: ’~’+class title+’ ’+selector.
self print:
(class title + ’ derstands: ’ + selector + ’.
̃’) asParagraph]
̃
Form asFollows̃
FILING
read: filename | f strip w h form stripheight leftoverlines i
["Reads the Form from the disk in the format width,height,bits."
f← dp0 oldFile: filename.
f readonly.
w ← f nextword.
h ←f nextword.
extent ← wh̆.
w*h < 64000
[bits ← (Form new extent: extent) bits.
f into: bits.
f close.
]
f close.
user notify: ’too many bits to be a Form’.
]
̃
BitRect asFollows̃
Filin and filout
filin: title | f i x y rect strips "read bits from a file"
[f←dp0 oldFile: (title concat: ’.pic.’).
f readonly.
f end[f close. user notify: ’no data’]
x←f nextword. y←f nextword.
rect←Rectangle new origin: [origin is: Point[origin] 00̆] extent: xy̆.
self title: title in: rect.
stripheightf̄ nextword[user notify: ’strip heights dont match’]
strips ← self strips.
forç i to: strips length doç
[f into: data‘i].
f close]
̃