// IfsTelnetFileUtil.bcpl -- Utilities for collecting and enumerating files
// Copyright Xerox Corporation 1979, 1981, 1982
// Last modified May 15, 1982 10:31 AM by Taft
get "Ifs.decl"
get "IfsFiles.decl"
get "IfsDirs.decl"
get "CmdScan.decl"
external
[
// outgoing procedures
CollectFilenames; NextFile; DestroyFGD; Subcommands; SetFGDLC
WhatChanged; CopyFD; PrintSubFilename
// incoming procedures
LookupIFSFile; DestroyFD; LookupFD; NextFD
GetString; TerminatingChar; CmdError
EnableCatch; EndCatch
ExtractSubstring
DefaultArgs; SysAllocate; SysAllocateZero; SysFree; FreePointer
Zero; MoveBlock
Puts; Errors; Wss
// incoming statics
primaryIFS
]
manifest maxNames = 10
//---------------------------------------------------------------------------
structure FGD: // File Group Designator
//---------------------------------------------------------------------------
[
nNames word // number of names
name↑1,maxNames+1 word // -> name strings
// (one extra to simplify CollectFilenames loop)
subcommands word // true iff subcommand mode requested
lc word // lookup control
iName word // index of current name
fd word // -> FD for enumeration
fs word // -> IFS
]
manifest lenFGD = size FGD/16
//---------------------------------------------------------------------------
let CollectFilenames(cs, lc, prompt, defStar, fs; numargs na) = valof
//---------------------------------------------------------------------------
// Collects a group of filenames and returns a file group designator (fgd).
// lc is the lookup control for each name. If the last name ends with
// comma return, the comma is stripped off and the subcommand word in fgd
// is set to true. If defStar is false, an empty input line causes an
// error; if true, an empty line silently defaults to "*".
// fs designates the file system; it defaults to primaryIFS.
[
DefaultArgs(lv na, -1, lcVHighest+lcMultiple, "file name(s)", false,
primaryIFS)
let fgd = nil
if EnableCatch(cs) then [ DestroyFGD(fgd); EndCatch(cs) ]
fgd = SysAllocateZero(lenFGD)
fgd>>FGD.lc = lc
fgd>>FGD.fs = fs
[ // loop to collect names
let iName = fgd>>FGD.nNames+1
let warn = cs>>CS.iPhOut eq cs>>CS.maxPhrases-1 % iName gr maxNames
if warn then CmdError(cs, "[file list full]") // warning, no abort yet
let name = GetString(cs, 0, Wss, prompt)
if TerminatingChar(cs) eq $*n then
[
let length = name>>String.length
if name>>String.char↑length eq $, then
[
length = length-1
name>>String.length = length
fgd>>FGD.subcommands = true
]
if length eq 0 then [ SysFree(name); break ]
]
fgd>>FGD.name↑iName = name // if error, will now be freed by DestroyFGD
if warn then Errors(cs, 0) // too many names
let fd = LookupIFSFile(name, lc, 0, fs)
if fd eq 0 then Errors(cs, 0)
test iName eq 1
ifso fgd>>FGD.fd = fd // retain the first FD
ifnot DestroyFD(fd)
fgd>>FGD.nNames = iName
Puts(cs, $*s)
] repeatuntil TerminatingChar(cs) eq $*n
if fgd>>FGD.nNames eq 0 then
test defStar
ifnot Errors(cs, 0)
ifso
[
fgd>>FGD.nNames = 1
fgd>>FGD.name↑1 = ExtractSubstring("**")
]
resultis fgd
]
//---------------------------------------------------------------------------
and NextFile(fgd) = valof
//---------------------------------------------------------------------------
// Returns an FD for the next file designated by the file group designator
// fgd, or zero if fgd is exhausted. Expects to be called with the directory
// unlocked, and returns with it unlocked.
[
let fd = fgd>>FGD.fd
if fd ne 0 then
[
if fgd>>FGD.iName eq 0 then
[ // first use of FD retained by CollectFilenames
fgd>>FGD.iName = 1
if LookupFD(fd) eq 0 resultis fd
// specific file no longer exists, but others may
]
if NextFD(fd) resultis fd
fgd>>FGD.fd = DestroyFD(fd)
]
fgd>>FGD.iName = fgd>>FGD.iName+1
if fgd>>FGD.iName gr fgd>>FGD.nNames resultis 0
fd = LookupIFSFile(fgd>>FGD.name↑(fgd>>FGD.iName), fgd>>FGD.lc, 0,
fgd>>FGD.fs)
fgd>>FGD.fd = fd
if fd ne 0 resultis fd
] repeat
//---------------------------------------------------------------------------
and DestroyFGD(fgd) be
//---------------------------------------------------------------------------
[
if fgd>>FGD.fd ne 0 then DestroyFD(fgd>>FGD.fd)
for i = 1 to maxNames+1 do FreePointer(lv fgd>>FGD.name↑i)
SysFree(fgd)
]
//---------------------------------------------------------------------------
and Subcommands(fgd) = fgd>>FGD.subcommands
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
and SetFGDLC(fgd, lc) be
//---------------------------------------------------------------------------
[
fgd>>FGD.lc = lc
// discard any retained FD, since its LC may no longer be valid.
if fgd>>FGD.fd ne 0 then fgd>>FGD.fd = DestroyFD(fgd>>FGD.fd)
]
//---------------------------------------------------------------------------
and WhatChanged(fd, lastFD) = valof
//---------------------------------------------------------------------------
// Compares the filename designated by fd against lastFD
// and updates lastFD. Returns a code describing how much of the
// filename changed:
// 0 directory (or subdirectory) changed
// 1 name body changed
// 2 version changed
// Intended use: caller should start by allocating and zeroing a block of
// size lenFD. During an enumeration of the (real) fd, this block should
// be passed as the lastFD argument to WhatChanged. At the end of the
// enumeration, caller must DestroyFD(lastFD).
[
let i = 1
while i le fd>>FD.lenBodyString do
[
let c1 = fd>>FD.dr>>DR.pathName.char↑i
let c2 = lastFD>>FD.dr>>DR.pathName.char↑i
if c1 ne c2 then
[
c1 = c1 & #137; c2 = c2 & #137
unless c1 eq c2 & c1 ge $A & c1 le $Z break
]
i = i+1
]
let res = fd>>FD.lenSubDirString ne lastFD>>FD.lenSubDirString %
i le fd>>FD.lenSubDirString? 0,
fd>>FD.lenBodyString ne lastFD>>FD.lenBodyString %
i le fd>>FD.lenBodyString? 1, 2
CopyFD(lastFD, fd)
resultis res
]
//---------------------------------------------------------------------------
and CopyFD(dFD, sFD) be
//---------------------------------------------------------------------------
// Replaces dFD with a copy of sFD. Precisely:
// 1. Copies all of the sFD structure itself, with the exception of pointers
// to other objects (dr, pathStk, and template).
// 2. Deallocates dFD's DR if there is one, and then makes a copy of sFD's DR
// and installs it in dFD.
// 3. Unconditionally deallocates dFD's pathStk and template.
// This is a rather specialized operation used principally by WhatChanged.
[
FreePointer(lv dFD>>FD.dr, lv dFD>>FD.pathStk, lv dFD>>FD.template)
MoveBlock(dFD, sFD, lenFD)
dFD>>FD.pathStk = 0
dFD>>FD.template = 0
dFD>>FD.dr = SysAllocate(sFD>>FD.dr>>DR.length)
MoveBlock(dFD>>FD.dr, sFD>>FD.dr, sFD>>FD.dr>>DR.length)
]
//---------------------------------------------------------------------------
and PrintSubFilename(str, fd, first, last; numargs na) be
//---------------------------------------------------------------------------
// Prints the 'first' through 'last' characters of the filename
// designated by FD.
[
DefaultArgs(lv na, -2, 1, fd>>FD.dr>>DR.pathName.length)
for i = first to last do Puts(str, fd>>FD.dr>>DR.pathName.char↑i)
]