// IFSGroupName.bcpl -- group number to name mapping // Copyright Xerox Corporation 1981, 1982 // Last modified May 26, 1982 5:18 PM by Taft get "Ifs.decl" get "IfsSystemInfo.decl" get "IfsFiles.decl" external [ // outgoing procedures InitGroupName; GetGroupName; SetGroupName // incoming procedures VFileReadPage; VFileWritePage LockCell; Lock; Unlock SysAllocateZero; Zero; MoveBlock; ExtractSubstring; StringCompare // incoming statics infoVMD ] static [ @gns = 0 ] //---------------------------------------------------------------------------- structure GNS: // Group Name State -- permanent resident data structure //---------------------------------------------------------------------------- [ gn word // -> in-core copy of GN page; zero if none. // Note that there is only one of these, regardless of // the number of simultaneous clients; we depend on // VMem to maintain at most one copy of GN at a time. lock @Lock // for mutual exclusion ] manifest [ lenGNS = size GNS/16 nGroupNames = size Protection +1 // extra one is "world" group name ] //---------------------------------------------------------------------------- structure GN: // layout of gnPage of <System>Info //---------------------------------------------------------------------------- [ seal word // = gnSeal blank word 2 // unused at present free word // offset of first free word name↑0,nGroupNames-1 word // offsets of name strings; 0 => none blank word 9 // for future expansion strings word 0 // rest of page holds name strings ] manifest [ gnSeal = 063512B // change this whenever GN structure changes incompatibly stdPageLength = 1 lshift logStdPageLength ecGroupNameTableFull = 70 ecDuplicateGroupName = 71 ecIllegalGroupNumber = 72 ecIllegalGroupName = 73 ] //---------------------------------------------------------------------------- let InitGroupName() be //---------------------------------------------------------------------------- // Initializes this module and initializes the table if its seal is bad. [ if gns eq 0 then [ gns = SysAllocateZero(lenGNS); LockCell(lv gns>>GNS.gn) ] let gn = LockGroupNameTable(true) UnlockGroupNameTable() unless gn>>GN.seal eq gnSeal do [ // Playing fast and loose with the unlocked GN page here....... // However, UnlockGroupNameTable does not call Block, so we are safe until // the calls to SetGroupName. Those calls must be made with the GN page // unlocked or else they will deadlock. Zero(gn, stdPageLength) gn>>GN.seal = gnSeal gn>>GN.free = offset GN.strings/16 SetGroupName(offset Protection.owner, "Owner") SetGroupName(offset Protection.world, "World") ] ] //---------------------------------------------------------------------------- and GetGroupName(groupNum) = valof //---------------------------------------------------------------------------- // Returns a new string which is the group name corresponding to groupNum, // or zero if groupNum has no name. // groupNum = nGroupNames-1 (= size Protection) refers to the special group // which holds the real name of the "world" group. [ let gn = LockGroupNameTable(false) let name = groupNum uge nGroupNames % gn>>GN.name↑groupNum eq 0? 0, ExtractSubstring(gn + gn>>GN.name↑groupNum) UnlockGroupNameTable() resultis name ] //---------------------------------------------------------------------------- and SetGroupName(groupNum, name) = valof //---------------------------------------------------------------------------- // Makes name be the new name for groupNum. Returns true normally, // or an error code if unsuccessful. name=0 or name.length=0 deletes // the existing name for groupNum. [ let gn = LockGroupNameTable(true) let res = valof [ if name>>String.length eq 0 then name = 0 if groupNum uge nGroupNames resultis ecIllegalGroupNumber let newLen = name eq 0? 0, name>>String.length rshift 1 +1 let oldOffset = gn>>GN.name↑groupNum let oldLen = oldOffset eq 0? 0, (gn+oldOffset)>>String.length rshift 1 +1 if gn>>GN.free+newLen-oldLen ugr stdPageLength then resultis ecGroupNameTableFull if name ne 0 then [ for i = 0 to (size Protection)-1 do if StringCompare(name, gn+gn>>GN.name↑i) eq 0 then resultis ecDuplicateGroupName if StringCompare(name, "None") eq 0 resultis ecIllegalGroupName let nonDigit = false for i = 1 to name>>String.length do if (name>>String.char↑i-$0) ugr 9 then [ nonDigit = true; break ] unless nonDigit resultis ecIllegalGroupName // all digits ] if oldLen ne 0 then [ // squeeze out old name and fix pointers to ones that move MoveBlock(gn+oldOffset, gn+oldOffset+oldLen, gn>>GN.free-(oldOffset+oldLen)) for i = 0 to nGroupNames-1 do if gn>>GN.name↑i gr oldOffset then gn>>GN.name↑i = gn>>GN.name↑i - oldLen gn>>GN.free = gn>>GN.free - oldLen gn>>GN.name↑groupNum = 0 ] if name ne 0 then [ // add new name at end and put offset in table MoveBlock(gn+gn>>GN.free, name, newLen) gn>>GN.name↑groupNum = gn>>GN.free gn>>GN.free = gn>>GN.free+newLen ] resultis 0 ] UnlockGroupNameTable() resultis res ] // Internal procedures //---------------------------------------------------------------------------- and LockGroupNameTable(write) = valof //---------------------------------------------------------------------------- [ Lock(lv gns>>GNS.lock, write) // If multiple reads are in progress, GNS.gn is already nonzero, but we // overwrite it anyway. This is ok because GNS.gn is a VMem lock word, // and VMem is guaranteed always to return the same pointer to a locked page. gns>>GNS.gn = (write? VFileWritePage, VFileReadPage)(infoVMD, gnPage) resultis gns>>GNS.gn ] //---------------------------------------------------------------------------- and UnlockGroupNameTable() be //---------------------------------------------------------------------------- [ // If we are the last locker of the GNS then unlock the page for VMem. // This order of operations is a bit flakey -- it depends on Unlock not // calling Block. But then the other order isn't really much of an // improvement, and it's more complicated to program. Unlock(lv gns>>GNS.lock) if gns>>GNS.lock.count eq 0 then gns>>GNS.gn = 0 ]