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