// 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 ]