Google
 

Trailing-Edge - PDP-10 Archives - decuslib20-01 - decus/20-0002/comnd.sai
There is 1 other file named comnd.sai in the archive. Click here to see a list.
COMMENT

	These procedures comprise a complete package interfacing
the SAIL programming language with the COMND jsys.  They were
written in 1978 and 1979 by Andrew R. Lowry and David S. Millman
of the Columbia University Center for Computing Activities, User
Services Group.  Many thanks go to Frank da Cruz, Chris Ryland and
Norman Kincl of the CUCCA Systems Group and to Ted Markowitz, Ken
Rossman, Harry Yudenfriend and Jeffrey Slavitz of the CUCCA User
Services Group for their assistance and many suggestions.
;
entry CM!SIZE;
entry CM!IOJ;
entry CM!TAKE;
entry CM!RETRY;
entry CM!GETATM;
entry CM!TBUILD;
entry CM!INI;
entry CM!KEY;
entry CM!CFM;
entry CM!NUM;
entry CM!NOI;
entry CM!IFI;
entry CM!OFI;
entry CM!FIL;
entry CM!CMA;
entry CM!SWI;
entry CM!FLD;
entry CM!USR;
entry CM!DIR;
entry CM!FLT;
entry CM!DEV;
entry CM!TXT;
entry CM!NUX;
entry CM!TOK;
entry CM!UQS;
entry CM!QST;
entry CM!TAD;
entry CM!ACT;
entry CM!NOD;
entry CM#KEY;
entry CM#CFM;
entry CM#NUM;
entry CM#NOI;
entry CM#IFI;
entry CM#OFI;
entry CM#CMA;
entry CM#SWI;
entry CM#FLD;
entry CM#USR;
entry CM#DIR;
entry CM#FLT;
entry CM#DEV;
entry CM#TXT;
entry CM#ACT;
entry CM#TOK;
entry CM#FIL;
entry CM#NOD;
entry CM#NUX;
entry CM#TAD;
entry CM#UQS;
entry Cm#QST;
entry CM#RESET;
entry CM#CALL;
begin "comnd"

require "{}{}" delimiters;
define ! = {comment};

! Macro Definitions
  ================= 
  ;

! *** Data Manipulations;

