! File NMOUSE:NAMER ! Namer %option "-nons-nodiag-low" %include "mouse.inc" ! 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. ! This scheme replaces the old "dictionary" package. ! 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. %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.(":").AFT ! But then if AFT begins with a colon, transfer it to the end of FORE. ! This temporary feature allows us to cope with the old "::" notation, ! which requires additional logical names to be defined. Example: ! Normally a file on filestore B might be referred to as B:L:X.MOB, ! with logical name B defined as :F:015; to accept B::L:X.MOB we also ! require B: to be defined as :F:015. %bytename f == length(fore), a == length(aft) %falseunless source -> fore.(":").aft %if a#0 %and a[1]=':' %start f = f+1; f[f] = ':'; aft = substring(aft,2,a) %finish %true %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 %returnif p=0 %if ':'#p[p]#']' %start p = p+1; p[p] = ':' %finish name = prefix.name %end %signal 3,3,,"Null file name" %if n=0 {"" no longer allowed} %if starts(':') %start name = ":T" %if n=1 {":" defaults to ":T"} %return %finish %while lives>0 %andnot starts(':') %cycle lives = lives-1 prefix = "" %if starts('^') %start {up a level} prefix = translate logical name(".") %cycle name = substring(name,2,n) shorten prefix %repeatuntilnot starts('^') %elseif splits(name,fore,aft) %if translated logical name(fore) %start {log:*} name = aft; prefix = fore %else {dir:*} prefix = translate logical name("Current_Filestore") %finish %elseunless translated logical name(name) prefix = translate logical name(".") %finish apply prefix %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 {{%integer i,p %record(mailbox fm)%name b %record(message fm)%name m %record(f)%name r %switch s(1:6) b == create mailbox(mailbox name,create semaphore("",0)) become process(15000) default name mode = 'L' default symbol mode = 'L' %cycle m == receive message(b) 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