// D I R E C T O R Y R O U T I N E S
// Copyright Xerox Corporation 1979, 1980
// E. McCreight
// last edited by R. Johnsson May 11, 1980 4:17 PM
get "sysdefs.d"
get "streams.d"
get "altofilesys.d"
get "COMSTRUCT.bcpl"
external [
InitScanStream
GetScanStreamBuffer
FinishScanStream
]
static [
MYDS
EOP
FILLQ
SWITCHQ
NMATCHES
MATCHQ
JUSTTAIL
GTYPE
PAUSE
DIAGQ
DIAGF
MYERRRTN
P
PLen
D
DLen
MP
FirstMisMatch
]
let MAPDIR(PATTERNQ, ACTION, PREFIXMATCH; numargs na) = valof
[ if na ls 3 then PREFIXMATCH = false
INITDIRBLK(SORTED)
let PATTERN = vec 129
QFTOSTRING(PATTERNQ, PATTERN)
unless PREFIXMATCH do AppendDot(PATTERN)
for I=FindFirst(PATTERN) to FindLast(PATTERN) do
[ let de = DIRHDBLK!I
if PMatchesD(PATTERN, de,
PREFIXMATCH)
then
[ let T = ACTION(DIRHDBLK!I, FirstMisMatch-1)
if T ne 0 then resultis T
]
]
resultis 0
]
and PMatchesD(Pattern, MyDE, MatchPrefix) = valof
[ let DString = vec 129
D = DString
MoveBlock(D, lv MyDE>>MYDE.S,
(MyDE>>MYDE.S.length rshift 1)+1)
P = Pattern
MP = MatchPrefix
PLen = P>>STRING.length
if PLen eq 0 then resultis MatchPrefix
DLen = D>>STRING.length
FirstMisMatch = MatchTails(1, 1, false)
resultis (FirstMisMatch ge DLen+1)? true,
((FirstMisMatch gr 1)? MatchPrefix,
false
)
]
and MatchTails(NextPChar, NextDChar, PrefixHasStar) = valof
[ // returns +i if the match "succeeded" and matched up
// through the i-th character of D.
// returns +infinity if all characters of D were matched.
// returns -j if there was no successful match,
// but the pattern was prefixed by a * and the
// first misaligned partial match occurred after
// ignoring the first j characters of D.
// returns -(DLen+2) if an exactly-aligned match
// could not be made and either the pattern
// was not prefixed by a * or not even a
// misaligned partial match could be made.
while (NextPChar le PLen)&
((P>>STRING.char↑NextPChar eq $**)%
(P>>STRING.char↑NextPChar eq $%)) do
[ PrefixHasStar = true
NextPChar = NextPChar+1
]
if NextPChar gr PLen then
resultis (NextDChar ls DLen+1 & PrefixHasStar)?
DLen+1,
NextDChar
let FirstMatch = DLen+2
let Alignment = 0
let PC = P>>STRING.char↑NextPChar
if (PC ge $a) & (PC le $z) then PC = PC+($A-$a)
while (Alignment eq 0) %
(PrefixHasStar &
(NextDChar+Alignment le DLen+2)) do
[ let CurPC = PC
let i = 0
while true do
[ let Index = NextDChar+Alignment+i
if Index gr DLen then Index = DLen
let CurDC = D>>STRING.char↑Index
if (CurDC ge $a) & (CurDC le $z) then
CurDC = CurDC+($A-$a)
if (CurDC ne CurPC) &
((CurPC ne $#) %
(CurDC eq $.)) then
break
i = i+1
if NextPChar+i gr PLen then
[ if (NextDChar+Alignment+i ls
DLen) & PrefixHasStar &
(not MP) then
break
resultis NextDChar+Alignment+i
]
CurPC = P>>STRING.char↑(NextPChar+i)
if (CurPC eq $**) % (CurPC eq $%) then
[ if FirstMatch gr Alignment then
FirstMatch = Alignment
let T = MatchTails(NextPChar+i,
NextDChar+Alignment+i)
if T gr 0 then resultis T
// That match failed, try another
Alignment = (-T gr Alignment)?
-T, Alignment
break
]
if (CurPC ge $a) & (CurPC le $z) then
CurPC = CurPC+($A-$a)
]
Alignment = Alignment+1
]
resultis -FirstMatch
]
and FindFirst(Pattern) = valof
[ let Prefix = vec 129
ComputePrefix(Pattern, Prefix)
resultis BinSearch(DIRHDBLK, Prefix)
]
and FindLast(Pattern) = valof
[ let Prefix = vec 129
ComputePrefix(Pattern, Prefix)
let Len = Prefix>>STRING.length+1
Prefix>>STRING.length = Len
Prefix>>STRING.char↑Len = #377
resultis BinSearch(DIRHDBLK, Prefix)
]
and ComputePrefix(Pattern, Prefix) be
[ let Length = 0
let PatLen = Pattern>>STRING.length
while Length ls PatLen do
[ let C = Pattern>>STRING.char↑(Length+1)
if selecton C into
[ case $**:
case $#:
case $%:
true
default: false
]
then break
Length = Length+1
Prefix>>STRING.char↑Length = C
]
if (Length eq PatLen)&(PatLen ge 1)&
(Pattern>>STRING.char↑PatLen eq $.) then
Length = Length-1 // remove final period
Prefix>>STRING.length = Length
]
and FilesWithSuffix(FNQ, SufQ, NewFNQ) = valof
[ let QCopy = vec size QS/16
INITQ(QCopy)
COPYQ(FNQ, QCopy)
JUSTTAIL = false
MATCHQ = NewFNQ
NMATCHES = 0
MAPDIR(QCopy, ESCMATCH)
if NMATCHES eq 1 then
[ let C = GETQR(NewFNQ)
while C eq $. do
C = GETQR(NewFNQ)
PUTQR(NewFNQ, C)
resultis 1
]
EMPTYOUTQ(NewFNQ)
let FileName = vec size STRING/16
COPYQ(FNQ, QCopy)
QFTOSTRING(QCopy, FileName)
let Suffix = vec size STRING/16
COPYQ(SufQ, QCopy)
QFTOSTRING(QCopy, Suffix)
let FNLen = FileName>>STRING.length
let SufLen = Suffix>>STRING.length
for Overlap=0 to ((SufLen gr FNLen)? FNLen, SufLen) do
[ let OverlapValid = true
for i=1 to Overlap do
if Capitalize(Suffix>>STRING.char↑i) ne
Capitalize(FileName>>STRING.char↑
(FNLen-Overlap+i))
then [ OverlapValid = false
break
]
unless OverlapValid do loop
EMPTYOUTQ(NewFNQ)
EMPTYOUTQ(QCopy)
for i=1 to FNLen-Overlap do
PUTQR(QCopy, FileName>>STRING.char↑i)
STRINGTOQR(Suffix, QCopy)
let MatchCount = EXPANDESC(QCopy, NewFNQ, false)
EMPTYOUTQ(QCopy)
if MatchCount ne 0 then
resultis MatchCount
]
resultis 0
]
and Capitalize(Char) = ((Char ge $a) & (Char le $z))?
Char+$A-$a,
Char
and ISSEP(C) = (C eq $.) % (C eq $;) % (C eq $<) % (C eq $>)
and ISFILECHAR(C) =
((C ge $A) & (C le $Z)) %
((C ge $a) & (C le $z)) %
((C ge $0) & (C le $9)) %
valof
[ let S = "+-.!$"
for i=1 to S>>STRING.length do
if C eq S>>STRING.char↑i then
resultis true
resultis false
]
and ISNTFILECHAR(C) = not ISFILECHAR(C)
and IsCommandChar(C) = ((C ge $A) & (C le $Z)) %
((C ge $a) & (C le $z)) %
((C ge $0) & (C le $9)) %
valof
[ let S = "+-.!$~?**#%"
for i=1 to S>>STRING.length do
if C eq S>>STRING.char↑i then
resultis true
resultis false
]
and IsntCommandChar(C) = not IsCommandChar(C)
and ISITEMCHAR(C) = IsCommandChar(C) % (C eq $/)
and EXPANDSTAR(Q) be
[
let ADDITEM(MYDE, Y) = valof
[ if MYDE>>MYDE.TYPE ne ISFILE then resultis 0
for I=1 to (MYDE>>MYDE.S.length)-1 do
PUTQR(FILLQ, MYDE>>MYDE.S.char↑I)
COPYQ(SWITCHQ, FILLQ) // Add the switches,
PUTQR(FILLQ, $*S) // and a final space
resultis 0
]
let MYFNQ = vec size QS/16
let MYSWQ = vec size QS/16
INITQ(MYFNQ)
INITQ(MYSWQ)
XFERQWHILE(GETQR, PUTQR, Q, PUTQF, MYSWQ, ISITEMCHAR)
XFERQWHILE(GETQF, PUTQF, MYSWQ, PUTQR, MYFNQ, IsCommandChar)
FILLQ = Q
SWITCHQ = MYSWQ
INITDIRBLK(SORTED)
MAPDIR(MYFNQ, ADDITEM)
EMPTYOUTQ(MYFNQ)
EMPTYOUTQ(MYSWQ)
return
]
and EXPANDESC(Q, RQ, TAILONLY) = valof
[ JUSTTAIL = TAILONLY
let FNQ = vec size QS/16
INITQ(FNQ)
XFERQWHILE(GETQR, PUTQR, Q, PUTQF, FNQ, IsCommandChar)
COPYQ(FNQ, Q)
NMATCHES = 0
MATCHQ = RQ
MAPDIR(FNQ, ESCMATCH, true)
resultis NMATCHES
]
and ESCMATCH(MYDE, MATCHLEN) = valof
[ let FIRSTCHAR = JUSTTAIL? MATCHLEN+1, 1
let fileName = vec 50
let fileNameLength = MYDE>>MYDE.S.length
MoveBlock(fileName, lv (MYDE>>MYDE.S),
(fileNameLength rshift 1)+1)
AppendDot(fileName)
fileNameLength = fileName>>STRING.length-1 // remove final "."
let I = nil
test NMATCHES eq 0
ifso [ NMATCHES = 1
I = fileNameLength+1
]
ifnot [ I = FIRSTCHAR
while I le fileNameLength do
[ if ISEMPTYQ(MATCHQ) then break
let C1 = fileName>>STRING.char↑I
if (C1 ge $a) & (C1 le $z) then C1 = C1+($A-$a)
let C2 = GETQF(MATCHQ)
if (C2 ge $a) & (C2 le $z) then C2 = C2+($A-$a)
if C2 ne C1 then
[ NMATCHES = NMATCHES+1
break
]
I = I+1
]
]
EMPTYOUTQ(MATCHQ)
for J = FIRSTCHAR to I-1 do
PUTQR(MATCHQ, fileName>>STRING.char↑J)
resultis 0
]
and MyOpenFile(fileName, ksType, itemSize,
versionControl, fp, errRtn, zone, logInfo,
disk, CreateStream; numargs na) = valof
[ static [ gSpecialFp; gVersionControl ]
let FindSpecialFp(MYDE) = valof
[
if MYDE>>MYDE.TYPE ne ISFILE then resultis 0
resultis lv (MYDE>>MYDE.FP)
]
switchon na into
[ case 0:
case 1: ksType = ksTypeReadWrite
case 2: itemSize = wordItem
case 3: versionControl = 0
case 4: fp = 0
case 5: errRtn = @lvSysErr
case 6: zone = sysZone
case 7: logInfo = 0
case 8: disk = sysDisk
case 9: CreateStream = CreateDiskStream
default:
]
let localName = vec 50
if (fp eq 0) & valof
[ let dirName = vec 129
SplitFileName(fileName, dirName, localName)
resultis (dirName>>STRING.length gr 0)
]
then
[ // let the system do this one...
let Value = OpenFile(fileName, ksType,
itemSize, versionControl,
fp, errRtn, zone, logInfo,
disk, CreateStream)
resultis Value
]
gVersionControl = (versionControl ne 0)? versionControl,
selecton ksType into
[ case ksTypeReadWrite:
verLatestCreate
case ksTypeReadOnly:
verLatest
case ksTypeWriteOnly:
verLatestCreate
]
if fp eq 0 then
[
let FNQ = vec size QS/16
INITQ(FNQ)
STRINGTOQR(localName, FNQ)
GETQR(FNQ) // remove training period
fp = MAPDIR(FNQ, FindSpecialFp)
]
let S = (fp ne 0 %
gVersionControl eq verLatestCreate %
gVersionControl eq verNew %
gVersionControl eq verNewAlways)?
OpenFile(fileName, ksType,
itemSize, versionControl,
fp, errRtn, zone, logInfo,
disk, CreateStream), 0
if (S ne 0) & (fp eq 0) then WIPEDIRBLK()
resultis S
]
and INITDIRBLK(P) be
[ static [ DIRHDQ
DECOUNT
LP
ssd
ssdBuffers
scanBuffer
wordsInBuffer
cl
cd
DirE
LocalE
]
let GetNWordsFromScan(n, dest, move) be
[
while n gr 0 do
[ let chunk = n ls wordsInBuffer? n, wordsInBuffer
move(dest, scanBuffer, chunk)
wordsInBuffer = wordsInBuffer - chunk
test wordsInBuffer eq 0
ifso
[ scanBuffer = GetScanStreamBuffer(ssd)
if scanBuffer eq 0 then break
wordsInBuffer = (ssd>>SSD.numChars+1) rshift 1
]
ifnot [ scanBuffer = scanBuffer + chunk; break]
dest = dest + chunk
n = n - chunk
]
]
PAUSE = P eq true
switchon DIRSTATE into
[ case EMPTY:
[ Resets(SYSTEMDIR)
ssdBuffers = Allocate(CZ, 512)
let ssdTable = vec 1
ssdTable!0, ssdTable!1 = ssdBuffers, ssdBuffers+256
ssd = InitScanStream(SYSTEMDIR, ssdTable, 2)
scanBuffer = GetScanStreamBuffer(ssd)
wordsInBuffer = (ssd>>SSD.numChars+1) rshift 1;
DIRSTATE = GETTINGFILE
]
case GETTINGFILE:
DECOUNT = 0
DIRHDQ = Allocate(CZ, size QS/16)
INITQ(DIRHDQ)
DIRSTATE = MAKINGBLK
LP = LOCALTABLE
cl, cd = 0, 0 // first chars from local and directory
DirE = Allocate(CZ, 256)
case MAKINGBLK:
[ until (cl % cd) eq 0 & scanBuffer eq 0 &
LP>>TE.pStatic eq 0 do
[ if ISPAUSE() then return
if cd eq 0 & scanBuffer ne 0 then
[ GetNWordsFromScan(1, DirE, MoveBlock)
let DELEN = DirE>>DV.length
GetNWordsFromScan(DELEN-1, DirE+1,
((DirE>>DV.type eq dvTypeFile)?
MoveBlock, Noop))
if DirE>>DV.type ne dvTypeFile then loop
cd = Capitalize(DirE>>DV.name.char↑1) lshift 7
cd = cd + Capitalize(DirE>>DV.name.char↑2)
]
if cl eq 0 & LP>>TE.pStatic ne 0 then
[ LocalE = LP
LP = LP+(offset TE.SUBSYSNAME/16)+
(LocalE>>TE.SUBSYSNAME.length/2)+1
cl = Capitalize(LocalE>>TE.SUBSYSNAME.char↑1) lshift 7
cl = cl + Capitalize(LocalE>>TE.SUBSYSNAME.char↑2)
]
let ISAFILE = nil
test cl ne 0 & cd ne 0
ifso test cd ls cl
ifso [ ISAFILE = true; cd = 0 ]
ifnot [ ISAFILE = false; cl = 0 ]
ifnot test cl eq 0
ifso [ ISAFILE = true; cd = 0 ]
ifnot [ ISAFILE = false; cl = 0 ]
DECOUNT = DECOUNT+1
let NCHARS = ISAFILE?
DirE>>DV.name.length,
LocalE>>TE.SUBSYSNAME.length
let BlockLength = ((offset MYDE.S.length)+
15)/16+((NCHARS+2) rshift 1)
let MYDE = Allocate(CZ, BlockLength)
PUTNWQR(1, DIRHDQ, lv MYDE)
test ISAFILE
ifso [ MoveBlock(MYDE, DirE,
BlockLength)
MYDE>>MYDE.TYPE = ISFILE
]
ifnot [ MYDE>>MYDE.TYPE = ISLOCALSUBSYS
MYDE>>MYDE.pStatic = LocalE>>TE.pStatic
MYDE>>MYDE.S.length = NCHARS
for I=1 to NCHARS do
MYDE>>MYDE.S.char↑I =
LocalE>>TE.SUBSYSNAME.char↑I
]
]
FinishScanStream(ssd)
Free(CZ, ssdBuffers)
Free(CZ, DirE)
DIRHDBLK = Allocate(CZ, DECOUNT+1)
DIRHDBLK!0 = DECOUNT
GETNWQF(DECOUNT, DIRHDQ, DIRHDBLK+1)
Free(CZ, DIRHDQ)
DIRSTATE = MADEBLK
]
case MADEBLK:
case SORTING:
if (P ge 0) & (P le MADEBLK) then return
if SORT(DIRHDBLK, ISPAUSE) then return
DIRSTATE = SORTED
case SORTED:
DIRSTATE = PAGESCOUNTED
case PAGESCOUNTED:
if ((P ge 0) & (P le PAGESCOUNTED)) %
ISPAUSE() then return
MAKETIMELINE()
endcase
]
]
and ReturnThirdArg(x, y, z) = z
and AppendDot(s) be
[ let len = s>>STRING.length
if s>>STRING.char↑len ne $. then
[ len = len + 1
s>>STRING.char↑len = $.
s>>STRING.length = len
]
]
and ISPAUSE() = PAUSE & (not Endofs(keys))
and WIPEDIRBLK() be
[ unless DIRSTATE eq EMPTY do
[ INITDIRBLK(MADEBLK)
for I=1 to DIRHDBLK!0 do
Free(CZ, DIRHDBLK!I)
Free(CZ, DIRHDBLK)
DIRSTATE = EMPTY
]
return
]
and SETUPCLK(EventTime, IntervalL, IntervalH; numargs na) be
[ let Interval = vec 2
Interval!1 = IntervalL
Interval!0 = (na ls 3)? 0, IntervalH
Timer(EventTime)
DoubleAdd(EventTime, Interval)
]
and TIMEHASCOME(EventTime) = valof
[ // resultis true if CurrentTime ge Time
let CurrentTime = vec 2
Timer(CurrentTime)
let TimeRemaining = vec 2
for i=0 to 1 do
TimeRemaining!i = (-1)-(CurrentTime!i)
DoubleAdd(TimeRemaining, EventTime)
// TimeRemaining = EventTime-CurrentTime-1
resultis TimeRemaining!0 ls 0
]
and DumpDirectory() = valof
[
external Wns
let s = OpenFile("Exec.data", ksTypeWriteOnly, charItem)
for i = 1 to DIRHDBLK!0 do
[
Wns(s,i,3,8); Puts(s,$*s)
let de = DIRHDBLK!i
let l = de>>MYDE.S.length
for j = 1 to l do Puts(s,de>>MYDE.S.char↑j)
Puts(s,$*n)
]
Closes(s)
resultis DIRHDBLK!0
]