define	ison(word,mask) = {((word land (mask)) neq 0)};
define	isoff(word,mask) = {((word land (mask))  =  0)};
define	right(word) = {(word land '777777)};
define	left(word) = {((word land '777777000000) rot 18)};
define	bpoint(word) = {(word lor '777777000000)};
define	memloc(str) = {memory[location(str),integer]};
define	hiord(word) = {(word lsh 27)};

! *** Jsys Codes;

define	Comnd = {jsys	'544};
define	Geter = {jsys	'12};
define  Stcmp = {jsys	'540};

! *** Comnd Jsys Function Codes;

define	#CmKey = {hiord('0)};
define	#CmNum = {hiord('1)};
define	#CmNoi = {hiord('2)};
define	#CmSwi = {hiord('3)};
define	#CmIfi = {hiord('4)};
define	#CmOfi = {hiord('5)};
define	#CmFil = {hiord('6)};
define	#CmFld = {hiord('7)};
define	#CmCfm = {hiord('10)};
define	#CmDir = {hiord('11)};
define	#CmUsr = {hiord('12)};
define	#CmCma = {hiord('13)};
define	#CmIni = {hiord('14)};
define	#CmFlt = {hiord('15)};
define	#CmDev = {hiord('16)};
define	#CmTxt = {hiord('17)};
define	#CmTad = {hiord('20)};
define	#CmQst = {hiord('21)};
define	#CmUqs = {hiord('22)};
define	#CmTok = {hiord('23)};
define	#CmNux = {hiord('24)};
define	#CmAct = {hiord('25)};
define	#CmNod = {hiord('26)};

! *** Command State Block;

define	CmFlg = {cm!csb['0]};
define	CmIoj = {cm!csb['1]};
define	CmRty = {cm!csb['2]};
define	CmBfp = {cm!csb['3]};
define	CmPtr = {cm!csb['4]};
define	CmCnt = {cm!csb['5]};
define	CmInc = {cm!csb['6]};
define	CmAbp = {cm!csb['7]};
define	CmAbc = {cm!csb['10]};
define	CmGjb = {cm!csb['11]};

! *** Function Descriptor Block;

define	CmFnp = {cm!fdb[0]};
define	CmDat = {cm!fdb[1]};
define	CmHlp = {cm!fdb[2]};
define	CmDef = {cm!fdb[3]};
define	CmBrk = {cm!fdb[4]};

! *** Multiple Function Descriptor Block;

define	CmMFnp = {cm#fdb[cm#level,0]};
define	CmMDat = {cm#fdb[cm#level,1]};
define	CmMHlp = {cm#fdb[cm#level,2]};
define	CmMDef = {cm#fdb[1,3]};
define	CmMBrk = {cm#fdb[cm#level,4]};

! *** Components of CmFnp;

define	Cm$Fnc = {((CmFnp land '777000000000) rot 9)};
define	Cm$Ffl = {((CmFnp land '000777000000) rot 18)};
define	Cm$Lst = {( CmFnp land '000000777777)};

! *** Address of GTJFN argument block;

define	Cm$Gjb = {(CmGjb land '777777)};

! *** Components of GTJFN argument block;

define	GjGen = {cm!gtbuf[0]};
define	GjSrc = {cm!gtbuf[1]};
define	GjDev = {cm!gtbuf[2]};
define	GjDir = {cm!gtbuf[3]};
define	GjNam = {cm!gtbuf[4]};
define	GjExt = {cm!gtbuf[5]};
define	GjPro = {cm!gtbuf[6]};
define	GjAct = {cm!gtbuf[7]};
define	GjJfn = {cm!gtbuf[8]};
define	GjF2  = {cm!gtbuf[9]};
define	GjCpp = {cm!gtbuf[10]};
define	GjCpc = {cm!gtbuf[11]};
define	GjRty = {cm!gtbuf[12]};
define	GjBfp = {cm!gtbuf[13]};

! *** Flags in CmFlg;

define	Cm$Esc = {'400000000000};
define	Cm$Nop = {'200000000000};
define	Cm$Eoc = {'100000000000};
define	Cm$Rpt = {'040000000000};
define	Cm$Swt = {'020000000000};
define	Cm$Pfe = {'010000000000};
define	Cm$Rai = {'004000000000};
define	Cm$Xif = {'002000000000};
define	Cm$Wkf = {'001000000000};
define	RetFlags = {'770000000000};	! Mask for flags returned by COMND;

! *** Flags in CmFnp;

define	Cm$Brk = {'000020000000};
define	Cm$Po  = {'000010000000};
define	Cm$Hpp = {'000004000000};
define	Cm$Dpp = {'000002000000};
define	Cm$Sdh = {'000001000000};

! Flag for CmDir function;

define	Cm$Dwc = {'400000000000};

! *** Flags for CmTad function;

define	Cm$Ida = {'400000000000};
define	Cm$Itm = {'200000000000};
define	Cm$Nci = {'100000000000};

! *** Flags in Keyword table (first word of string if B0-6 - 0);

define	Cm$Inv = {'000000000001};
define	Cm$Nor = {'000000000002};
define	Cm$Abr = {'000000000004};		! Nonstandard abbreviations are not
					! implemented in these routines. How-
					! ever, the user is free to modify the
					! keyword lookup tables created by
					! tbuild or to build his own in order
					! to use this feature;
define	Cm$Fw  = {'002000000000};		! Must always be on if other flags on.
					! Otherwise first word of entry should
					! actually be beginning of ASCIZ string;

! *** Input/Output Channels;

define	priin = {'000100};
define	priou = {'000101};
define	nulio = {'377777};
define  ttdes = {'400000};
define	ttdes1 = {'777777400000};
define  dvdes = {'600000000000};

! *** Self-process handle;

define	FhSlf = {'400000};

! *** Error Codes found in AC2 upon unparsable field (returned in cm!err)
!     for CmKey and CmSwi;

define	NPXAMB = {'602044};
define	NPXNSW = {'602045};
define	NPXNOM = {'602046};
define	NPXNUL = {'602047};
define	NPXINW = {'602050};
define	NPXNC  = {'602051};
define	NPXICN = {'602052};
define	NPXIDT = {'602053};
define	NPXNQS = {'602054};
define	NPXNMT = {'602055};
define	NPXNMD = {'602056};
define	NPXCMA = {'602057};
define	COMX18 = {'602134};
define	COMX19 = {'602135};

! *** Error codes causing Illegal Instruction Interrupts (not including
!     errors caused by jsyses called by COMND);

define	COMNX1 = {'601257};
define	COMNX2 = {'601260};
define	COMNX3 = {'601261};
define	COMNX5 = {'601265};
define	COMNX8 = {'601321};
define	COMNX9 = {'601413};
define	IOX4 = {'600220};	! Mon Call Ref lied - EOF gives this, not COMNX9;
define	COMX10 = {'601767};
define	COMX11 = {'602035};
define	COMX12 = {'602036};
define	COMX13 = {'602037};
define	COMX14 = {'602040};
define	COMX15 = {'602041};
define	COMX16 = {'602042};
define	COMX17 = {'602043};

! *** End of Macro Definitions;

! Outer Block Declarations
  ========================
  ;

internal integer array cm!csb[0:'11];
internal integer array cm!fdb[0:4];
internal integer array cm!gtbuf[0:13];
internal integer array cm!buffer[0:99];
internal integer array cm!atom[0:99];
internal integer array cm#fdb[1:10,0:4];
internal integer array cm!datime[2:4];
internal boolean cm!major,cm!minor,cm!fatal,cm!eof,cm!abort;
internal boolean cm!reparse,cm!colon;
internal integer cm!err;
internal integer cm#int;
internal string cm#str;
internal real cm#real;
internal integer cm#level;
external integer !skip!;
record!class jfnstack (integer ichan, ochan;
		       boolean errpop;
		       record!pointer(jfnstack) next);
record!pointer(jfnstack) jfnhead;
string array cm#hlp,cm#nze[1:10],cm#token[1:10];
integer csbad,fdbad;
integer array break!tables[0:10,0:3];
boolean minor;
string promptz,devz,dirz,namz,extz,protz,acctz;

! *** End of Outer Block Declarations;
! Procedure Definitions
  =====================
  ;
internal integer procedure cm!size
	(string array strarr);

	COMMENT
		This procedure computes a generous allocation for a lookup
	table to contain the elements of strarr.  The strings must all be
	copied into such a lookup table since the TBLUK jsys requires all
	entries to be alligned on word boundaries, and this is not generally
	the case with SAIL strings.
	;

	begin "size"
	  integer i,sum,len;

	  sum := 1;
	  for i := arrinfo(strarr,1) step 1 until arrinfo(strarr,2) do
	    begin "add"
	      len := length(strarr[i]);
	      sum := sum+1+((len+5) div 5);
	    end "add";
	  sum := sum+2+arrinfo(strarr,2)-arrinfo(strarr,1);
	  return(sum);
	end "size";
integer procedure compare
	(string a,b);

	COMMENT
	 	This procedure compares two character strings a and b,
	and returns -1 if a < b alphabetically, 1 if a > b, and 0 if 
	a = b.
	;

	begin "compare"
	  integer result,loca,locb;

	  loca := memloc(a);
	  locb := memloc(b);
	  start!code "stcmp"
		move	1,loca;
		move	2,locb;
		stcmp;
		movem	1,result;
	  end "stcmp";
	  if result=0 then return(0)
	    else if result='100000000000 then return(1)
	    else return(-1);
	end "compare";
procedure tagsort
	(string array scrmbld;
	 reference integer array tag);

	COMMENT
		This procedure does a tag sort on the strings in array
	scrmbld.  The string array is left unchanged, but indexing it through
	the tag array will result in accessing the strings in ascending alpha-
	betical order.  The two arrays should both have the same number of
	elements, and the lower bound on the indices for tag should be 1.
	Also, then indices for the scrmbld array should initially be stored
	in ascending order in the tag array.
	;

	begin "tagsort"
	  integer i,j,temp;
	  boolean changed;

	  for i := 1 step 1 until arrinfo(tag,2)-1 do
	    begin "pass"
	      changed := false;
	      for j := 1 step 1 until arrinfo(tag,2)-1 do
		if compare(scrmbld[tag[j]],scrmbld[tag[j+1]])=1 then
		  begin "switch"
		    temp := tag[j];
		    tag[j] := tag[j+1];
		    tag[j+1] := temp;
		    changed := true;
		  end "switch";
	      if not changed then done;
	    end "pass";
	  return;
	end "tagsort";
internal procedure cm!retry
	(string errmsg);

	COMMENT
		This procedure allows the user to try again on the current
	field.  The procedure prints out the error message, then retypes
	the command line, including the prompt, up to the unparsable field,
	and the user may retype that field.  The procedure also resets CMINC
	(cm!csb[6]) so that COMND will not think that anything is in the field
	yet.
	;

	begin "retry"
	  integer ptr,char;

	  print(errmsg&'15&'12);
	  CmInc := 0;
	  ibp(ptr := CmPtr);
	  if ldb(ptr)=" " then
	    begin
	      ibp(CmPtr);
	      CmCnt := CmCnt-1;
	      end;
	  ptr := CmRty;
	  while true do
	    begin "print prompt"
	      char := ildb(ptr);
	      if char=0 then done;
	      print(char&null);
	    end "print prompt";
	  ptr := CmBfp;
	  while true do
	    begin "print cm!buffer";
	      if ptr=CmPtr then done;
	      char := ildb(ptr);
	      print(char&null);
	    end "print cm!buffer";
	  return;
	end "retry";
procedure prepare;

	COMMENT
		This procedure sets up the various pointers in the cm!csb,
	and also the pointers to the cm!csb and the cm!fdb.  This procedure should
	be used before every call to COMND to minimize the possibility of the
	SAIL runtime system moving the various arrays and strings without their
	pointers being updated.
	;

	begin "prepare"
	  integer count,loc;

	  count := 5*arrinfo(cm!buffer,0)-CmCnt;
	  loc := location(cm!buffer[0])+((count-1) div 5);
	  if count=0 then CmPtr := bpoint(location(cm!buffer[0]))
	    else CmPtr := right(loc) +
			(7 lsh 24) +
			((29-7*((count-1) mod 5)) lsh 30);
	  CmRty := memloc(promptz);
	  CmBfp := bpoint(location(cm!buffer[0]));
	  CmAbp := bpoint(location(cm!atom[0]));
	  CmGjb := location(cm!gtbuf[0]);
	  CmIoj := (jfnstack:ichan[jfnhead] lsh 18) lor jfnstack:ochan[jfnhead];
	  csbad := location(cm!csb[0]);
	  fdbad := location(cm!fdb[0]);
	  return;
	end "prepare";
procedure make!break(integer tabno; string chars);

	COMMENT
		Builds a table of break characters by setting the bits
	in the table corresponding to the characters in chars.  The
	first 32 bits of each table word are used, spanning the complete
	ASCII collating sequence in ascending order.
	;

	begin "make!break"
	  integer char,indx,num;

	  for indx := 0 step 1 until 3 do
	    break!tables[tabno,indx] := 0;
  	  while length(chars) neq 0 do
	    begin "load"
	      char := lop(chars);
	      indx := char div 32;
	      num := char-32*indx;
	      break!tables[tabno,indx] :=
		break!tables[tabno,indx] lor (1 rot (35-num));
	    end "load";
	end "make!break";
internal simple procedure cm!ioj;

	COMMENT
		Sets up the initial jfn chain, consisting of a single
	entry containing priio.
	;

	begin "cm!ioj"
	  jfnhead := new!record(jfnstack);
	  jfnstack:ichan[jfnhead] := priin;
	  jfnstack:ochan[jfnhead] := priou;
	end "cm!ioj";
internal procedure cm!take(integer ichan, ochan(nulio);
			     boolean errpop(true));

	COMMENT
		This procedure facilitates the redirection of input and output
	from COMND.  The name is derived from its similarity to the 'take'
	command of the EXEC.  The files represented by ichan and ochan,
	which should not be open before the call, are first opened, and
	then they are made the current input and output jfns for COMND
	calls.  The old jfns are pushed onto a stack.  When the new input
	file is finished, the old jfns are popped back and the finished
	file is closed, along with the associated output file.  If a parsing
	error occurs during the reading of the new file, then if errpop is
	true, the old jfns are popped back, and a message printed.  If errpop
	is false, then only the normal minor error procedures are followed.
	;
	begin "cm!take"
	  record!pointer(jfnstack) newjfn;
	  integer errchan;
	  procedure open!error(integer chan,error; string mode);
	    begin "open!error"
	      cprint (errchan,"?",('15&'12),
		"Fatal error using SAIL-COMND interface package");
	      cprint (errchan,"?",('15&'12),
		"Could not open ",jfns(chan,0)," for ",mode,('15&'12));
	      cprint(errchan,"?",erstring(error, FhSlf));
	      start!code
		haltf;
	      end;
	    end "open!error";

	  errchan := jfnstack:ochan[jfnhead];
	  define jfnok(jfn) =
	    {(jfn neq nulio) and (jfn neq priin) and
	     (jfn neq priou) and
	     ((jfn land ttdes1) neq ttdes) and
	     ((jfn land dvdes) neq dvdes)};
	  if jfnok(ichan) then openf(ichan,0);
	  if !skip! then
	    if cm!major then open!error(ichan,!skip!,"input")
	      else begin
		cm!fatal := true;
		cm!err := !skip!;
		return;
	      end;
	  if jfnok(ochan) then openf(ochan,1);
	  if !skip! then
	    if cm!major then open!error(ochan,!skip!,"output")
	      else begin
		cm!fatal := true;
		cm!err := !skip!;
		cfile(ichan);
		return;
	      end;
	  newjfn := new!record(jfnstack);
	  jfnstack:ichan[newjfn] := ichan;
	  jfnstack:ochan[newjfn] := ochan;
	  jfnstack:errpop[newjfn] := errpop;
	  jfnstack:next[newjfn] := jfnhead;
	  jfnhead := newjfn;
	end "cm!take";
procedure err!handle;

	COMMENT
		This procedure handles all errors that arise during operation
	of the COMND jsys.  When an error is detected upon return from the
	jsys call, control is transfered to this procedure, which determines
	the nature of the error and takes appropriate action, which is
	as follows:
		If the error would have caused an illegal instruction interrupt
	had it not been caught (e.g. input buffer overflow), it is termed a
	"major" error.  Otherwise it is a "minor" error (e.g. input did not
	correspond to a valid keyword in cm!key).
		When a minor error occurs, the cm!minor flag is checked. If
	the flag is true, an appropriate error message is printed. Otherwise
	no message is printed. In either case, control returns to the user's
	program with the error code in cm!err.
		When a major error occurs, the cm!major flag is checked.  If
	the flag is true, an error message is printed and the program halts.
	If the flag is false, no action is taken, the error code is put in
	cm!err, and cm!fatal is set to true.  The user's program may then
	take whatever action it desires.  

		There is one major error which is not signalled in any case,
	that is, coming to the end of the input file when that file was opened
	due to a call to the take procedure.  In that case, the cm!eof variable
	is set to true, the old jfns are popped back into the CSB, and the
	program is continued silently.  Note that the call in progress is
	not automatically reissued.

		When the program is started, both cm!minor and cm!major
	are set to true.
	;

	begin "err!handler"
	  integer chan;

	  start!code "geter"
		hrrzi	1,FhSlf;
		Geter;
		hrrzm	2,cm!err;
	  end "geter";
	  if cm!err=COMNX9 or cm!err=IOX4 then
	    begin "eof"
	      cm!eof := true;
	      if jfnstack:next[jfnhead] neq null!record then
		define jfnok(jfn) =
		  {(jfn neq nulio) and (jfn neq priin) and
		   (jfn neq priou) and
		   ((jfn land ttdes1) neq ttdes) and
		   ((jfn land dvdes) neq dvdes)};
		begin "popjfn"
		  if jfnok(jfnstack:ichan[jfnhead]) then
		    cfile(jfnstack:ichan[jfnhead]);
		  if jfnok(jfnstack:ochan[jfnhead]) then
		    cfile(jfnstack:ochan[jfnhead]);
		  jfnhead := jfnstack:next[jfnhead];
		  return;
		end "popjfn";
	    end "eof";
	  chan := jfnstack:ochan[jfnhead];
	  if minor then
	    begin
	      if cm!minor then
		begin
		  if isoff(CmFlg,Cm$Eoc) then cprint (chan,'15&'12);
		  cprint(chan,"?");
		  cprint(chan,erstring(cm!err,FhSlf));
		end;
	      if jfnstack:errpop[jfnhead] then
		begin
		  if jfnok(jfnstack:ichan[jfnhead]) then
		    cfile(jfnstack:ichan[jfnhead]);
		  if jfnok(jfnstack:ochan[jfnhead]) then
		    cfile(jfnstack:ochan[jfnhead]);
		  jfnhead := jfnstack:next[jfnhead];
		  chan := jfnstack:ochan[jfnhead];
		  if cm!minor then cprint(chan,
		    ('15&'12),"?Error detected while reading commands from ",
			"external file - file aborted",('15&'12));
		  cm!abort := true;
		end;
	    end
	  else if cm!major then
	    begin
	      if isoff(CmFlg,Cm$Eoc) then cprint (chan, '15&'12);
	      cprint(chan, "?Fatal error using SAIL-COMND interface package");
	      cprint(chan,('15&'12&"?"),erstring(cm!err,FhSlf));
	      start!code
		  haltf;
	      end;
	    end
	  else
	    begin
	      if jfnstack:errpop[jfnhead] then
		begin
		  cm!abort := true;
		  if jfnok(jfnstack:ichan[jfnhead]) then
		    cfile(jfnstack:ichan[jfnhead]);
		  if jfnok(jfnstack:ichan[jfnhead]) then
		    cfile(jfnstack:ochan[jfnhead]);
		  jfnhead := jfnstack:next[jfnhead];
		end;
	      cm!fatal := true;
	    end;
	  return;
	end "err!handler";
internal string procedure cm!getatm;

	COMMENT
		This procedure returns a SAIL-type string containing the
	current contents of the cm!atom cm!buffer, with the final null character
	stripped off.
	;

	begin "getatm"
	  integer ptr,char,i;
	  string atmstr;

	  ptr := CmAbp;
	  atmstr := null;
	  while true do
	    begin "transfr"
	      char := ildb(ptr);
	      if char=0 then done;
	      atmstr := atmstr&char;
	    end "transfr";
	  return(atmstr);
	end "getatm";
internal integer procedure cm!tbuild
	(string array keys;
	 reference integer array table);

	COMMENT
		This procedure facilitates the setting up of a keyword table
	to be used with the CmKey and CmSwi COMND jsys funcion calls.  The
	procedure returns a zero if there is room in the table array to
	store the entire keyword table (including all keyword strings alligned
	on word boundaries), and -1 if not.  In the latter case the table
	will probably not be in an acceptable format for the TBLUK jsys. One
	convenient way of declaring a suitable size for the table is by using
	the size procedure (above).
		The keys parameter is a string array containing the keywords
	to be included in the table, and does not have to be alphabetized.
	Tbuild will not insert duplicate entries, and if two elements of keys
	are identical it will place the index of the last duplicate entry found
	in the cm!err variable.  Each string in keys may be prefixed by either
	or both of two punctuation characters.  If a "%" character appears
	within the first two characters of a string the Cm$Inv bit will be
	turned on for the corresponding table entry. If a "#" character is
	found the Cm$Nor bit will be turned on.  In either case the punctuation
	character will be stripped before the keyword is entered into the table.
	;

	begin "tbuild"
	  integer array tags[1:1+arrinfo(keys,2)-arrinfo(keys,1)];
	  string array copy[arrinfo(keys,1):arrinfo(keys,2)];
	  integer first,i,j,k,strip,thistag,last;
	  string trans;
	  boolean array nor,inv[arrinfo(keys,1):arrinfo(keys,2)];

	  first := arrinfo(table,1);
	  last := arrinfo(table,2);
	  for i := 1 step 1 until arrinfo(tags,2) do
	    begin "initialize"
	      thistag := (tags[i] := i+arrinfo(keys,1)-1);
	      strip := 1;
	      nor[thistag] := (inv[thistag] := true);
	      if (keys[thistag] = "#") or (keys[thistag][2 for 1] =  "#") then
		strip := strip+1 else nor[thistag] := false;
	      if (keys[thistag] = "%") or (keys[thistag][2 for 1] = "%") then
		strip := strip+1 else inv[thistag] := false;
	      copy[thistag] := keys[thistag][strip to inf]&0;
	    end "initialize";
	  table[first] := arrinfo(tags,2);
	  tagsort(copy,tags);
	  j := first+arrinfo(tags,2)+1;
	  if j > last then return (-1);
	  k := first+1;
	  for i := 1 step 1 until arrinfo(tags,2) do
	    begin "insert"
	      thistag := tags[i];
	      if i > 1 then
		if 0=compare(copy[thistag],copy[tags[i-1]]) then
		  begin "duplicate"
		    cm!err := thistag;
		    continue "insert";
		  end "duplicate";
	      table[k] := (right(location(table[j])) lsh 18) + thistag;
	      k := k+1;
	      table[j] := Cm$Fw +
			(if nor[thistag] then Cm$Nor else 0) +
			(if inv[thistag] then Cm$Inv else 0);
	      j := j+1;
	      if j > last then return(-1);
	      trans := copy[thistag];
	      while length(trans) > 0 do
		begin "transfer"
		  table[j] := cvasc(trans[1 for 5]);
		  trans := trans[6 to inf];
		  j := j+1;
		  if j > last then return(-1);
		end "transfer";
	      table[first] := table[first]+'1000000;
	    end "insert";
	  return(0);
	end "tbuild";
internal integer procedure cm!key
	(integer array table;
	 string help(null),def(null);
	 boolean sup$help(false),
		raise$input(false),
		no$indirect(false),
		wake$always(false);
	 string brchars(null));

	COMMENT
		This procedure performs the COMND jsys CmKey function for
	parsing keywords.  The keyword table is ordinarily set up by using
	the tbuild procedure. Upon successful parsing of a keyword cm!key
	returns the index of the parsed keyword in the array passed to tbuild
	containing the keyword strings.  If the input was unparsable a 0 is
	returned and the cm!err variable is set to the error condition returned
	by COMND in AC2. If reparsing is required (the user deleted into a 
	previous field) a -1 is returned, and cm!reparse is set to true.  In
	this case the entire command line must be reparsed from the beginning.

	The parameters and their defaults are as follows:

	table - Contains a keyword table in the format required by the TBLUK
		jsys. See tbuild procedure. No default.
	help  - Contains a help string to be typed when the user types a ques-
		tion mark at the keyboard. This will precede the standard help
		message if that message is not suppressed (see sup$help) -
		default is null.
	def   - Contains the default value for this field, which will be used
		if the user enters no value for this field. If def is null,
		no default value will be recognized for this field. Default
		is null string.
	sup$help - If true this will suppress the printing of the standard
		error message when the user types a question mark. Default
		is false.
	raise$input - If true the user's input will be converted to upper
		case, although he will not see the conversion. Default is false.
	no$indirect - If this is true the user will not be allowed to use an
		indirect file to supply this field value. An at-sign (@) will
		be taken as just another punctuation character. Default false.
	wake$always - If this is true each field will be parsed immediately, 
		instead of waiting for an activation character to be typed.
		This is useful for changing terminal characteristics according
		to input, e.g. turning off terminal echo before a password is
		typed in.  It requires greater overhead, however. Default false.
	brchars - An optional string of characters on which to break the
		input field.  If this is not specified it defaults to null,
		and the standard break table is used.  There is no way for
		the condition mentioned in the Monitor Calls Reference Manual
		to occur wherein the field breaks on no character, and input
		simply continues until the input buffer is full.
	;

	begin "CmKey"
	  string helpz,defz;
	  integer index,ac2;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  defz := def&0;
	  CmFnp := #CmKey+
		(if length(brchars) > 0 then Cm$Brk else 0) +
		(if length(help) > 0 then Cm$Hpp else 0)+
		(if length(def)  > 0 then Cm$Dpp else 0)+
		(if sup$help then Cm$Sdh else 0);
	  CmDat := location(table[arrinfo(table,1)]);
	  CmHlp := memloc(helpz);
	  CmDef := memloc(defz);
	  CmFlg := (CmFlg land RetFlags)+
		(if raise$input then Cm$Rai else 0)+
		(if no$indirect then Cm$Xif else 0)+
		(if wake$always then Cm$Wkf else 0);
	  if length(brchars) > 0 then
	    begin
	      make!break(0,brchars);
	      CmBrk := location(break!tables[0,0]);
	    end;
	  prepare;
	  start!code "call$CmKey"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
		movem	2,ac2;
	  end "call$CmKey";
	  if isoff(CmFlg,Cm$Nop) then
	    start!code "getindex"
		move	2,ac2;
		hrrz	1,0(2);
		movem	1,index;
	    end "getindex";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	      return(0);
	    end;
	  cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then
	    begin
	      cm!reparse := true;
	      return(-1);
	    end;
	  return(index);
	end "CmKey";
internal integer procedure cm!num
	(string help(null),def(null);
	 boolean sup$help(false);
	 integer radix(10);
	 boolean no$indirect(false),
		 wake$always(false));

	COMMENT
		This procedure will parse an integer number field.  The
	number parsed is returned.  If the field cannot be parsed then cm!err
	will be set to the error code returned in AC2, else it will be zero.
	If the command line must be reparsed, then cm!reparse will be set to
	true.  Paramters are as in the cm!key procedure, except for optional
	paramter radix, which specifies the radix from 2 to 10 in which the
	input is to be interpreted. The default is 10.
	;

	begin "CmNum"
	  string helpz,defz;
	  integer num;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  defz := def&0;
	  CmFnp := #CmNum +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if length(def)  > 0 then Cm$Dpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmDat := radix;
	  CmHlp := memloc(helpz);
	  CmDef := memloc(defz);
	  CmFlg := (CmFlg land RetFlags)+
		(if no$indirect then Cm$Xif else 0) +
		(if wake$always then cm$Wkf else 0);
	  prepare;
	  start!code "call$CmNum"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
		movem	2,num;
	  end "call$CmNum";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	      return(0);
	    end;
	  cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then 
	    begin
	      cm!reparse := true;
	      return(-1);
	    end;
	  return(num);
	end "CmNum";
internal integer procedure cm!noi
	(string noise);

	COMMENT
		This procedure will put out a guide word using the CmNoi
	function call to the COMND jsys.  The guide word is usually printed
	if the previously parsed field was terminated by an ESC and was
	recognized.  Guide words are not output if the caller hasn't started
	parsing the next field yet.
		The noise parameter is the guide word string without the
	surrounding parentheses that always accompany guide words when they
	are typed.  If the user deletes into a previous field, cm!reparse will
	be set to true before the return.
		If the guide word is parsed correctly a 1 is returned. If a
	reparse is needed, -1 is returned.  If the guide word could not be
	parsed,  0 is returned.
	;

	begin "CmNoi"
	  string noisez;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  noisez := noise&0;
	  CmFnp := #CmNoi;
	  CmDat := memloc(noisez);
	  prepare;
	  start!code "call$CmNoi"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
	  end "call$CmNoi";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err:err!handle;
	      return(0);
	    end;
	  cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then cm!reparse := true;
	  return(if cm!reparse then -1 else 1);
	end "CmNoi";
internal integer procedure cm!swi
	(integer array table;
	 string help(null),def(null);
	 boolean sup$help(false),
		 raise$input(false),
		 no$indirect(false),
		 wake$always(false);
	 string brchars(null));

	COMMENT
		This procedure performs the COMND jsys CmSwi function call
	for parsing a switch field.  All parameters and returns are exactly
	as in the cm!key procedure except that if the field is terminated
	by a colon (indicating that a value is to follow), the variable cm!colon
	will be set to true. Note that the keywords making up the keyword table
	for this call should not include slashes, although they may end in

	colons if values are desired upon recognition.
	;

	begin "CmSwi"
	  string helpz,defz;
	  integer index,ac2;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  defz := def&0;
	  CmFnp := #CmSwi +
		(if length(brchars) > 0 then Cm$Brk else 0) +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if length(def)  > 0 then Cm$Dpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmDat := location(table[arrinfo(table,1)]);
	  CmHlp := memloc(helpz);
	  CmDef := memloc(defz);
	  CmFlg := (CmFlg land RetFlags)+
		(if raise$input then Cm$Rai else 0) +
		(if no$indirect then Cm$Xif else 0) +
		(if wake$always then Cm$Wkf else 0);
	  if length(brchars) > 0 then
	    begin
	      make!break(0,brchars);
	      CmBrk := location(break!tables[0,0]);
	    end;
	  prepare;
	  start!code "call$CmSwi"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
		movem	2,ac2;
	  end "call$CmSwi";
	  if isoff(CmFlg,Cm$Nop) then
	    start!code "getindex"
		move	2,ac2;
		hrrz	1,0(2);
		movem	1,index;
	    end "getindex";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	      return(0);
	    end;
	  cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then
	    begin
	      cm!reparse := true;
	      return(-1);
	    end;
	  cm!colon := ison(CmFlg,Cm$Swt);
	  return(index);
	end "CmSwi";
internal integer procedure cm!ifi
	(string help(null),def(null);
	 boolean sup$help(false),
		 raise$input(false),
		 no$indirect(false),
		 wake$always(false));

	COMMENT
		This procedure parses an input file specification using
	the CmIfi function call of the COMND jsys.  No special options
	are permitted. The JFN of the file is returned.  Parameters are
	as in the cm!key procedure. If the field is unparsable cm!err is
	set to the error code returned in AC2. If the user deletes into
	a previous field, cm!reparse will be set to true indicating that
	a reparse is needed.
	;

	begin "CmIfi"
	  integer ac2;
	  string helpz,defz;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  defz := def&0;
	  CmFnp := #CmIfi +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if length(def)  > 0 then Cm$Dpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmDat := 0;
	  CmHlp := memloc(helpz);
	  CmDef := memloc(defz);
	  CmFlg := (CmFlg land RetFlags)+
		(if raise$input then Cm$Rai else 0) +
		(if no$indirect then Cm$Xif else 0) +
		(if wake$always then Cm$Wkf else 0);
	  prepare;
	  start!code "call$CmIfi"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
		movem	2,ac2;
	  end "call$CmIfi";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	      return(0);
	    end
	  else cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then cm!reparse := true;
	  if not cm!reparse then ac2 := setchan(ac2,cm!gtbuf[0],0);
	  return(ac2);
	end "CmIfi";
internal integer procedure cm!ofi
	(string help(null),def(null);
	 boolean sup$help(false),
		 raise$input(false),
		 no$indirect(false),
		 wake$always(false));

	COMMENT
		Same as cm!ifi, but for an output file specification.
	;

	begin "CmOfi"
	  integer ac2;
	  string helpz,defz;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  defz := def&0;
	  CmFnp := #CmOfi +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if length(def)  > 0 then Cm$Dpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmDat := 0;
	  CmHlp := memloc(helpz);
	  CmDef := memloc(defz);
	  CmFlg := (CmFlg land RetFlags)+
		(if raise$input then Cm$Rai else 0) +
		(if no$indirect then Cm$Xif else 0) +
		(if wake$always then Cm$Wkf else 0);
	  prepare;
	  start!code "call$CmOfi"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
		movem	2,ac2;
	  end "call$CmOfi";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	      return(0);
	    end
	  else cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then cm!reparse := true;
	  if not cm!reparse then ac2 := setchan(ac2,cm!gtbuf[0],0);
	  return(ac2);
	end "CmOfi";
internal integer procedure cm!fil
	(string help(null),def(null);
	 integer flag$gen('440004000000);
	 string device(null),
		directory(null),
		name(null),
		extension(null),
		protection(null),
		account(null);
	 integer jfn(0);
	 boolean sup$help(false),
		 raise$input(false),
		 no$indirect(false),
		 wake$always(false));

	COMMENT
		This procedure parses an arbitrary file specification
	using the CmFil function call of the COMND jsys.  The flag$gen
	parameter gives the contents to be set in the first word (.GTGEN)
	of the GTJFN block.  See the description of the GTJFN jsys in
	the Monitor Calls Reference Manual.  The device, directory, name,
	extension, protection and account parameters give the defaults
	which are to be given to the appropriate fields of the file
	specification.  Note that any fields present in the def parameter
	will take precedence over these parameters.  The jfn parameter
	specifies a jfn to be associated with the file.  See the GJ%JFN
	bits (9 & 10) of the .GJGEN word in the description of GTJFN in
	Monitor Calls Reference Manual.

	All other parameters are as in cm!key.  If the parse is successful,
	the SAIL channel number is returned (the file is not opened).
	Otherwise cm!err is set to the TOPS-20 error code.  If a reparse
	is needed, cm!reparse is set to true.
	;

	begin "CmFil"
	  integer ac2;
	  string helpz,defz;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  defz := def&0;
	  devz := device&0;
	  dirz := directory&0;
	  namz := name&0;
	  extz := extension&0;
	  protz := protection&0;
	  acctz := account&0;
	  CmFnp := #CmFil +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if length(def)  > 0 then Cm$Dpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmDat := 0;
	  CmHlp := memloc(helpz);
	  CmDef := memloc(defz);
	  CmFlg := (CmFlg land RetFlags)+
		(if raise$input then Cm$Rai else 0)+
		(if no$indirect then Cm$Xif else 0)+
		(if wake$always then Cm$Wkf else 0);
	  GjGen := flag$gen;
	  GjDev := if length(device) > 0 then memloc(devz) else 0;
	  GjDir := if length(directory) > 0 then memloc(dirz) else 0;
	  GjNam := if length(name) > 0 then memloc(namz) else 0;
	  GjExt := if length(extension) > 0 then memloc(extz) else 0;
	  GjPro := if length(protection) > 0 then memloc(protz) else 0;
	  GjAct := if length(account) > 0 then memloc(acctz) else 0;
	  GjJfn := jfn;
	  prepare;
	  start!code "call$CmFil"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
		movem	2,ac2;
	  end "call$CmFil";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	      return(0);
	    end
	  else cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then cm!reparse := true;
	  if not cm!reparse then ac2 := setchan(ac2,GjGen,0);
	  return(ac2);
	end "CmFil";
internal string procedure cm!fld
	(string help(null),def(null);
	 boolean raise$input(false),
		 no$indirect(false),
		 wake$always(false);
	 string brchars(null));

	COMMENT
		This procedure parses an arbitrary field up to the first non-
	alphanumeric character. Anything goes here, and the data typed, not
	including the terminator is returned by the procedure. the input
	is also available in ASCIZ form for those who want it in integer
	array cm!atom[0:99], but will remain there only until the next field is
	parsed. Parameters are as in cm!key, but note that since COMND hasn't
	the foggiest idea what you are looking for in this field, there is no
	standard help message, so no sup$help parameter.  You are free to supply
	you own help message.
	;

	begin "CmFld"
	  string helpz,defz;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  defz := def&0;
	  CmFnp := #CmFld +
		(if length(brchars) > 0 then Cm$Brk else 0) +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if length(def)  > 0 then Cm$Dpp else 0);
	  CmHlp := memloc(helpz);
	  CmDef := memloc(defz);
	  CmFlg := (CmFlg land RetFlags)+
		(if raise$input then Cm$Rai else 0) +
		(if no$indirect then Cm$Xif else 0) +
		(if wake$always then Cm$Wkf else 0);
	  if length(brchars) > 0 then
	    begin
	      make!break(0,brchars);
	      CmBrk := location(break!tables[0,0]);
	    end;
	  prepare;
	  start!code "call$CmFld"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
	  end "call$CmFld";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	      return(null);
	    end;
	  cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then
	    begin 
	      cm!reparse := true;
	      return(null);
	    end;
	  return(cm!getatm);
	end "CmFld";
internal integer procedure cm!cfm
	(string help(null);
	 boolean sup$help(false));

	COMMENT
		This procedure performs the COMND jsys CmCfm function,
	which merely waits for the user to confirm the command line by
	typing a carriage return. The parameters are as in the cm!key 
	procedure.  If proper confirmation is given a 1 is returned.  
	Otherwise, cm!err is set to the error code returned in AC2 and a 0
	is returned.  If the user deletes into a previous field, cm!reparse is
	set to true and a -1 is returned.
	;

	begin "CmCfm"
	  string helpz;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  CmFnp := #CmCfm +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmHlp := memloc(helpz);
	  prepare;
	  start!code "call$CmCfm"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
	  end "call$CmCfm";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	      return(0);
	    end;
	  cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then
	    begin
	      cm!reparse := true;
	      return(-1);
	    end;
	  return(1);
	end "CmCfm";
internal integer procedure cm!dir
	(string help(null),def(null);
	 boolean sup$help(false),
		 allow$wild(false),
		 raise$input(false),
		 no$indirect(false),
	 	 wake$always(false),
		 parse$only(false));

	COMMENT
		This procedure performs the CmDir function call of the COMND
	jsys for parsing direct names.  The 36-bit direct number associated with
	the parsed name is returned. The directory name may be obtained from 
	this using the DIRST built-in Sail function.  Parameters are as in the
	cm!key procedure, with one addition: if parse$only is true, the field
	will be parsed, but not verified.  The default is false.
		One additional feature is the allow$wild parameter which, if
	true, will allow the user to use wild card characters in the directory
	name. The default is false.
		Values returned are exactly as in the cm!usr procedure.
	;

	begin "CmDir"
	  integer direct;
	  string helpz,defz;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  defz := def&0;
	  CmFnp := #CmDir +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if length(def)  > 0 then Cm$Dpp else 0) +
		(if sup$help then Cm$Sdh else 0) +
		(if parse$only then Cm$Po else 0);
	  CmHlp := memloc(helpz);
	  CmDef := memloc(defz);
	  CmDat := (if allow$wild then Cm$Dwc else 0);
	  CmFlg := (CmFlg land RetFlags)+
		(if raise$input then Cm$Rai else 0) +
		(if no$indirect then Cm$Xif else 0) +
		(if wake$always then Cm$Wkf else 0);
	  prepare;
	  start!code "call$CmDir"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
		movem	2,direct;
	  end "call$CmDir";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	      return(0);
	    end;
	  cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then
	    begin
	      cm!reparse := true;
	      return(0);
	    end;
	  return(direct);
	end "CmDir";
internal integer procedure cm!usr
	(string help(null),def(null);
	 boolean sup$help(false),
		 raise$input(false),
		 no$indirect(false),
		 wake$always(false),
		 parse$only(false));

	COMMENT
		This procedure performs the CmUsr function call of the COMND
	jsys for parsing user names.  The 36-bit user number associated with
	the parsed name is returned.  The user name may be obtained from this
	using the DIRST built-in Sail function.  Parameters are as in the cm!key
	procedure, with one addition: if parse$only is true, the field will
	be parsed, but not verified.  The default is false.  If the field
	was unparsable (even if parse$only was true), then cm!err will contain
	the error code returned in AC2.  If a reparse is required then cm!reparse
	will be set to true.
	;

	begin "CmUsr"
	  integer user;
	  string helpz,defz;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  defz := def&0;
	  CmFnp := #CmUsr +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if length(def)  > 0 then Cm$Dpp else 0) +
		(if sup$help then Cm$Sdh else 0) +
		(if parse$only then Cm$Po else 0);
	  CmHlp := memloc(helpz);
	  CmDef := memloc(defz);
	  CmFlg := (CmFlg land RetFlags)+
		(if raise$input then Cm$Rai else 0) +
		(if no$indirect then Cm$Xif else 0) +
		(if wake$always then Cm$Wkf else 0);
	  prepare;
	  start!code "call$CmUsr"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
		movem	2,user;
	  end "call$CmUsr";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	      return(0);
	    end;
	  cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then
	    begin
	      cm!reparse := true;
	      return(0);
	    end;
	  return(user);
	end "CmUsr";
internal procedure cm!cma
	(string help(null);
	 boolean sup$help(false));

	COMMENT
		This procedure parses a comma.  Blanks can appear on either
	side of it. cm!err is set to true if a comma is not found.  cm!reparse
	is set to true if a reparse is needed.
	;

	begin "CmCma"
	  label err;
	  string helpz;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  CmFnp := #CmCma+
		   (if length(help) > 0 then Cm$Hpp else 0)+
		   (if sup$help then Cm$Sdh else 0);
	  CmDat := 0;
	  prepare;
	  start!code "call$CmCma"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
	  end "call$CmCma";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	    end
	  else cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then cm!reparse := true;
	  return;
	end "CmCma";
internal boolean procedure cm!ini
	(string prompt;
	 boolean newcomm(true));

	COMMENT
		This procedure gives a call to the COMND jsys with function
	code CmIni. This function sets up the command status block and prints
	the supplied prompt string.  This function should be used to start
	the parsing of all command lines.  If the user types a ctrl/h as the
	first character after the prompt, and the CSB is in a proper state,
	the call will automatically cause all the correct fields of the
	previous command line to be re-used, up to a bad field.  In this
	case, although no special attention is actually required in the
	following calls, the cm!ini procedure returns the value true.  Other-
	wise it returns false.  If the newcomm parameter is true, the
	entire CSB will be reset as for a new command.  This will cause the
	ctrl/h feature to fail, so this is normally not done when reinitiating
	a command line after a parse error.
	;

	begin "cmini"
	  label ctrl$h;

	  promptz := prompt&0;
	  if newcomm then
	    begin
	      CmFlg := 0;
	      CmCnt := 5*arrinfo(cm!buffer,0);
	      CmInc := 0;
	      CmAbc := 5*arrinfo(cm!atom,0);
	    end;
	  CmFlg := (CmFlg land '777777000000) lor location(ctrl$h);
	  CmFnp := #CmIni;
	  CmDat := 0;
	  CmHlp := 0;
	  CmDef := 0;
	  prepare;
	  start!code "call$cmini";
		move	1,csbad;
		move	2,fdbad;
		Comnd;
	  end "call$cmini";
	  return(false);

	  ctrl$h: return(true);
	end "cmini";
internal real procedure cm!flt
	(string help(null),def(null);
	 boolean sup$help(false),
		 no$indirect(false),
		 wake$always(false));

	COMMENT
		This procedure uses COMND to parse a real number from the
	keyboard.  Parameters are as in the cm!key procedure, but there
	is no raise$input parameter, since, of course, no alphabetic
	data is expected anyway.   Successful parsing returns the number
	typed as the value of the function.  If the field could not be
	parsed, cm!err is set to the error code returned in AC2.  If
	reparsing is needed, cm!reparse is set to true.
	;

	begin "CmFlt"
	  real num;
	  string helpz,defz;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  defz := def&0;
	  CmFnp := #CmFlt +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if length(def)  > 0 then Cm$Dpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmHlp := memloc(helpz);
	  CmDef := memloc(defz);
	  CmFlg := (CmFlg land RetFlags)+
		(if no$indirect then Cm$Xif else 0) +
		(if wake$always then Cm$Wkf else 0);
	  prepare;
	  start!code "call$CmFlt"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
		movem	2,num;
	  end "call$CmFlt";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	      return(0.0);
	    end;
	  cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then
	    begin
	      cm!reparse := true;
	      return(0.0);
	    end;
	  return(num);
	end "CmFlt";
internal integer procedure cm!dev
	(string help(null),def(null);
	 boolean sup$help(false),
		 raise$input(false),
		 no$indirect(false),
		 wake$always(false);
	 string brchars(null));

	COMMENT
		This procedure uses the CmDev function call of the COMND jsys
	to parse a device name.  Parameters are as in the cm!key procedure,
	and the procedure normally returns the device designator.  If reparsing
	is needed cm!reparse is set to true, and if the field is unparsabel as
	a device name, cm!err will contain the error code returned in AC2.
	;

	begin "CmDev"
	  integer devdeg;
	  string helpz,defz;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  defz := def&0;
	  CmFnp := #CmDev +
		(if length(brchars) > 0 then Cm$Brk else 0) +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if length(def)  > 0 then Cm$Dpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmHlp := memloc(helpz);
	  CmDef := memloc(defz);
	  CmFlg := (CmFlg land RetFlags)+
		(if raise$input then Cm$Rai else 0) +
		(if no$indirect then Cm$Xif else 0) +
		(if wake$always then Cm$Wkf else 0);
	  if length(brchars) > 0 then
	    begin
	      make!break(0,brchars);
	      CmBrk := location(break!tables[0,0]);
	    end;
	  prepare;
	  start!code "call$CmDev"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
		movem	2,devdeg;
	  end "call$CmDev";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	      return(0);
	    end;
	  cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then
	    begin
	      cm!reparse := true;
	      return(0);
	    end;
	  return(devdeg);
	end "CmDev";
internal string procedure cm!txt
	(string help(null),def(null);
	 boolean sup$help(false),
		 raise$input(false),
		 no$indirect(false),
		 wake$always(false);
	 string brchars(null));

	COMMENT
		This procedure does the CmTxt function call on the COMND jsys.
	It will return all text typed until the next carriage return. The
	text is also available in ASCIZ representation in integer array cm!atom
	[0:99], but only until the next field is parsed.  If a reparse is
	required, cm!reparse will be set to true. There is no such thing as
	not being able to parse this field.
	;

	begin "CmTxt"
	  string helpz,defz;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  defz := def&0;
	  CmFnp := #CmTxt +
		(if length(brchars) > 0 then Cm$Brk else 0) +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if length(def)  > 0 then Cm$Dpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmHlp := memloc(helpz);
	  CmDef := memloc(defz);
	  CmFlg := (CmFlg land RetFlags)+
		(if raise$input then Cm$Rai else 0) +
		(if no$indirect then Cm$Xif else 0) +
		(if wake$always then Cm$Wkf else 0);
	  if length(brchars) > 0 then
	    begin
	      make!break(0,brchars);
	      CmBrk := location(break!tables[0,0]);
	    end;
	  prepare;
	  start!code "call$CmTxt"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
	  end "call$CmTxt";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	      return(null);
	    end;
	  cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then
	    begin
	      cm!reparse := true;
	      return(null);
	    end;
	  return(cm!getatm);
	end "CmTxt";
internal integer procedure cm!tad
	(string help(null),def(null);
	 boolean sup$help(false),
		 date(true),time(true),
		 no$convert(false),
		 raise$input(false),
		 no$indirect(false),
		 wake$always(false));

	COMMENT
		This procedure does the CmTad function call of the COMND
	jsys, which parses a date and/or time.  If date is true, the date
	is parsed, and if time is true, the time is parsed.  Both default 
	to true.  If no$convert is false (the default), then the date/time
	is returned in internal format.  Otherwise a zero is returned, and
	the date and time information are stored in integer array cm!datime
	(dimensioned [2:4] so as to agree with accumulator assignments in
	the IDTNC monitor call return).  cm!datime[2] contains the year
	in the left half and the month (0=Jan) in the right half. cm!datime[3]
	contains the day of the month (0=first day) in the left half and
	the day of the week (0=Mon) in the right half. The right half of
	cm!datime[4] contains the time as seconds from midnight, and the left
	half contains the following flag bits:

		B0	- on if a time zone was input
		B1	- on if daylight savings time was input
		B2	- on if a time zone was input
		B3	- on if a number in Julian day format was input
		B12-B17 - time zone if one was specified or the local time
			  if none was specified.

	;

	begin "CmTad"
	  string helpz,defz;
	  integer intern;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  defz := def&0;
	  CmFnp := #CmTad +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if length(def)  > 0 then Cm$Dpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmFlg := (CmFlg land RetFlags)+
		(if raise$input then Cm$Rai else 0) +
		(if no$indirect then Cm$Xif else 0) +
		(if wake$always then Cm$Wkf else 0);
	  CmHlp := memloc(helpz);
	  CmDef := memloc(defz);
	  CmDat := (if date then Cm$Ida else 0) +
		(if time then Cm$Itm else 0) +
		(if no$convert then Cm$Nci else 0) +
		location(cm!datime[2]);
	  prepare;
	  start!code "call$CmTad"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
		movem	2,intern;
	  end "call$CmTad";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	      return(0);
	    end;
	  cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then
	    begin
	      cm!reparse := true;
	      return(0);
	    end;
	  return(if no$convert then 0 else intern);
	end "CmTad";
internal string procedure cm!qst
	(string help(null),def(null);
	 boolean sup$help(false),
		 raise$input(false),
		 no$indirect(false),
		 wake$always(false));

	COMMENT
		This procedure does the CmQst function call to the COMND
	jsys.  It returns the contents of a quoted string (not included
	the double quotes which must delimit the string).  This is useful
	for obtaining strings which may include action characters (ESC, ?,
	^F). A carriage return is an illegal character and will cause cm!err
	to be set.  A double quote may be entered in the string as two con-
	secutive double quotes.
	;

	begin "CmQst"
	  string helpz,defz;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  defz := def&null;
	  CmFnp := #CmQst +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if length(def)  > 0 then Cm$Dpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmFlg := (CmFlg land RetFlags)+
		(if raise$input then Cm$Rai else 0) +
		(if no$indirect then Cm$Xif else 0) +
		(if wake$always then Cm$Wkf else 0);
	  CmHlp := memloc(helpz);
	  CmDef := memloc(defz);
	  prepare;
	  start!code "call$CmQst"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
	  end "call$CmQst";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	      return(null);
	    end;
	  cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then 
	    begin
	      cm!reparse := true;
	      return(null);
	    end;
	  return(cm!getatm);
	end "CmQst";
internal string procedure cm!uqs
	(string brchars,
	 	help(null),def(null);
	 boolean raise$input(false),
		 no$indirect(false),
		 wake$always(false));

	COMMENT
		This procedure executes the CMUQS function call of the COMND
	jsys.  It is used for parsing a field with arbitrary break characters.
	The characters to be used as break characters are supplied in the 
	string parameter brchars.  The procedure will return all characters 
	typed up to, but not including the first of these characters typed. 
	Note that in this call all action characters lose their significance
	unless they are included in the brchars string.
	;

	begin "CmUqs"
	  integer ptr;
	  string result,helpz,defz;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  defz := def&0;

	  CmFnp := #CmUqs+
		 (if length(help) > 0 then Cm$Hpp else 0)+
		 (if length(def)  > 0 then Cm$Dpp else 0);
	  CmFlg := (CmFlg land RetFlags)+
		 (if raise$input then Cm$Rai else 0)+
		 (if no$indirect then Cm$Xif else 0)+
		 (if wake$always then Cm$Wkf else 0);
	  make!break(0,brchars);
	  CmDat := location(break!tables[0,0]);
	  ptr := CmPtr;
	  prepare;
	  start!code "call$CmUqs"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
	  end "call$CmUqs";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	      return(null);
	    end;
	  if ison(CmFlg,Cm$Rpt) then
	    begin
	      cm!reparse := true;
	      return(null);
	    end;
	  result := null;
	  while ptr neq CmPtr do
	    result := result&ildb(ptr);
	  return(result);
	end "CmUqs";
internal boolean procedure cm!tok
	(string token,
	        help(null),def(null);
	 boolean sup$help(false),
		 raise$input(false),
		 no$indirect(false),
		 wake$always(false));

	COMMENT
		This procedure performst the CMTOK function call of the COMND
	jsys.  It returns true if what is typed by the user matches the token
	parameter, false otherwise.
	;

	begin "CmTok"
	  string tokenz,helpz,defz;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  defz := def&0;
	  tokenz := token&0;
	  CmFnp := #CmTok +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if length(def)  > 0 then Cm$Dpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmHlp := memloc(helpz);
	  CmDef := memloc(defz);
	  CmDat := memloc(tokenz);
	  CmFlg := (CmFlg land RetFlags)+
		(if raise$input then Cm$Rai else 0) +
		(if no$indirect then Cm$Xif else 0) +
		(if wake$always then Cm$Wkf else 0);
	  prepare;
	  start!code "call$CmTok"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump '16,err;
		setom	minor;
	  end "call$CmTok";
	  cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then
	    begin
	      cm!reparse := true;
	      return(false);
	    end;
	  if ison(CmFlg,Cm$Nop) then return(false)
	  else return(true);
	  err: err!handle;
	  return(false);
	end "CmTok";
internal integer procedure cm!nux
	(string help(null),def(null);
	 boolean sup$help(false);
	 integer radix(10);
	 boolean no$indirect(false),
		 wake$always(false));

	COMMENT
		This procedure will parse an integer number field.  The 
	difference between cm!nux and cm!num is that cm!nux will terminate
	on the first non-numeric character, without giving a minor error if
	that character is not one of the valid terminators for cm!num.  The
	number parsed is returned.  If the field cannot be parsed then cm!err
	will be set to the error code returned in AC2, else it will be zero.
	If the command line must be reparsed, then cm!reparse will be set to
	true.  Paramters are as in the cm!key procedure, except for optional
	paramter radix, which specifies the radix from 2 to 10 in which the
	input is to be interpreted. The default is 10.
	;

	begin "CmNux"
	  string helpz,defz;
	  integer num;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  defz := def&0;
	  CmFnp := #CmNux +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if length(def)  > 0 then Cm$Dpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmDat := radix;
	  CmHlp := memloc(helpz);
	  CmDef := memloc(defz);
	  CmFlg := (CmFlg land RetFlags)+
		(if no$indirect then Cm$Xif else 0) +
		(if wake$always then cm$Wkf else 0);
	  prepare;
	  start!code "call$CmNux"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
		movem	2,num;
	  end "call$CmNux";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	      return(0);
	    end;
	  cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then 
	    begin
	      cm!reparse := true;
	      return(0);
	    end;
	  return(num);
	end "CmNux";
internal string procedure cm!act
	(string help(null),def(null);
	 boolean sup$help(false),
		 raise$input(false),
		 no$indirect(false),
		 wake$always(false));

	COMMENT
		This procedure does the CmAct function call of the COMND
	jsys.  It returns the account string up to, but not including, the
	first non-alphanumeric character typed.  No verification is done,
	so cm!err is never set.  cm!reparse is set to true if a reparse is
	needed.
	;

	begin "CmAct"
	  string helpz,defz;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  defz := def&0;
	  CmFnp := #CmAct +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if length(def)  > 0 then Cm$Dpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmFlg := (CmFlg land RetFlags)+
		(if raise$input then Cm$Rai else 0) +
		(if no$indirect then Cm$Xif else 0) +
		(if wake$always then Cm$Wkf else 0);
	  CmHlp := memloc(helpz);
	  CmDef := memloc(defz);
	  prepare;
	  start!code "call$CmAct"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
	  end "call$CmAct";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	      return(null);
	    end;
	  cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then
	    begin
	      cm!reparse := true;
	      return(null);
	    end;
	  return (cm!getatm);
	end "CmAct";
internal string procedure cm!nod
	(string help(null),def(null);
	 boolean sup$help(false),
	 	 no$indirect(false),
		 wake$always(false));

	COMMENT -
		This procedure performs the CMNOD function of the COMND jsys.
	It parses a network node name.  A node name consists of 1 to 6 alpha-
	numeric characters.  Lowercase characters are always converted to upper
	case (hence no raise$input parameter).  The node name, as delimited
	by the first non-alphanumeric character, is returned as the value of
	the function.  No verification is done to ensure that the named node
	actually exists.  If a reparse is needed, the variable cm!reparse
	will be set to true.  Any errors will be returned in the variable
	cm!err, which will otherwise be zero.
	;

	begin "CmNod"
	  string helpz,defz;
	  label err;

	  minor := cm!fatal := cm!eof := cm!abort := cm!err := cm!reparse := false;
	  helpz := help&0;
	  defz := def&0;
	  CmFnp := #CmNod +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if length(def)  > 0 then Cm$Dpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmHlp := memloc(helpz);
	  CmDef := memloc(defz);
	  CmFlg := (CmFlg land RetFlags)+
		(if no$indirect then Cm$Xif else 0) +
		(if wake$always then Cm$Wkf else 0);
	  prepare;
	  start!code "call$CmNod"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
	  end "call$CmNod";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	      return(null);
	    end;
	  cm!err := 0;
	  if ison(CmFlg,Cm$Rpt) then
	    begin
	      cm!reparse := true;
	      return(null);
	    end;
	  return(cm!getatm);
	end "CmNod";
internal procedure cm#reset;

	COMMENT - 
		This procedure resets the multiple fdb block by setting
	the level indicator to zero and zeroing the cm#fdb array.
	;

	begin "cm#reset"
	  integer i,j;

	  cm#level := 0;
	  for i := 1 step 1 until 10 do
	    for j := 0 step 1 until 3 do
	      cm#fdb[i,j] := 0;
	end "cm#reset";
internal integer procedure cm#call
	(string def(null);
	 boolean raise$input(false),
		 no$indirect(false),
		 wake$always(false));

	COMMENT
		This procedure makes a call to COMND using the multiple fdb
	blocks, which should previously have been set up using the various
	cm#... procedures.  If the blocks have not been set up since the
	last call to cm#reset, the procedure returns -1. Otherwise, it returns
	the position in the cm#fdb array corresponding to the function that
	was actually used.  This would also correspond to the order in which
	you called the cm#... procedures.  For instance, supposed you set
	up the fdb blocks by calling first cm#key, then cm#cma, then cm#num,
	then cm#flt.  Then if cm#call returned 3 as a value, it would mean
	that the user had typed in an integer, since cm#num was the call that
	eventually succeeded.  A zero is returned if all functions fail.
		The value actually returned by the succeeding function can be
	found in either cm#int, cm#real, or cm#str, according to whether
	that value is supposed to be an integer, real or string value, 
	respectively.  It is up to the user program to find the correct
	value on the basis of which function succeeded.
	;

	begin "cm#call"
	  label err;
	  integer i,loc,row,fnc,ptr;
	  string defz;

	  if cm#level=0 then return(-1);
	  minor := cm!fatal := cm!eof := cm!abort :=  cm!err := cm!reparse := false;
	  defz := def&0;
	  if length(def) > 0 then cm#fdb[1,0] := cm#fdb[1,0] lor Cm$Dpp;
	  cm#str := null;
	  CmFlg := (CmFlg land RetFlags)+
		(if raise$input then Cm$Rai else 0)+
		(if no$indirect then Cm$Xif else 0)+
		(if wake$always then Cm$Wkf else 0);
	  for i := 1 step 1 while i < cm#level do
	    begin
	      cm#fdb[i,0] := cm#fdb[i,0]+right(location(cm#fdb[i+1,0]));
	      cm#fdb[i,2] := memloc(cm#hlp[i]);
	    end;
	  CmMHlp := memloc(cm#hlp[cm#level]);
	  CmMDef := memloc(defz);
	  ptr := CmPtr;
	  prepare;
	  fdbad := location(cm#fdb[1,0]);
	  start!code "call$mult"
		move	1,csbad;
		move	2,fdbad;
		Comnd;
		jump	'16,err;
		setom	minor;
		movem	2,cm#int;
		movem	2,cm#real;
		hrrzm	3,loc;
	  end "call$mult";
	  if ison(CmFlg,Cm$Nop) then
	    begin
	      err: err!handle;
	    end;
	  if ison(CmFlg,Cm$Rpt) then cm!reparse := true;
	  if ison(CmFlg,Cm$Rpt lor Cm$Nop) then
	    begin
	      cm#int := 0;
	      cm#real := 0.0;
	      return(0);
	    end;
	  row := 1+((loc-location(cm#fdb[1,0])) div 4);
	  fnc := cm#fdb[row,0] land '777000000000;
	  if fnc = #CmTok then cm#int := true;
	  if (fnc = #CmKey) or (fnc = #CmSwi) then
	    start!code "getindex"
		move	2,cm#int;
		hrrz	1,0(2);
		movem	1,cm#int;
	    end "getindex";
	  if fnc = #CmSwi then cm!colon := ison(CmFlg,Cm$Swt);
	  if fnc = #CmFlt then cm#int := 0 else cm#real := 0;
	  if (fnc = #CmFld) or (fnc = #CmTxt) or (fnc = #CmAct)  or
	     (fnc = #CmNod) or (fnc = #CmQst) then
	    begin
	      cm#int := 0;
	      cm#str := cm!getatm;
	    end;
	  if fnc = #CmUqs then
	    begin "Get uqs string"
	      cm#int := 0;
	      while ptr neq CmPtr do
		cm#str := cm#str & ildb(ptr);
	    end "Get uqs string";
	  if (fnc = #CmIfi) or (fnc = #CmOfi) or (fnc = #CmFil) then
	    cm#int := setchan(cm#int,cm!gtbuf[0],0);
	  if fnc = #CmCma then cm#int := 0;
	  if (fnc = #CmCfm) or (fnc = #CmNoi) then cm#int := 1;
	  if fnc = #CmTad and ison(cm#fdb[row,1],Cm$Nci) then cm#int := 0;
	  return(row);
	end "cm#call";
internal integer procedure cm#key
	(integer array table;
	 string help(null);
	 boolean sup$help(false);
	 string brchars(null));

	COMMENT
		This procedure is the multiple fdb counterpart to cm!key
	;

	begin "CmMKey"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  make!break(cm#level,brchars);
	  CmMBrk := location(break!tables[cm#level,0]);
	  CmMFnp := #CmKey+
		(if length(brchars) > 0 then Cm$Brk else 0) +
		(if length(help) > 0 then Cm$Hpp else 0)+
		(if sup$help then Cm$Sdh else 0);
	  CmMDat := location(table[arrinfo(table,1)]);
	  return(0);
	end "CmMKey";
internal integer procedure cm#cfm
	(string help(null);
	 boolean sup$help(false));

	COMMENT
		This procedure is the multiple fdb counterpart to cm!cfm
	;

	begin "CmMCfm"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  CmMFnp := #CmCfm +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  return(0);
	end "CmMCfm";
internal integer procedure cm#num
	(string help(null);
	 boolean sup$help(false);
	 integer radix(10));

	COMMENT
		This is the multiple fdb counterpart to cm!num
	;

	begin "CmMNum"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  CmMFnp := #CmNum +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmMDat := radix;
	  return(0);
	end "CmMNum";
internal integer procedure cm#noi
	(string noise);

	COMMENT
		This is the multiple fdb counterpart to cm!noi
	;

	begin "CmMNoi"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#nze[cm#level] := noise&0;
	  CmMFnp := #CmNoi;
	  CmMDat := memloc(cm#nze[cm#level]);
	  return(0);
	end "CmMNoi";
internal integer procedure cm#ifi
	(string help(null);
	 boolean sup$help(false));

	COMMENT
		This is the multiple fdb counterpart to cm!ifi
	;

	begin "CmMIfi"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  CmMFnp := #CmIfi +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmMDat := 0;
	  return(0);
	end "CmMIfi";
internal integer procedure cm#ofi
	(string help(null);
	 boolean sup$help(false));

	COMMENT
		This is the multiple fdb counterpart to cm!ofi
	;

	begin "CmMOfi"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  CmMFnp := #CmOfi +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmMDat := 0;
	  return(0);
	end "CmMOfi";
internal integer procedure cm#cma
	(string help(null);
	 boolean sup$help(false));

	COMMENT
		This is the multiple fdb counterpart to cm!cma
	;

	begin "CmMCma"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  CmMFnp := #CmCma +
		    (if length(help) > 0 then Cm$Hpp else 0)+
		    (if sup$help then Cm$Sdh else 0);
	  CmMDat := 0;
	  return(0);
	end "CmMCma";
internal integer procedure cm#swi
	(integer array table;
	 string help(null);
	 boolean sup$help(false);
	 string brchars(null));

	COMMENT
		This is the multiple fdb counterpart to cm!swi
	;

	begin "CmMSwi"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  make!break(cm#level,brchars);
	  CmMBrk := location(break!tables[cm#level,0]);
	  CmMFnp := #CmSwi +
		(if length(brchars) > 0 then Cm$Brk else 0) +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmMDat := location(table[arrinfo(table,1)]);
	  return(0);
	end "CmMSwi";
internal integer procedure cm#fld
	(string help(null),
		brchars(null));

	COMMENT
		This is the multiple fdb counterpart to cm!fld
	;

	begin "CmMFld"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  make!break(cm#level,brchars);
	  CmMBrk := location(break!tables[cm#level,0]);
	  CmMFnp := #CmFld +
		(if length(brchars) > 0 then Cm$Brk else 0) +
		(if length(help) > 0 then Cm$Hpp else 0);
	  return(0);
	end "CmMFld";
internal integer procedure cm#usr
	(string help(null);
	 boolean sup$help(false),
		 parse$only(false));

	COMMENT
		This is the multiple fdb counterpart to cm!usr
	;

	begin "CmMUsr"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  CmMFnp := #CmUsr +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if sup$help then Cm$Sdh else 0) +
		(if parse$only then Cm$Po else 0);
	  return(0);
	end "CmMUsr";
internal integer procedure cm#dir
	(string help(null);
	 boolean sup$help(false),
		 allow$wild(false),
		 parse$only(false));

	COMMENT
		This is the multiple fdb counterpart to cm!dir
	;

	begin "CmMDir"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  CmMFnp := #CmDir +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if sup$help then Cm$Sdh else 0) +
		(if parse$only then Cm$Po else 0);
	  CmMDat := (if allow$wild then Cm$Dwc else 0);
	  return(0);
	end "CmMDir";
internal integer procedure cm#flt
	(string help(null);
	 boolean sup$help(false));

	COMMENT
		This is the multiple fdb counterpart to cm!flt
	;

	begin "CmMFlt"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  CmMFnp := #CmFlt +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  return(0);
	end "CmMFlt";
internal integer procedure cm#dev
	(string help(null);
	 boolean sup$help(false);
	 string brchars(null));

	COMMENT
		This is the multiple fdb counterpart to cm!dev
	;

	begin "CmMDev"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  make!break(cm#level,brchars);
	  CmMBrk := location(break!tables[cm#level,0]);
	  CmMFnp := #CmDev +
		(if length(brchars) > 0 then Cm$Brk else 0) +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  return(0);
	end "CmMDev";
internal integer procedure cm#txt
	(string help(null);
	 boolean sup$help(false);
	 string brchars(null));

	COMMENT
		This is the multiple fdb counterpart to cm!txt
	;

	begin "CmMTxt"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  make!break(cm#level,brchars);
	  CmMBrk := location(break!tables[cm#level,0]);
	  CmMFnp := #CmTxt +
		(if length(brchars) > 0 then Cm$Brk else 0) +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  return(0);
	end "CmMTxt";
internal integer procedure cm#act
	(string help(null);
	 boolean sup$help(false));

	COMMENT
		This is the multiple fdb counterpart to cm!act
	;

	begin "CmMAct"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  CmMFnp := #CmAct +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  return(0);
	end "CmMAct";
internal integer procedure cm#tok
	(string token,
	        help(null);
	 boolean sup$help(false));

	COMMENT
		This is the multiple fdb counterpart to cm!tok;

	begin "CmMTok"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  cm#token[cm#level] := token&0;
	  CmMDat := memory[location(cm#token[cm#level])];
	  CmMFnp := #CmTok +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  return(0);
	end "CmMTok";
internal integer procedure cm#fil
	(string help(null);
	 integer flag$gen('440004000000);
	 string device(null),
		directory(null),
		name(null),
		extension(null),
		protection(null),
		account(null);
	 integer jfn(0);
	 boolean sup$help(false));

	COMMENT
		This is the multiple fdb counterpart to cm!tok;

	begin "CmMFil"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  devz := device&0;
	  dirz := directory&0;
	  namz := name&0;
	  extz := extension&0;
	  protz := protection&0;
	  acctz := account&0;
	  CmMFnp := #CmFil +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  CmMDat := 0;
	  GjGen := flag$gen;
	  GjDev := if length(device) > 0 then memloc(devz) else 0;
	  GjDir := if length(directory) > 0 then memloc(dirz) else 0;
	  GjNam := if length(name) > 0 then memloc(namz) else 0;
	  GjExt := if length(extension) > 0 then memloc(extz) else 0;
	  GjPro := if length(protection) > 0 then memloc(protz) else 0;
	  GjAct := if length(account) > 0 then memloc(acctz) else 0;
	  GjJfn := jfn;
	  return(0);
	end "CmMFil";
internal integer procedure cm#nod
	(string help(null);
	 boolean sup$help(false));

	COMMENT
		This is the multiple fdb counterpart to cm!nod;

	begin "CmMNod"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  CmMDat := 0;
	  CmMFnp := #CmNod +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  return(0);
	end "CmMNod";
internal integer procedure cm#nux
	(string help(null);
	 boolean sup$help(false);
	 integer radix(10));

	COMMENT
		This is the multiple fdb counterpart to cm!nux;

	begin "CmMNux"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  CmMDat := radix;
	  CmMFnp := #CmNux +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  return(0);
	end "CmMNux";
internal integer procedure cm#tad
	(string help(null);
	 boolean sup$help(false),
	 date(true),time(true),
	 no$convert(false));

	COMMENT
		This is the multiple fdb counterpart to cm!tad;

	begin "CmMTad"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  CmMDat := (if date then Cm$Ida else 0) +
		(if time then Cm$Itm else 0) +
		(if no$convert then Cm$Nci else 0) +
		location(cm!datime[2]);
	  CmMFnp := #CmTad +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  return(0);
	end "CmMTad";
internal integer procedure cm#uqs
	(string brchars,help(null));	 	 

	COMMENT
		This is the multiple fdb counterpart to cm!uqs;

	begin "CmMUqs"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help & 0;
	  make!break(cm#level,brchars);
	  CmMDat := location(break!tables[cm#level,0]);
	  CmMFnp := #CmUqs +
		(if length(help) > 0 then Cm$Hpp else 0);
	  return(0);
	end "CmMUqs";
internal integer procedure cm#qst
	(string help(null);
	 boolean sup$help(false));

	COMMENT
		This is the multiple fdb counterpart to cm!qst;

	begin "CmMQst"
	  if cm#level = 10 then return(-1);
	  cm#level := cm#level+1;
	  cm#hlp[cm#level] := help&0;
	  CmMDat := 0;
	  CmMFnp := #CmQst +
		(if length(help) > 0 then Cm$Hpp else 0) +
		(if sup$help then Cm$Sdh else 0);
	  return(0);
	end "CmMQst";

end "comnd"