! New dictionary package (name manager) ! This module manages process-local tables for both logical names ! and command symbols. There is also a global mode, in which ! requests are passed to another process for handling there. ! The tables themselves are linear linked lists hung off ! a hash table. Each entry is a pair of heap strings. ! Although nothing is sorted, procedures are available ! for scanning the whole table "in order", so that every ! entry is visited. To guarantee the proper operation of ! this scanning scheme, no entries should be added or ! removed during the scan. %option "-nons-nodiag" %include "nmouse.inc" %systemintegerfnspec stringdiff(%string(255)s,t) %constinteger hashmax=31 {2^n-1} %recordformat entry fm(%record(entry fm)%name next, %string(*)%name key,info) %recordformat table fm(%record(entry fm)%namearray list(0:hashmax)) %integerfn hash(%string(*)%name s) %bytename b==length(s) %integer i=b,n=0 %cycle %exitif i=0 n = (n>>4&1+n<<1) {5 bit field rotated left 1} !! b[i] i = i-1 %repeat %result = n&hashmax %end %record(entry fm)%map find(%string(*)%name key,%record(entry fm)%name e) ! Locate the cell containing KEY in the list E, NIL if not there. %cycle %exitif e==nil %or stringdiff(e_key,key)=0 e == e_next %repeat %result == e %end %routine define(%string(255)%name key,info,%record(table fm)%name table) ! Add an entry for (KEY,INFO) into TABLE. ! Or, if INFO="", delete entry for (KEY,*) from TABLE. %integer h %record(entry fm)%name l,e h = hash(key) l == table_list(h) e == find(key,l) %if e==nil %start {Not there - add a new cell} %returnif info="" {unless request was to delete} e == new(e); e_next == l e_key == newstring(key); e_info == newstring(info) table_list(h) == e %elseif info="" {it's there and we want to get rid of it} dispose(e_info); dispose(e_key) %if e==l %then table_list(h) == e_next %elsestart l == l_next %while l_next##e l_next == e_next dispose(e) %finish %elseunless e_info=info {it's there and we want to change it} dispose(e_info); e_info == newstring(info) %finish %end %predicate translated(%string(255)%name key,info,%record(table fm)%name table) ! If (KEY,*) is in TABLE, copy * into INFO and return TRUE. ! Otherwise leave INFO unchanged and return FALSE. %integer h %record(entry fm)%name e h = hash(key) e == find(key,table_list(h)) info = e_info %andtrueunless e==nil %false %end %routine next(%string(*)%name key,info,%record(table fm)%name table) ! If (KEY,*) is in TABLE, return in (KEY,INFO) whatever is in the ! next entry in the table. Otherwise, or if there are no more entries, ! return ("",""). If KEY="" to start with, return the first entry. %integer h %record(entry fm)%name e %if key="" %start h = 0; e == nil %else h = hash(key); e == find(key,table_list(h)) %finish e == e_next %unless e==nil {next entry on same list, if any} %unless e==nil %start {yes, there it is} key = e_key; info = e_info %return %finish %while h fore.(match).aft %false %end %routine check for colon colon ! EFTP-compatibility: ! Names of the form ::zz are turned into :F:zz, ! names of the form x::y are turned into :F:x:y. %if resolves(name,coloncolon,fore,aft) %start %if fore="" %start name = ":F:".aft %else name = ":F:".fore.":".aft %finish %finish %end %predicate starts(%integer k) ! True iff NAME starts with character K. %falseif n=0; %trueif n[1]=k; %false %end %routine shorten prefix ! Strip a trailing component off string PREFIX. ! If (vax) it ends in *.x] turn it into *] ! If (vax) it ends in *[x] (no dot in the []), turn it into *[x.-] ! If it ends in *x:y: or *x:y turn it into *x: %integer vax %if p[p]=']' %start vax = p %cycle vax = vax-1 %if p[vax]='[' %start p[p] ='.'; p[p+1] = '-'; p[p+2] = ']'; vax = p+2 %exit %finish %if p[vax]='.' %start p[vax] = ']' %exit %finish %repeatuntil vax=0 %or p[vax]=':' p = vax %else p = 1 %if p=0 p = p-1 %until p=0 %or p[p]=':' %finish %end %routine apply prefix ! NAME = PREFIX.NAME, but make sure one (if needed) ! and no more than one colon gets put inbetween. %returnif p=0 %if ':'#p[p]#']' %start p = p+1; p[p] = ':' %finish name = substring(name,2,n) %while n>1 %and n[1]=':' name = prefix.name %end %signal 3,3,,"Null file name" %if n=0 %if n[1]=':' %start name = ":T" %if n=1 %returnunless n[2]=':' %finish check for colon colon %while lives>0 %andnot starts(':') %cycle lives = lives-1 %if starts('^') %start prefix = translate logical name(".") %cycle name = substring(name,2,n) shorten prefix %repeatuntilnot starts('^') apply prefix check for colon colon %finish %if resolves(name,colon,fore,aft) %start %if translated logical name(fore) %start name = aft; prefix = fore %else prefix = "Current_Filestore" prefix = "^" %unless translated logical name(prefix) %finish %elseunless translated logical name(name) prefix = translate logical name(".") %finish apply prefix check for colon colon %repeat %unless n>1 %and n[1]=':' %start name = "Standardise file name fails: ".name %signal 3,3,,name %finish %end ! Name manager process starts here %begin %record(mailbox fm)%name mbx %record(message fm)%name m %record(f)%name r %switch s(1:6) mbx == create mailbox(mbx name,create semaphore("",0)) become process(15000) default name mode = 'L' define logical name(":a","114") define logical name(":b","015") define logical name(":c","01B") define logical name(":d","135") define logical name(":m","044") define logical name(":v","272") define logical name("a",":f:a") define logical name("b",":f:b") define logical name("c",":f:c") define logical name("d",":f:d") define logical name("m",":f:m") define logical name("v",":f:v") define logical name(".",cliparam) %cycle m == receive message(mbx); r == record(addr(m_data)) ->s(r_op) %if 1<=r_op<=6 r_op = -1; ->end s(1): define local logical name(r_key,r_info); ->end s(2): define local command symbol(r_key,r_info); ->end s(3): r_info = r_key r_op = 0 %unless translated local logical name(r_info); ->end s(4): r_info = r_key r_op = 0 %unless translatedlocal command symbol(r_info); ->end s(5): next local logical name(r_key,r_info); ->end s(6): next local command symbol(r_key,r_info) end: send message(m,m_reply) %repeat %end